diff --git a/.github/actions/get-gtest/action.yml b/.github/actions/get-gtest/action.yml index 1df1052285d..5de2b10cd32 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 ab0927919db..faedcc18807 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 @@ -56,8 +56,14 @@ runs: - name: 'Build JTReg' run: | + # If runner architecture is x64 set JAVA_HOME_17_X64 otherwise set to JAVA_HOME_17_arm64 + if [[ '${{ runner.arch }}' == 'X64' ]]; then + JDK="$JAVA_HOME_17_X64" + else + JDK="$JAVA_HOME_17_arm64" + fi # Build JTReg and move files to the proper locations - bash make/build.sh --jdk "$JAVA_HOME_11_X64" + bash make/build.sh --jdk "$JDK" mkdir ../installed mv build/images/jtreg/* ../installed working-directory: jtreg/src diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index b03fcb144fd..c3d499a32a0 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 9e2f11eb4d3..07c47c9d592 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 @@ -223,7 +223,8 @@ jobs: 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' @@ -234,8 +235,8 @@ jobs: 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: '14.3.1' configure-arguments: ${{ github.event.inputs.configure-arguments }} make-arguments: ${{ github.event.inputs.make-arguments }} if: needs.select.outputs.macos-aarch64 == 'true' @@ -298,7 +299,17 @@ jobs: with: platform: macos-x64 bootjdk-platform: macos-x64 - runs-on: macos-11 + runs-on: macos-13 + + 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 test-windows-x64: name: windows-x64 @@ -333,26 +344,23 @@ jobs: - 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')" + 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 6889a5b0237..a8885866c12 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 @@ -147,7 +147,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_14.3.1.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 c39a4fe49e6..046197ae090 100644 --- a/.jcheck/conf +++ b/.jcheck/conf @@ -1,10 +1,11 @@ [general] project=jdk-updates jbs=JDK -version=17.0.12 +version=17.0.13 [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/doc/building.html b/doc/building.html index 31044787bba..bcc81dd3d38 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 6a8eabfc7b5..150a3306c29 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 a64a891b5b8..862b207a2d5 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -67,7 +67,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.

diff --git a/doc/testing.md b/doc/testing.md index 3cdc0d662d6..bddc6ba3ee9 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 diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 52baed8af93..173d533359d 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 f5ce6d27992..f7f2ad53000 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/lib-tests.m4 b/make/autoconf/lib-tests.m4 index 69ea97ddf4a..93a2b30dc7e 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/conf/github-actions.conf b/make/conf/github-actions.conf index 1d2c643d0d4..28323491481 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 -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_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_AARCH64_BOOT_JDK_EXT=tar.gz +MACOS_AARCH64_BOOT_JDK_URL=https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.11%2B9/OpenJDK17U-jdk_aarch64_mac_hotspot_17.0.11_9.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=09a162c58dd801f7cfacd87e99703ed11fb439adc71cfa14ceb2d3194eaca01c + +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 diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 4ee43abe774..64dcb7723a9 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 63ce4fe97be..d5ba88b6955 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=17 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=12 +DEFAULT_VERSION_UPDATE=13 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=2024-10-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/ssltlsrootecc2022 b/make/data/cacerts/ssltlsrootecc2022 new file mode 100644 index 00000000000..706e6aefb4e --- /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 00000000000..ad456b0b5f4 --- /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 02feba420c8..05bb2c6db95 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 26f4aa24d88..550662ec38a 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 4737c50e425..3079d77ed8b 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-06-14 %% 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 @@ -48003,7 +48009,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 +48387,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/devkit/createJMHBundle.sh b/make/devkit/createJMHBundle.sh index b2b10769d15..889b7f914a4 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 65edd047571..230aa54f73a 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 @@ -151,6 +151,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 0c292ad866c..943e8dcbb8c 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/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk index 700ddefda49..1ab3846c143 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 bb09d8cf8a2..c1db028fff8 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 4b1c14a1133..b7cf961a847 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 46e3b1677f7..bf6a987149a 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 @@ -767,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), \ @@ -828,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/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 1fea866cc1d..e755d4cee88 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 a35b8b84481..7abb2205414 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 3f6ebb41f2a..9b1c0a93539 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/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index d5a5503213c..66b2769aca5 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/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 039e9c17b46..0d53be49de5 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -212,10 +212,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 +237,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 diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 3f186f56e75..46c77e48b8f 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 acef8d21abc..11aba1d339b 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 d9ade024b91..f40595ca62f 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/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index d26e3c883e7..87ceeeda94a 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 diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index b05646ac20c..b24778aee30 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -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 e8a61efe992..d6c6a5d01ef 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); @@ -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 da4a40ba3f9..0422d51821a 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -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 88c2b0c6641..7bad3b11064 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -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 dcc48a6c1f0..78372e1408c 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 09322205f06..30032260469 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 c6181f2d007..0d17756a30d 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 461b7fc874f..fbc77f873eb 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 32e014059da..3744f49a65e 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 b8702c5aa76..a79df5dbdd4 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 071045c6a6e..65101650372 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,25 @@ 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; - -#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 + rlim_t rlim_original = nbr_files.rlim_cur; + + // 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, so try the max value + // that the system claims can be used first, same as other BSD OSes. + // However, some terminals (ksh) will internally use "int" type + // to store this value and since RLIM_INFINITY overflows an "int" + // we might end up with a negative value, so cap the system limit max + // at INT_MAX instead, just in case, for everyone. + nbr_files.rlim_cur = MIN(INT_MAX, 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 e69bfc79558..546cef8edc5 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 0ce76721ec3..ecdd21a4869 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/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 63b7562f118..7c951cee51c 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); @@ -2745,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; } @@ -3589,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; } @@ -4992,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) { @@ -5036,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 @@ -5055,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) { @@ -5067,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 7c42379a0a7..c37e25c42eb 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 370396bb9c6..4307a189edf 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; } diff --git a/src/hotspot/os/posix/os_posix.hpp b/src/hotspot/os/posix/os_posix.hpp index c1f9601cff9..261921adec4 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 5d3416e2869..5652802aac2 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 d3d359e90c2..c4b82d4f6a1 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 @@ -4842,8 +4843,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) { 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 f8e6787505b..f3b80be3c02 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/share/adlc/adlc.hpp b/src/hotspot/share/adlc/adlc.hpp index 19567f05d40..ec3a0c4e153 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 283713bb1f8..bcbf3ab7e1b 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 cd9aab9e2ed..1d83cb18117 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 5abc4365297..b277c5c63f0 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 854b1b310e4..b45f432dac9 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 6f6c1bc6e30..5ffd258dc96 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 0a183ab2602..5c7d7ab7e31 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/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index fb178432d8b..14cf71eb09a 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 242b57ee579..a045b18e41c 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/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp index 72a1e78838f..c1fb1d68f4b 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 e5b51ac277c..e4b10f95656 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 bf817989dc1..cac737125f1 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 b8cccbb934a..8df69fd6496 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -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; diff --git a/src/hotspot/share/classfile/symbolTable.hpp b/src/hotspot/share/classfile/symbolTable.hpp index a0b7287f936..8441a52713e 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 590cce18ba2..5ad53e9eca1 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 bca8b7dc9d0..f4753fd6cff 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 306280dfc43..bb597e50106 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 eef8e6b4bbb..f5861d1f3ed 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 026915246cc..d40a0183415 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 db5f2b5e42f..f54308944e2 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 1aca61234e5..9d045d4d900 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 c9bf3015dfc..16feef2e6b1 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 eb3586231bd..8acbaae9bb5 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 f8acabf6be6..62d5cc3b257 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 0bbd1d9661f..c3ecf98efab 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 9ebc1dd57a8..4b4c6e104b7 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 f97727c8bc8..e5817ca6f5c 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 c39514922fc..1b058e2dd29 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 c4011fff4e5..ef14ad53aa0 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 c1a10fd2b69..c0ed793fb67 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/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp index a68a3604a6a..1167a013e2c 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 4bcff174b5f..000700afde6 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 3f3d11393d8..e76541483e5 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 ce37d4ede93..9e48ea7c77b 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 3b167d752e7..56762d9e5eb 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/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp index ebc288efda9..d26e085a4b2 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 1230030da64..ab80fd59390 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 817f43e6932..9d49d4ca4dd 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 d0cc794eeaa..2d2c86d1dfd 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 67ea25d31fa..44443fea52e 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); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index e0f5b9ee71b..6c1f08096fe 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 ba67517fe26..3b4045666de 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 f18d1e4153e..62c3495c57b 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 9db212bd4c2..460193827e0 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 830c015cc39..4070a68e1f9 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 b93bb30f0c8..3fc57c76903 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/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 8e2a1060528..ed5144d4bfa 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/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 5ebb5502b0b..2b3cc8812b5 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 @@ - + diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index affd8849245..dc59166bb51 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 78d6ab48f97..dc2add22020 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 5eb70fb23d2..cad6f6bc079 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 29bade3def6..9b7ddcd1273 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 c7cd5a4d2dd..a611707462c 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 0627593611c..657c5645ae7 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 82f3362d6ee..1e940ef6f97 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 132295230ac..367b4714406 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 f8900a13b4d..52c69473a14 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 41478060acc..82fc404c166 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/memory/iterator.inline.hpp b/src/hotspot/share/memory/iterator.inline.hpp index cc339f2213d..428a057797f 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 1e897615eaf..d1882c70e2c 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 066d36279a6..1c474eb9b52 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/metaprogramming/conditional.hpp b/src/hotspot/share/metaprogramming/conditional.hpp deleted file mode 100644 index 2d4fe1a86e8..00000000000 --- 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 4dff0500f13..00000000000 --- 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 ce22849e3be..00000000000 --- 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/isFloatingPoint.hpp b/src/hotspot/share/metaprogramming/isFloatingPoint.hpp deleted file mode 100644 index cbc2d838fe6..00000000000 --- 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 cbae6bd5efc..00000000000 --- 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/isSigned.hpp b/src/hotspot/share/metaprogramming/isSigned.hpp deleted file mode 100644 index f04065d2180..00000000000 --- 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/removeCV.hpp b/src/hotspot/share/metaprogramming/removeCV.hpp deleted file mode 100644 index 0c0b2f47fa7..00000000000 --- 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/removePointer.hpp b/src/hotspot/share/metaprogramming/removePointer.hpp deleted file mode 100644 index be0f5b6e6fb..00000000000 --- 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 274c327a0ad..00000000000 --- 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 4fa92c53c16..d267e217734 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 1dacb48427f..677af8115c3 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 21bcff9113d..f7a3462f59a 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 631afa0d321..74d902c86f0 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 fdee66a8866..2e9a5125014 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -3941,18 +3941,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 +4027,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 +4137,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 23b773d0195..9f03bc50d48 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 797cd398093..a597f76a2e6 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/oopHandle.hpp b/src/hotspot/share/oops/oopHandle.hpp index fd4c9679219..96cbf609e5e 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 a7f8d5ba653..7bbfd558472 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/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 837bbd91013..4025b78ade2 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 fa793454e83..f2d259131ba 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 734eba0000e..0c66de7f1fc 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 d0d09ce6190..4a61e953d5c 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 612b62186b9..61df334fcc6 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/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index c7cfbf0ce93..ecea558f5e9 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 742e88e0a70..4c0b8bd4b74 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 757f7a9cee3..e4ea12f72ba 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 78fdf01d62c..e7e827974bb 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/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index ce6ecc4d012..a6a6cc0861f 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -2128,7 +2128,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 +2137,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) { diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 8a1ed0d3160..43c46a0eb8f 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/regalloc.hpp b/src/hotspot/share/opto/regalloc.hpp index d7bbae19871..ecdf2e2bee8 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/superword.cpp b/src/hotspot/share/opto/superword.cpp index db214da125f..779e5b3cbe7 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -3742,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 @@ -3800,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"); @@ -3811,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 @@ -3818,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)); @@ -3867,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; } @@ -3914,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);) @@ -3933,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; } @@ -3959,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) { @@ -3968,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; } @@ -3987,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);) @@ -4002,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);) @@ -4037,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 f53a25c5bfb..5d95a14cd06 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 e99bf8eb8bb..f13ffbc12af 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 302fd71ac6a..2549cae17a7 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/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 9e29c3300cf..7419921cd9a 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/wbtestmethods/parserTests.cpp b/src/hotspot/share/prims/wbtestmethods/parserTests.cpp index c6ef6cf8296..9f9afaf2761 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/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index df23967afc1..c4550ea2f1a 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 46a90b678c3..f02f921ad81 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -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 10909a25356..aac0dc88482 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/os.cpp b/src/hotspot/share/runtime/os.cpp index 69cf6f143c4..e786b057262 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. @@ -1294,7 +1304,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) @@ -1356,6 +1366,22 @@ bool os::file_exists(const char* filename) { 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 6bed70b7f9b..582ade5fbfe 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(); @@ -580,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); @@ -680,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); diff --git a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp index 9ce8f3fff93..741e7e73e9c 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/thread.cpp b/src/hotspot/share/runtime/thread.cpp index ece43e350a4..fe4f555df2a 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -3577,7 +3577,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 +3625,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 +3828,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 825b04cc272..42ac37a601f 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -668,15 +668,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); } }; diff --git a/src/hotspot/share/services/classLoadingService.cpp b/src/hotspot/share/services/classLoadingService.cpp index 9da4496971c..524851dd6e1 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 db422cbf11f..a2137ccd6ab 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 222c6e383f2..92a11c7b940 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 abeab0944f0..82f396beb3e 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 7a04ee2a6a4..58da3f831a5 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 45b3774f27d..136baf7f9eb 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 50ffd1feadd..af342493217 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 343703213ad..28aaa63d565 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 b9edf887b83..b3d57ea25de 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 a00e49dd0a7..78214548e91 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 6c87a664f79..e78f1b0605e 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 5caad66bb89..fb279b7c44e 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/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index 8e8d3a7f268..35652d5db44 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 ddb551dd004..c40dfc7de10 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()) { diff --git a/src/hotspot/share/utilities/events.cpp b/src/hotspot/share/utilities/events.cpp index 100acb3e90f..5a8281eb088 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 9afda084120..ef4e69a94ea 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 @@ -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/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index 0ca2f6384c3..4d7af94d0c3 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); } } diff --git a/src/hotspot/share/utilities/population_count.hpp b/src/hotspot/share/utilities/population_count.hpp index a88a27246fc..499574e1974 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 1977e613701..a1624058592 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; } } @@ -518,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/metaprogramming/isArray.hpp b/src/hotspot/share/utilities/vmassert_reinstall.hpp similarity index 69% rename from src/hotspot/share/metaprogramming/isArray.hpp rename to src/hotspot/share/utilities/vmassert_reinstall.hpp index 10040ccdf4d..32d31ac0c4f 100644 --- a/src/hotspot/share/metaprogramming/isArray.hpp +++ b/src/hotspot/share/utilities/vmassert_reinstall.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,14 +22,15 @@ * */ -#ifndef SHARE_METAPROGRAMMING_ISARRAY_HPP -#define SHARE_METAPROGRAMMING_ISARRAY_HPP +// Intentionally no #include guard. May be included multiple times for effect. -#include "metaprogramming/integralConstant.hpp" +// See vmassert_uninstall.hpp for usage. -template struct IsArray: public FalseType {}; +// Remove possible stdlib assert macro (or any others, for that matter). +#undef assert -template struct IsArray: public TrueType {}; -template struct IsArray: public TrueType {}; +// Reinstall HotSpot's assert macro, if previously defined. +#ifdef vmassert +#define assert(p, ...) vmassert(p, __VA_ARGS__) +#endif -#endif // SHARE_METAPROGRAMMING_ISARRAY_HPP diff --git a/test/hotspot/gtest/metaprogramming/test_conditional.cpp b/src/hotspot/share/utilities/vmassert_uninstall.hpp similarity index 52% rename from test/hotspot/gtest/metaprogramming/test_conditional.cpp rename to src/hotspot/share/utilities/vmassert_uninstall.hpp index 30fce1e22e8..dd6d51633dd 100644 --- a/test/hotspot/gtest/metaprogramming/test_conditional.cpp +++ b/src/hotspot/share/utilities/vmassert_uninstall.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,25 +22,24 @@ * */ -#include "precompiled.hpp" -#include "memory/allocation.hpp" -#include "metaprogramming/conditional.hpp" -#include "metaprogramming/isSame.hpp" -#include "utilities/debug.hpp" +// Intentionally no #include guard. May be included multiple times for effect. -class ConditionalTest { - class A: AllStatic {}; - class B: AllStatic {}; +// 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. - typedef Conditional::type A_B_if_true; - static const bool A_B_if_true_is_A = IsSame::value; - static const bool A_B_if_true_is_B = IsSame::value; - STATIC_ASSERT(A_B_if_true_is_A); - STATIC_ASSERT(!A_B_if_true_is_B); +// Remove HotSpot's assert macro, if present. +#ifdef vmassert +#undef assert +#endif // vmassert - typedef Conditional::type A_B_if_false; - static const bool A_B_if_false_is_A = IsSame::value; - static const bool A_B_if_false_is_B = IsSame::value; - STATIC_ASSERT(!A_B_if_false_is_A); - STATIC_ASSERT(A_B_if_false_is_B); -}; diff --git a/src/hotspot/share/utilities/waitBarrier_generic.cpp b/src/hotspot/share/utilities/waitBarrier_generic.cpp index b5d9ff67eb7..dbf4db336c2 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 50bfea6aebf..d3a45b33b82 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/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java index be1535d9a2b..c50e3297d56 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java @@ -72,9 +72,10 @@ protected void engineInit(AlgorithmParameterSpec params, } protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + if (((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) + && protocolVersion != 0x0101) { throw new InvalidAlgorithmParameterException( - "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); + "Only SSL 3.0, TLS 1.0/1.1/1.2, TLCP 1.1 supported"); } } @@ -126,7 +127,7 @@ private SecretKey engineGenerateKey0(byte[] masterSecret) throws GeneralSecurity MessageDigest sha = null; // generate key block - if (protocolVersion >= 0x0303) { + if (protocolVersion >= 0x0303 || protocolVersion == 0x0101) { // TLS 1.2 byte[] seed = concat(serverRandom, clientRandom); keyBlock = doTLS12PRF(masterSecret, LABEL_KEY_EXPANSION, seed, 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 09ab8143bdd..6d24f21439f 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 @@ -75,9 +75,10 @@ protected void engineInit(AlgorithmParameterSpec params, } protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + if (((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) + && protocolVersion != 0x0101) { throw new InvalidAlgorithmParameterException( - "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); + "Only SSL 3.0, TLS 1.0/1.1/1.2, TLCP 1.1 supported"); } } @@ -106,7 +107,7 @@ protected SecretKey engineGenerateKey() { try { byte[] master; - if (protocolVersion >= 0x0301) { + if (protocolVersion >= 0x0301 || protocolVersion == 0x0101) { byte[] label; byte[] seed; byte[] extendedMasterSecretSessionHash = @@ -120,7 +121,7 @@ protected SecretKey engineGenerateKey() { label = LABEL_MASTER_SECRET; seed = concat(clientRandom, serverRandom); } - master = ((protocolVersion >= 0x0303) ? + master = ((protocolVersion >= 0x0303 || protocolVersion == 0x0101) ? doTLS12PRF(premaster, label, seed, 48, spec.getPRFHashAlg(), spec.getPRFHashLength(), spec.getPRFBlockSize()) : diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index c77a626493f..d1c2b8c93cf 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/VersionProps.java.template b/src/java.base/share/classes/java/lang/VersionProps.java.template index 6037c61b3be..33066cea912 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/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html index b7ed2f9924f..2c10d18a46e 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/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index af8ebeeda57..72ddb412578 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/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index e304d072411..5ac88f0cfaa 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/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index 9a18002ebd5..83de94cd612 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 618ba7b05f3..7a320b6f432 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 550415e3559..efc92158dfe 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/javax/crypto/DecapsulateException.java b/src/java.base/share/classes/javax/crypto/DecapsulateException.java new file mode 100644 index 00000000000..5d27caa5bde --- /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 00000000000..708b4f99107 --- /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 00000000000..83318145866 --- /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/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index 02b9614971c..e8a648c4644 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 578519f7dcf..b4565daa466 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 43a98a86869..74c0cef881e 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 22b16407dd2..4a60ab482c7 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/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 7dc9f99eb18..288ebfd8504 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 @@ -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,6 +287,19 @@ 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"; @@ -759,7 +774,7 @@ private void writeRequests() throws IOException { } ps = (PrintStream) http.getOutputStream(); connected=true; - responses = new MessageHeader(); + responses = new MessageHeader(maxHeaderSize); setRequests=false; writeRequests(); } @@ -917,7 +932,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 +2887,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 +3077,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/security/provider/PolicyFile.java b/src/java.base/share/classes/sun/security/provider/PolicyFile.java index 3dfc12ac377..85e27a401cc 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/SM3Engine.java b/src/java.base/share/classes/sun/security/provider/SM3Engine.java index 87f2b28908a..f26cbbf44c5 100644 --- a/src/java.base/share/classes/sun/security/provider/SM3Engine.java +++ b/src/java.base/share/classes/sun/security/provider/SM3Engine.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, 2023, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, 2024, 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 @@ -97,7 +97,8 @@ public void update(byte message) { word[wordOffset++] = message; if (wordOffset >= word.length) { - processWord(); + processWord(word, 0); + wordOffset = 0; } countOfBytes++; @@ -115,7 +116,8 @@ public void update(byte[] message, int offset, int length) { while (consumed < length) { word[wordOffset++] = message[offset + consumed++]; if (wordOffset >= word.length) { - processWord(); + processWord(word, 0); + wordOffset = 0; break; } } @@ -123,12 +125,8 @@ public void update(byte[] message, int offset, int length) { // Process one word in each iteration while (consumed < length - 3) { - word[0] = message[offset + consumed++]; - word[1] = message[offset + consumed++]; - word[2] = message[offset + consumed++]; - word[3] = message[offset + consumed++]; - - processWord(); + processWord(message, offset + consumed); + consumed += 4; } // Process the remainder bytes if any @@ -161,13 +159,12 @@ public byte[] doFinal() { return digest; } - private void processWord() { + private void processWord(byte[] word, int offset) { block[blockOffset] - = ((word[0] & 0xFF) << 24) - | ((word[1] & 0xFF) << 16) - | ((word[2] & 0xFF) << 8) - | ((word[3] & 0xFF)); - wordOffset = 0; + = ((word[offset] & 0xFF) << 24) + | ((word[offset + 1] & 0xFF) << 16) + | ((word[offset + 2] & 0xFF) << 8) + | ((word[offset + 3] & 0xFF)); blockOffset++; if (blockOffset >= SM3_BLOCK_INT_SIZE) { diff --git a/src/java.base/share/classes/sun/security/ssl/Authenticator.java b/src/java.base/share/classes/sun/security/ssl/Authenticator.java index 1e6ba7f5c94..83d7ddb9cb9 100644 --- a/src/java.base/share/classes/sun/security/ssl/Authenticator.java +++ b/src/java.base/share/classes/sun/security/ssl/Authenticator.java @@ -61,7 +61,7 @@ static Authenticator valueOf(ProtocolVersion protocolVersion) { } else { if (protocolVersion.useTLS13PlusSpec()) { return new TLS13Authenticator(protocolVersion); - } else if (protocolVersion.useTLS10PlusSpec()) { + } else if (protocolVersion.useTLS10PlusSpec() || protocolVersion.isTLCP11()) { return new TLS10Authenticator(protocolVersion); } else { return new SSL30Authenticator(); @@ -83,7 +83,7 @@ static Authenticator valueOf(ProtocolVersion protocolVersion) { } else { if (protocolVersion.useTLS13PlusSpec()) { throw new RuntimeException("No MacAlg used in TLS 1.3"); - } else if (protocolVersion.useTLS10PlusSpec()) { + } else if (protocolVersion.useTLS10PlusSpec() || protocolVersion.isTLCP11()) { return (T)(new TLS10Mac(protocolVersion, macAlg, key)); } else { return (T)(new SSL30Mac(protocolVersion, macAlg, key)); @@ -480,6 +480,9 @@ private MacImpl(ProtocolVersion protocolVersion, MacAlg macAlg, case M_SHA384: algorithm = "HmacSHA384"; // TLS 1.2+ break; + case M_SM3: + algorithm = "HmacSM3"; // TLS 1.2+ and TLCP 1.1 + break; default: throw new RuntimeException("Unknown MacAlg " + macAlg); } 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 5921b7cfca5..840c0055b9a 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/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java index 2134506f8ea..cb3da7e6fe6 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -66,7 +66,7 @@ final class CertificateRequest { new T13CertificateRequestProducer(); // TLS 1.2 and prior versions - private static enum ClientCertificateType { + static enum ClientCertificateType { // RFC 2246 RSA_SIGN ((byte)0x01, "rsa_sign", List.of("RSA"), true), DSS_SIGN ((byte)0x02, "dss_sign", List.of("DSA"), true), @@ -112,7 +112,7 @@ private ClientCertificateType(byte id, String name, this.isAvailable = isAvailable; } - private static String nameOf(byte id) { + static String nameOf(byte id) { for (ClientCertificateType cct : ClientCertificateType.values()) { if (cct.id == id) { return cct.name; @@ -131,7 +131,7 @@ private static ClientCertificateType valueOf(byte id) { return null; } - private static String[] getKeyTypes(byte[] ids) { + static String[] getKeyTypes(byte[] ids) { ArrayList keyTypes = new ArrayList<>(3); for (byte id : ids) { ClientCertificateType cct = ClientCertificateType.valueOf(id); diff --git a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java index 7cd8e663ea3..cdc412f9644 100644 --- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java +++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java @@ -184,6 +184,24 @@ enum CipherSuite { ProtocolVersion.PROTOCOLS_OF_12, K_DHE_DSS, B_AES_128, M_SHA256, H_SHA256), + // TLCP cipher suites + TLCP_ECC_SM4_GCM_SM3( + 0xE053, true, "TLCP_ECC_SM4_GCM_SM3", "ECC_SM4_GCM_SM3", + ProtocolVersion.PROTOCOLS_OF_TLCP11, + K_SM2, B_SM4_GCM, M_NULL, H_SM3), + TLCP_ECC_SM4_CBC_SM3( + 0xE013, true, "TLCP_ECC_SM4_CBC_SM3", "ECC_SM4_CBC_SM3", + ProtocolVersion.PROTOCOLS_OF_TLCP11, + K_SM2, B_SM4, M_SM3, H_SM3), + TLCP_ECDHE_SM4_GCM_SM3( + 0xE051, true, "TLCP_ECDHE_SM4_GCM_SM3", "ECDHE_SM4_GCM_SM3", + ProtocolVersion.PROTOCOLS_OF_TLCP11, + K_SM2E, B_SM4_GCM, M_NULL, H_SM3), + TLCP_ECDHE_SM4_CBC_SM3( + 0xE011, true, "TLCP_ECDHE_SM4_CBC_SM3", "ECDHE_SM4_CBC_SM3", + ProtocolVersion.PROTOCOLS_OF_TLCP11, + K_SM2E, B_SM4, M_SM3, H_SM3), + // // not forward secret cipher suites. // @@ -1112,6 +1130,10 @@ static enum KeyExchange { K_ECDH_ANON ("ECDH_anon", JsseJce.ALLOW_ECC, true, NAMED_GROUP_ECDHE, NAMED_GROUP_XDH), + // KeyExchanges on SM2 standardized by GB/T 32918.3-2016 + K_SM2 ("SM2", true, false, NAMED_GROUP_ECDHE), + K_SM2E ("SM2E", true, false, NAMED_GROUP_ECDHE), + // renegotiation protection request signaling cipher suite K_SCSV ("SCSV", true, true, NAMED_GROUP_NONE); 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 be06d5b9024..1d66316a6b8 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 } @@ -468,6 +466,8 @@ public byte[] produce(ConnectionContext context) throws IOException { } if (session != null && + // TLCP 1.1 doesn't apply ExtendedMasterSecret + !sessionVersion.isTLCP11() && !sessionVersion.useTLS13PlusSpec() && SSLConfiguration.useExtendedMasterSecret) { @@ -904,8 +904,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 +1427,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 931f45e68ff..123b1667dfd 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/Finished.java b/src/java.base/share/classes/sun/security/ssl/Finished.java index a1ecca51920..6606d262928 100644 --- a/src/java.base/share/classes/sun/security/ssl/Finished.java +++ b/src/java.base/share/classes/sun/security/ssl/Finished.java @@ -185,6 +185,7 @@ static VerifyDataScheme valueOf(ProtocolVersion protocolVersion) { case TLS11: case DTLS10: return VerifyDataScheme.TLS10; + case TLCP11: case TLS12: case DTLS12: return VerifyDataScheme.TLS12; diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java b/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java index 2ab184a3a63..b4f55617efb 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java @@ -57,7 +57,8 @@ void determine(ProtocolVersion protocolVersion, CacheOnlyHash coh = (CacheOnlyHash)transcriptHash; if (protocolVersion.useTLS13PlusSpec()) { transcriptHash = new T13HandshakeHash(cipherSuite); - } else if (protocolVersion.useTLS12PlusSpec()) { + } else if (protocolVersion.useTLS12PlusSpec() + || protocolVersion.isTLCP11()) { transcriptHash = new T12HandshakeHash(cipherSuite); } else if (protocolVersion.useTLS10PlusSpec()) { transcriptHash = new T10HandshakeHash(cipherSuite); diff --git a/src/java.base/share/classes/sun/security/ssl/JsseJce.java b/src/java.base/share/classes/sun/security/ssl/JsseJce.java index cddbcef8ef9..4438181ac63 100644 --- a/src/java.base/share/classes/sun/security/ssl/JsseJce.java +++ b/src/java.base/share/classes/sun/security/ssl/JsseJce.java @@ -75,6 +75,12 @@ final class JsseJce { */ static final String CIPHER_AES_GCM = "AES/GCM/NoPadding"; + /** + * JCE transformation string for SM4 in CBC mode + * without padding. + */ + static final String CIPHER_SM4 = "SM4/CBC/NoPadding"; + /** * JCE transformation string for SM4 in GCM mode * without padding. diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java index 6d27d3a745c..7e4e7bf3b08 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -216,7 +216,7 @@ enum NamedGroup { // ShangMi curve defined by RFC 8998 CURVESM2(0x0029, "curvesm2", NamedGroupSpec.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_13_TLCP11, CurveDB.lookup("curvesm2")), // Elliptic Curves (RFC 4492) diff --git a/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java b/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java index e4cb5637c60..46a6baa2026 100644 --- a/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java +++ b/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java @@ -40,6 +40,7 @@ enum ProtocolVersion { TLS13 (0x0304, "TLSv1.3", false), TLS12 (0x0303, "TLSv1.2", false), + TLCP11 (0x0101, "TLCPv1.1", false), TLS11 (0x0302, "TLSv1.1", false), TLS10 (0x0301, "TLSv1", false), SSL30 (0x0300, "SSLv3", false), @@ -80,11 +81,21 @@ enum ProtocolVersion { TLS12, TLS11, TLS10, SSL30, DTLS12, DTLS10 }; + // (D)TLS ProtocolVersion array for (D)TLS 1.2 and previous versions, including TLCP 1.1 + static final ProtocolVersion[] PROTOCOLS_TO_12_TLCP11 = new ProtocolVersion[] { + TLS12, TLCP11, TLS11, TLS10, SSL30, DTLS12, DTLS10 + }; + // (D)TLS ProtocolVersion array for (D)TLS 1.3 and previous versions. static final ProtocolVersion[] PROTOCOLS_TO_13 = new ProtocolVersion[] { TLS13, TLS12, TLS11, TLS10, SSL30, DTLS12, DTLS10 }; + // (D)TLS ProtocolVersion array for (D)TLS 1.3 and previous versions, including TLCP 1.1 + static final ProtocolVersion[] PROTOCOLS_TO_13_TLCP11 = new ProtocolVersion[] { + TLS13, TLS12, TLCP11, TLS11, TLS10, SSL30, DTLS12, DTLS10 + }; + // No protocol version specified. static final ProtocolVersion[] PROTOCOLS_OF_NONE = new ProtocolVersion[] { NONE @@ -105,6 +116,11 @@ enum ProtocolVersion { TLS12, DTLS12 }; + // TLS ProtocolVersion array for TLCP 1.1. + static final ProtocolVersion[] PROTOCOLS_OF_TLCP11 = new ProtocolVersion[] { + TLCP11 + }; + // (D)TLS ProtocolVersion array for (D)TLS 1.3. static final ProtocolVersion[] PROTOCOLS_OF_13 = new ProtocolVersion[] { TLS13 @@ -230,6 +246,12 @@ static ProtocolVersion nameOf(String name) { static boolean isNegotiable( byte major, byte minor, boolean isDTLS, boolean allowSSL20Hello) { int v = ((major & 0xFF) << 8) | (minor & 0xFF); + + // TLCP 1.1 must be negotiable + if (v == TLCP11.id) { + return true; + } + if (isDTLS) { return v <= DTLS10.id; } else { @@ -367,6 +389,13 @@ boolean useTLS10PlusSpec() { return isDTLS || (this.id >= TLS10.id); } + /** + * Return true if this ProtocolVersion object is of TLCP 1.1. + */ + boolean isTLCP11() { + return this.id == TLCP11.id; + } + /** * Return true if this ProtocolVersion object is of TLS 1.0 or * newer version. diff --git a/src/java.base/share/classes/sun/security/ssl/SM2ClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SM2ClientKeyExchange.java new file mode 100644 index 00000000000..548c98b34cb --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SM2ClientKeyExchange.java @@ -0,0 +1,292 @@ +/* + * 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 + * 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.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.SM2ParameterSpec; +import java.text.MessageFormat; +import java.util.Locale; +import javax.crypto.SecretKey; + +import sun.security.ssl.SM2KeyExchange.SM2PremasterSecret; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.TLCPAuthentication.TLCP11Credentials; +import sun.security.ssl.TLCPAuthentication.TLCP11Possession; +import sun.security.util.HexDumpEncoder; + +/** + * Pack of the "ClientKeyExchange" handshake message. + */ +final class SM2ClientKeyExchange { + + static final SSLConsumer sm2HandshakeConsumer + = new SM2ClientKeyExchangeConsumer(); + static final HandshakeProducer sm2HandshakeProducer + = new SM2ClientKeyExchangeProducer(); + + /** + * The SM2 ClientKeyExchange handshake message. + */ + private static final class SM2ClientKeyExchangeMessage + extends HandshakeMessage { + + final int protocolVersion; + final byte[] encrypted; + + SM2ClientKeyExchangeMessage(HandshakeContext context, + SM2PremasterSecret premaster, PublicKey publicKey) + throws GeneralSecurityException { + super(context); + this.protocolVersion = context.clientHelloVersion; + this.encrypted = premaster.getEncoded( + publicKey, context.sslContext.getSecureRandom()); + } + + SM2ClientKeyExchangeMessage(HandshakeContext context, + ByteBuffer m) throws IOException { + super(context); + + if (m.remaining() < 2) { + throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid SM2 ClientKeyExchange message: insufficient data"); + } + + this.protocolVersion = context.clientHelloVersion; + this.encrypted = Record.getBytes16(m); + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CLIENT_KEY_EXCHANGE; + } + + @Override + public int messageLength() { + return encrypted.length + 2; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putBytes16(encrypted); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"SM2 ClientKeyExchange\": '{'\n" + + " \"client_version\": {0}\n" + + " \"encncrypted\": '{'\n" + + "{1}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + ProtocolVersion.nameOf(protocolVersion), + Utilities.indent( + hexEncoder.encodeBuffer(encrypted), " "), + }; + return messageFormat.format(messageFields); + } + } + + /** + * The SM2 "ClientKeyExchange" handshake message producer. + */ + private static final class SM2ClientKeyExchangeProducer + implements HandshakeProducer { + + // Prevent instantiation of this class. + private SM2ClientKeyExchangeProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // This happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + TLCP11Credentials tlcpCredentials = null; + for (SSLCredentials credential : chc.handshakeCredentials) { + if (credential instanceof TLCP11Credentials) { + tlcpCredentials = (TLCP11Credentials)credential; + break; + } + } + + if (tlcpCredentials == null) { + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "No SM2 credentials negotiated for client key exchange"); + } + + ECPublicKey publicKey = (ECPublicKey) tlcpCredentials.popEncPublicKey; + if (!publicKey.getAlgorithm().equals("EC") + || publicKey.getParams() instanceof SM2ParameterSpec) { + // unlikely + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Not SM2 public key for client key exchange"); + } + + SM2PremasterSecret premaster; + SM2ClientKeyExchangeMessage ckem; + try { + premaster = SM2PremasterSecret.createPremasterSecret(chc); + chc.handshakePossessions.add(premaster); + ckem = new SM2ClientKeyExchangeMessage( + chc, premaster, publicKey); + } catch (GeneralSecurityException gse) { + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Cannot generate SM2 premaster secret", gse); + } + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced SM2 ClientKeyExchange handshake message", ckem); + } + + // Output the handshake message. + ckem.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // update the states + SSLKeyExchange ke = SSLKeyExchange.valueOf( + chc.negotiatedCipherSuite.keyExchange, + chc.negotiatedProtocol); + if (ke == null) { // unlikely + throw chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key exchange type"); + } else { + SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); + SecretKey masterSecret = + masterKD.deriveKey("MasterSecret", null); + + // update the states + chc.handshakeSession.setMasterSecret(masterSecret); + SSLTrafficKeyDerivation kd = + SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); + if (kd == null) { // unlikely + throw chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + chc.negotiatedProtocol); + } else { + chc.handshakeKeyDerivation = + kd.createKeyDerivation(chc, masterSecret); + } + } + + // The handshake message has been delivered. + return null; + } + } + + /** + * The SM2 "ClientKeyExchange" handshake message consumer. + */ + private static final class SM2ClientKeyExchangeConsumer + implements SSLConsumer { + + // Prevent instantiation of this class. + private SM2ClientKeyExchangeConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + TLCP11Possession tlcpPossession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof TLCP11Possession) { + tlcpPossession = (TLCP11Possession)possession; + break; + } + } + + if (tlcpPossession == null) { // unlikely + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "No SM2 possessions negotiated for client key exchange"); + } + + PrivateKey privateKey = tlcpPossession.popEncPrivateKey; + if (!privateKey.getAlgorithm().equals("EC")) { // unlikely + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Not SM2 private key for client key exchange"); + } + + SM2ClientKeyExchangeMessage ckem = + new SM2ClientKeyExchangeMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming SM2 ClientKeyExchange handshake message", ckem); + } + + // create the credentials + SM2PremasterSecret premaster; + try { + premaster = + SM2PremasterSecret.decode(shc, privateKey, ckem.encrypted); + shc.handshakeCredentials.add(premaster); + } catch (GeneralSecurityException gse) { + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Cannot decode SM2 premaster secret", gse); + } + + // update the states + SSLKeyExchange ke = SSLKeyExchange.valueOf( + shc.negotiatedCipherSuite.keyExchange, + shc.negotiatedProtocol); + if (ke == null) { // unlikely + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key exchange type"); + } else { + SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); + SecretKey masterSecret = + masterKD.deriveKey("MasterSecret", null); + + // update the states + shc.handshakeSession.setMasterSecret(masterSecret); + SSLTrafficKeyDerivation kd = + SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); + if (kd == null) { // unlikely + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + shc.negotiatedProtocol); + } else { + shc.handshakeKeyDerivation = + kd.createKeyDerivation(shc, masterSecret); + } + } + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SM2EClientKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SM2EClientKeyExchange.java new file mode 100644 index 00000000000..b82c844f4d2 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SM2EClientKeyExchange.java @@ -0,0 +1,348 @@ +/* + * 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 + * 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.ssl; + +import javax.crypto.SecretKey; +import javax.net.ssl.SSLHandshakeException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.CryptoPrimitive; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.SM2KeyAgreementParamSpec; +import java.security.spec.SM2PublicKeySpec; +import java.text.MessageFormat; +import java.util.EnumSet; +import java.util.Locale; + +import sun.security.ssl.SM2EKeyExchange.SM2ECredentials; +import sun.security.ssl.SM2EKeyExchange.SM2EPossession; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.NamedGroup; +import sun.security.ssl.TLCPAuthentication.TLCP11Credentials; +import sun.security.ssl.TLCPAuthentication.TLCP11Possession; +import sun.security.util.HexDumpEncoder; + +/** + * Pack of the ephemeral SM2 ClientKeyExchange handshake message. + */ +public class SM2EClientKeyExchange { + + static final SSLConsumer sm2eHandshakeConsumer + = new SM2EClientKeyExchangeConsumer(); + static final HandshakeProducer sm2eHandshakeProducer + = new SM2EClientKeyExchangeProducer(); + + private static final class SM2EClientKeyExchangeMessage + extends HandshakeMessage { + private static final byte CURVE_NAMED_CURVE = (byte)0x03; + private final byte[] encodedPoint; + + SM2EClientKeyExchangeMessage(HandshakeContext handshakeContext, + byte[] encodedPublicKey) { + super(handshakeContext); + + this.encodedPoint = encodedPublicKey; + } + + SM2EClientKeyExchangeMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + Record.getInt8(m); + Record.getInt16(m); + + if (m.remaining() != 0) { // explicit PublicValueEncoding + this.encodedPoint = Record.getBytes8(m); + } else { + this.encodedPoint = new byte[0]; + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CLIENT_KEY_EXCHANGE; + } + + @Override + public int messageLength() { + if (encodedPoint == null || encodedPoint.length == 0) { + return 0; + } else { + return 1 + encodedPoint.length + 3; + } + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putInt8(CURVE_NAMED_CURVE); + hos.putInt16(NamedGroup.CURVESM2.id); + + if (encodedPoint != null && encodedPoint.length != 0) { + hos.putBytes8(encodedPoint); + } + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"SM2 ClientKeyExchange\": '{'\n" + + " \"SM2 public\": '{'\n" + + "{0}\n" + + " '}',\n" + + "'}'", + Locale.ENGLISH); + if (encodedPoint == null || encodedPoint.length == 0) { + Object[] messageFields = { + " " + }; + return messageFormat.format(messageFields); + } else { + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + Utilities.indent( + hexEncoder.encodeBuffer(encodedPoint), " "), + }; + return messageFormat.format(messageFields); + } + } + } + + private static final class SM2EClientKeyExchangeProducer + implements HandshakeProducer { + + // Prevent instantiation of this class. + private SM2EClientKeyExchangeProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + SM2ECredentials sm2eCredentials = null; + for (SSLCredentials cd : chc.handshakeCredentials) { + if (cd instanceof SM2ECredentials) { + sm2eCredentials = (SM2ECredentials) cd; + break; + } + } + + if (sm2eCredentials == null) { + throw chc.conContext.fatal(Alert.INTERNAL_ERROR, + "No SM2E credentials negotiated for client key exchange"); + } + + TLCP11Possession tlcpPossession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof TLCP11Possession) { + tlcpPossession = (TLCP11Possession) possession; + break; + } + } + SM2EPossession sm2ePossession = new SM2EPossession( + tlcpPossession, sm2eCredentials.namedGroup, + chc.sslContext.getSecureRandom()); + + chc.handshakePossessions.add(sm2ePossession); + + // Write the EC/XEC message. + SM2EClientKeyExchangeMessage cke = + new SM2EClientKeyExchangeMessage( + chc, sm2ePossession.encode()); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced SM2E ClientKeyExchange handshake message", cke); + } + + // Output the handshake message. + cke.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + TLCP11Credentials tlcpCredentials = null; + for (SSLCredentials sslCredentials : chc.handshakeCredentials) { + if (sslCredentials instanceof TLCP11Credentials) { + tlcpCredentials = (TLCP11Credentials)sslCredentials; + break; + } + } + + // update the states + SSLKeyExchange ke = SSLKeyExchange.valueOf( + chc.negotiatedCipherSuite.keyExchange, + chc.negotiatedProtocol); + if (ke == null) { + // unlikely + throw chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key exchange type"); + } else { + SM2KeyAgreementParamSpec params = new SM2KeyAgreementParamSpec( + (ECPrivateKey) tlcpPossession.popEncPrivateKey, + (ECPublicKey) tlcpPossession.popEncPublicKey, + (ECPublicKey) tlcpCredentials.popEncPublicKey, + false, + 48); + SSLKeyDerivation masterKD = ke.createKeyDerivation(chc); + SecretKey masterSecret = + masterKD.deriveKey("MasterSecret", params); + chc.handshakeSession.setMasterSecret(masterSecret); + + SSLTrafficKeyDerivation kd = + SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); + if (kd == null) { + // unlikely + throw chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + chc.negotiatedProtocol); + } else { + chc.handshakeKeyDerivation = + kd.createKeyDerivation(chc, masterSecret); + } + } + + // The handshake message has been delivered. + return null; + } + } + + private static final class SM2EClientKeyExchangeConsumer + implements SSLConsumer { + + // Prevent instantiation of this class. + private SM2EClientKeyExchangeConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // Find a good EC/XEC credential to use, determine the + // NamedGroup to use for creating Possessions/Credentials/Keys. + SM2EPossession sm2ePossession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof SM2EPossession) { + sm2ePossession = (SM2EPossession)possession; + break; + } + } + + if (sm2ePossession == null) { + // unlikely + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "No expected SM2E possessions for client key exchange"); + } + + NamedGroup namedGroup = NamedGroup.valueOf( + sm2ePossession.popEncPublicKey.getParams()); + if (namedGroup != NamedGroup.CURVESM2) { + // unlikely, have been checked during cipher suite negotiation + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Unsupported EC server cert for SM2E client key exchange"); + } + + SSLKeyExchange ke = SSLKeyExchange.valueOf( + shc.negotiatedCipherSuite.keyExchange, + shc.negotiatedProtocol); + if (ke == null) { + // unlikely + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key exchange type"); + } + + // parse the EC/XEC handshake message + SM2EClientKeyExchangeMessage cke = + new SM2EClientKeyExchangeMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming SM2E ClientKeyExchange handshake message", cke); + } + + // create the credentials + try { + KeyFactory keyFactory = KeyFactory.getInstance("SM2"); + ECPublicKey ecPublicKey = (ECPublicKey) keyFactory.generatePublic( + new SM2PublicKeySpec(cke.encodedPoint)); + SSLCredentials sslCredentials = new SM2ECredentials(ecPublicKey, namedGroup); + if (shc.algorithmConstraints != null && + sslCredentials instanceof NamedGroupCredentials) { + NamedGroupCredentials namedGroupCredentials + = (NamedGroupCredentials) sslCredentials; + if (!shc.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCredentials.getPublicKey())) { + shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ClientKeyExchange for " + namedGroup + + " does not comply with algorithm constraints"); + } + } + + shc.handshakeCredentials.add(sslCredentials); + } catch (GeneralSecurityException e) { + throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Cannot decode named group: " + namedGroup); + } + + TLCP11Credentials tlcpCredentials = null; + for (SSLCredentials sslCredentials : shc.handshakeCredentials) { + if (sslCredentials instanceof TLCP11Credentials) { + tlcpCredentials = (TLCP11Credentials)sslCredentials; + break; + } + } + + // update the states + SM2KeyAgreementParamSpec params = new SM2KeyAgreementParamSpec( + sm2ePossession.popEncPrivateKey, + sm2ePossession.popEncPublicKey, + (ECPublicKey) tlcpCredentials.popEncPublicKey, + true, + 48); + SSLKeyDerivation masterKD = ke.createKeyDerivation(shc); + SecretKey masterSecret = + masterKD.deriveKey("MasterSecret", params); + shc.handshakeSession.setMasterSecret(masterSecret); + + SSLTrafficKeyDerivation kd = + SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); + if (kd == null) { + // unlikely + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + shc.negotiatedProtocol); + } else { + shc.handshakeKeyDerivation = + kd.createKeyDerivation(shc, masterSecret); + } + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SM2EKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SM2EKeyExchange.java new file mode 100644 index 00000000000..d0faaa98ee7 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SM2EKeyExchange.java @@ -0,0 +1,313 @@ +/* + * 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 + * 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.ssl; + +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import javax.net.ssl.SSLHandshakeException; +import java.io.IOException; +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.SM2KeyAgreementParamSpec; +import java.util.EnumSet; + +import sun.security.ssl.NamedGroup; +import sun.security.ssl.NamedGroup.NamedGroupSpec; +import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; +import sun.security.ssl.TLCPAuthentication.TLCP11Possession; +import sun.security.util.ECUtil; + +/** + * TLCP 1.1 ephemeral SM2 key exchange. + */ +public class SM2EKeyExchange { + + static final SSLPossessionGenerator sm2ePoGenerator + = new SM2EPossessionGenerator(); + static final SSLKeyAgreementGenerator sm2eKAGenerator + = new SM2EKAGenerator(); + + static final class SM2ECredentials implements NamedGroupCredentials { + + final ECPublicKey ephemeralPublicKey; + final NamedGroup namedGroup; + + SM2ECredentials(ECPublicKey ephemeralPublicKey, NamedGroup namedGroup) { + this.ephemeralPublicKey = ephemeralPublicKey; + this.namedGroup = namedGroup; + } + + @Override + public PublicKey getPublicKey() { + return ephemeralPublicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + static SM2ECredentials valueOf(NamedGroup namedGroup, + byte[] encodedPoint) throws IOException, GeneralSecurityException { + + if (namedGroup != NamedGroup.CURVESM2) { + throw new RuntimeException( + "Credentials decoding: Not named group curveSM2"); + } + + if (encodedPoint == null || encodedPoint.length == 0) { + return null; + } + + ECParameterSpec parameters = + (ECParameterSpec)namedGroup.keAlgParamSpec; + ECPoint point = ECUtil.decodePoint( + encodedPoint, parameters.getCurve()); + KeyFactory factory = KeyFactory.getInstance("SM2"); + ECPublicKey publicKey = (ECPublicKey)factory.generatePublic( + new ECPublicKeySpec(point, parameters)); + return new SM2ECredentials(publicKey, namedGroup); + } + } + + static final class SM2EPossession implements NamedGroupPossession { + + final ECPrivateKey ephemeralPrivateKey; + final ECPublicKey ephemeralPublicKey; + + final ECPrivateKey popEncPrivateKey; + final ECPublicKey popEncPublicKey; + final NamedGroup namedGroup; + + SM2EPossession(TLCP11Possession tlcpPossession, + NamedGroup namedGroup, SecureRandom random) { + try { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); + kpg.initialize(namedGroup.keAlgParamSpec, random); + KeyPair kp = kpg.generateKeyPair(); + ephemeralPrivateKey = (ECPrivateKey) kp.getPrivate(); + ephemeralPublicKey = (ECPublicKey)kp.getPublic(); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Could not generate SM2 keypair", e); + } + + popEncPrivateKey = (ECPrivateKey) tlcpPossession.popEncPrivateKey; + popEncPublicKey = (ECPublicKey) tlcpPossession.popEncPublicKey; + this.namedGroup = namedGroup; + } + + @Override + public byte[] encode() { + return ECUtil.encodePoint( + ephemeralPublicKey.getW(), + ephemeralPublicKey.getParams().getCurve()); + } + + // called by ClientHandshaker with either the server's static or + // ephemeral public key + SecretKey getAgreedSecret( + ECPublicKey peerEphemeralPublicKey, boolean isInitiator) + throws SSLHandshakeException { + try { + SM2KeyAgreementParamSpec params = new SM2KeyAgreementParamSpec( + popEncPrivateKey, + popEncPublicKey, + peerEphemeralPublicKey, + isInitiator, + 32); + + KeyAgreement ka = KeyAgreement.getInstance("SM2"); + ka.init(ephemeralPrivateKey, params); + ka.doPhase(peerEphemeralPublicKey, true); + return ka.generateSecret("TlsPremasterSecret"); + } catch (GeneralSecurityException e) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(e); + } + } + + // called by ServerHandshaker + SecretKey getAgreedSecret( + byte[] peerEphemeralEncodedPoint, boolean initiator) + throws SSLHandshakeException { + try { + ECParameterSpec params = ephemeralPublicKey.getParams(); + ECPoint point = ECUtil.decodePoint( + peerEphemeralEncodedPoint, params.getCurve()); + KeyFactory kf = KeyFactory.getInstance("SM2"); + ECPublicKeySpec spec = new ECPublicKeySpec(point, params); + ECPublicKey peerPublicKey = (ECPublicKey) kf.generatePublic(spec); + return getAgreedSecret(peerPublicKey, initiator); + } catch (GeneralSecurityException | IOException e) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(e); + } + } + + // Check constraints of the specified EC public key. + void checkConstraints(AlgorithmConstraints constraints, + byte[] encodedPoint) throws SSLHandshakeException { + try { + + ECParameterSpec params = ephemeralPublicKey.getParams(); + ECPoint point = + ECUtil.decodePoint(encodedPoint, params.getCurve()); + ECPublicKeySpec spec = new ECPublicKeySpec(point, params); + + KeyFactory kf = KeyFactory.getInstance("SM2"); + ECPublicKey pubKey = (ECPublicKey)kf.generatePublic(spec); + + // check constraints of ECPublicKey + if (!constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), pubKey)) { + throw new SSLHandshakeException( + "ECPublicKey does not comply to algorithm constraints"); + } + } catch (GeneralSecurityException | IOException e) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate ECPublicKey").initCause(e); + } + } + + @Override + public PublicKey getPublicKey() { + return ephemeralPublicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + @Override + public PrivateKey getPrivateKey() { + return ephemeralPrivateKey; + } + } + + private static final class SM2EPossessionGenerator + implements SSLPossessionGenerator { + + // Prevent instantiation of this class. + private SM2EPossessionGenerator() { + // blank + } + + @Override + public SSLPossession createPossession(HandshakeContext context) { + NamedGroup preferableNamedGroup; + + // Find most preferred EC or XEC groups + if ((context.clientRequestedNamedGroups != null) && + (!context.clientRequestedNamedGroups.isEmpty())) { + preferableNamedGroup = SupportedGroups.getPreferredGroup( + context.negotiatedProtocol, + context.algorithmConstraints, + new NamedGroup.NamedGroupSpec[] { + NamedGroup.NamedGroupSpec.NAMED_GROUP_ECDHE }, + context.clientRequestedNamedGroups); + } else { + preferableNamedGroup = SupportedGroups.getPreferredGroup( + context.negotiatedProtocol, + context.algorithmConstraints, + new NamedGroup.NamedGroupSpec[] { + NamedGroup.NamedGroupSpec.NAMED_GROUP_ECDHE }); + } + + ServerHandshakeContext shc = (ServerHandshakeContext) context; + TLCP11Possession tlcpPossession = null; + if (shc.interimAuthn instanceof TLCP11Possession) { + tlcpPossession = ((TLCP11Possession) shc.interimAuthn); + } + if (preferableNamedGroup == NamedGroup.CURVESM2) { + return new SM2EPossession(tlcpPossession, + preferableNamedGroup, context.sslContext.getSecureRandom()); + } + + // no match found, cannot use this cipher suite. + return null; + } + } + + private static final class SM2EKAGenerator + implements SSLKeyAgreementGenerator { + + // Prevent instantiation of this class. + private SM2EKAGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + SM2EPossession sm2ePossession = null; + SM2ECredentials sm2eCredentials = null; + for (SSLPossession poss : context.handshakePossessions) { + if (!(poss instanceof SM2EPossession)) { + continue; + } + + NamedGroup ng = ((SM2EPossession)poss).namedGroup; + for (SSLCredentials cred : context.handshakeCredentials) { + if (!(cred instanceof SM2ECredentials)) { + continue; + } + if (ng.equals(((SM2ECredentials)cred).namedGroup)) { + sm2eCredentials = (SM2ECredentials)cred; + break; + } + } + + if (sm2eCredentials != null) { + sm2ePossession = (SM2EPossession)poss; + break; + } + } + + if (sm2ePossession == null || sm2eCredentials == null) { + throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No sufficient SM2 key agreement parameters negotiated"); + } + + return new SM2KAKeyDerivation("SM2", context, + sm2ePossession.ephemeralPrivateKey, + sm2eCredentials.ephemeralPublicKey); + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SM2EServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SM2EServerKeyExchange.java new file mode 100644 index 00000000000..fe67d7e6f3d --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SM2EServerKeyExchange.java @@ -0,0 +1,427 @@ +/* + * 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 + * 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.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.CryptoPrimitive; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.SM2PublicKeySpec; +import java.security.spec.SM2SignatureParameterSpec; +import java.text.MessageFormat; +import java.util.EnumSet; +import java.util.Locale; + +import sun.security.ssl.NamedGroup; +import sun.security.ssl.SM2EKeyExchange.SM2ECredentials; +import sun.security.ssl.SM2EKeyExchange.SM2EPossession; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; +import sun.security.ssl.TLCPAuthentication.TLCP11Credentials; +import sun.security.ssl.TLCPAuthentication.TLCP11Possession; +import sun.security.util.HexDumpEncoder; +import sun.security.util.SMUtil; + +/** + * Pack of the ephemeral SM2 ServerKeyExchange handshake message. + */ +public class SM2EServerKeyExchange { + + static final SSLConsumer sm2eHandshakeConsumer + = new SM2EServerKeyExchangeConsumer(); + static final HandshakeProducer sm2eHandshakeProducer + = new SM2EServerKeyExchangeProducer(); + + private static final class SM2EServerKeyExchangeMessage + extends HandshakeMessage { + + private static final byte CURVE_NAMED_CURVE = (byte)0x03; + + // id of the named curve + private final NamedGroup namedGroup; + + // encoded public point + private final byte[] publicPoint; + + // signature bytes, or null if anonymous + private final byte[] paramsSignature; + + // the parsed credential object + private SSLCredentials sslCredentials; + + SM2EServerKeyExchangeMessage( + HandshakeContext handshakeContext) throws IOException { + super(handshakeContext); + + // This happens in server side only. + ServerHandshakeContext shc = + (ServerHandshakeContext)handshakeContext; + + SM2EPossession sm2ePossession = null; + TLCP11Possession tlcpPossession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof SM2EPossession) { + sm2ePossession = (SM2EPossession) possession; + if (tlcpPossession != null) { + break; + } + } else if (possession instanceof TLCP11Possession) { + tlcpPossession = (TLCP11Possession) possession; + if (sm2ePossession != null) { + break; + } + } + } + + if (sm2ePossession == null) { + // unlikely + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "No SM2 credentials negotiated for server key exchange"); + } + + // Find the NamedGroup used for the ephemeral keys. + ECParameterSpec params = sm2ePossession.popEncPublicKey.getParams(); + namedGroup = params != null ? NamedGroup.valueOf(params) : null; + if ((namedGroup == null) || namedGroup != NamedGroup.CURVESM2) { + // unlikely + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Missing or improper named group: " + namedGroup); + } + + ECPoint ecPoint = sm2ePossession.popEncPublicKey.getW(); + if (ecPoint == null) { + // unlikely + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Missing public point for named group: " + namedGroup); + } + + publicPoint = SMUtil.encodePubPoint( + sm2ePossession.ephemeralPublicKey.getW()); + + Signature signer; + try { + signer = Signature.getInstance( + SignatureScheme.SM2SIG_SM3.algorithm); + + signer.setParameter(new SM2SignatureParameterSpec( + (ECPublicKey) tlcpPossession.popSignPublicKey)); + + signer.initSign(tlcpPossession.popSignPrivateKey); + } catch (NoSuchAlgorithmException | InvalidKeyException | + InvalidAlgorithmParameterException e) { + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm: " + + sm2ePossession.popEncPrivateKey.getAlgorithm(), e); + } + + byte[] signature; + try { + updateSignature(signer, + shc.clientHelloRandom.randomBytes, + shc.serverHelloRandom.randomBytes, + namedGroup.id, + publicPoint); + signature = signer.sign(); + } catch (SignatureException ex) { + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Failed to sign ecdhe parameters: " + + sm2ePossession.popEncPrivateKey.getAlgorithm(), ex); + } + paramsSignature = signature; + } + + SM2EServerKeyExchangeMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // This happens in client side only. + ClientHandshakeContext chc = + (ClientHandshakeContext)handshakeContext; + + byte curveType = (byte)Record.getInt8(m); + if (curveType != CURVE_NAMED_CURVE) { + // Unlikely as only the named curves should be negotiated. + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Unsupported ECCurveType: " + curveType); + } + + int namedGroupId = Record.getInt16(m); + this.namedGroup = NamedGroup.valueOf(namedGroupId); + if (namedGroup == null) { + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Unknown named group ID: " + namedGroupId); + } + + if (!SupportedGroups.isSupported(namedGroup)) { + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Unsupported named group: " + namedGroup); + } + + publicPoint = Record.getBytes8(m); + if (publicPoint.length == 0) { + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Insufficient Point data: " + namedGroup); + } + + try { + KeyFactory keyFactory = KeyFactory.getInstance("SM2"); + ECPublicKey ecPublicKey = (ECPublicKey) keyFactory.generatePublic( + new SM2PublicKeySpec(publicPoint)); + sslCredentials = new SM2ECredentials(ecPublicKey, namedGroup); + if (handshakeContext.algorithmConstraints != null && + sslCredentials instanceof NamedGroupCredentials) { + NamedGroupCredentials namedGroupCredentials + = (NamedGroupCredentials) sslCredentials; + if (!handshakeContext.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCredentials.getPublicKey())) { + chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ServerKeyExchange for " + namedGroup + + " does not comply with algorithm constraints"); + } + } + } catch (GeneralSecurityException ex) { + throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Cannot decode named group: " + + NamedGroup.nameOf(namedGroupId)); + } + + TLCP11Credentials tlcpCredentials = null; + for (SSLCredentials cd : chc.handshakeCredentials) { + if (cd instanceof TLCP11Credentials) { + tlcpCredentials = (TLCP11Credentials)cd; + break; + } + } + + if (tlcpCredentials == null) { + // anonymous, no authentication, no signature + if (m.hasRemaining()) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid DH ServerKeyExchange: unknown extra data"); + } + this.paramsSignature = null; + + return; + } + + // read and verify the signature + paramsSignature = Record.getBytes16(m); + Signature signer; + try { + signer = Signature.getInstance( + SignatureScheme.SM2SIG_SM3.algorithm); + + signer.setParameter(new SM2SignatureParameterSpec( + (ECPublicKey) tlcpCredentials.popSignPublicKey)); + + signer.initVerify(tlcpCredentials.popSignPublicKey); + } catch (NoSuchAlgorithmException | InvalidKeyException + | InvalidAlgorithmParameterException e) { + throw chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm: " + + tlcpCredentials.popSignPublicKey.getAlgorithm(), e); + } + + try { + updateSignature(signer, + chc.clientHelloRandom.randomBytes, + chc.serverHelloRandom.randomBytes, + namedGroup.id, + publicPoint); + + if (!signer.verify(paramsSignature)) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid SM2 ServerKeyExchange signature"); + } + } catch (SignatureException ex) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot verify SM2 ServerKeyExchange signature", ex); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.SERVER_KEY_EXCHANGE; + } + + @Override + public int messageLength() { + int sigLen = 0; + if (paramsSignature != null) { + sigLen = 2 + paramsSignature.length; + } + + return 4 + publicPoint.length + sigLen; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putInt8(CURVE_NAMED_CURVE); + hos.putInt16(namedGroup.id); + hos.putBytes8(publicPoint); + if (paramsSignature != null) { + hos.putBytes16(paramsSignature); + } + } + + @Override + public String toString() { + if (paramsSignature != null) { + MessageFormat messageFormat = new MessageFormat( + "\"SM2 ServerKeyExchange\": '{'\n" + + " \"parameters\": '{'\n" + + " \"named group\": \"{0}\"\n" + + " \"ecdh public\": '{'\n" + + "{1}\n" + + " '}',\n" + + " '}',\n" + + " \"signature\": '{'\n" + + "{2}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + namedGroup.name, + Utilities.indent( + hexEncoder.encodeBuffer(publicPoint), " "), + Utilities.indent( + hexEncoder.encodeBuffer(paramsSignature), " ") + }; + + return messageFormat.format(messageFields); + } else { // anonymous + MessageFormat messageFormat = new MessageFormat( + "\"SM2 ServerKeyExchange\": '{'\n" + + " \"parameters\": '{'\n" + + " \"named group\": \"{0}\"\n" + + " \"ecdh public\": '{'\n" + + "{1}\n" + + " '}',\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + namedGroup.name, + Utilities.indent( + hexEncoder.encodeBuffer(publicPoint), " "), + }; + + return messageFormat.format(messageFields); + } + } + + private static void updateSignature(Signature sig, + byte[] clntNonce, byte[] svrNonce, int namedGroupId, + byte[] publicPoint) throws SignatureException { + sig.update(clntNonce); + sig.update(svrNonce); + + sig.update(CURVE_NAMED_CURVE); + sig.update((byte)((namedGroupId >> 8) & 0xFF)); + sig.update((byte)(namedGroupId & 0xFF)); + sig.update((byte)publicPoint.length); + sig.update(publicPoint); + } + } + + private static final class SM2EServerKeyExchangeProducer + implements HandshakeProducer { + + // Prevent instantiation of this class. + private SM2EServerKeyExchangeProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + SM2EServerKeyExchangeMessage skem = + new SM2EServerKeyExchangeMessage(shc); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced SM2 ServerKeyExchange handshake message", skem); + } + + // Output the handshake message. + skem.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + private static final class SM2EServerKeyExchangeConsumer + implements SSLConsumer { + + // Prevent instantiation of this class. + private SM2EServerKeyExchangeConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // AlgorithmConstraints are checked during decoding + SM2EServerKeyExchangeMessage skem = + new SM2EServerKeyExchangeMessage(chc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming SM2 ServerKeyExchange handshake message", skem); + } + + // + // update + // + chc.handshakeCredentials.add(skem.sslCredentials); + + // + // produce + // + // Need no new handshake message producers here. + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SM2KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SM2KAKeyDerivation.java new file mode 100644 index 00000000000..6a21e53755a --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SM2KAKeyDerivation.java @@ -0,0 +1,79 @@ +/* + * 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 + * 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.ssl; + +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import javax.net.ssl.SSLHandshakeException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +final class SM2KAKeyDerivation implements SSLKeyDerivation { + + private final String algorithmName; + private final HandshakeContext context; + private final ECPrivateKey localEphemeralPrivateKey; + private final ECPublicKey peerEphemeralPublicKey; + + SM2KAKeyDerivation(String algorithmName, + HandshakeContext context, + ECPrivateKey localEphemeralPrivateKey, + ECPublicKey peerEphemeralPublicKey) { + this.algorithmName = algorithmName; + this.context = context; + this.localEphemeralPrivateKey = localEphemeralPrivateKey; + this.peerEphemeralPublicKey = peerEphemeralPublicKey; + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + try { + KeyAgreement ka = KeyAgreement.getInstance(algorithmName); + ka.init(localEphemeralPrivateKey, params, null); + ka.doPhase(peerEphemeralPublicKey, true); + SecretKey preMasterSecret = ka.generateSecret("TlsPremasterSecret"); + + SSLMasterKeyDerivation mskd = SSLMasterKeyDerivation.valueOf( + context.negotiatedProtocol); + if (mskd == null) { + // unlikely + throw new SSLHandshakeException( + "No expected master key derivation for protocol: " + + context.negotiatedProtocol.name); + } + SSLKeyDerivation kd = mskd.createKeyDerivation( + context, preMasterSecret); + return kd.deriveKey("MasterSecret", params); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(gse); + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SM2KeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SM2KeyExchange.java new file mode 100644 index 00000000000..215f802c5f4 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SM2KeyExchange.java @@ -0,0 +1,284 @@ +/* + * 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 + * 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.ssl; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.net.ssl.SSLHandshakeException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; +import sun.security.util.KeyUtil; + +/** + * TLCP 1.1 static SM2 key exchange. + */ +final class SM2KeyExchange { + + static final SSLPossessionGenerator sm2PoGenerator = + new SM2PossessionGenerator(); + static final SSLKeyAgreementGenerator sm2KAGenerator = + new SM2KAGenerator(); + + private static final class SM2PossessionGenerator + implements SSLPossessionGenerator { + + // Prevent instantiation of this class. + private SM2PossessionGenerator() { + // blank + } + + @Override + public SSLPossession createPossession(HandshakeContext context) { + return null; + } + } + + static final class SM2PremasterSecret + implements SSLPossession, SSLCredentials { + + final SecretKey premasterSecret; + + SM2PremasterSecret(SecretKey premasterSecret) { + this.premasterSecret = premasterSecret; + } + + byte[] getEncoded(PublicKey publicKey, + SecureRandom secureRandom) throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance("SM2"); + cipher.init(Cipher.WRAP_MODE, publicKey, secureRandom); + return cipher.wrap(premasterSecret); + } + + @SuppressWarnings("deprecation") + static SM2PremasterSecret createPremasterSecret( + ClientHandshakeContext chc) throws GeneralSecurityException { + String algorithm = chc.negotiatedProtocol.useTLS12PlusSpec() ? + "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret"; + KeyGenerator kg = KeyGenerator.getInstance(algorithm); + TlsRsaPremasterSecretParameterSpec spec = + new TlsRsaPremasterSecretParameterSpec( + chc.clientHelloVersion, + chc.negotiatedProtocol.id); + kg.init(spec, chc.sslContext.getSecureRandom()); + + return new SM2PremasterSecret(kg.generateKey()); + } + + @SuppressWarnings("deprecation") + static SM2PremasterSecret decode(ServerHandshakeContext shc, + PrivateKey privateKey, byte[] encrypted) + throws GeneralSecurityException { + + byte[] encoded = null; + boolean needFailover; + Cipher cipher = Cipher.getInstance("SM2"); + try { + // Try UNWRAP_MODE mode firstly. + cipher.init(Cipher.UNWRAP_MODE, privateKey, + new TlsRsaPremasterSecretParameterSpec( + shc.clientHelloVersion, + shc.negotiatedProtocol.id), + shc.sslContext.getSecureRandom()); + + // The provider selection can be delayed, please don't call + // any Cipher method before the call to Cipher.init(). + String providerName = cipher.getProvider().getName(); + needFailover = !KeyUtil.isOracleJCEProvider(providerName); + } catch (InvalidKeyException | UnsupportedOperationException iue) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning("The Cipher provider " + + safeProviderName(cipher) + + " caused exception: " + iue.getMessage()); + } + + needFailover = true; + } + + SecretKey preMaster; + if (needFailover) { + // The cipher might be spoiled by unsuccessful call to init(), + // so request a fresh instance + cipher = Cipher.getInstance("SM2"); + + // Use DECRYPT_MODE and dispose the previous initialization. + cipher.init(Cipher.DECRYPT_MODE, privateKey); + boolean failed = false; + try { + encoded = cipher.doFinal(encrypted); + } catch (BadPaddingException bpe) { + // Note: encoded == null + failed = true; + } + encoded = KeyUtil.checkTlsPreMasterSecretKey( + shc.clientHelloVersion, shc.negotiatedProtocol.id, + shc.sslContext.getSecureRandom(), encoded, failed); + preMaster = generatePremasterSecret( + shc.clientHelloVersion, shc.negotiatedProtocol.id, + encoded, shc.sslContext.getSecureRandom()); + } else { + // the cipher should have been initialized + preMaster = (SecretKey)cipher.unwrap(encrypted, + "TlsRsaPremasterSecret", Cipher.SECRET_KEY); + } + + return new SM2PremasterSecret(preMaster); + } + + /* + * Retrieving the cipher's provider name for the debug purposes + * can throw an exception by itself. + */ + private static String safeProviderName(Cipher cipher) { + try { + return cipher.getProvider().toString(); + } catch (Exception e) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Retrieving The Cipher provider name" + + " caused exception ", e); + } + } + try { + return cipher.toString() + " (provider name not available)"; + } catch (Exception e) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Retrieving The Cipher name" + + " caused exception ", e); + } + } + + return "(cipher/provider names not available)"; + } + + // generate a premaster secret with the specified version number + @SuppressWarnings("deprecation") + private static SecretKey generatePremasterSecret( + int clientVersion, int serverVersion, byte[] encodedSecret, + SecureRandom generator) throws GeneralSecurityException { + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Generating a premaster secret"); + } + + try { + String s = ((clientVersion >= ProtocolVersion.TLS12.id) ? + "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret"); + KeyGenerator kg = KeyGenerator.getInstance(s); + kg.init(new TlsRsaPremasterSecretParameterSpec( + clientVersion, serverVersion, encodedSecret), + generator); + return kg.generateKey(); + } catch (InvalidAlgorithmParameterException | + NoSuchAlgorithmException iae) { + // unlikely to happen, otherwise, must be a provider exception + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("ECC premaster secret generation error:"); + iae.printStackTrace(System.out); + } + + throw new GeneralSecurityException( + "Could not generate premaster secret", iae); + } + } + } + + private static final class SM2KAGenerator + implements SSLKeyAgreementGenerator { + + // Prevent instantiation of this class. + private SM2KAGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + SM2PremasterSecret premaster = null; + if (context instanceof ClientHandshakeContext) { + for (SSLPossession possession : context.handshakePossessions) { + if (possession instanceof SM2PremasterSecret) { + premaster = (SM2PremasterSecret)possession; + break; + } + } + } else { + for (SSLCredentials credential : context.handshakeCredentials) { + if (credential instanceof SM2PremasterSecret) { + premaster = (SM2PremasterSecret)credential; + break; + } + } + } + + if (premaster == null) { + throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No sufficient SM2 key agreement parameters negotiated"); + } + + return new SM2KAKeyDerivation(context, premaster.premasterSecret); + } + + private static final class SM2KAKeyDerivation + implements SSLKeyDerivation { + + private final HandshakeContext context; + private final SecretKey preMasterSecret; + + SM2KAKeyDerivation( + HandshakeContext context, SecretKey preMasterSecret) { + this.context = context; + this.preMasterSecret = preMasterSecret; + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + SSLMasterKeyDerivation mskd = + SSLMasterKeyDerivation.valueOf( + context.negotiatedProtocol); + if (mskd == null) { + // unlikely + throw new SSLHandshakeException( + "No expected master key derivation for protocol: " + + context.negotiatedProtocol.name); + } + SSLKeyDerivation kd = mskd.createKeyDerivation( + context, preMasterSecret); + return kd.deriveKey("MasterSecret", params); + } + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SM2ServerKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SM2ServerKeyExchange.java new file mode 100644 index 00000000000..e33c1012c09 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SM2ServerKeyExchange.java @@ -0,0 +1,345 @@ +/* + * 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 + * 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.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.spec.SM2SignatureParameterSpec; +import java.text.MessageFormat; +import java.util.Locale; + +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.TLCPAuthentication.TLCP11Credentials; +import sun.security.ssl.TLCPAuthentication.TLCP11Possession; +import sun.security.util.HexDumpEncoder; + +/** + * Pack of the static SM2 ServerKeyExchange handshake message. + */ +final class SM2ServerKeyExchange { + + static final SSLConsumer sm2HandshakeConsumer + = new SM2ServerKeyExchangeConsumer(); + static final HandshakeProducer sm2HandshakeProducer + = new SM2ServerKeyExchangeProducer(); + + private static final class SM2ServerKeyExchangeMessage + extends HandshakeMessage { + + // signature bytes, or null if anonymous + private final byte[] paramsSignature; + + private final boolean useExplicitSigAlgorithm; + + // the signature algorithm used by this ServerKeyExchange message + private final SignatureScheme signatureScheme; + + SM2ServerKeyExchangeMessage( + HandshakeContext handshakeContext) throws IOException { + super(handshakeContext); + + // This happens in server side only. + ServerHandshakeContext shc = + (ServerHandshakeContext)handshakeContext; + + TLCP11Possession tlcpPossession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof TLCP11Possession) { + tlcpPossession = (TLCP11Possession) possession; + break; + } + } + + if (tlcpPossession == null) { + // unlikely + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "No SM2 credentials negotiated for server key exchange"); + } + + useExplicitSigAlgorithm = + shc.negotiatedProtocol.useTLS12PlusSpec(); + if (useExplicitSigAlgorithm) { + if (shc.peerRequestedSignatureSchemes == null + || !shc.peerRequestedSignatureSchemes.contains( + SignatureScheme.SM2SIG_SM3)) { + // Unlikely, the credentials generator should have + // selected the preferable signature algorithm properly. + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "No supported signature algorithm for " + + tlcpPossession.popSignPrivateKey.getAlgorithm() + + " key"); + } + signatureScheme = SignatureScheme.SM2SIG_SM3; + } else { + signatureScheme = null; + } + + byte[] signature; + try { + Signature signer = Signature.getInstance( + SignatureScheme.SM2SIG_SM3.algorithm); + + // Set ID and public key for SM3withSM2. + signer.setParameter(new SM2SignatureParameterSpec( + (ECPublicKey) tlcpPossession.popSignPublicKey)); + + signer.initSign(tlcpPossession.popSignPrivateKey); + + updateSignature(signer, + shc.clientHelloRandom.randomBytes, + shc.serverHelloRandom.randomBytes, + tlcpPossession.popEncCert); + + signature = signer.sign(); + } catch (SignatureException | NoSuchAlgorithmException + | InvalidKeyException + | InvalidAlgorithmParameterException + | CertificateEncodingException ex) { + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Failed to sign SM2 parameters: " + + tlcpPossession.popSignPrivateKey.getAlgorithm(), ex); + } + paramsSignature = signature; + } + + SM2ServerKeyExchangeMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // This happens in client side only. + ClientHandshakeContext chc = + (ClientHandshakeContext)handshakeContext; + + TLCP11Credentials tlcpCredentials = null; + for (SSLCredentials cd : chc.handshakeCredentials) { + if (cd instanceof TLCP11Credentials) { + tlcpCredentials = (TLCP11Credentials)cd; + break; + } + } + + if (tlcpCredentials == null) { + // unlikely + throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "No SM2 credentials negotiated for server key exchange"); + } + + this.useExplicitSigAlgorithm = + chc.negotiatedProtocol.useTLS12PlusSpec(); + if (useExplicitSigAlgorithm) { + int ssid = Record.getInt16(m); + signatureScheme = SignatureScheme.valueOf(ssid); + if (signatureScheme == null) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid signature algorithm (" + ssid + + ") used in SM2 ServerKeyExchange handshake message"); + } + + if (!chc.localSupportedSignAlgs.contains(signatureScheme)) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Unsupported signature algorithm (" + + signatureScheme.name + + ") used in SM2 ServerKeyExchange handshake message"); + } + } else { + signatureScheme = null; + } + + // read and verify the signature + paramsSignature = Record.getBytes16(m); + + try { + Signature signer = Signature.getInstance( + SignatureScheme.SM2SIG_SM3.algorithm); + + // Set ID and public key for SM3withSM2. + signer.setParameter(new SM2SignatureParameterSpec( + (ECPublicKey) tlcpCredentials.popSignCert.getPublicKey())); + + signer.initVerify(tlcpCredentials.popSignPublicKey); + + updateSignature(signer, + chc.clientHelloRandom.randomBytes, + chc.serverHelloRandom.randomBytes, + tlcpCredentials.popEncCert); + + if (!signer.verify(paramsSignature)) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid SM2 ServerKeyExchange signature"); + } + } catch (NoSuchAlgorithmException | InvalidKeyException + | InvalidAlgorithmParameterException + | SignatureException | CertificateEncodingException ex) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot verify SM2 ServerKeyExchange signature", ex); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.SERVER_KEY_EXCHANGE; + } + + @Override + public int messageLength() { + int sigLen = 2 + paramsSignature.length; + if (useExplicitSigAlgorithm) { + sigLen += SignatureScheme.sizeInRecord(); + } + return sigLen; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + if (useExplicitSigAlgorithm) { + hos.putInt16(signatureScheme.id); + } + hos.putBytes16(paramsSignature); + } + + @Override + public String toString() { + if (useExplicitSigAlgorithm) { + MessageFormat messageFormat = new MessageFormat( + "\"SM2E ServerKeyExchange\": '{'\n" + + " \"digital signature\": '{'\n" + + " \"signature algorithm\": \"{0}\"\n" + + " \"signature\": '{'\n" + + "{1}\n" + + " '}',\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + signatureScheme.name, + Utilities.indent( + hexEncoder.encodeBuffer(paramsSignature), " ") + }; + return messageFormat.format(messageFields); + + } else { + MessageFormat messageFormat = new MessageFormat( + "\"SM2 ServerKeyExchange\": '{'\n" + + " \"digital signature\": '{'\n" + + " \"signature\": '{'\n" + + "{0}\n" + + " '}',\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + Utilities.indent( + hexEncoder.encodeBuffer(paramsSignature), " ") + }; + return messageFormat.format(messageFields); + } + } + + private static void updateSignature(Signature sig, + byte[] clntNonce, byte[] svrNonce, X509Certificate encCert) + throws SignatureException, CertificateEncodingException { + sig.update(clntNonce); + sig.update(svrNonce); + + byte[] encodedEncCert = encCert.getEncoded(); + int certLength = encodedEncCert.length; + sig.update((byte)(certLength >> 16 & 0x0ff)); + sig.update((byte)(certLength >> 8 & 0x0ff)); + sig.update((byte)(certLength & 0x0ff)); + sig.update(encodedEncCert); + } + } + + private static final class SM2ServerKeyExchangeProducer + implements HandshakeProducer { + + // Prevent instantiation of this class. + private SM2ServerKeyExchangeProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + SM2ServerKeyExchangeMessage skem = + new SM2ServerKeyExchangeMessage(shc); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced SM2 ServerKeyExchange handshake message", skem); + } + + // Output the handshake message. + skem.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + private static final class SM2ServerKeyExchangeConsumer + implements SSLConsumer { + + // Prevent instantiation of this class. + private SM2ServerKeyExchangeConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // AlgorithmConstraints are checked during decoding + SM2ServerKeyExchangeMessage skem = + new SM2ServerKeyExchangeMessage(chc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming SM2 ServerKeyExchange handshake message", skem); + } + + // + // produce + // + // Need no new handshake message producers here. + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SMEntries.java b/src/java.base/share/classes/sun/security/ssl/SMEntries.java new file mode 100644 index 00000000000..c2bc76ab325 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SMEntries.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024, 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.security.ssl; + +import java.security.Provider; + +/** + * Defines the entries on ShangMi protocol. + */ +final class SMEntries { + + static void putEntries(Provider p) { + p.put("SSLContext.TLCPv1.1", + "sun.security.ssl.TLCPContexts$TLCP11Context"); + p.put("SSLContext.TLCP", + "sun.security.ssl.TLCPContexts$TLCPContext"); + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SSLCipher.java b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java index 285f83f08ea..d672910bb75 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLCipher.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java @@ -265,6 +265,31 @@ enum SSLCipher { ) })), + @SuppressWarnings({"unchecked", "rawtypes"}) + B_SM4(CIPHER_SM4, BLOCK_CIPHER, 16, 16, 16, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T11BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), + new SimpleImmutableEntry( + new T11BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T11BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), + new SimpleImmutableEntry( + new T11BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ) + })), + @SuppressWarnings({"unchecked", "rawtypes"}) B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 16, 12, 4, true, false, (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T12GcmReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T12GcmWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ) + })), + @SuppressWarnings({"unchecked", "rawtypes"}) B_AES_128_GCM_IV(CIPHER_AES_GCM, AEAD_CIPHER, 16, 16, 12, 0, true, false, (Map.Entry keyHashMap = new HashMap<>(); - SSLContextImpl() { ephemeralKeyManager = new EphemeralKeyManager(); @@ -347,7 +344,7 @@ private static List getApplicableSupportedCipherSuites( * Return the list of all available CipherSuites that are default enabled * in client or server side. */ - private static List getApplicableEnabledCipherSuites( + static List getApplicableEnabledCipherSuites( List protocols, boolean isClient) { if (isClient) { @@ -473,7 +470,7 @@ private static Collection getCustomizedCipherSuites( } - private static List getAvailableProtocols( + static List getAvailableProtocols( ProtocolVersion[] protocolCandidates) { List availableProtocols = @@ -528,7 +525,7 @@ private static List getAvailableProtocols( * * @see SSLContext */ - private abstract static class AbstractTLSContext extends SSLContextImpl { + abstract static class AbstractTLSContext extends SSLContextImpl { private static final List supportedProtocols; private static final List serverDefaultProtocols; @@ -727,7 +724,7 @@ List getClientDefaultCipherSuites() { * * @see SSLContext */ - private static class CustomizedSSLProtocols { + static class CustomizedSSLProtocols { private static final String JDK_TLS_CLIENT_PROTOCOLS = "jdk.tls.client.protocols"; private static final String JDK_TLS_SERVER_PROTOCOLS = diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index 47272f8e0b8..cb877f42f6e 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -38,7 +38,7 @@ enum SSLExtension implements SSLStringizer { // Extensions defined in RFC 6066 (TLS Extensions: Extension Definitions) CH_SERVER_NAME (0x0000, "server_name", SSLHandshake.CLIENT_HELLO, - ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_13_TLCP11, ServerNameExtension.chNetworkProducer, ServerNameExtension.chOnLoadConsumer, null, @@ -47,7 +47,7 @@ enum SSLExtension implements SSLStringizer { ServerNameExtension.chStringizer), SH_SERVER_NAME (0x0000, "server_name", SSLHandshake.SERVER_HELLO, - ProtocolVersion.PROTOCOLS_TO_12, + ProtocolVersion.PROTOCOLS_TO_12_TLCP11, ServerNameExtension.shNetworkProducer, ServerNameExtension.shOnLoadConsumer, null, @@ -186,7 +186,7 @@ enum SSLExtension implements SSLStringizer { // Extensions defined in RFC 7301 (TLS Application-Layer Protocol Negotiation Extension) CH_ALPN (0x0010, "application_layer_protocol_negotiation", SSLHandshake.CLIENT_HELLO, - ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_13_TLCP11, AlpnExtension.chNetworkProducer, AlpnExtension.chOnLoadConsumer, AlpnExtension.chOnLoadAbsence, @@ -195,7 +195,7 @@ enum SSLExtension implements SSLStringizer { AlpnExtension.alpnStringizer), SH_ALPN (0x0010, "application_layer_protocol_negotiation", SSLHandshake.SERVER_HELLO, - ProtocolVersion.PROTOCOLS_TO_12, + ProtocolVersion.PROTOCOLS_TO_12_TLCP11, AlpnExtension.shNetworkProducer, AlpnExtension.shOnLoadConsumer, AlpnExtension.shOnLoadAbsence, diff --git a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java index 12ef8ba9cb5..9bd20c4e5a3 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java @@ -52,13 +52,13 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ClientHello.handshakeConsumer, - ProtocolVersion.PROTOCOLS_TO_13 + ProtocolVersion.PROTOCOLS_TO_13_TLCP11 ) }), (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ClientHello.handshakeProducer, - ProtocolVersion.PROTOCOLS_TO_13 + ProtocolVersion.PROTOCOLS_TO_13_TLCP11 ) })), @@ -67,13 +67,13 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ServerHello.handshakeConsumer, - ProtocolVersion.PROTOCOLS_TO_13 + ProtocolVersion.PROTOCOLS_TO_13_TLCP11 ) }), (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ServerHello.t12HandshakeProducer, - ProtocolVersion.PROTOCOLS_TO_12 + ProtocolVersion.PROTOCOLS_TO_12_TLCP11 ), new SimpleImmutableEntry( ServerHello.t13HandshakeProducer, @@ -149,6 +149,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { @SuppressWarnings({"unchecked", "rawtypes"}) CERTIFICATE ((byte)0x0B, "certificate", (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + TLCPCertificateMessage.tlcp11HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( CertificateMessage.t12HandshakeConsumer, ProtocolVersion.PROTOCOLS_TO_12 @@ -159,6 +163,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { ) }), (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + TLCPCertificateMessage.tlcp11HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( CertificateMessage.t12HandshakeProducer, ProtocolVersion.PROTOCOLS_TO_12 @@ -174,13 +182,13 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ServerKeyExchange.handshakeConsumer, - ProtocolVersion.PROTOCOLS_TO_12 + ProtocolVersion.PROTOCOLS_TO_12_TLCP11 ) }), (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ServerKeyExchange.handshakeProducer, - ProtocolVersion.PROTOCOLS_TO_12 + ProtocolVersion.PROTOCOLS_TO_12_TLCP11 ) })), @@ -191,6 +199,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { CertificateRequest.t10HandshakeConsumer, ProtocolVersion.PROTOCOLS_TO_11 ), + new SimpleImmutableEntry( + TLCPCertificateRequest.tlcp11HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( CertificateRequest.t12HandshakeConsumer, ProtocolVersion.PROTOCOLS_OF_12 @@ -205,6 +217,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { CertificateRequest.t10HandshakeProducer, ProtocolVersion.PROTOCOLS_TO_11 ), + new SimpleImmutableEntry( + TLCPCertificateRequest.tlcp11HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( CertificateRequest.t12HandshakeProducer, ProtocolVersion.PROTOCOLS_OF_12 @@ -220,13 +236,13 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ServerHelloDone.handshakeConsumer, - ProtocolVersion.PROTOCOLS_TO_12 + ProtocolVersion.PROTOCOLS_TO_12_TLCP11 ) }), (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ServerHelloDone.handshakeProducer, - ProtocolVersion.PROTOCOLS_TO_12 + ProtocolVersion.PROTOCOLS_TO_12_TLCP11 ) })), @@ -241,6 +257,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { CertificateVerify.t10HandshakeConsumer, ProtocolVersion.PROTOCOLS_10_11 ), + new SimpleImmutableEntry( + TLCPCertificateVerify.tlcp11HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( CertificateVerify.t12HandshakeConsumer, ProtocolVersion.PROTOCOLS_OF_12 @@ -259,6 +279,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { CertificateVerify.t10HandshakeProducer, ProtocolVersion.PROTOCOLS_10_11 ), + new SimpleImmutableEntry( + TLCPCertificateVerify.tlcp11HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( CertificateVerify.t12HandshakeProducer, ProtocolVersion.PROTOCOLS_OF_12 @@ -274,13 +298,13 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ClientKeyExchange.handshakeConsumer, - ProtocolVersion.PROTOCOLS_TO_12 + ProtocolVersion.PROTOCOLS_TO_12_TLCP11 ) }), (Map.Entry[])(new Map.Entry[] { new SimpleImmutableEntry( ClientKeyExchange.handshakeProducer, - ProtocolVersion.PROTOCOLS_TO_12 + ProtocolVersion.PROTOCOLS_TO_12_TLCP11 ) })), @@ -291,6 +315,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { Finished.t12HandshakeConsumer, ProtocolVersion.PROTOCOLS_TO_12 ), + new SimpleImmutableEntry( + Finished.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( Finished.t13HandshakeConsumer, ProtocolVersion.PROTOCOLS_OF_13 @@ -301,6 +329,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { Finished.t12HandshakeProducer, ProtocolVersion.PROTOCOLS_TO_12 ), + new SimpleImmutableEntry( + Finished.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_TLCP11 + ), new SimpleImmutableEntry( Finished.t13HandshakeProducer, ProtocolVersion.PROTOCOLS_OF_13 diff --git a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java index 5f0c5107e05..da0a7401f1a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; +import sun.security.ssl.TLCPKeyExchange.TLCP11KeyAgreement; import sun.security.ssl.X509Authentication.X509Possession; final class SSLKeyExchange implements SSLKeyAgreementGenerator, @@ -39,7 +40,7 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator, private final List authentication; private final SSLKeyAgreement keyAgreement; - SSLKeyExchange(List authentication, + SSLKeyExchange(List authentication, SSLKeyAgreement keyAgreement) { if (authentication != null) { this.authentication = List.copyOf(authentication); @@ -98,7 +99,8 @@ SSLPossession[] createPossessions(HandshakeContext context) { if (kaPossession == null) { // special cases if (keyAgreement == T12KeyAgreement.RSA || - keyAgreement == T12KeyAgreement.ECDH) { + keyAgreement == T12KeyAgreement.ECDH || + keyAgreement == TLCP11KeyAgreement.SM2) { return authentication != null ? new SSLPossession[] {authPossession} : new SSLPossession[0]; @@ -249,6 +251,12 @@ static SSLKeyExchange valueOf( } case K_ECDH_ANON: return SSLKeyExECDHANON.KE; + + // TLCP 1.1 key exchanges + case K_SM2: + return TLCPKeyExSM2.KE; + case K_SM2E: + return TLCPKeyExSM2E.KE; } return null; @@ -340,6 +348,16 @@ private static class SSLKeyExECDHANON { null, T12KeyAgreement.ECDHE); } + private static class TLCPKeyExSM2 { + private static final SSLKeyExchange KE = new SSLKeyExchange( + Arrays.asList(TLCPAuthentication.SM2), TLCP11KeyAgreement.SM2); + } + + private static class TLCPKeyExSM2E { + private static final SSLKeyExchange KE = new SSLKeyExchange( + Arrays.asList(TLCPAuthentication.SM2), TLCP11KeyAgreement.SM2E); + } + private enum T12KeyAgreement implements SSLKeyAgreement { RSA ("rsa", null, RSAKeyExchange.kaGenerator), diff --git a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java index 4ef4d3e7d63..57081340d1e 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java @@ -55,6 +55,7 @@ static SSLMasterKeyDerivation valueOf(ProtocolVersion protocolVersion) { case TLS11: case DTLS10: return SSLMasterKeyDerivation.TLS10; + case TLCP11: case TLS12: case DTLS12: return SSLMasterKeyDerivation.TLS12; @@ -112,7 +113,8 @@ public SecretKey deriveKey(String algorithm, hashAlg = cipherSuite.hashAlg; } } else { - if (protocolVersion.id >= ProtocolVersion.TLS12.id) { + if (protocolVersion.id >= ProtocolVersion.TLS12.id + || protocolVersion.isTLCP11()) { masterAlg = "SunTls12MasterSecret"; hashAlg = cipherSuite.hashAlg; } else { @@ -122,7 +124,8 @@ public SecretKey deriveKey(String algorithm, } TlsMasterSecretParameterSpec spec; - if (context.handshakeSession.useExtendedMasterSecret) { + if (context.handshakeSession.useExtendedMasterSecret + && !protocolVersion.isTLCP11()) { // reset to use the extended master secret algorithm masterAlg = "SunTlsExtendedMasterSecret"; 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 9584a35c5de..2dd629b1f1e 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 f288e210aa3..387c4ca69e8 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/SSLTrafficKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java index ac39cbb16fb..aa576aeb871 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java @@ -63,6 +63,7 @@ static SSLTrafficKeyDerivation valueOf(ProtocolVersion protocolVersion) { case TLS11: case DTLS10: return SSLTrafficKeyDerivation.TLS10; + case TLCP11: case TLS12: case DTLS12: return SSLTrafficKeyDerivation.TLS12; @@ -245,7 +246,8 @@ static final class LegacyTrafficKeyDerivation implements SSLKeyDerivation { hashAlg = cipherSuite.hashAlg; } } else { - if (protocolVersion.id >= ProtocolVersion.TLS12.id) { + if (protocolVersion.id >= ProtocolVersion.TLS12.id + || protocolVersion.isTLCP11()) { keyMaterialAlg = "SunTls12KeyMaterial"; hashAlg = cipherSuite.hashAlg; } else { 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 829fa2af96c..11b625e5791 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/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 2905cbf6192..7db21e28e37 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -325,7 +325,10 @@ public byte[] produce(ConnectionContext context, if ((ke != null) && (shc.sslConfig.clientAuthType != - ClientAuthType.CLIENT_AUTH_NONE) && + ClientAuthType.CLIENT_AUTH_NONE + // TLCP 1.1 ECDHE cipher suites must require client's certificates + || shc.negotiatedCipherSuite == CipherSuite.TLCP_ECDHE_SM4_GCM_SM3 + || shc.negotiatedCipherSuite == CipherSuite.TLCP_ECDHE_SM4_CBC_SM3) && !shc.negotiatedCipherSuite.isAnonymous()) { for (SSLHandshake hs : ke.getRelatedHandshakers(shc)) { 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 74f38bf5b56..18d05ca3585 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 af9cd69575f..1132ec120da 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/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java index b36f03ffad6..bab3e7754f5 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -127,7 +127,7 @@ enum SignatureScheme { "SM3withSM2", "EC", NamedGroup.CURVESM2, - ProtocolVersion.PROTOCOLS_TO_13), + ProtocolVersion.PROTOCOLS_TO_13_TLCP11), // Legacy algorithms DSA_SHA256 (0x0402, "dsa_sha256", "SHA256withDSA", @@ -158,7 +158,7 @@ enum SignatureScheme { final int id; // hash + signature final String name; // literal name - private final String algorithm; // signature algorithm + final String algorithm; // signature algorithm final String keyAlgorithm; // signature key algorithm private final SigAlgParamSpec signAlgParams; // signature parameters private final NamedGroup namedGroup; // associated named group @@ -615,7 +615,7 @@ Signature getVerifier(PublicKey publicKey) throws NoSuchAlgorithmException, // specific private key. If the private key does not support the signature // scheme, {@code null} is returned, and the caller may fail back to next // available signature scheme. - private Signature getSigner(PrivateKey privateKey, PublicKey publicKey, + Signature getSigner(PrivateKey privateKey, PublicKey publicKey, boolean isTLS13) { if (!isAvailable) { return null; diff --git a/src/java.base/share/classes/sun/security/ssl/SunJSSE.java b/src/java.base/share/classes/sun/security/ssl/SunJSSE.java index dce2aad8400..a8daeec10f7 100644 --- a/src/java.base/share/classes/sun/security/ssl/SunJSSE.java +++ b/src/java.base/share/classes/sun/security/ssl/SunJSSE.java @@ -81,6 +81,8 @@ private void doRegister() { List.of("SSLv3"), null); ps("SSLContext", "TLSv1.1", "sun.security.ssl.SSLContextImpl$TLS11Context", null, null); + ps("SSLContext", "TLCPv1.1", + "sun.security.ssl.TLCPContexts$TLCP11Context", null, null); ps("SSLContext", "TLSv1.2", "sun.security.ssl.SSLContextImpl$TLS12Context", null, null); ps("SSLContext", "TLSv1.3", @@ -88,6 +90,8 @@ private void doRegister() { ps("SSLContext", "TLS", "sun.security.ssl.SSLContextImpl$TLSContext", List.of("SSL"), null); + ps("SSLContext", "TLCP", + "sun.security.ssl.TLCPContexts$TLCPContext", null, null); ps("SSLContext", "DTLSv1.0", "sun.security.ssl.SSLContextImpl$DTLS10Context", null, null); diff --git a/src/java.base/share/classes/sun/security/ssl/TLCPAuthentication.java b/src/java.base/share/classes/sun/security/ssl/TLCPAuthentication.java new file mode 100644 index 00000000000..d36823b2fe8 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/TLCPAuthentication.java @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * 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.ssl; + +import javax.net.ssl.X509ExtendedKeyManager; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Map; + +import sun.security.ssl.NamedGroup; +import sun.security.util.SMUtil; + +enum TLCPAuthentication implements SSLAuthentication { + + SM2("EC", "EC"); + + final String keyAlgorithm; + final String[] keyTypes; + + TLCPAuthentication(String keyAlgorithm, + String... keyTypes) { + this.keyAlgorithm = keyAlgorithm; + this.keyTypes = keyTypes; + } + + @Override + public SSLPossession createPossession(HandshakeContext handshakeContext) { + return createPossession(handshakeContext, keyTypes); + } + + @Override + public SSLHandshake[] getRelatedHandshakers( + HandshakeContext handshakeContext) { + if (handshakeContext.negotiatedProtocol.isTLCP11()) { + return new SSLHandshake[] { + SSLHandshake.CERTIFICATE, + SSLHandshake.CERTIFICATE_REQUEST + }; + } // Otherwise, SSL/TLS does not use this method. + + return new SSLHandshake[0]; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public Map.Entry[] getHandshakeProducers( + HandshakeContext handshakeContext) { + if (handshakeContext.negotiatedProtocol.isTLCP11()) { + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry( + SSLHandshake.CERTIFICATE.id, + SSLHandshake.CERTIFICATE + ) + }); + } // Otherwise, SSL/TLS does not use this method. + + return (Map.Entry[])(new Map.Entry[0]); + } + + static final class TLCP11Possession implements SSLPossession { + + // Proof of possession of the private key corresponding to the public + // key for which a certificate is being provided for authentication. + final PrivateKey popSignPrivateKey; + final X509Certificate[] popSignCerts; + final X509Certificate popSignCert; + final PublicKey popSignPublicKey; + + final PrivateKey popEncPrivateKey; + final X509Certificate[] popEncCerts; + final X509Certificate popEncCert; + final PublicKey popEncPublicKey; + + TLCP11Possession(PrivateKey popSignPrivateKey, + X509Certificate[] popSignCerts, + PrivateKey popEncPrivateKey, + X509Certificate[] popEncCerts) { + this.popSignPrivateKey = popSignPrivateKey; + this.popSignCerts = popSignCerts; + if (popSignCerts != null && popSignCerts.length > 0) { + popSignCert = popSignCerts[0]; + popSignPublicKey = popSignCert.getPublicKey(); + } else { + popSignCert = null; + popSignPublicKey = null; + } + + this.popEncPrivateKey = popEncPrivateKey; + this.popEncCerts = popEncCerts; + if (popEncCerts != null && popEncCerts.length > 0) { + popEncCert = popEncCerts[0]; + popEncPublicKey = popEncCert.getPublicKey(); + } else { + popEncCert = null; + popEncPublicKey = null; + } + } + + TLCP11Possession(PossessionEntry signPossEntry, + PossessionEntry encPossEntry) { + this.popSignPrivateKey = signPossEntry.popPrivateKey; + this.popSignCerts = signPossEntry.popCerts; + this.popSignCert = signPossEntry.popCert; + popSignPublicKey = signPossEntry.popPublicKey; + + this.popEncPrivateKey = encPossEntry.popPrivateKey; + this.popEncCerts = encPossEntry.popCerts; + this.popEncCert = encPossEntry.popCert; + popEncPublicKey = encPossEntry.popPublicKey; + } + + ECParameterSpec getECParameterSpec() { + return getECParameterSpec(popSignPrivateKey, popSignCerts); + } + + ECParameterSpec getECParameterSpec( + PrivateKey popPrivateKey, X509Certificate[] popCerts) { + if (popPrivateKey == null || + !"EC".equals(popPrivateKey.getAlgorithm())) { + return null; + } + + if (popPrivateKey instanceof ECKey) { + return ((ECKey) popPrivateKey).getParams(); + } else if (popCerts != null && popCerts.length != 0) { + // The private key not extractable, get the parameters from + // the X.509 certificate. + PublicKey publicKey = popCerts[0].getPublicKey(); + if (publicKey instanceof ECKey) { + return ((ECKey)publicKey).getParams(); + } + } + + return null; + } + } + + static final class PossessionEntry { + + final PrivateKey popPrivateKey; + final X509Certificate[] popCerts; + final X509Certificate popCert; + final PublicKey popPublicKey; + + PossessionEntry(PrivateKey popPrivateKey, + X509Certificate[] popCerts) { + this.popPrivateKey = popPrivateKey; + this.popCerts = popCerts; + popCert = popCerts[0]; + popPublicKey = popCert.getPublicKey(); + } + } + + static final class TLCP11Credentials implements SSLCredentials { + + final PublicKey popSignPublicKey; + final X509Certificate[] popSignCerts; + final X509Certificate popSignCert; + + final PublicKey popEncPublicKey; + final X509Certificate[] popEncCerts; + final X509Certificate popEncCert; + + TLCP11Credentials(PublicKey popSignPublicKey, + X509Certificate[] popSignCerts, + PublicKey popEncPublicKey, + X509Certificate[] popEncCerts) { + this.popSignPublicKey = popSignPublicKey; + this.popSignCerts = popSignCerts; + this.popSignCert = popSignCerts[0]; + + this.popEncPublicKey = popEncPublicKey; + this.popEncCerts = popEncCerts; + this.popEncCert = popEncCerts[0]; + } + } + + public static SSLPossession createPossession( + HandshakeContext context, String[] keyTypes) { + if (context.sslConfig.isClientMode) { + return createClientPossession( + (ClientHandshakeContext) context, keyTypes); + } else { + return createServerPossession( + (ServerHandshakeContext) context, keyTypes); + } + } + + private static SSLPossession createClientPossession( + ClientHandshakeContext chc, String[] keyTypes) { + X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager(); + for (String keyType : keyTypes) { + String[] clientAliases = km.getClientAliases( + keyType, + chc.peerSupportedAuthorities == null ? null : + chc.peerSupportedAuthorities.clone()); + if (clientAliases == null || clientAliases.length == 0) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("No X.509 cert selected for " + + Arrays.toString(keyTypes)); + } + return null; + } + + PossessionEntry signPossEntry = null; + PossessionEntry encPossEntry = null; + for (String clientAlias : clientAliases) { + PossessionEntry bufPossEntry = clientPossEntry( + chc, keyType, km, clientAlias); + if (bufPossEntry == null) { + continue; + } + + if (signPossEntry == null + && SMUtil.isSignCert(bufPossEntry.popCert)) { + signPossEntry = bufPossEntry; + } else if (SMUtil.isEncCert(bufPossEntry.popCert)) { + encPossEntry = bufPossEntry; + } + + if (signPossEntry != null && encPossEntry != null) { + break; + } + } + + TLCP11Possession tlcpPossession + = createPossession(signPossEntry, encPossEntry); + if (tlcpPossession != null) { + return tlcpPossession; + } + } + + return null; + } + + private static PossessionEntry clientPossEntry( + ClientHandshakeContext chc, String keyType, + X509ExtendedKeyManager km, String clientAlias) { + PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias); + if (clientPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest( + clientAlias + " is not a private key entry"); + } + return null; + } + + X509Certificate[] clientCerts = km.getCertificateChain(clientAlias); + if ((clientCerts == null) || (clientCerts.length == 0)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest(clientAlias + + " is a private key entry with no cert chain stored"); + } + return null; + } + + String privateKeyAlgorithm = clientPrivateKey.getAlgorithm(); + if (!Arrays.asList(keyType).contains(privateKeyAlgorithm)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine( + clientAlias + " private key algorithm " + + privateKeyAlgorithm + " not in request list"); + } + return null; + } + + PublicKey clientPublicKey = clientCerts[0].getPublicKey(); + String publicKeyAlgorithm = clientPublicKey.getAlgorithm(); + if (!privateKeyAlgorithm.equals(publicKeyAlgorithm)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine( + clientAlias + " private or public key is not of " + + "same algorithm: " + + privateKeyAlgorithm + " vs " + + publicKeyAlgorithm); + } + return null; + } + + if (!checkPublicKey(clientAlias, clientPublicKey, chc)) { + return null; + } + + return new PossessionEntry(clientPrivateKey, clientCerts); + } + + private static SSLPossession createServerPossession( + ServerHandshakeContext shc, String[] keyTypes) { + X509ExtendedKeyManager km = shc.sslContext.getX509KeyManager(); + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("X509KeyManager class: " + + km.getClass().getName()); + } + for (String keyType : keyTypes) { + String[] serverAliases = km.getServerAliases(keyType, + shc.peerSupportedAuthorities == null ? null : + shc.peerSupportedAuthorities.clone()); + + if (serverAliases == null || serverAliases.length == 0) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("No X.509 cert selected for " + keyType); + } + continue; + } + + PossessionEntry signPossEntry = null; + PossessionEntry encPossEntry = null; + for (String serverAlias : serverAliases) { + PossessionEntry bufPossEntry = serverPossEntry( + shc, keyType, km, serverAlias); + if (bufPossEntry == null) { + continue; + } + + if (signPossEntry == null + && SMUtil.isSignCert(bufPossEntry.popCert)) { + signPossEntry = bufPossEntry; + } else if (SMUtil.isEncCert(bufPossEntry.popCert)) { + encPossEntry = bufPossEntry; + } + + if (signPossEntry != null && encPossEntry != null) { + break; + } + } + + TLCP11Possession tlcpPossession + = createPossession(signPossEntry, encPossEntry); + if (tlcpPossession != null) { + return tlcpPossession; + } + } + + return null; + } + + private static TLCP11Possession createPossession( + PossessionEntry signPossEntry, PossessionEntry encPossEntry) { + if (signPossEntry == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("No X.509 sign cert selected"); + } + + return null; + } + + // Use sign cert as enc cert if possible + if (encPossEntry == null + && SMUtil.isEncCert(signPossEntry.popCert)) { + encPossEntry = signPossEntry; + } + + if (encPossEntry == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("No X.509 enc cert selected"); + } + + return null; + } + + return new TLCP11Possession(signPossEntry, encPossEntry); + } + + private static PossessionEntry serverPossEntry( + ServerHandshakeContext shc, String keyType, + X509ExtendedKeyManager km, String serverAlias) { + PrivateKey serverPrivateKey = km.getPrivateKey(serverAlias); + if (serverPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest( + serverAlias + " is not a private key entry"); + } + return null; + } + + X509Certificate[] serverCerts = km.getCertificateChain(serverAlias); + if ((serverCerts == null) || (serverCerts.length == 0)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest( + serverAlias + " is not a certificate entry"); + } + return null; + } + + PublicKey serverPublicKey = serverCerts[0].getPublicKey(); + if ((!serverPrivateKey.getAlgorithm().equals(keyType)) + || (!serverPublicKey.getAlgorithm().equals(keyType))) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine( + serverAlias + " private or public key is not of " + + keyType + " algorithm"); + } + return null; + } + + // For TLS 1.2 and prior versions, the public key of an EC cert + // MUST use a curve and point format supported by the client. + // But for TLS 1.3, signature algorithms are negotiated + // independently via the "signature_algorithms" extension. + if (!shc.negotiatedProtocol.useTLS13PlusSpec() && + keyType.equals("EC")) { + if (!checkPublicKey(serverAlias, serverPublicKey, shc)) { + return null; + } + } + + return new PossessionEntry(serverPrivateKey, serverCerts); + } + + private static boolean checkPublicKey(String alias, PublicKey publicKey, + HandshakeContext hc) { + if (!(publicKey instanceof ECPublicKey)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning(alias + + " public key is not an instance of ECPublicKey"); + } + return false; + } + + // For ECC certs, check whether we support the EC domain + // parameters. If the client sent a supported_groups + // ClientHello extension, check against that too for + // TLS 1.2 and prior versions. + ECParameterSpec params = + ((ECPublicKey) publicKey).getParams(); + NamedGroup namedGroup = NamedGroup.valueOf(params); + if (namedGroup != NamedGroup.CURVESM2) { // Only accept curveSM2 + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning( + "Unsupported named group (" + namedGroup + + ") used in the " + alias + " certificate"); + } + + return false; + } + + return true; + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/TLCPCertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/TLCPCertificateMessage.java new file mode 100644 index 00000000000..26ceca37c50 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/TLCPCertificateMessage.java @@ -0,0 +1,855 @@ +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * 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.ssl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PublicKey; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.CertPathValidatorException.Reason; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLProtocolException; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509ExtendedTrustManager; +import javax.net.ssl.X509TrustManager; +import javax.security.auth.x500.X500Principal; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.TLCPAuthentication.TLCP11Credentials; +import sun.security.ssl.TLCPAuthentication.TLCP11Possession; +import sun.security.util.SMUtil; + +public class TLCPCertificateMessage { + + static final SSLConsumer tlcp11HandshakeConsumer = + new TLCP11CertificateConsumer(); + static final HandshakeProducer tlcp11HandshakeProducer = + new TLCP11CertificateProducer(); + + private enum CertListFormat { + + // sign_cert | enc_cert | ca_list, the default + SIGN_ENC_CA("SIGN|ENC|CA"), + + // sign_cert | ca_list | enc_cert + SIGN_CA_ENC("SIGN|CA|ENC"); + + private final String format; + + CertListFormat(String format) { + this.format = format; + } + + static CertListFormat format(String format) { + return SIGN_CA_ENC.format.equalsIgnoreCase(format) + ? SIGN_CA_ENC : SIGN_ENC_CA; + } + } + + // The default format is SIGN_ENC_CA("SIGN|ENC|CA"). + @SuppressWarnings("removal") + private static final CertListFormat DEF_CERT_LIST_FORMAT + = CertListFormat.format(AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return System.getProperty("jdk.tlcp.certListFormat", + CertListFormat.SIGN_ENC_CA.format); + } + })); + + /** + * The Certificate handshake message for TLCP 1.1. + */ + static final class TLCP11CertificateMessage extends HandshakeMessage { + final List encodedCertChain; + + TLCP11CertificateMessage(HandshakeContext handshakeContext, + X509Certificate[] certChain) throws SSLException { + super(handshakeContext); + + List encodedCerts = new ArrayList<>(certChain.length); + for (X509Certificate cert : certChain) { + try { + encodedCerts.add(cert.getEncoded()); + } catch (CertificateEncodingException cee) { + // unlikely + throw handshakeContext.conContext.fatal( + Alert.INTERNAL_ERROR, + "Could not encode certificate (" + + cert.getSubjectX500Principal() + ")", cee); + } + } + + this.encodedCertChain = encodedCerts; + } + + TLCP11CertificateMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + int listLen = Record.getInt24(m); + if (listLen > m.remaining()) { + throw handshakeContext.conContext.fatal( + Alert.ILLEGAL_PARAMETER, + "Error parsing certificate message:no sufficient data"); + } + if (listLen > 0) { + List encodedCerts = new LinkedList<>(); + while (listLen > 0) { + byte[] encodedCert = Record.getBytes24(m); + listLen -= (3 + encodedCert.length); + encodedCerts.add(encodedCert); + if (encodedCerts.size() > SSLConfiguration.maxCertificateChainLength) { + throw new SSLProtocolException( + "The certificate chain length (" + + encodedCerts.size() + + ") exceeds the maximum allowed length (" + + SSLConfiguration.maxCertificateChainLength + + ")"); + } + + } + this.encodedCertChain = encodedCerts; + } else { + this.encodedCertChain = Collections.emptyList(); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE; + } + + @Override + public int messageLength() { + int msgLen = 3; + for (byte[] encodedCert : encodedCertChain) { + msgLen += (encodedCert.length + 3); + } + + return msgLen; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + int listLen = 0; + for (byte[] encodedCert : encodedCertChain) { + listLen += (encodedCert.length + 3); + } + + hos.putInt24(listLen); + for (byte[] encodedCert : encodedCertChain) { + hos.putBytes24(encodedCert); + } + } + + @Override + public String toString() { + if (encodedCertChain.isEmpty()) { + return "\"Certificates\": "; + } + + Object[] x509Certs = new Object[encodedCertChain.size()]; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (byte[] encodedCert : encodedCertChain) { + Object obj; + try { + obj = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(encodedCert)); + } catch (CertificateException ce) { + obj = encodedCert; + } + x509Certs[i++] = obj; + } + } catch (CertificateException ce) { + // no X.509 certificate factory service + int i = 0; + for (byte[] encodedCert : encodedCertChain) { + x509Certs[i++] = encodedCert; + } + } + + MessageFormat messageFormat = new MessageFormat( + "\"Certificates\": [\n" + + "{0}\n" + + "]", + Locale.ENGLISH); + Object[] messageFields = { + SSLLogger.toString(x509Certs) + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "Certificate" handshake message producer for TLCP 1.1. + */ + private static final + class TLCP11CertificateProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private TLCP11CertificateProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + if (hc.sslConfig.isClientMode) { + return onProduceCertificate( + (ClientHandshakeContext)context, message); + } else { + return onProduceCertificate( + (ServerHandshakeContext)context, message); + } + } + + private byte[] onProduceCertificate(ServerHandshakeContext shc, + SSLHandshake.HandshakeMessage message) throws IOException { + TLCP11Possession tlcpPossession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof TLCP11Possession) { + tlcpPossession = (TLCP11Possession)possession; + break; + } + } + + if (tlcpPossession == null) { // unlikely + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "No expected X.509 certificate for server authentication"); + } + + shc.handshakeSession.setLocalPrivateKey( + tlcpPossession.popSignPrivateKey); + + X509Certificate[] certs = mergeCertChains( + tlcpPossession.popSignCerts, tlcpPossession.popEncCerts); + shc.handshakeSession.setLocalCertificates(certs); + TLCP11CertificateMessage cm = + new TLCP11CertificateMessage(shc, certs); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced server Certificate handshake message", cm); + } + + // Output the handshake message. + cm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + + private byte[] onProduceCertificate(ClientHandshakeContext chc, + HandshakeMessage message) throws IOException { + TLCP11Possession tlcpPossession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof TLCP11Possession) { + tlcpPossession = (TLCP11Possession)possession; + break; + } + } + + // Report to the server if no appropriate cert was found. For + // SSL 3.0, send a no_certificate alert; TLCP and TLS 1.0/1.1/1.2 + // uses an empty cert chain instead. + if (tlcpPossession == null) { + if (chc.negotiatedProtocol.isTLCP11() + || chc.negotiatedProtocol.useTLS10PlusSpec()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 certificate for client authentication, " + + "use empty Certificate message instead"); + } + + tlcpPossession = new TLCP11Possession( + null, new X509Certificate[0], + null, new X509Certificate[0]); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 certificate for client authentication, " + + "send a no_certificate alert"); + } + + chc.conContext.warning(Alert.NO_CERTIFICATE); + return null; + } + } + + chc.handshakeSession.setLocalPrivateKey( + tlcpPossession.popSignPrivateKey); + X509Certificate[] certs = mergeCertChains( + tlcpPossession.popSignCerts, tlcpPossession.popEncCerts); + if (certs != null && certs.length != 0) { + chc.handshakeSession.setLocalCertificates(certs); + } else { + chc.handshakeSession.setLocalCertificates(null); + } + TLCP11CertificateMessage cm = + new TLCP11CertificateMessage(chc, certs); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced client Certificate handshake message", cm); + } + + // Output the handshake message. + cm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + // Merge the sign cert chain and enc cert chain to a single chain. + private static X509Certificate[] mergeCertChains( + X509Certificate[] signCertChain, X509Certificate[] encCertChain) { + if (signCertChain == null || signCertChain.length == 0 + || encCertChain == null || encCertChain.length == 0) { + return new X509Certificate[0]; + } + + X509Certificate[] mergedCerts + = new X509Certificate[signCertChain.length + 1]; + + if (DEF_CERT_LIST_FORMAT == CertListFormat.SIGN_CA_ENC) { + System.arraycopy(signCertChain, 0, mergedCerts, 0, + signCertChain.length); + mergedCerts[mergedCerts.length - 1] = encCertChain[0]; + } else { + mergedCerts[0] = signCertChain[0]; + mergedCerts[1] = encCertChain[0]; + if (signCertChain.length > 1) { + System.arraycopy(signCertChain, 1, mergedCerts, 2, + signCertChain.length - 1); + } + } + + return mergedCerts; + } + + /** + * The "Certificate" handshake message consumer for TLCP 1.1. + */ + static final + class TLCP11CertificateConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private TLCP11CertificateConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + + // clean up this consumer + hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); + + TLCP11CertificateMessage cm = new TLCP11CertificateMessage(hc, message); + if (hc.sslConfig.isClientMode) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming server Certificate handshake message", cm); + } + onCertificate((ClientHandshakeContext)context, cm); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming client Certificate handshake message", cm); + } + onCertificate((ServerHandshakeContext)context, cm); + } + } + + private void onCertificate(ServerHandshakeContext shc, + TLCP11CertificateMessage certificateMessage) throws IOException { + List encodedCerts = certificateMessage.encodedCertChain; + if (encodedCerts == null || encodedCerts.isEmpty()) { + // For empty Certificate messages, we should not expect + // a CertificateVerify message to follow + shc.handshakeConsumers.remove( + SSLHandshake.CERTIFICATE_VERIFY.id); + if (shc.sslConfig.clientAuthType != + ClientAuthType.CLIENT_AUTH_REQUESTED) { + // unexpected or require client authentication + throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Empty client certificate chain"); + } else { + return; + } + } + + X509Certificate[] x509Certs = + new X509Certificate[encodedCerts.size()]; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (byte[] encodedCert : encodedCerts) { + x509Certs[i++] = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(encodedCert)); + } + } catch (CertificateException ce) { + throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Failed to parse server certificates", ce); + } + + checkClientCerts(shc, x509Certs); + + // + // update + // + shc.handshakeCredentials.add(createCredentials(x509Certs, shc)); + shc.handshakeSession.setPeerCertificates(x509Certs); + } + + private void onCertificate(ClientHandshakeContext chc, + TLCP11CertificateMessage certificateMessage) throws IOException { + List encodedCerts = certificateMessage.encodedCertChain; + if (encodedCerts == null || encodedCerts.isEmpty()) { + throw chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Empty server certificate chain"); + } + + X509Certificate[] x509Certs = + new X509Certificate[encodedCerts.size()]; + + if (x509Certs.length < 2) { + throw chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Server must send at least two certificates"); + } + + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (byte[] encodedCert : encodedCerts) { + x509Certs[i++] = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(encodedCert)); + } + } catch (CertificateException ce) { + throw chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Failed to parse server certificates", ce); + } + + // Allow server certificate change in client side during + // renegotiation after a session-resumption abbreviated + // initial handshake? + // + // DO NOT need to check allowUnsafeServerCertChange here. We only + // reserve server certificates when allowUnsafeServerCertChange is + // false. + if (chc.reservedServerCerts != null && + !chc.handshakeSession.useExtendedMasterSecret) { + // It is not necessary to check the certificate update if + // endpoint identification is enabled. + String identityAlg = chc.sslConfig.identificationProtocol; + if ((identityAlg == null || identityAlg.isEmpty()) && + !isIdentityEquivalent(x509Certs[0], + chc.reservedServerCerts[0])) { + throw chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "server certificate change is restricted " + + "during renegotiation"); + } + } + + // ask the trust manager to verify the chain + if (chc.staplingActive) { + // Defer the certificate check until after we've received the + // CertificateStatus message. If that message doesn't come in + // immediately following this message we will execute the + // check from CertificateStatus' absent handler. + chc.deferredCerts = x509Certs; + } else { + // We're not doing stapling, so perform the check right now + checkServerCerts(chc, x509Certs); + } + + // + // update + // + chc.handshakeCredentials.add(createCredentials(x509Certs, chc)); + chc.handshakeSession.setPeerCertificates(x509Certs); + } + + // Assume the cert list contains at least one certificate, + // and the structure is either of: + // 1. sign_cert | enc_cert | sign_cert_CA_list + // 2. sign_cert | sign_cert_CA_list | enc_cert + // + // sign_cert and enc_cert are issued by the same CA. + private static TLCP11Credentials createCredentials( + X509Certificate[] certs, HandshakeContext hc) throws SSLException { + X509Certificate signCert = null; + X509Certificate encCert = null; + X509Certificate[] signCertChain = null; + X509Certificate[] encCertChain = null; + + if (certs.length == 1) { + signCert = certs[0]; + encCert = certs[0]; + signCertChain = new X509Certificate[] { signCert }; + encCertChain = new X509Certificate[] { encCert }; + } else if (certs.length == 2) { + signCert = certs[0]; + encCert = certs[1]; + signCertChain = new X509Certificate[] { signCert }; + encCertChain = new X509Certificate[] { encCert }; + } else if (certs.length > 2) { + CertListFormat format = null; + if (SMUtil.isCA(certs[certs.length - 1])) { + format = CertListFormat.SIGN_ENC_CA; + } else if (SMUtil.isCA(certs[1])) { + format = CertListFormat.SIGN_CA_ENC; + } else { + // Cannot determine the CA, just use the default format + format = DEF_CERT_LIST_FORMAT; + } + + signCertChain = new X509Certificate[certs.length - 1]; + encCertChain = new X509Certificate[certs.length - 1]; + if (format == CertListFormat.SIGN_CA_ENC) { + signCert = certs[0]; + System.arraycopy(certs, 0, signCertChain, 0, certs.length - 1); + + encCert = certs[certs.length - 1]; + encCertChain[0] = encCert; + System.arraycopy(certs, 1, encCertChain, 1, certs.length - 2); + } else { // SIGN_ENC_CA + signCert = certs[0]; + signCertChain[0] = signCert; + System.arraycopy(certs, 2, signCertChain, 1, certs.length - 2); + + encCert = certs[1]; + encCertChain[0] = encCert; + System.arraycopy(certs, 2, encCertChain, 1, certs.length - 2); + } + } + + // Check sign cert + if (!SMUtil.isSignCert(signCert)) { + throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, + "The sign cert doesn't contain key usage digitalSignature"); + } + + // Check enc cert + if (!SMUtil.isEncCert(encCert)) { + throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, + "The enc cert doesn't contain key usages in " + + "keyEncipherment, dataEncipherment and keyAgreement."); + } + + return new TLCP11Credentials( + signCert.getPublicKey(), signCertChain, + encCert.getPublicKey(), encCertChain); + } + + /* + * Whether the certificates can represent the same identity? + * + * The certificates can be used to represent the same identity: + * 1. If the subject alternative names of IP address are present + * in both certificates, they should be identical; otherwise, + * 2. if the subject alternative names of DNS name are present in + * both certificates, they should be identical; otherwise, + * 3. if the subject fields are present in both certificates, the + * certificate subjects and issuers should be identical. + */ + private static boolean isIdentityEquivalent(X509Certificate thisCert, + X509Certificate prevCert) { + if (thisCert.equals(prevCert)) { + return true; + } + + // check subject alternative names + Collection> thisSubjectAltNames = null; + try { + thisSubjectAltNames = thisCert.getSubjectAlternativeNames(); + } catch (CertificateParsingException cpe) { + if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + SSLLogger.fine( + "Attempt to obtain subjectAltNames extension failed!"); + } + } + + Collection> prevSubjectAltNames = null; + try { + prevSubjectAltNames = prevCert.getSubjectAlternativeNames(); + } catch (CertificateParsingException cpe) { + if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + SSLLogger.fine( + "Attempt to obtain subjectAltNames extension failed!"); + } + } + + if (thisSubjectAltNames != null && prevSubjectAltNames != null) { + // check the iPAddress field in subjectAltName extension + // + // 7: subject alternative name of type IP. + Collection thisSubAltIPAddrs = + getSubjectAltNames(thisSubjectAltNames, 7); + Collection prevSubAltIPAddrs = + getSubjectAltNames(prevSubjectAltNames, 7); + if (thisSubAltIPAddrs != null && prevSubAltIPAddrs != null && + isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs)) { + return true; + } + + // check the dNSName field in subjectAltName extension + // 2: subject alternative name of type IP. + Collection thisSubAltDnsNames = + getSubjectAltNames(thisSubjectAltNames, 2); + Collection prevSubAltDnsNames = + getSubjectAltNames(prevSubjectAltNames, 2); + if (thisSubAltDnsNames != null && prevSubAltDnsNames != null && + isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames)) { + return true; + } + } + + // check the certificate subject and issuer + X500Principal thisSubject = thisCert.getSubjectX500Principal(); + X500Principal prevSubject = prevCert.getSubjectX500Principal(); + X500Principal thisIssuer = thisCert.getIssuerX500Principal(); + X500Principal prevIssuer = prevCert.getIssuerX500Principal(); + + return (!thisSubject.getName().isEmpty() && + !prevSubject.getName().isEmpty() && + thisSubject.equals(prevSubject) && + thisIssuer.equals(prevIssuer)); + } + + /* + * Returns the subject alternative name of the specified type in the + * subjectAltNames extension of a certificate. + * + * Note that only those subjectAltName types that use String data + * should be passed into this function. + */ + private static Collection getSubjectAltNames( + Collection> subjectAltNames, int type) { + HashSet subAltDnsNames = null; + for (List subjectAltName : subjectAltNames) { + int subjectAltNameType = (Integer)subjectAltName.get(0); + if (subjectAltNameType == type) { + String subAltDnsName = (String)subjectAltName.get(1); + if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) { + if (subAltDnsNames == null) { + subAltDnsNames = + new HashSet<>(subjectAltNames.size()); + } + subAltDnsNames.add(subAltDnsName); + } + } + } + + return subAltDnsNames; + } + + private static boolean isEquivalent(Collection thisSubAltNames, + Collection prevSubAltNames) { + for (String thisSubAltName : thisSubAltNames) { + for (String prevSubAltName : prevSubAltNames) { + // Only allow the exactly match. No wildcard character + // checking. + if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) { + return true; + } + } + } + + return false; + } + + /** + * Perform client-side checking of server certificates. + * + * @param certs an array of {@code X509Certificate} objects presented + * by the server in the ServerCertificate message. + * + * @throws IOException if a failure occurs during validation or + * the trust manager associated with the {@code SSLContext} is not + * an {@code X509ExtendedTrustManager}. + */ + static void checkServerCerts(ClientHandshakeContext chc, + X509Certificate[] certs) throws IOException { + + X509TrustManager tm = chc.sslContext.getX509TrustManager(); + + // find out the key exchange algorithm used + // use "RSA" for non-ephemeral "RSA_EXPORT" + String keyExchangeString; + if (chc.negotiatedCipherSuite.keyExchange == + CipherSuite.KeyExchange.K_RSA_EXPORT || + chc.negotiatedCipherSuite.keyExchange == + CipherSuite.KeyExchange.K_DHE_RSA_EXPORT) { + keyExchangeString = CipherSuite.KeyExchange.K_RSA.name; + } else { + keyExchangeString = chc.negotiatedCipherSuite.keyExchange.name; + } + + try { + if (tm instanceof X509ExtendedTrustManager) { + if (chc.conContext.transport instanceof SSLEngine) { + SSLEngine engine = (SSLEngine)chc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkServerTrusted( + certs.clone(), + keyExchangeString, + engine); + } else { + SSLSocket socket = (SSLSocket)chc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkServerTrusted( + certs.clone(), + keyExchangeString, + socket); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + + // Once the server certificate chain has been validated, set + // the certificate chain in the TLS session. + chc.handshakeSession.setPeerCertificates(certs); + } catch (CertificateException ce) { + throw chc.conContext.fatal(getCertificateAlert(chc, ce), ce); + } + } + + private static void checkClientCerts(ServerHandshakeContext shc, + X509Certificate[] certs) throws IOException { + X509TrustManager tm = shc.sslContext.getX509TrustManager(); + + // find out the types of client authentication used + PublicKey key = certs[0].getPublicKey(); + String keyAlgorithm = key.getAlgorithm(); + String authType; + switch (keyAlgorithm) { + case "RSA": + case "DSA": + case "EC": + case "RSASSA-PSS": + authType = keyAlgorithm; + break; + default: + // unknown public key type + authType = "UNKNOWN"; + } + + try { + if (tm instanceof X509ExtendedTrustManager) { + if (shc.conContext.transport instanceof SSLEngine) { + SSLEngine engine = (SSLEngine)shc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkClientTrusted( + certs.clone(), + authType, + engine); + } else { + SSLSocket socket = (SSLSocket)shc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkClientTrusted( + certs.clone(), + authType, + socket); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + } catch (CertificateException ce) { + throw shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce); + } + } + + /** + * When a failure happens during certificate checking from an + * {@link X509TrustManager}, determine what TLS alert description + * to use. + * + * @param cexc The exception thrown by the {@link X509TrustManager} + * + * @return A byte value corresponding to a TLS alert description number. + */ + private static Alert getCertificateAlert( + ClientHandshakeContext chc, CertificateException cexc) { + // The specific reason for the failure will determine how to + // set the alert description value + Alert alert = Alert.CERTIFICATE_UNKNOWN; + + Throwable baseCause = cexc.getCause(); + if (baseCause instanceof CertPathValidatorException) { + CertPathValidatorException cpve = + (CertPathValidatorException)baseCause; + Reason reason = cpve.getReason(); + if (reason == BasicReason.REVOKED) { + alert = chc.staplingActive ? + Alert.BAD_CERT_STATUS_RESPONSE : + Alert.CERTIFICATE_REVOKED; + } else if ( + reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { + alert = chc.staplingActive ? + Alert.BAD_CERT_STATUS_RESPONSE : + Alert.CERTIFICATE_UNKNOWN; + } else if (reason == BasicReason.ALGORITHM_CONSTRAINED) { + alert = Alert.UNSUPPORTED_CERTIFICATE; + } else if (reason == BasicReason.EXPIRED) { + alert = Alert.CERTIFICATE_EXPIRED; + } else if (reason == BasicReason.INVALID_SIGNATURE || + reason == BasicReason.NOT_YET_VALID) { + alert = Alert.BAD_CERTIFICATE; + } + } + + return alert; + } + + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/TLCPCertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/TLCPCertificateRequest.java new file mode 100644 index 00000000000..f356a49a205 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/TLCPCertificateRequest.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * 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.ssl; + +import javax.security.auth.x500.X500Principal; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +import sun.security.ssl.CertificateRequest.ClientCertificateType; +import sun.security.ssl.SSLHandshake.HandshakeMessage; + +final class TLCPCertificateRequest { + + static final SSLConsumer tlcp11HandshakeConsumer = + new TLCP11CertificateRequestConsumer(); + static final HandshakeProducer tlcp11HandshakeProducer = + new TLCP11CertificateRequestProducer(); + + /** + * The CertificateRequest handshake message for TLCP 1.1. + */ + static final class TLCP11CertificateRequestMessage extends HandshakeMessage { + final byte[] types; // certificate types + final List authorities; // certificate authorities + + TLCP11CertificateRequestMessage(HandshakeContext handshakeContext, + X509Certificate[] trustedCerts) throws IOException { + super(handshakeContext); + + this.types = new byte[] { + ClientCertificateType.ECDSA_SIGN.id, + ClientCertificateType.RSA_SIGN.id}; + + this.authorities = new ArrayList<>(trustedCerts.length); + for (X509Certificate cert : trustedCerts) { + X500Principal x500Principal = cert.getSubjectX500Principal(); + authorities.add(x500Principal.getEncoded()); + } + } + + TLCP11CertificateRequestMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // struct { + // ClientCertificateType certificate_types<1..2^8-1>; + // SignatureAndHashAlgorithm + // supported_signature_algorithms<2..2^16-2>; + // DistinguishedName certificate_authorities<0..2^16-1>; + // } CertificateRequest; + + // certificate_authorities + int minLen = handshakeContext.negotiatedProtocol.isTLCP11() ? 4 : 8; + if (m.remaining() < minLen) { + throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateRequest handshake message: " + + "no sufficient data"); + } + this.types = Record.getBytes8(m); + + // certificate_authorities + if (m.remaining() < 2) { + throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateRequest handshake message: " + + "no sufficient data"); + } + + int listLen = Record.getInt16(m); + if (listLen > m.remaining()) { + throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateRequest message: no sufficient data"); + } + + if (listLen > 0) { + this.authorities = new LinkedList<>(); + while (listLen > 0) { + // opaque DistinguishedName<1..2^16-1>; + byte[] encoded = Record.getBytes16(m); + listLen -= (2 + encoded.length); + authorities.add(encoded); + } + } else { + this.authorities = Collections.emptyList(); + } + } + + String[] getKeyTypes() { + return ClientCertificateType.getKeyTypes(types); + } + + X500Principal[] getAuthorities() { + X500Principal[] principals = new X500Principal[authorities.size()]; + int i = 0; + for (byte[] encoded : authorities) { + principals[i++] = new X500Principal(encoded); + } + + return principals; + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE_REQUEST; + } + + @Override + public int messageLength() { + int len = 1 + types.length + 2; + for (byte[] encoded : authorities) { + len += encoded.length + 2; + } + return len; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putBytes8(types); + + int listLen = 0; + for (byte[] encoded : authorities) { + listLen += encoded.length + 2; + } + + hos.putInt16(listLen); + for (byte[] encoded : authorities) { + hos.putBytes16(encoded); + } + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"CertificateRequest\": '{'\n" + + " \"certificate types\": {0}\n" + + " \"certificate authorities\": {1}\n" + + "'}'", + Locale.ENGLISH); + + List typeNames = new ArrayList<>(types.length); + for (byte type : types) { + typeNames.add(ClientCertificateType.nameOf(type)); + } + + List authorityNames = new ArrayList<>(authorities.size()); + for (byte[] encoded : authorities) { + X500Principal principal = new X500Principal(encoded); + authorityNames.add(principal.toString()); + } + Object[] messageFields = { + typeNames, + authorityNames + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "CertificateRequest" handshake message producer for TLCP 1.1. + */ + private static final + class TLCP11CertificateRequestProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private TLCP11CertificateRequestProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + SSLHandshake.HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + X509Certificate[] caCerts = + shc.sslContext.getX509TrustManager().getAcceptedIssuers(); + TLCP11CertificateRequestMessage crm + = new TLCP11CertificateRequestMessage(shc, caCerts); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced CertificateRequest handshake message", crm); + } + + // Output the handshake message. + crm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // + // update + // + shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id, + SSLHandshake.CERTIFICATE); + shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id, + SSLHandshake.CERTIFICATE_VERIFY); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "CertificateRequest" handshake message consumer for TLCP 1.1. + */ + private static final + class TLCP11CertificateRequestConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private TLCP11CertificateRequestConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // clean up this consumer + chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_REQUEST.id); + chc.receivedCertReq = true; + + // If we're processing this message and the server's certificate + // message consumer has not already run then this is a state + // machine violation. + if (chc.handshakeConsumers.containsKey( + SSLHandshake.CERTIFICATE.id)) { + throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected CertificateRequest handshake message"); + } + + SSLConsumer certStatCons = chc.handshakeConsumers.remove( + SSLHandshake.CERTIFICATE_STATUS.id); + if (certStatCons != null) { + // Stapling was active but no certificate status message + // was sent. We need to run the absence handler which will + // check the certificate chain. + CertificateStatus.handshakeAbsence.absent(context, null); + } + + TLCP11CertificateRequestMessage crm + = new TLCP11CertificateRequestMessage(chc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming CertificateRequest handshake message", crm); + } + + // + // validate + // + // blank + + // + // update + // + + // An empty client Certificate handshake message may be allow. + chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id, + SSLHandshake.CERTIFICATE); + + try { + chc.peerSupportedAuthorities = crm.getAuthorities(); + } catch (IllegalArgumentException iae) { + chc.conContext.fatal(Alert.DECODE_ERROR, "The " + + "distinguished names of the peer's certificate " + + "authorities could not be parsed", iae); + } + + SSLPossession pos = TLCPAuthentication.createPossession( + chc, new String[] {"EC"}); + if (pos == null) { + return; + } + + chc.handshakePossessions.add(pos); + chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id, + SSLHandshake.CERTIFICATE_VERIFY); + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/TLCPCertificateVerify.java b/src/java.base/share/classes/sun/security/ssl/TLCPCertificateVerify.java new file mode 100644 index 00000000000..7e72e8e5be9 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/TLCPCertificateVerify.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2015, 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 + * 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.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.text.MessageFormat; +import java.util.Locale; + +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.TLCPAuthentication.TLCP11Credentials; +import sun.security.ssl.TLCPAuthentication.TLCP11Possession; +import sun.security.util.HexDumpEncoder; +import sun.security.util.SMUtil; + +final class TLCPCertificateVerify { + + static final SSLConsumer tlcp11HandshakeConsumer = + new TLCP11CertificateVerifyConsumer(); + static final HandshakeProducer tlcp11HandshakeProducer = + new TLCP11CertificateVerifyProducer(); + + /** + * The CertificateVerify handshake message (TLCP 1.1). + */ + private static final + class TLCP11CertificateVerifyMessage extends HandshakeMessage { + + // signature bytes + private final byte[] signature; + + TLCP11CertificateVerifyMessage(HandshakeContext context, + TLCP11Possession tlcpPossession) throws IOException { + super(context); + + // This happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext) context; + + byte[] temporary; + try { + Signature signer = SignatureScheme.SM2SIG_SM3.getSigner( + tlcpPossession.popSignPrivateKey, + tlcpPossession.popSignPublicKey, + false); + signer.update(chc.handshakeHash.digest()); + temporary = signer.sign(); + } catch (SignatureException se) { + throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot produce CertificateVerify signature", se); + } + + this.signature = temporary; + } + + TLCP11CertificateVerifyMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // This happens in server side only. + ServerHandshakeContext shc = + (ServerHandshakeContext) handshakeContext; + + // struct { + // SignatureAndHashAlgorithm algorithm; + // opaque signature<0..2^16-1>; + // } DigitallySigned; + + if (m.remaining() < 2) { + throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateVerify message: no sufficient data"); + } + + // read and verify the signature + TLCP11Credentials tlcpCredentials = null; + for (SSLCredentials cd : shc.handshakeCredentials) { + if (cd instanceof TLCP11Credentials) { + tlcpCredentials = (TLCP11Credentials) cd; + break; + } + } + + if (tlcpCredentials == null || + tlcpCredentials.popSignPublicKey == null) { + throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No X509 credentials negotiated for CertificateVerify"); + } + + // opaque signature<0..2^16-1>; + this.signature = Record.getBytes16(m); + + if (!(SMUtil.isSMCert(tlcpCredentials.popSignCert))) { + throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Only support SM certificate"); + } + + try { + Signature signer = SignatureScheme.SM2SIG_SM3.getVerifier( + tlcpCredentials.popSignPublicKey); + + signer.update(shc.handshakeHash.digest()); + if (!signer.verify(signature)) { + throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid CertificateVerify signature"); + } + } catch (NoSuchAlgorithmException | + InvalidAlgorithmParameterException nsae) { + throw shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (sm2sig_sm3) " + + "used in CertificateVerify handshake message", nsae); + } catch (InvalidKeyException | SignatureException ikse) { + throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot verify CertificateVerify signature", ikse); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE_VERIFY; + } + + @Override + public int messageLength() { + // It isn't (4 + signature.length) due to no signature scheme. + return 2 + signature.length; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putBytes16(signature); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"CertificateVerify\": '{'\n" + + " \"signature algorithm\": sm2sig_sm3\n" + + " \"signature\": '{'\n" + + "{0}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + Utilities.indent( + hexEncoder.encodeBuffer(signature), " ") + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "CertificateVerify" handshake message producer. + */ + private static final + class TLCP11CertificateVerifyProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private TLCP11CertificateVerifyProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + SSLHandshake.HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + TLCP11Possession tlcpPossession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof TLCP11Possession) { + tlcpPossession = (TLCP11Possession)possession; + break; + } + } + + if (tlcpPossession == null || + tlcpPossession.popSignPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 credentials negotiated for CertificateVerify"); + } + + return null; + } + + TLCP11CertificateVerifyMessage cvm = + new TLCP11CertificateVerifyMessage(chc, tlcpPossession); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced CertificateVerify handshake message", cvm); + } + + // Output the handshake message. + cvm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "CertificateVerify" handshake message consumer. + */ + private static final + class TLCP11CertificateVerifyConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private TLCP11CertificateVerifyConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // Clean up this consumer + shc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_VERIFY.id); + + // Ensure that the CV message follows the CKE + if (shc.handshakeConsumers.containsKey( + SSLHandshake.CLIENT_KEY_EXCHANGE.id)) { + throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected CertificateVerify handshake message"); + } + + TLCP11CertificateVerifyMessage cvm = + new TLCP11CertificateVerifyMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming CertificateVerify handshake message", cvm); + } + + // + // update + // + // Need no additional validation. + + // + // produce + // + // Need no new handshake message producers here. + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/TLCPContexts.java b/src/java.base/share/classes/sun/security/ssl/TLCPContexts.java new file mode 100644 index 00000000000..6f5ea0b408a --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/TLCPContexts.java @@ -0,0 +1,160 @@ +/* + * 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 + * 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.ssl; + +import java.util.ArrayList; +import java.util.List; + +import sun.security.ssl.SSLContextImpl.AbstractTLSContext; + +public class TLCPContexts { + + public static final class TLCP11Context extends AbstractTLSContext { + + private static final List serverDefaultProtocols; + private static final List serverDefaultCipherSuites; + + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; + + static { + serverDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLCP11, + }); + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLCP11, + }); + + serverDefaultCipherSuites = getApplicableEnabledCipherSuites( + serverDefaultProtocols, false); + clientDefaultCipherSuites = getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); + } + + @Override + List getServerDefaultProtocolVersions() { + return serverDefaultProtocols; + } + + @Override + List getServerDefaultCipherSuites() { + return serverDefaultCipherSuites; + } + + @Override + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; + } + + @Override + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; + } + } + + public static final class TLCPContext extends AbstractTLSContext { + + private static final List serverDefaultProtocols; + private static final List serverDefaultCipherSuites; + + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; + + private static final IllegalArgumentException reservedException; + + static { + reservedException = CustomizedSSLProtocols.reservedException; + if (reservedException == null) { + clientDefaultProtocols = customizedProtocols(true, + CustomizedSSLProtocols.customizedClientProtocols); + serverDefaultProtocols = customizedProtocols(false, + CustomizedSSLProtocols.customizedServerProtocols); + + clientDefaultCipherSuites = + getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); + serverDefaultCipherSuites = + getApplicableEnabledCipherSuites( + serverDefaultProtocols, false); + } else { + // unlikely to be used + clientDefaultProtocols = null; + serverDefaultProtocols = null; + clientDefaultCipherSuites = null; + serverDefaultCipherSuites = null; + } + } + + private static List customizedProtocols( + boolean client, List customized) { + List refactored = new ArrayList<>(); + for (ProtocolVersion pv : customized) { + refactored.add(pv); + } + + // Use the default enabled protocols if no customization + ProtocolVersion[] candidates; + if (refactored.isEmpty()) { + // Client and server use the same default protocols. + candidates = new ProtocolVersion[] { + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLCP11, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10 + }; + } else { + // Use the customized TLS protocols. + candidates = + refactored.toArray(new ProtocolVersion[0]); + } + + return getAvailableProtocols(candidates); + } + + @Override + List getServerDefaultProtocolVersions() { + return serverDefaultProtocols; + } + + @Override + List getServerDefaultCipherSuites() { + return serverDefaultCipherSuites; + } + + @Override + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; + } + + @Override + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/TLCPKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/TLCPKeyExchange.java new file mode 100644 index 00000000000..ae7f66db4d7 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/TLCPKeyExchange.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2022, 2024, 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.util.AbstractMap; +import java.util.Map; + +final class TLCPKeyExchange { + + enum TLCP11KeyAgreement implements SSLKeyAgreement { + SM2 ("sm2", SM2KeyExchange.sm2PoGenerator, + SM2KeyExchange.sm2KAGenerator), + SM2E ("sm2e", SM2EKeyExchange.sm2ePoGenerator, + SM2EKeyExchange.sm2eKAGenerator); + + final String name; + final SSLPossessionGenerator possessionGenerator; + final SSLKeyAgreementGenerator keyAgreementGenerator; + + TLCP11KeyAgreement(String name, + SSLPossessionGenerator possessionGenerator, + SSLKeyAgreementGenerator keyAgreementGenerator) { + this.name = name; + this.possessionGenerator = possessionGenerator; + this.keyAgreementGenerator = keyAgreementGenerator; + } + + @Override + public SSLPossession createPossession(HandshakeContext context) { + if (possessionGenerator != null) { + return possessionGenerator.createPossession(context); + } + + return null; + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + return keyAgreementGenerator.createKeyDerivation(context); + } + + @Override + public SSLHandshake[] getRelatedHandshakers( + HandshakeContext handshakeContext) { + if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + if (this.possessionGenerator != null) { + return new SSLHandshake[] { + SSLHandshake.SERVER_KEY_EXCHANGE + }; + } + } + + return new SSLHandshake[0]; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public Map.Entry[] getHandshakeProducers( + HandshakeContext handshakeContext) { + if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + return (Map.Entry[])(new Map.Entry[0]); + } + + if (handshakeContext.sslConfig.isClientMode) { + switch (this) { + case SM2: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + SM2ClientKeyExchange.sm2HandshakeProducer + ) + }); + + case SM2E: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + SM2EClientKeyExchange.sm2eHandshakeProducer + ) + }); + } + } else { + switch (this) { + case SM2: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + SM2ServerKeyExchange.sm2HandshakeProducer + ) + }); + case SM2E: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + SM2EServerKeyExchange.sm2eHandshakeProducer + ) + }); + } + } + + return (Map.Entry[])(new Map.Entry[0]); + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public Map.Entry[] getHandshakeConsumers( + HandshakeContext handshakeContext) { + if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + return (Map.Entry[])(new Map.Entry[0]); + } + + if (handshakeContext.sslConfig.isClientMode) { + switch (this) { + case SM2: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + SM2ServerKeyExchange.sm2HandshakeConsumer + ) + }); + case SM2E: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + SM2EServerKeyExchange.sm2eHandshakeConsumer + ) + }); + } + } else { + switch (this) { + case SM2: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + SM2ClientKeyExchange.sm2HandshakeConsumer + ) + }); + + case SM2E: + return (Map.Entry[])(new Map.Entry[] { + new AbstractMap.SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + SM2EClientKeyExchange.sm2eHandshakeConsumer + ) + }); + } + } + + return (Map.Entry[])(new Map.Entry[0]); + } + } +} 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 b0572d8a567..b9e62092840 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/util/Debug.java b/src/java.base/share/classes/sun/security/util/Debug.java index aca672bdb31..ef03423f322 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,11 @@ public static String toString(byte[] b) { return HexFormat.ofDelimiter(":").formatHex(b); } + // 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/SMUtil.java b/src/java.base/share/classes/sun/security/util/SMUtil.java index f0576cb2d20..c9f2ac49812 100644 --- a/src/java.base/share/classes/sun/security/util/SMUtil.java +++ b/src/java.base/share/classes/sun/security/util/SMUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2023, 2024, 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 @@ -22,10 +22,13 @@ import java.math.BigInteger; import java.security.InvalidKeyException; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; import java.security.spec.ECField; import java.security.spec.ECFieldFp; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; +import java.security.spec.SM2ParameterSpec; /** * The utilities for ShangMi features. @@ -113,6 +116,21 @@ public static byte[] hexToBytesLE(String hex) { return byteArr; } + public static byte[] encodePubPoint(ECPoint pubPoint) { + byte[] x = bigIntToBytes32(pubPoint.getAffineX()); + byte[] y = bigIntToBytes32(pubPoint.getAffineY()); + + byte[] encoded = new byte[65]; + encoded[0] = 0x04; + System.arraycopy(x, 0, encoded, 1, 32); + System.arraycopy(y, 0, encoded, 33, 32); + return encoded; + } + + public static byte[] encodePrivKey(BigInteger privKeyValue) { + return bigIntToBytes32(privKeyValue); + } + // Partial Public key validation as described in NIST SP 800-186 Appendix D.1.1.1. // The extra step in the full validation (described in Appendix D.1.1.2) is implemented // as sun.security.ec.ECOperations#checkOrder inside the jdk.crypto.ec module. @@ -150,5 +168,43 @@ public static void validatePublicKey(ECPoint point, ECParameterSpec spec) } } + // An SM certificate must use curveSM2 as ECC curve + // and SM2withSM3 as signature scheme. + public static boolean isSMCert(X509Certificate cert) { + if (!(cert.getPublicKey() instanceof ECPublicKey)) { + return false; + } + + ECParameterSpec ecParams = ((ECPublicKey) cert.getPublicKey()).getParams(); + return SM2ParameterSpec.ORDER.equals(ecParams.getOrder()) + && KnownOIDs.SM3withSM2.value().equals(cert.getSigAlgOID()); + } + + // CA has basic constraints extension. + public static boolean isCA(X509Certificate certificate) { + return certificate.getBasicConstraints() != -1; + } + + // If the key usage is critical, it must contain digitalSignature. + public static boolean isSignCert(X509Certificate certificate) { + if (certificate == null) { + return false; + } + + boolean[] keyUsage = certificate.getKeyUsage(); + return keyUsage == null || keyUsage[0]; + } + + // If the key usage is critical, it must contain one or more of + // keyEncipherment, dataEncipherment and keyAgreement. + public static boolean isEncCert(X509Certificate certificate) { + if (certificate == null) { + return false; + } + + boolean[] keyUsage = certificate.getKeyUsage(); + return keyUsage == null || keyUsage[2] || keyUsage[3] || keyUsage[4]; + } + private SMUtil() { } } 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 8e201e550ed..17b9e7248c0 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, 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,6 +53,22 @@ 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); + } }; /** diff --git a/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java b/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java index 03675d0b257..93902b72756 100644 --- a/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java +++ b/src/java.base/share/classes/sun/security/validator/EndEntityChecker.java @@ -281,7 +281,12 @@ private void checkTLSServer(X509Certificate cert, String parameter, ("KeyUsage does not allow key encipherment", ValidatorException.T_EE_EXTENSIONS, cert); } - } else if (KU_SERVER_SIGNATURE.contains(parameter)) { + } else if (KU_SERVER_SIGNATURE.contains(parameter) + // SM2 and SM2E are used on TLCP 1.1 only, + // and the first certificate, namely sign certificate, + // always has digitalSignature key usage. + || "SM2".equalsIgnoreCase(parameter) + || "SM2E".equalsIgnoreCase(parameter)) { if (checkKeyUsage(cert, KU_SIGNATURE) == false) { throw new ValidatorException ("KeyUsage does not allow digital signatures", 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 00000000000..4c4906d8eb3 --- /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/util/resources/CurrencyNames.properties b/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties index c231cadc416..3ed4f873a3c 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 67f294355a1..2aa9a9630be 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 1554541d126..bac4a949d4c 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 8f6e1e12a7e..9bd39207b79 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -737,7 +737,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) @@ -1280,6 +1281,9 @@ 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. +# # 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. @@ -1291,7 +1295,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 # # FilePermission path canonicalization diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c index 2993d2551f6..18c515d8678 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 fbbd9d850fc..68fd756ada3 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 5bdd85c4f89..a3ebfdf271d 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 7f86826f077..5f2f04da52c 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/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index bb340a8f120..640f449d3f0 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -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. diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 660665392c1..5c40fc8b320 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 503a2457b04..71c162b1813 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/libnet/NetworkInterface.c b/src/java.base/unix/native/libnet/NetworkInterface.c index 990bc1bcc51..dcaf3fd51d1 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 4ec11a13626..139cb27b4fb 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 be578a0bb4b..ea9eac66963 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,7 +699,7 @@ 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 @@ -748,7 +760,7 @@ 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 @@ -779,7 +791,7 @@ 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 @@ -812,7 +824,7 @@ 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 diff --git a/src/java.base/windows/native/libjava/Console_md.c b/src/java.base/windows/native/libjava/Console_md.c index 02b56cc531a..1c41d07d1a4 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 061600c87a3..c5d0edcd732 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 9fe8485171d..33cf1abb6b9 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, 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 @@ -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; } @@ -575,7 +576,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 83e30287196..a5741a2c1ad 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 230be0c7c8f..3e29f2debc4 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 792f7ec869e..fb1501c8fde 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_MidiUtils.c b/src/java.desktop/linux/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiUtils.c index 3f482c824ad..de988fd86b7 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) @@ -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 da7b9a84745..c4d16f3e10e 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 b973cc839fd..bd1127c2e31 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/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index 31047d2f5f5..068a1b46af3 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 19ac4818928..0ab38fc06b4 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/LWCToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java index 1b037510299..a3281ce7b68 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/CPrinterJob.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m index 545138de717..9cbd48bf843 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/OutlineAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/OutlineAccessibility.h index 2992d82cbe4..8281f0b0213 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 cdf6dbbd4ab..08240614bf7 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 a35d5401474..1e2ba23d9aa 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 c745e8715c1..fdd855b6994 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 ed2de311c22..5f868f6e408 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/java/awt/Robot.java b/src/java.desktop/share/classes/java/awt/Robot.java index 692da44c956..2054954fee3 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/javax/swing/JEditorPane.java b/src/java.desktop/share/classes/javax/swing/JEditorPane.java index 6d708f1b8b8..fa63442c637 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 @@ -618,35 +618,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); } } diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index e4eb45c28a9..115a39893b3 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/plaf/basic/BasicDirectoryModel.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java index 34c53ca915f..16cbaad53a7 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/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index c14b5a126e4..58eb7c9c8eb 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 a100581567f..89a335f19ba 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 { 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 9d423dcded9..4d6ae0e0ae1 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 958b3a899a0..5ef1e54b585 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 @@ -2822,18 +2858,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/sun/awt/SunToolkit.java b/src/java.desktop/share/classes/sun/awt/SunToolkit.java index 195c246b2ff..6f501acc346 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. * @@ -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/font/FileFontStrike.java b/src/java.desktop/share/classes/sun/font/FileFontStrike.java index ea2a1608f2d..bf98b8ca578 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/legal/giflib.md b/src/java.desktop/share/legal/giflib.md index 0be4fb8226e..8b8fde8706d 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 f420ccd94ed..cbffed81332 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 cd6dbe233d3..3cbe05327c2 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 117ebab6e5e..31424fb6c3c 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 b7e59341cf2..b1b216287a5 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 ae416c232d2..89f02cf67fb 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/libfontmanager/freetypeScaler.c b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c
    index 4cdd0855fe8..7d5b1ed94fe 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/libharfbuzz/hb-meta.hh b/src/java.desktop/share/native/libharfbuzz/hb-meta.hh
    index c3af0e75e5f..d8d12f0aeb3 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/libsplashscreen/giflib/dgif_lib.c b/src/java.desktop/share/native/libsplashscreen/giflib/dgif_lib.c
    index 6ddfb46060d..0b2860b4b50 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 db08838efff..3b6785f7c63 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 6cabd0866ed..bd00af64161 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 f739b36adfd..74a2e969c0d 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 4f832676ffc..f905e0d7b48 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 75b74b4fba0..5aef3044558 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 452df69d7cd..7420af674c5 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 2d8c585c0e7..441b57ecf1a 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 086d1c2fda6..25f298f0fcf 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 dedd2c1639e..a6ca3ae9f94 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 93c8f5bb703..88200db5d73 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 91a92e5f718..232dff876c7 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 578841c9580..9f61a773c1d 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 41cbc91d398..b3b441b1122 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 623735f06f1..ea8dd172197 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 6e510b27327..41e0a5abc3a 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 e98d49cf0cc..e238ccdb9a4 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 a98b2013256..816631cae18 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 914d0b97b1d..18424542b00 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 3631e60f36b..e9e94477545 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 843eb5fff2a..a393de4b79d 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 524297c5a10..5280140d12b 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 62612a02278..f53ab6fa1d1 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 89a62191b6f..2350057e70e 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 d785823ea8e..7446c196a46 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/XBaseWindow.java b/src/java.desktop/unix/classes/sun/awt/X11/XBaseWindow.java
    index 939fbcbbcd4..aaad0f15047 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 c885d36b20e..29d3c962e79 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 ab929c324c2..37d99690013 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/XPopupMenuPeer.java b/src/java.desktop/unix/classes/sun/awt/X11/XPopupMenuPeer.java
    index a19f56249ae..7a3bd18ae28 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 2fd47bf458a..6d155c0bcc8 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 409b12b1425..0365bffebcb 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/X11GraphicsEnvironment.java b/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java
    index 8206d18906f..93f5eaf6656 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/screencast/ScreencastHelper.java b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java
    new file mode 100644
    index 00000000000..78661587554
    --- /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 00000000000..74055d821b2
    --- /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 00000000000..3daf9b2a8b8
    --- /dev/null
    +++ b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java
    @@ -0,0 +1,461 @@
    +/*
    + * 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.
    + */
    +@SuppressWarnings("removal")
    +final class TokenStorage {
    +
    +    private TokenStorage() {}
    +
    +    private static final String REL_NAME =
    +            ".awt/robot/screencast-tokens.properties";
    +
    +    private static final Properties PROPS = new Properties();
    +    private static final Path PROPS_PATH;
    +    private static final Path PROP_FILENAME;
    +
    +    private static void doPrivilegedRunnable(Runnable runnable) {
    +        AccessController.doPrivileged(new PrivilegedAction() {
    +            @Override
    +            public Void run() {
    +                runnable.run();
    +                return null;
    +            }
    +        });
    +    }
    +
    +    static {
    +        PROPS_PATH = AccessController.doPrivileged(new PrivilegedAction() {
    +            @Override
    +            public Path run() {
    +                return setupPath();
    +            }
    +        });
    +
    +        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 workdir = path.getParent();
    +
    +        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 (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("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 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()
    +            || 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("remove malformed records");
    +        }
    +    }
    +
    +    private static void store(String failMsg) {
    +        if (!isWritable()) {
    +            return;
    +        }
    +
    +        synchronized (PROPS) {
    +            try (BufferedWriter writer = Files.newBufferedWriter(PROPS_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() {
    +        if (PROPS_PATH == null
    +            || (Files.exists(PROPS_PATH) && !Files.isWritable(PROPS_PATH))) {
    +
    +            if (SCREENCAST_DEBUG) {
    +                System.err.printf(
    +                        "Token storage: %s is not writable\n", PROPS_PATH);
    +            }
    +            return false;
    +        } else {
    +            return true;
    +        }
    +    }
    +}
    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 536962bd820..588dfbf882a 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/legal/pipewire.md b/src/java.desktop/unix/legal/pipewire.md
    new file mode 100644
    index 00000000000..88389a74e69
    --- /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 dbe236eee47..817707789c9 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 00000000000..e5985af5eee --- /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 d786117ce78..4a9773d333a 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, 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 @@ -86,13 +86,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 467ca3f8f1a..112b1b28f57 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 19a1e2ddaf5..ed5997cb0cd 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, 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 @@ -235,13 +235,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 +368,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 +486,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 +513,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 +528,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 +652,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 98097e18301..aac18571815 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 00000000000..e2f4ea31d2e --- /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 00000000000..07a7e91304c --- /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 00000000000..8590cf27da2 --- /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 00000000000..9ac210217ff --- /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 00000000000..b02baf6c4c5 --- /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 00000000000..23a9e16dfbf --- /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 00000000000..b7de764657a --- /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 00000000000..bb58cab97f5 --- /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 00000000000..3966be417bc --- /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 00000000000..f119a31b5db --- /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 00000000000..ecd8038a2c0 --- /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 00000000000..90096056a0c --- /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 00000000000..19110db81a4 --- /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 00000000000..a5e2ac7ffff --- /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 00000000000..bf67f12b7f1 --- /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 00000000000..a5208c0b944 --- /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 00000000000..ed04c7e5b96 --- /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 00000000000..a3a61a153c7 --- /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 00000000000..dda912e7c04 --- /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 00000000000..423127e6120 --- /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 00000000000..f5920c20600 --- /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 00000000000..0915fca6cee --- /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 00000000000..94c2ef1316a --- /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 00000000000..a25c8fd11c5 --- /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 00000000000..3d3d7908519 --- /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 00000000000..26c80bfa9db --- /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 00000000000..dc5257c2dfd --- /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 00000000000..5e07a784524 --- /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 00000000000..88b2c4cbcf3 --- /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 00000000000..fc8243a769b --- /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 00000000000..103f235cb66 --- /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 00000000000..6907090faaf --- /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 00000000000..51f4c2eaf75 --- /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 00000000000..50a42157602 --- /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 00000000000..54052f75311 --- /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 00000000000..8a3aa49aabf --- /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 00000000000..0309223ac70 --- /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 00000000000..84a78a7e12d --- /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 00000000000..8561a00aebd --- /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 00000000000..a7ce08246c2 --- /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 00000000000..987d75a1669 --- /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 00000000000..6834c6e5e39 --- /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 00000000000..a02aa3c4c3e --- /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 00000000000..b4edb6f6efe --- /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 00000000000..a7b42546523 --- /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 00000000000..6b9822bad35 --- /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 00000000000..5fa40b59b9f --- /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 00000000000..ff2ddde1cd9 --- /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 00000000000..48e7d9349d5 --- /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 00000000000..b25d9ced193 --- /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 00000000000..ab77a183414 --- /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 00000000000..1176a0dd5a3 --- /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 00000000000..c31b714d17e --- /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 00000000000..a1abc02a130 --- /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 00000000000..e65835a1a85 --- /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 00000000000..5e4e0a0c9d6 --- /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 00000000000..8ecf6f69bc4 --- /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 00000000000..8eb839d3f7b --- /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 00000000000..ad4f7ff40bf --- /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 00000000000..730a1f53607 --- /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 00000000000..528018dbd3a --- /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 00000000000..0fdb968c841 --- /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 00000000000..2d4a83c2928 --- /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 00000000000..6bd5d3c5a39 --- /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 00000000000..670da5af3f2 --- /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 00000000000..31d33101774 --- /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 00000000000..b4daf69e030 --- /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 00000000000..89c73c98f7e --- /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 00000000000..b5071ff0a35 --- /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 00000000000..5cb323e8077 --- /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 00000000000..59725d7148b --- /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 00000000000..10a19323f44 --- /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 00000000000..743dd4cd1a5 --- /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 00000000000..e39c430136d --- /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 00000000000..5f5a5b64a51 --- /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 00000000000..04e00c9c1af --- /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 00000000000..d9283baa036 --- /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 00000000000..9290a44b8db --- /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 00000000000..23a75a2fd43 --- /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 00000000000..3dd24a8eb44 --- /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 00000000000..76c73e2e6ca --- /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 00000000000..9d54d57522a --- /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 00000000000..b64761604ba --- /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 00000000000..4beac560b25 --- /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 00000000000..6bd9dcd0159 --- /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 00000000000..66c238987d0 --- /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 00000000000..f34cd21f840 --- /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 00000000000..b374995b5cf --- /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 00000000000..81fc07b8a05 --- /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 00000000000..5d4169217b5 --- /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 00000000000..529b8fa38cd --- /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 00000000000..86a6b87bc49 --- /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 00000000000..fcadda93101 --- /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 929cfd313f4..8cab841c0da 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/shell/Win32ShellFolder2.java b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java index c8fc3a4627e..db3bde5889a 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 c75d7738144..20df99eb7d6 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/native/libawt/java2d/d3d/D3DShaderGen.c b/src/java.desktop/windows/native/libawt/java2d/d3d/D3DShaderGen.c index 88b38cd2582..e7e9f859116 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/ShellFolder2.cpp b/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp index cc2f567dc68..2d0745f763f 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_Component.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index 32ead20707e..03f54d6491c 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -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; } 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 706ab62177b..4d1f4fee19e 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 572d7427ddb..d48798b0d3f 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 c0c26b28f71..55dcb6e2882 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 38a5b6af0cc..46f3b03b18e 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 281e3d0ffac..4553a02c086 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 5c4b9ab0f6c..dc69b355850 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 4d7ce28a841..7d11ead1964 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/module-info.java b/src/java.naming/share/classes/module-info.java index 09e1093c13a..b354dad89d5 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 36af3005032..60d2b2b410a 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 07ba2f0ee60..9f2a4008d18 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 6edfe99f13a..efe0ccfb79d 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 669c173e3f8..8c796193015 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 9f74a70d318..0f2db7738fc 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 1aa19eeb16d..f363231e1c8 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 28c30bbf2c3..bb0dd914afb 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 d2b908dffa5..27e65446844 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 66d89ae1fc5..22e03238d21 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 5fc8eb36772..0b9cf64d490 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 00000000000..d81f52e6630 --- /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 3f3e4ff339a..aefae6507dc 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 a3f6a11aa3e..323042de3d7 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 00000000000..db873cdb05e --- /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 9d57a734ac3..881be12c67c 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 5e9df860feb..228f9bf0206 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 4188937b1ad..c603e917ca4 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 492915f638e..9ca1272dfb0 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 c39ae01d913..08b67e0abaf 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 b5874f5637e..43d74c37a6b 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 59c1a4458f8..caa702c2ed4 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 3cb0bf46cb8..67cd1315886 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 @@ -914,15 +914,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 +1063,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 +1413,8 @@ 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()); } } 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 24353cceeca..54102205026 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 c7dc72fdebe..d812fd63d2c 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/Krb5.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java index fabff57ae64..93831f5a3e6 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 813939643c3..4f124771d51 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 f81b7cf41ee..85d4ac0a50c 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/native/common/AccessBridgeDebug.cpp b/src/jdk.accessibility/windows/native/common/AccessBridgeDebug.cpp index f2dabc232e0..448fc8a631f 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/jaccessinspector/jaccessinspector.cpp b/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp index 5cfec9f2f63..de673a9408f 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 3a5353812a4..726ed4010bd 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 d5600ef5cc1..b9841d409b4 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 3d189600b42..3c00459df48 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 6c6ad58ecea..4863581d2c8 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 564c2e29bce..bf8d25f8c6a 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 f08a7c004b3..3e3118c327f 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 1968d182fe0..80b70ddd957 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.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java index eaac4f008c4..26b4171d165 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/wrapper/CK_PBE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java index e8b048869c4..7b874ced493 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 @@ -121,11 +121,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 5c0aacd1a67..421c4212361 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 @@ -730,6 +730,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. @@ -825,6 +843,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/native/libj2pkcs11/p11_convert.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c index d941b574cc7..3ea91a6cfd1 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 6d8cca4da83..d969fabffd0 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 520bd52a2cd..84edb3c5105 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: @@ -432,43 +432,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.mscapi/windows/native/libsunmscapi/security.cpp b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp index da1e3222632..82e6f2cc5bd 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/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c index 1101b999961..3068f475626 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/share/classes/sun/jvm/hotspot/code/CodeBlob.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/CodeBlob.java index d2f8db5efb9..74cc759dc0e 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/test/libproc/Makefile b/src/jdk.hotspot.agent/test/libproc/Makefile index 81fadaeb552..c7b171bc633 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 0d8e5d108e1..4ae0e895fc4 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 b79dc75bb09..9b7e0328dd5 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 454b6cd435c..9a27eeaf230 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 4935214c2e1..277e6bb4228 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 03e2a9ee8f5..0e136d80437 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 303db604a19..63d6a57258a 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 5555294cc27..4043fdd880d 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 2918e42a05f..76a1be6ec5f 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.jartool/share/man/jar.1 b/src/jdk.jartool/share/man/jar.1 index c0edcd46910..0e0c58bf426 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 dae63c2f0bf..92f2b9081db 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 86c57d2bf3a..494b78d8ade 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 3268bb55333..d7a0785190d 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.min.js b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.6.1.min.js deleted file mode 100644 index 2c69bc908b1..00000000000 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.6.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 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 ) { + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +} +var pop = arr.pop; + + +var sort = arr.sort; + + +var splice = arr.splice; + + +var whitespace = "[\\x20\\t\\r\\n\\f]"; + + +var rtrimCSS = new RegExp( + "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", + "g" +); + + + + +// Note: an element does not contain itself +jQuery.contains = function( a, b ) { + var bup = b && b.parentNode; + + return a === bup || !!( bup && bup.nodeType === 1 && ( + + // Support: IE 9 - 11+ + // IE doesn't have `contains` on SVG. + a.contains ? + a.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); +}; + + + + +// CSS string/identifier serialization +// https://drafts.csswg.org/cssom/#common-serializing-idioms +var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; + +function fcssescape( 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; +} + +jQuery.escapeSelector = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + + + + +var preferredDoc = document, + pushNative = push; + +( function() { + var i, - support, Expr, - getText, - isXML, - tokenize, - compile, - select, outermostContext, sortInput, hasDuplicate, + push = pushNative, // Local document vars - setDocument, document, - docElem, + documentElement, documentIsHTML, rbuggyQSA, - rbuggyMatches, matches, - contains, // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, + expando = jQuery.expando, dirruns = 0, done = 0, classCache = createCache(), @@ -570,47 +664,22 @@ var i, 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", + 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 + // Attribute selectors: https://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]" + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", @@ -629,101 +698,88 @@ var i, // 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 + - "*" ), + rleadingCombinator = 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" ), + 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 + + 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" ), + // https://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 ? + if ( 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 ) + " "; + return nonHex; } - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; + // 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 + return high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, - // Used for iframes - // See setDocument() + // Used for iframes; see `setDocument`. + // Support: IE 9 - 11+, Edge 12 - 18+ // Removing the function wrapper causes a "Permission Denied" - // error in IE + // error in IE/Edge. unloadHandler = function() { setDocument(); }, inDisabledFieldset = addCombinator( function( elem ) { - return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + return elem.disabled === true && nodeName( elem, "fieldset" ); }, { dir: "parentNode", next: "legend" } ); +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + // Optimize for push.apply( _, NodeList ) try { push.apply( @@ -731,32 +787,22 @@ try { preferredDoc.childNodes ); - // Support: Android<4.0 + // 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 ) { + push = { + apply: 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; + }, + call: function( target ) { + pushNative.apply( target, slice.call( arguments, 1 ) ); } }; } -function Sizzle( selector, context, results, seed ) { +function find( selector, context, results, seed ) { var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, @@ -790,11 +836,10 @@ function Sizzle( selector, context, results, seed ) { if ( nodeType === 9 ) { if ( ( elem = context.getElementById( m ) ) ) { - // Support: IE, Opera, Webkit - // TODO: identify versions + // Support: IE 9 only // getElementById can match elements by name instead of ID if ( elem.id === m ) { - results.push( elem ); + push.call( results, elem ); return results; } } else { @@ -804,14 +849,13 @@ function Sizzle( selector, context, results, seed ) { // Element context } else { - // Support: IE, Opera, Webkit - // TODO: identify versions + // Support: IE 9 only // getElementById can match elements by name instead of ID if ( newContext && ( elem = newContext.getElementById( m ) ) && - contains( context, elem ) && + find.contains( context, elem ) && elem.id === m ) { - results.push( elem ); + push.call( results, elem ); return results; } } @@ -822,22 +866,15 @@ function Sizzle( selector, context, results, seed ) { return results; // Class selector - } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && - context.getElementsByClassName ) { - + } else if ( ( m = match[ 3 ] ) && 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" ) ) { + if ( !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) { newSelector = selector; newContext = context; @@ -850,7 +887,7 @@ function Sizzle( selector, context, results, seed ) { // as such selectors are not recognized by querySelectorAll. // Thanks to Andrew Dupont for this technique. if ( nodeType === 1 && - ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) { // Expand context for sibling selectors newContext = rsibling.test( selector ) && testContext( context.parentNode ) || @@ -858,11 +895,15 @@ function Sizzle( selector, context, results, seed ) { // 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 ) { + // 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 ( newContext != context || !support.scope ) { // Capture the context ID, setting it first if necessary if ( ( nid = context.getAttribute( "id" ) ) ) { - nid = nid.replace( rcssescape, fcssescape ); + nid = jQuery.escapeSelector( nid ); } else { context.setAttribute( "id", ( nid = expando ) ); } @@ -895,7 +936,7 @@ function Sizzle( selector, context, results, seed ) { } // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); + return select( selector.replace( rtrimCSS, "$1" ), context, results, seed ); } /** @@ -909,7 +950,8 @@ function createCache() { function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + // Use (key + " ") to avoid collision with native prototype properties + // (see https://github.com/jquery/sizzle/issues/157) if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries @@ -921,7 +963,7 @@ function createCache() { } /** - * Mark a function for special use by Sizzle + * Mark a function for special use by jQuery selector module * @param {Function} fn The function to mark */ function markFunction( fn ) { @@ -952,56 +994,13 @@ function assert( fn ) { } } -/** - * 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; + return nodeName( elem, "input" ) && elem.type === type; }; } @@ -1011,8 +1010,8 @@ function createInputPseudo( type ) { */ function createButtonPseudo( type ) { return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return ( name === "input" || name === "button" ) && elem.type === type; + return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) && + elem.type === type; }; } @@ -1048,14 +1047,13 @@ function createDisabledPseudo( disabled ) { } } - // Support: IE 6 - 11 + // 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; + inDisabledFieldset( elem ) === disabled; } return elem.disabled === disabled; @@ -1095,7 +1093,7 @@ function createPositionalPseudo( fn ) { } /** - * Checks a node for validity as a Sizzle context + * Checks a node for validity as a jQuery selector context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ @@ -1103,31 +1101,13 @@ 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 + * @param {Element|Object} [node] 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, +function setDocument( node ) { + var subWindow, doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected @@ -1141,87 +1121,90 @@ setDocument = Sizzle.setDocument = function( node ) { // Update global variables document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); + documentElement = document.documentElement; + documentIsHTML = !jQuery.isXMLDoc( document ); + + // Support: iOS 7 only, IE 9 - 11+ + // Older browsers didn't support unprefixed `matches`. + matches = documentElement.matches || + documentElement.webkitMatchesSelector || + documentElement.msMatchesSelector; // 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 ) { + // Accessing iframe documents after unload throws "permission denied" errors + // (see trac-13936). + // Limit the fix to IE & Edge Legacy; despite Edge 15+ implementing `matches`, + // all IE 9+ and Edge Legacy versions implement `msMatchesSelector` as well. + if ( documentElement.msMatchesSelector && - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); + // 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 + preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } + // Support: IE 9 - 11+, Edge 12 - 18+ + subWindow.addEventListener( "unload", 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; + // 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 ) { + documentElement.appendChild( el ).id = jQuery.expando; + return !document.getElementsByName || + !document.getElementsByName( jQuery.expando ).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" ); + // Support: IE 9 only + // Check to see if it's possible to do matchesSelector + // on a disconnected node. + support.disconnectedMatch = assert( function( el ) { + return matches.call( el, "*" ); } ); - /* 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 - 11+, Edge 12 - 18+ + // IE/Edge don't support the :scope pseudo-class. + support.scope = assert( function() { + return document.querySelectorAll( ":scope" ); } ); - // 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; + // Support: Chrome 105 - 111 only, Safari 15.4 - 16.3 only + // Make sure the `:has()` argument is parsed unforgivingly. + // We include `*` in the test to detect buggy implementations that are + // _selectively_ forgiving (specifically when the list includes at least + // one valid selector). + // Note that we treat complete lack of support for `:has()` as if it were + // spec-compliant support, which is fine because use of `:has()` in such + // environments will fail in the qSA path and fall back to jQuery traversal + // anyway. + support.cssHas = assert( function() { + try { + document.querySelector( ":has(*,:jqfake)" ); + return false; + } catch ( e ) { + return true; + } } ); // ID filter and find if ( support.getById ) { - Expr.filter[ "ID" ] = function( id ) { + 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 ) { + 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 ) { + Expr.filter.ID = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== "undefined" && @@ -1232,7 +1215,7 @@ setDocument = Sizzle.setDocument = function( node ) { // Support: IE 6 - 7 only // getElementById is not reliable as a find shortcut - Expr.find[ "ID" ] = function( id, context ) { + Expr.find.ID = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var node, i, elems, elem = context.getElementById( id ); @@ -1262,40 +1245,18 @@ setDocument = Sizzle.setDocument = function( node ) { } // 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 ); + Expr.find.TAG = function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); - // Filter out possible comments - if ( tag === "*" ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; + // DocumentFragment nodes don't have gEBTN + } else { + return context.querySelectorAll( tag ); + } + }; // Class - Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + Expr.find.CLASS = function( className, context ) { if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { return context.getElementsByClassName( className ); } @@ -1306,177 +1267,94 @@ setDocument = Sizzle.setDocument = function( node ) { // 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 ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { - var input; + 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 + "*(?:''|\"\")" ); - } + documentElement.appendChild( el ).innerHTML = + "" + + ""; - // 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: iOS <=7 - 8 only + // Boolean attributes and "value" are not treated correctly in some XML documents + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } - // 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: iOS <=7 - 8 only + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } - // Support: Opera 10 - 11 only - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll( "*,:x" ); - rbuggyQSA.push( ",.*:" ); - } ); - } + // Support: iOS 8 only + // 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( ".#.+[+~]" ); + } - if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector ) ) ) ) { + // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ + // In some of the document kinds, these selectors wouldn't work natively. + // This is probably OK but for backwards compatibility we want to maintain + // handling them through jQuery traversal in jQuery 3.x. + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } - assert( function( el ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE 9 - 11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ + // In some of the document kinds, these selectors wouldn't work natively. + // This is probably OK but for backwards compatibility we want to maintain + // handling them through jQuery traversal in jQuery 3.x. + documentElement.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // 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 + "*(?:''|\"\")" ); + } + } ); - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); + if ( !support.cssHas ) { - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - } ); + // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ + // Our regular `try-catch` mechanism fails to detect natively-unsupported + // pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`) + // in browsers that parse the `:has()` argument as a forgiving selector list. + // https://drafts.csswg.org/selectors/#relational now requires the argument + // to be parsed unforgivingly, but browsers have not yet fully adjusted. + rbuggyQSA.push( ":has" ); } 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 ) { + sortOrder = function( a, b ) { // Flag for duplicate removal if ( a === b ) { @@ -1510,8 +1388,8 @@ setDocument = Sizzle.setDocument = function( node ) { // 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 ) ) { + if ( a === document || a.ownerDocument == preferredDoc && + find.contains( preferredDoc, a ) ) { return -1; } @@ -1519,100 +1397,33 @@ setDocument = Sizzle.setDocument = function( node ) { // 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 ) ) { + if ( b === document || b.ownerDocument == preferredDoc && + find.contains( preferredDoc, b ) ) { return 1; } // Maintain original order return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + ( indexOf.call( sortInput, a ) - indexOf.call( 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 ); +find.matches = function( expr, elements ) { + return find( expr, null, null, elements ); }; -Sizzle.matchesSelector = function( elem, expr ) { +find.matchesSelector = function( elem, expr ) { setDocument( elem ); - if ( support.matchesSelector && documentIsHTML && + if ( documentIsHTML && !nonnativeSelectorCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); @@ -1620,9 +1431,9 @@ Sizzle.matchesSelector = function( 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 ) { + // 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 ) { @@ -1630,10 +1441,10 @@ Sizzle.matchesSelector = function( elem, expr ) { } } - return Sizzle( expr, document, null, [ elem ] ).length > 0; + return find( expr, document, null, [ elem ] ).length > 0; }; -Sizzle.contains = function( context, elem ) { +find.contains = function( context, elem ) { // Set document vars if needed // Support: IE 11+, Edge 17 - 18+ @@ -1643,10 +1454,11 @@ Sizzle.contains = function( context, elem ) { if ( ( context.ownerDocument || context ) != document ) { setDocument( context ); } - return contains( context, elem ); + return jQuery.contains( context, elem ); }; -Sizzle.attr = function( elem, name ) { + +find.attr = function( elem, name ) { // Set document vars if needed // Support: IE 11+, Edge 17 - 18+ @@ -1659,25 +1471,19 @@ Sizzle.attr = function( elem, name ) { var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) + // Don't get fooled by Object.prototype properties (see trac-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; -}; + if ( val !== undefined ) { + return val; + } -Sizzle.escape = function( sel ) { - return ( sel + "" ).replace( rcssescape, fcssescape ); + return elem.getAttribute( name ); }; -Sizzle.error = function( msg ) { +find.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; @@ -1685,16 +1491,20 @@ Sizzle.error = function( msg ) { * Document sorting and removing duplicates * @param {ArrayLike} results */ -Sizzle.uniqueSort = function( results ) { +jQuery.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 ); + // + // Support: Android <=4.0+ + // Testing for detecting duplicates is unpredictable so instead assume we can't + // depend on duplicate detection in all browsers without a stable sort. + hasDuplicate = !support.sortStable; + sortInput = !support.sortStable && slice.call( results, 0 ); + sort.call( results, sortOrder ); if ( hasDuplicate ) { while ( ( elem = results[ i++ ] ) ) { @@ -1703,7 +1513,7 @@ Sizzle.uniqueSort = function( results ) { } } while ( j-- ) { - results.splice( duplicates[ j ], 1 ); + splice.call( results, duplicates[ j ], 1 ); } } @@ -1714,47 +1524,11 @@ Sizzle.uniqueSort = function( results ) { 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; +jQuery.fn.uniqueSort = function() { + return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) ); }; -Expr = Sizzle.selectors = { +Expr = jQuery.expr = { // Can be adjusted by the user cacheLength: 50, @@ -1775,12 +1549,12 @@ Expr = Sizzle.selectors = { }, preFilter: { - "ATTR": function( match ) { + 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 ); + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" ) + .replace( runescape, funescape ); if ( match[ 2 ] === "~=" ) { match[ 3 ] = " " + match[ 3 ] + " "; @@ -1789,7 +1563,7 @@ Expr = Sizzle.selectors = { return match.slice( 0, 4 ); }, - "CHILD": function( match ) { + CHILD: function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) @@ -1807,29 +1581,30 @@ Expr = Sizzle.selectors = { // nth-* requires argument if ( !match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); + find.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" ) ); + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) + ); match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); - // other types prohibit arguments + // other types prohibit arguments } else if ( match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); + find.error( match[ 0 ] ); } return match; }, - "PSEUDO": function( match ) { + PSEUDO: function( match ) { var excess, unquoted = !match[ 6 ] && match[ 2 ]; - if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + if ( matchExpr.CHILD.test( match[ 0 ] ) ) { return null; } @@ -1858,36 +1633,36 @@ Expr = Sizzle.selectors = { filter: { - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + TAG: function( nodeNameSelector ) { + var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + return nodeName( elem, expectedNodeName ); }; }, - "CLASS": function( className ) { + 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" ) || - "" - ); + ( 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 ) { + ATTR: function( name, operator, check ) { return function( elem ) { - var result = Sizzle.attr( elem, name ); + var result = find.attr( elem, name ); if ( result == null ) { return operator === "!="; @@ -1898,22 +1673,34 @@ Expr = Sizzle.selectors = { 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 */ + if ( operator === "=" ) { + return result === check; + } + if ( operator === "!=" ) { + return result !== check; + } + if ( operator === "^=" ) { + return check && result.indexOf( check ) === 0; + } + if ( operator === "*=" ) { + return check && result.indexOf( check ) > -1; + } + if ( operator === "$=" ) { + return check && result.slice( -check.length ) === check; + } + if ( operator === "~=" ) { + return ( " " + result.replace( rwhitespace, " " ) + " " ) + .indexOf( check ) > -1; + } + if ( operator === "|=" ) { + return result === check || result.slice( 0, check.length + 1 ) === check + "-"; + } + return false; }; }, - "CHILD": function( type, what, _argument, first, last ) { + CHILD: function( type, what, _argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; @@ -1926,7 +1713,7 @@ Expr = Sizzle.selectors = { } : function( elem, _context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, + var cache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), @@ -1941,7 +1728,7 @@ Expr = Sizzle.selectors = { node = elem; while ( ( node = node[ dir ] ) ) { if ( ofType ? - node.nodeName.toLowerCase() === name : + nodeName( node, name ) : node.nodeType === 1 ) { return false; @@ -1960,17 +1747,8 @@ Expr = Sizzle.selectors = { 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 ] || []; + outerCache = parent[ expando ] || ( parent[ expando ] = {} ); + cache = outerCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex && cache[ 2 ]; node = nodeIndex && parent.childNodes[ nodeIndex ]; @@ -1982,7 +1760,7 @@ Expr = Sizzle.selectors = { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } @@ -1991,17 +1769,8 @@ Expr = Sizzle.selectors = { // 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 ] || []; + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + cache = outerCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex; } @@ -2015,7 +1784,7 @@ Expr = Sizzle.selectors = { ( diff = nodeIndex = 0 ) || start.pop() ) ) { if ( ( ofType ? - node.nodeName.toLowerCase() === name : + nodeName( node, name ) : node.nodeType === 1 ) && ++diff ) { @@ -2023,13 +1792,7 @@ Expr = Sizzle.selectors = { 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 ]; + outerCache[ type ] = [ dirruns, diff ]; } if ( node === elem ) { @@ -2047,19 +1810,19 @@ Expr = Sizzle.selectors = { }; }, - "PSEUDO": function( pseudo, argument ) { + PSEUDO: function( pseudo, argument ) { // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes + // https://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 ); + find.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function - // just as Sizzle does + // just as jQuery does if ( fn[ expando ] ) { return fn( argument ); } @@ -2073,7 +1836,7 @@ Expr = Sizzle.selectors = { matched = fn( seed, argument ), i = matched.length; while ( i-- ) { - idx = indexOf( seed, matched[ i ] ); + idx = indexOf.call( seed, matched[ i ] ); seed[ idx ] = !( matches[ idx ] = matched[ i ] ); } } ) : @@ -2089,14 +1852,14 @@ Expr = Sizzle.selectors = { pseudos: { // Potentially complex pseudos - "not": markFunction( function( selector ) { + 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" ) ); + matcher = compile( selector.replace( rtrimCSS, "$1" ) ); return matcher[ expando ] ? markFunction( function( seed, matches, _context, xml ) { @@ -2115,22 +1878,23 @@ Expr = Sizzle.selectors = { input[ 0 ] = elem; matcher( input, null, xml, results ); - // Don't keep the element (issue #299) + // Don't keep the element + // (see https://github.com/jquery/sizzle/issues/299) input[ 0 ] = null; return !results.pop(); }; } ), - "has": markFunction( function( selector ) { + has: markFunction( function( selector ) { return function( elem ) { - return Sizzle( selector, elem ).length > 0; + return find( selector, elem ).length > 0; }; } ), - "contains": markFunction( function( text ) { + contains: markFunction( function( text ) { text = text.replace( runescape, funescape ); return function( elem ) { - return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1; }; } ), @@ -2140,12 +1904,12 @@ Expr = Sizzle.selectors = { // 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 ) { + // https://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 ); + find.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { @@ -2164,38 +1928,39 @@ Expr = Sizzle.selectors = { } ), // Miscellaneous - "target": function( elem ) { + target: function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, - "root": function( elem ) { - return elem === docElem; + root: function( elem ) { + return elem === documentElement; }, - "focus": function( elem ) { - return elem === document.activeElement && - ( !document.hasFocus || document.hasFocus() ) && + focus: function( elem ) { + return elem === safeActiveElement() && + document.hasFocus() && !!( elem.type || elem.href || ~elem.tabIndex ); }, // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), + enabled: createDisabledPseudo( false ), + disabled: createDisabledPseudo( true ), - "checked": function( elem ) { + 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 ); + // https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + return ( nodeName( elem, "input" ) && !!elem.checked ) || + ( nodeName( elem, "option" ) && !!elem.selected ); }, - "selected": function( elem ) { + selected: function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly + // Support: IE <=11+ + // Accessing the selectedIndex property + // forces the browser to treat the default option as + // selected when in an optgroup. if ( elem.parentNode ) { // eslint-disable-next-line no-unused-expressions elem.parentNode.selectedIndex; @@ -2205,9 +1970,9 @@ Expr = Sizzle.selectors = { }, // Contents - "empty": function( elem ) { + empty: function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo + // https://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 @@ -2219,49 +1984,49 @@ Expr = Sizzle.selectors = { return true; }, - "parent": function( elem ) { - return !Expr.pseudos[ "empty" ]( elem ); + parent: function( elem ) { + return !Expr.pseudos.empty( elem ); }, // Element/input types - "header": function( elem ) { + header: function( elem ) { return rheader.test( elem.nodeName ); }, - "input": function( elem ) { + input: function( elem ) { return rinputs.test( elem.nodeName ); }, - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; + button: function( elem ) { + return nodeName( elem, "input" ) && elem.type === "button" || + nodeName( elem, "button" ); }, - "text": function( elem ) { + text: function( elem ) { var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && + return nodeName( elem, "input" ) && elem.type === "text" && - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + // Support: IE <10 only + // 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() { + first: createPositionalPseudo( function() { return [ 0 ]; } ), - "last": createPositionalPseudo( function( _matchIndexes, length ) { + last: createPositionalPseudo( function( _matchIndexes, length ) { return [ length - 1 ]; } ), - "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + eq: createPositionalPseudo( function( _matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; } ), - "even": createPositionalPseudo( function( matchIndexes, length ) { + even: createPositionalPseudo( function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); @@ -2269,7 +2034,7 @@ Expr = Sizzle.selectors = { return matchIndexes; } ), - "odd": createPositionalPseudo( function( matchIndexes, length ) { + odd: createPositionalPseudo( function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); @@ -2277,19 +2042,24 @@ Expr = Sizzle.selectors = { return matchIndexes; } ), - "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? - argument + length : - argument > length ? - length : - argument; + lt: createPositionalPseudo( function( matchIndexes, length, argument ) { + var i; + + if ( argument < 0 ) { + i = argument + length; + } else if ( argument > length ) { + i = length; + } else { + i = argument; + } + for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; } ), - "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + gt: createPositionalPseudo( function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); @@ -2299,7 +2069,7 @@ Expr = Sizzle.selectors = { } }; -Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; +Expr.pseudos.nth = Expr.pseudos.eq; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { @@ -2314,7 +2084,7 @@ function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { +function tokenize( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; @@ -2342,13 +2112,13 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) { matched = false; // Combinators - if ( ( match = rcombinators.exec( soFar ) ) ) { + if ( ( match = rleadingCombinator.exec( soFar ) ) ) { matched = match.shift(); tokens.push( { value: matched, // Cast descendant combinators to space - type: match[ 0 ].replace( rtrim, " " ) + type: match[ 0 ].replace( rtrimCSS, " " ) } ); soFar = soFar.slice( matched.length ); } @@ -2375,14 +2145,16 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) { // 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 ) : + if ( parseOnly ) { + return soFar.length; + } - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; + return soFar ? + find.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} function toSelector( tokens ) { var i = 0, @@ -2415,7 +2187,7 @@ function addCombinator( matcher, combinator, base ) { // Check against all ancestor/preceding elements function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, + var oldCache, outerCache, newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching @@ -2432,14 +2204,9 @@ function addCombinator( matcher, combinator, base ) { 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() ) { + if ( skip && nodeName( elem, skip ) ) { elem = elem[ dir ] || elem; - } else if ( ( oldCache = uniqueCache[ key ] ) && + } else if ( ( oldCache = outerCache[ key ] ) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements @@ -2447,7 +2214,7 @@ function addCombinator( matcher, combinator, base ) { } else { // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; + outerCache[ key ] = newCache; // A match means we're done; a fail means we have to keep checking if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { @@ -2479,7 +2246,7 @@ function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { - Sizzle( selector, contexts[ i ], results ); + find( selector, contexts[ i ], results ); } return results; } @@ -2513,38 +2280,37 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS postFinder = setMatcher( postFinder, postSelector ); } return markFunction( function( seed, results, context, xml ) { - var temp, i, elem, + var temp, i, elem, matcherOut, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context - elems = seed || multipleContexts( - selector || "*", - context.nodeType ? [ context ] : 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, + elems; - matcherOut = matcher ? + if ( matcher ) { - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + // If we have a postFinder, or filtered seed, or non-seed postFilter + // or preexisting results, + matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - // ...intermediate processing is necessary - [] : + // ...intermediate processing is necessary + [] : - // ...otherwise use results directly - results : - matcherIn; + // ...otherwise use results directly + results; - // Find primary matches - if ( matcher ) { + // Find primary matches matcher( matcherIn, matcherOut, context, xml ); + } else { + matcherOut = matcherIn; } // Apply postFilter @@ -2582,7 +2348,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS i = matcherOut.length; while ( i-- ) { if ( ( elem = matcherOut[ i ] ) && - ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + ( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) { seed[ temp ] = !( results[ temp ] = elem ); } @@ -2617,15 +2383,21 @@ function matcherFromTokens( tokens ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; + return indexOf.call( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + + // 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 + var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || ( ( checkContext = context ).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) + // Avoid hanging onto element + // (see https://github.com/jquery/sizzle/issues/299) checkContext = null; return ret; } ]; @@ -2650,11 +2422,10 @@ function matcherFromTokens( tokens ) { 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" ), + // 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( rtrimCSS, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), @@ -2680,7 +2451,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { contextBackup = outermostContext, // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + 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 ), @@ -2696,8 +2467,9 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + // Support: iOS <=7 - 9 only + // Tolerate NodeList properties (IE: "length"; Safari: ) matching + // elements by id. (see trac-14142) for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { if ( byElement && elem ) { j = 0; @@ -2712,7 +2484,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } while ( ( matcher = elementMatchers[ j++ ] ) ) { if ( matcher( elem, context || document, xml ) ) { - results.push( elem ); + push.call( results, elem ); break; } } @@ -2775,7 +2547,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { - Sizzle.uniqueSort( results ); + jQuery.uniqueSort( results ); } } @@ -2793,7 +2565,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { superMatcher; } -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { +function compile( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], @@ -2816,27 +2588,25 @@ compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { } // Cache the compiled function - cached = compilerCache( - selector, - matcherFromGroupMatchers( elementMatchers, setMatchers ) - ); + 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 + * A low-level selection function that works with jQuery's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile + * selector function built with jQuery selector 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 ) { +function select( selector, context, results, seed ) { var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize( ( selector = compiled.selector || selector ) ); @@ -2850,10 +2620,12 @@ select = Sizzle.select = function( selector, context, results, seed ) { // 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.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { - context = ( Expr.find[ "ID" ]( token.matches[ 0 ] - .replace( runescape, funescape ), context ) || [] )[ 0 ]; + context = ( Expr.find.ID( + token.matches[ 0 ].replace( runescape, funescape ), + context + ) || [] )[ 0 ]; if ( !context ) { return results; @@ -2866,7 +2638,7 @@ select = Sizzle.select = function( selector, context, results, seed ) { } // Fetch a seed set for right-to-left matching - i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[ i ]; @@ -2879,8 +2651,8 @@ select = Sizzle.select = function( selector, context, results, seed ) { // 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 + rsibling.test( tokens[ 0 ].type ) && + testContext( context.parentNode ) || context ) ) ) { // If seed is empty or no tokens remain, we can return early @@ -2907,21 +2679,18 @@ select = Sizzle.select = function( selector, context, results, seed ) { !context || rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; -}; +} // One-time assignments +// Support: Android <=4.0 - 4.1+ // 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) +// Support: Android <=4.0 - 4.1+ // Detached nodes confoundingly follow *each other* support.sortDetached = assert( function( el ) { @@ -2929,68 +2698,29 @@ support.sortDetached = assert( function( el ) { 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; +jQuery.find = find; // 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; +jQuery.unique = jQuery.uniqueSort; +// These have always been private, but they used to be documented as part of +// Sizzle so let's maintain them for now for backwards compatibility purposes. +find.compile = compile; +find.select = select; +find.setDocument = setDocument; +find.tokenize = tokenize; +find.escape = jQuery.escapeSelector; +find.getText = jQuery.text; +find.isXML = jQuery.isXMLDoc; +find.selectors = jQuery.expr; +find.support = jQuery.support; +find.uniqueSort = jQuery.uniqueSort; + + /* eslint-enable */ + +} )(); var dir = function( elem, dir, until ) { @@ -3024,13 +2754,6 @@ var siblings = function( n, elem ) { 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 ); @@ -3281,7 +3004,7 @@ jQuery.fn.extend( { if ( cur.nodeType < 11 && ( targets ? targets.index( cur ) > -1 : - // Don't pass non-elements to Sizzle + // Don't pass non-elements to jQuery#find cur.nodeType === 1 && jQuery.find.matchesSelector( cur, selectors ) ) ) { @@ -3836,7 +3559,7 @@ jQuery.extend( { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, - process.stackTrace ); + process.error ); } // Support: Promises/A+ section 2.3.3.3.4.1 @@ -3864,10 +3587,17 @@ jQuery.extend( { process(); } else { - // Call an optional hook to record the stack, in case of exception + // Call an optional hook to record the error, in case of exception // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); + if ( jQuery.Deferred.getErrorHook ) { + process.error = jQuery.Deferred.getErrorHook(); + + // The deprecated alias of the above. While the name suggests + // returning the stack, not an error instance, jQuery just passes + // it directly to `console.warn` so both will work; an instance + // just better cooperates with source maps. + } else if ( jQuery.Deferred.getStackHook ) { + process.error = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } @@ -4042,12 +3772,16 @@ jQuery.extend( { // 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 ) { +// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error +// captured before the async barrier to get the original error cause +// which may otherwise be hidden. +jQuery.Deferred.exceptionHook = function( error, asyncError ) { // 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 ); + window.console.warn( "jQuery.Deferred exception: " + error.message, + error.stack, asyncError ); } }; @@ -5103,25 +4837,6 @@ 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; @@ -5559,7 +5274,7 @@ jQuery.event = { el.click && nodeName( el, "input" ) ) { // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", returnTrue ); + leverageNative( el, "click", true ); } // Return false to allow normal processing in the caller @@ -5610,10 +5325,10 @@ jQuery.event = { // 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 ) { +function leverageNative( el, type, isSetup ) { - // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add - if ( !expectSync ) { + // Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add + if ( !isSetup ) { if ( dataPriv.get( el, type ) === undefined ) { jQuery.event.add( el, type, returnTrue ); } @@ -5625,15 +5340,13 @@ function leverageNative( el, type, expectSync ) { jQuery.event.add( el, type, { namespace: false, handler: function( event ) { - var notAsync, result, + var 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 ) { + if ( !saved ) { // Store arguments for use when handling the inner native event // There will always be at least one argument (an event object), so this array @@ -5642,33 +5355,22 @@ function leverageNative( el, type, expectSync ) { 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 = {}; - } + dataPriv.set( this, type, false ); + 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; + return result; } // 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. + // (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. @@ -5678,22 +5380,25 @@ function leverageNative( el, type, expectSync ) { // 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 ) { + } else if ( saved ) { // ...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(); + dataPriv.set( this, type, jQuery.event.trigger( + saved[ 0 ], + saved.slice( 1 ), + this + ) ); + + // Abort handling of the native event by all jQuery handlers while allowing + // native handlers on the same element to run. On target, this is achieved + // by stopping immediate propagation just on the jQuery event. However, + // the native event is re-wrapped by a jQuery one on each level of the + // propagation so the only way to stop it for jQuery is to stop it for + // everyone via native `stopPropagation()`. This is not a problem for + // focus/blur which don't bubble, but it does also stop click on checkboxes + // and radios. We accept this limitation. + event.stopPropagation(); + event.isImmediatePropagationStopped = returnTrue; } } } ); @@ -5832,18 +5537,73 @@ jQuery.each( { }, jQuery.event.addProp ); jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + + function focusMappedHandler( nativeEvent ) { + if ( document.documentMode ) { + + // Support: IE 11+ + // Attach a single focusin/focusout handler on the document while someone wants + // focus/blur. This is because the former are synchronous in IE while the latter + // are async. In other browsers, all those handlers are invoked synchronously. + + // `handle` from private data would already wrap the event, but we need + // to change the `type` here. + var handle = dataPriv.get( this, "handle" ), + event = jQuery.event.fix( nativeEvent ); + event.type = nativeEvent.type === "focusin" ? "focus" : "blur"; + event.isSimulated = true; + + // First, handle focusin/focusout + handle( nativeEvent ); + + // ...then, handle focus/blur + // + // focus/blur don't bubble while focusin/focusout do; simulate the former by only + // invoking the handler at the lower level. + if ( event.target === event.currentTarget ) { + + // The setup part calls `leverageNative`, which, in turn, calls + // `jQuery.event.add`, so event handle will already have been set + // by this point. + handle( event ); + } + } else { + + // For non-IE browsers, attach a single capturing handler on the document + // while someone wants focusin/focusout. + jQuery.event.simulate( delegateType, nativeEvent.target, + jQuery.event.fix( nativeEvent ) ); + } + } + jQuery.event.special[ type ] = { // Utilize native event if possible so blur/focus sequence is correct setup: function() { + var attaches; + // Claim the first handler // dataPriv.set( this, "focus", ... ) // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, expectSync ); + leverageNative( this, type, true ); - // Return false to allow normal processing in the caller - return false; + if ( document.documentMode ) { + + // Support: IE 9 - 11+ + // We use the same native handler for focusin & focus (and focusout & blur) + // so we need to coordinate setup & teardown parts between those events. + // Use `delegateType` as the key as `type` is already used by `leverageNative`. + attaches = dataPriv.get( this, delegateType ); + if ( !attaches ) { + this.addEventListener( delegateType, focusMappedHandler ); + } + dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 ); + } else { + + // Return false to allow normal processing in the caller + return false; + } }, trigger: function() { @@ -5854,6 +5614,24 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp return true; }, + teardown: function() { + var attaches; + + if ( document.documentMode ) { + attaches = dataPriv.get( this, delegateType ) - 1; + if ( !attaches ) { + this.removeEventListener( delegateType, focusMappedHandler ); + dataPriv.remove( this, delegateType ); + } else { + dataPriv.set( this, delegateType, attaches ); + } + } else { + + // Return false to indicate standard teardown should be applied + return false; + } + }, + // Suppress native focus or blur if we're currently inside // a leveraged native-event stack _default: function( event ) { @@ -5862,6 +5640,58 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp delegateType: delegateType }; + + // 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 + // + // Support: IE 9 - 11+ + // To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch, + // attach a single handler for both events in IE. + jQuery.event.special[ delegateType ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + dataHolder = document.documentMode ? this : doc, + attaches = dataPriv.get( dataHolder, delegateType ); + + // Support: IE 9 - 11+ + // We use the same native handler for focusin & focus (and focusout & blur) + // so we need to coordinate setup & teardown parts between those events. + // Use `delegateType` as the key as `type` is already used by `leverageNative`. + if ( !attaches ) { + if ( document.documentMode ) { + this.addEventListener( delegateType, focusMappedHandler ); + } else { + doc.addEventListener( type, focusMappedHandler, true ); + } + } + dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + dataHolder = document.documentMode ? this : doc, + attaches = dataPriv.get( dataHolder, delegateType ) - 1; + + if ( !attaches ) { + if ( document.documentMode ) { + this.removeEventListener( delegateType, focusMappedHandler ); + } else { + doc.removeEventListener( type, focusMappedHandler, true ); + } + dataPriv.remove( dataHolder, delegateType ); + } else { + dataPriv.set( dataHolder, delegateType, attaches ); + } + } + }; } ); // Create mouseenter/leave events using mouseover/out and event-time checks @@ -6093,7 +5923,7 @@ function domManip( collection, args, callback, ignored ) { if ( hasScripts ) { doc = scripts[ scripts.length - 1 ].ownerDocument; - // Reenable scripts + // Re-enable scripts jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion @@ -6164,7 +5994,8 @@ jQuery.extend( { 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 + // We eschew jQuery#find here for performance reasons: + // https://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); @@ -6440,15 +6271,6 @@ var swap = function( elem, options, callback ) { var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); -var whitespace = "[\\x20\\t\\r\\n\\f]"; - - -var rtrimCSS = new RegExp( - "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", - "g" -); - - ( function() { @@ -6558,7 +6380,7 @@ var rtrimCSS = new RegExp( trChild = document.createElement( "div" ); table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; - tr.style.cssText = "border:1px solid"; + tr.style.cssText = "box-sizing:content-box;border:1px solid"; // Support: Chrome 86+ // Height set through cssText does not get applied. @@ -6570,7 +6392,7 @@ var rtrimCSS = new RegExp( // 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 + // Ensuring the div is `display: block` // gets around this issue. trChild.style.display = "block"; @@ -6608,17 +6430,37 @@ function curCSS( elem, name, computed ) { // .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 ) { + // Support: IE <=9 - 11+ + // IE only supports `"float"` in `getPropertyValue`; in computed styles + // it's only available as `"cssFloat"`. We no longer modify properties + // sent to `.css()` apart from camelCasing, so we need to check both. + // Normally, this would create difference in behavior: if + // `getPropertyValue` returns an empty string, the value returned + // by `.css()` would be `undefined`. This is usually the case for + // disconnected elements. However, in IE even disconnected elements + // with no styles return `"none"` for `getPropertyValue( "float" )` + ret = computed.getPropertyValue( name ) || computed[ name ]; - // rtrim treats U+000D CARRIAGE RETURN and U+000C FORM FEED + if ( isCustomProp && ret ) { + + // Support: Firefox 105+, Chrome <=105+ + // Spec requires trimming whitespace for custom properties (gh-4926). + // Firefox only trims leading whitespace. Chrome just collapses + // both leading & trailing whitespace to a single space. + // + // Fall back to `undefined` if empty string returned. + // This collapses a missing definition with property defined + // and set to an empty string but there's no standard API + // allowing us to differentiate them without a performance penalty + // and returning `undefined` aligns with older jQuery. + // + // rtrimCSS 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" ); + ret = ret.replace( rtrimCSS, "$1" ) || undefined; } if ( ret === "" && !isAttached( elem ) ) { @@ -6737,7 +6579,8 @@ function setPositiveNumber( _elem, value, subtract ) { function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { var i = dimension === "width" ? 1 : 0, extra = 0, - delta = 0; + delta = 0, + marginDelta = 0; // Adjustment may not be necessary if ( box === ( isBorderBox ? "border" : "content" ) ) { @@ -6747,8 +6590,10 @@ function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computed for ( ; i < 4; i += 2 ) { // Both box models exclude margin + // Count margin delta separately to only add it after scroll gutter adjustment. + // This is needed to make negative margins work with `outerHeight( true )` (gh-3982). if ( box === "margin" ) { - delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); } // If we get here with a content-box, we're seeking "padding" or "border" or "margin" @@ -6799,7 +6644,7 @@ function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computed ) ) || 0; } - return delta; + return delta + marginDelta; } function getWidthOrHeight( elem, dimension, extra ) { @@ -6897,26 +6742,35 @@ jQuery.extend( { // 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 + animationIterationCount: true, + aspectRatio: true, + borderImageSlice: true, + columnCount: 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, + scale: true, + widows: true, + zIndex: true, + zoom: true, + + // SVG-related + fillOpacity: true, + floodOpacity: true, + stopOpacity: true, + strokeMiterlimit: true, + strokeOpacity: true }, // Add in properties whose names you wish to fix before @@ -8642,9 +8496,39 @@ jQuery.each( [ "radio", "checkbox" ], function() { // Return jQuery for attributes-only inclusion +var location = window.location; + +var nonce = { guid: Date.now() }; +var rquery = ( /\?/ ); -support.focusin = "onfocusin" in window; + + +// 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 rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, @@ -8832,85 +8716,6 @@ jQuery.fn.extend( { } ); -// 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, @@ -10755,7 +10560,9 @@ jQuery.fn.extend( { }, hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + return this + .on( "mouseenter", fnOver ) + .on( "mouseleave", fnOut || fnOver ); } } ); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.7.1.min.js b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.7.1.min.js new file mode 100644 index 00000000000..7f37b5d9912 --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script-dir/jquery-3.7.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
      ",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0 0) { char buf[10]; - sprintf(buf, ".%d", i+1); + snprintf(buf, sizeof(buf), ".%d", i+1); strcat(nameBuffer, buf); } error = func(nameBuffer, arg); @@ -433,14 +433,14 @@ createStream(char *name, Stream *stream) jint error; char objectName[MAX_IPC_NAME]; - sprintf(objectName, "%s.mutex", name); + snprintf(objectName, sizeof(objectName), "%s.mutex", name); error = createWithGeneratedName(objectName, stream->shared->mutexName, createMutex, &stream->mutex); if (error != SYS_OK) { return error; } - sprintf(objectName, "%s.hasData", name); + snprintf(objectName, sizeof(objectName), "%s.hasData", name); error = createWithGeneratedName(objectName, stream->shared->hasDataEventName, createEvent, &stream->hasData); if (error != SYS_OK) { @@ -448,7 +448,7 @@ createStream(char *name, Stream *stream) return error; } - sprintf(objectName, "%s.hasSpace", name); + snprintf(objectName, sizeof(objectName), "%s.hasSpace", name); error = createWithGeneratedName(objectName, stream->shared->hasSpaceEventName, createEvent, &stream->hasSpace); if (error != SYS_OK) { @@ -571,7 +571,7 @@ openConnection(SharedMemoryTransport *transport, jlong otherPID, return SYS_NOMEM; } - sprintf(connection->name, "%s.%" PRId64, transport->name, sysProcessGetID()); + snprintf(connection->name, sizeof(connection->name), "%s.%" PRId64, transport->name, sysProcessGetID()); error = sysSharedMemOpen(connection->name, &connection->sharedMemory, &connection->shared); if (error != SYS_OK) { @@ -639,7 +639,7 @@ createConnection(SharedMemoryTransport *transport, jlong otherPID, return SYS_NOMEM; } - sprintf(connection->name, "%s.%" PRId64, transport->name, otherPID); + snprintf(connection->name, sizeof(connection->name), "%s.%" PRId64, transport->name, otherPID); error = sysSharedMemCreate(connection->name, sizeof(SharedMemory), &connection->sharedMemory, &connection->shared); if (error != SYS_OK) { @@ -738,7 +738,7 @@ openTransport(const char *address, SharedMemoryTransport **transportPtr) if (strlen(address) >= MAX_IPC_PREFIX) { char buf[128]; - sprintf(buf, "Error: address strings longer than %d characters are invalid\n", MAX_IPC_PREFIX); + snprintf(buf, sizeof(buf), "Error: address strings longer than %d characters are invalid\n", MAX_IPC_PREFIX); setLastErrorMsg(buf); closeTransport(transport); return SYS_ERR; @@ -802,7 +802,7 @@ createTransport(const char *address, SharedMemoryTransport **transportPtr) } else { if (strlen(address) >= MAX_IPC_PREFIX) { char buf[128]; - sprintf(buf, "Error: address strings longer than %d characters are invalid\n", MAX_IPC_PREFIX); + snprintf(buf, sizeof(buf), "Error: address strings longer than %d characters are invalid\n", MAX_IPC_PREFIX); setLastErrorMsg(buf); closeTransport(transport); return SYS_ERR; @@ -820,7 +820,7 @@ createTransport(const char *address, SharedMemoryTransport **transportPtr) memset(transport->shared, 0, sizeof(SharedListener)); transport->shared->acceptingPID = sysProcessGetID(); - sprintf(objectName, "%s.mutex", transport->name); + snprintf(objectName, sizeof(objectName), "%s.mutex", transport->name); error = createWithGeneratedName(objectName, transport->shared->mutexName, createMutex, &transport->mutex); if (error != SYS_OK) { @@ -828,7 +828,7 @@ createTransport(const char *address, SharedMemoryTransport **transportPtr) return error; } - sprintf(objectName, "%s.accept", transport->name); + snprintf(objectName, sizeof(objectName), "%s.accept", transport->name); error = createWithGeneratedName(objectName, transport->shared->acceptEventName, createEvent, &transport->acceptEvent); if (error != SYS_OK) { @@ -836,7 +836,7 @@ createTransport(const char *address, SharedMemoryTransport **transportPtr) return error; } - sprintf(objectName, "%s.attach", transport->name); + snprintf(objectName, sizeof(objectName), "%s.attach", transport->name); error = createWithGeneratedName(objectName, transport->shared->attachEventName, createEvent, &transport->attachEvent); if (error != SYS_OK) { @@ -1263,7 +1263,7 @@ exitTransportWithError(char *message, char *fileName, jint error; char buffer[500]; - sprintf(buffer, "Shared Memory Transport \"%s\" (%s), line %d: %s\n", + snprintf(buffer, sizeof(buffer), "Shared Memory Transport \"%s\" (%s), line %d: %s\n", fileName, date, lineNumber, message); error = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2); if (error != JNI_OK) { diff --git a/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c b/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c index a40448bb482..9d4e589c358 100644 --- a/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c +++ b/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.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 @@ -220,11 +220,7 @@ handshake(int fd, jlong timeout) { if (strncmp(b, hello, received) != 0) { char msg[80+2*16]; b[received] = '\0'; - /* - * We should really use snprintf here but it's not available on Windows. - * We can't use jio_snprintf without linking the transport against the VM. - */ - sprintf(msg, "handshake failed - received >%s< - expected >%s<", b, hello); + snprintf(msg, sizeof(msg), "handshake failed - received >%s< - expected >%s<", b, hello); setLastError(0, msg); return JDWPTRANSPORT_ERROR_IO_ERROR; } @@ -690,7 +686,7 @@ static jdwpTransportError startListening(struct addrinfo *ai, int *socket, char* } portNum = getPort((struct sockaddr *)&addr); - sprintf(buf, "%d", portNum); + snprintf(buf, sizeof(buf), "%d", portNum); *actualAddress = (*callback->alloc)((int)strlen(buf) + 1); if (*actualAddress == NULL) { RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory"); @@ -858,7 +854,7 @@ socketTransport_accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handsha int err2 = getnameinfo((struct sockaddr *)&clientAddr, clientAddrLen, addrStr, sizeof(addrStr), NULL, 0, NI_NUMERICHOST); - sprintf(ebuf, "ERROR: Peer not allowed to connect: %s\n", + snprintf(ebuf, sizeof(ebuf), "ERROR: Peer not allowed to connect: %s\n", (err2 != 0) ? "" : addrStr); dbgsysSocketClose(socketFD); socketFD = -1; diff --git a/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c b/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c index ab7044afb89..48cd59ee4cb 100644 --- a/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c +++ b/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, 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 @@ -23,12 +23,16 @@ * questions. */ +#include +#include #include +#include #include #include #include #include "sys.h" #include "util.h" +#include "error_messages.h" static char *skipWhitespace(char *p) { while ((*p != '\0') && isspace(*p)) { @@ -44,6 +48,106 @@ static char *skipNonWhitespace(char *p) { return p; } +#if defined(_AIX) + /* AIX does not understand '/proc/self' - it requires the real process ID */ + #define FD_DIR aix_fd_dir + #define DIR DIR64 + #define dirent dirent64 + #define opendir opendir64 + #define readdir readdir64 + #define closedir closedir64 +#elif defined(_ALLBSD_SOURCE) + #define FD_DIR "/dev/fd" +#else + #define FD_DIR "/proc/self/fd" +#endif + +// Closes every file descriptor that is listed as a directory +// entry in "/proc/self/fd" (or its equivalent). Standard +// input/output/error file descriptors will not be closed +// by this function. This function returns 0 on failure +// and 1 on success. +int +closeDescriptors(void) +{ + DIR *dp; + struct dirent *dirp; + /* leave out standard input/output/error descriptors */ + int from_fd = STDERR_FILENO + 1; + + /* We're trying to close all file descriptors, but opendir() might + * itself be implemented using a file descriptor, and we certainly + * don't want to close that while it's in use. We assume that if + * opendir() is implemented using a file descriptor, then it uses + * the lowest numbered file descriptor, just like open(). So + * before calling opendir(), we close a couple explicitly, so that + * opendir() can then use these lowest numbered closed file + * descriptors afresh. */ + + close(from_fd); /* for possible use by opendir() */ + close(from_fd + 1); /* another one for good luck */ + from_fd += 2; /* leave out the 2 we just closed, which the opendir() may use */ + +#if defined(_AIX) + /* set FD_DIR for AIX which does not understand '/proc/self' - it + * requires the real process ID */ + char aix_fd_dir[32]; /* the pid has at most 19 digits */ + snprintf(aix_fd_dir, 32, "/proc/%d/fd", getpid()); +#endif + + if ((dp = opendir(FD_DIR)) == NULL) { + ERROR_MESSAGE(("failed to open dir %s while determining" + " file descriptors to close for process %d", + FD_DIR, getpid())); + return 0; // failure + } + + while ((dirp = readdir(dp)) != NULL) { + if (!isdigit(dirp->d_name[0])) { + continue; + } + const long fd = strtol(dirp->d_name, NULL, 10); + if (fd <= INT_MAX && fd >= from_fd) { + (void)close((int)fd); + } + } + + (void)closedir(dp); + + return 1; // success +} + +// Does necessary housekeeping of a forked child process +// (like closing copied file descriptors) before +// execing the child process. This function never returns. +void +forkedChildProcess(const char *file, char *const argv[]) +{ + /* Close all file descriptors that have been copied over + * from the parent process due to fork(). */ + if (closeDescriptors() == 0) { /* failed, close the old way */ + /* Find max allowed file descriptors for a process + * and assume all were opened for the parent process and + * copied over to this child process. We close them all. */ + const rlim_t max_fd = sysconf(_SC_OPEN_MAX); + JDI_ASSERT(max_fd != (rlim_t)-1); // -1 represents error + /* close(), that we subsequently call, takes only int values */ + JDI_ASSERT(max_fd <= INT_MAX); + /* leave out standard input/output/error file descriptors */ + rlim_t i = STDERR_FILENO + 1; + ERROR_MESSAGE(("failed to close file descriptors of" + " child process optimally, falling back to closing" + " %d file descriptors sequentially", (max_fd - i + 1))); + for (; i < max_fd; i++) { + (void)close(i); + } + } + + (void)execvp(file, argv); /* not expected to return */ + + exit(errno); /* errno will have been set by the failed execvp */ +} + int dbgsysExec(char *cmdLine) { @@ -93,21 +197,11 @@ dbgsysExec(char *cmdLine) argv[i] = NULL; /* NULL terminate */ if ((pid = fork()) == 0) { - /* Child process */ - int i; - long max_fd; - - /* close everything */ - max_fd = sysconf(_SC_OPEN_MAX); - /*LINTED*/ - for (i = 3; i < (int)max_fd; i++) { - (void)close(i); - } - - (void)execvp(argv[0], argv); - - exit(-1); + // manage the child process + forkedChildProcess(argv[0], argv); } + // call to forkedChildProcess(...) will never return for a forked process + JDI_ASSERT(pid != 0); jvmtiDeallocate(args); jvmtiDeallocate(argv); if (pid == pid_err) { diff --git a/src/jdk.jdwp.agent/windows/native/libdt_socket/socket_md.c b/src/jdk.jdwp.agent/windows/native/libdt_socket/socket_md.c index 63f4bce61f7..da7518d6a4c 100644 --- a/src/jdk.jdwp.agent/windows/native/libdt_socket/socket_md.c +++ b/src/jdk.jdwp.agent/windows/native/libdt_socket/socket_md.c @@ -402,7 +402,7 @@ dbgsysGetLastIOError(char *buf, jint size) { if (i < table_size) { strcpy(buf, winsock_errors[i].errString); } else { - sprintf(buf, "winsock error %d", error); + snprintf(buf, size, "winsock error %d", error); } return 0; } diff --git a/src/jdk.jdwp.agent/windows/native/libjdwp/linker_md.c b/src/jdk.jdwp.agent/windows/native/libjdwp/linker_md.c index 6d9319ad812..97eb1498f58 100644 --- a/src/jdk.jdwp.agent/windows/native/libjdwp/linker_md.c +++ b/src/jdk.jdwp.agent/windows/native/libjdwp/linker_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 @@ -119,7 +119,7 @@ dbgsysBuildLibName(char *holder, int holderlen, const char *pname, const char *f EXIT_ERROR(JVMTI_ERROR_INVALID_LOCATION, "One or more of the library paths supplied to jdwp, " "likely by sun.boot.library.path, is too long."); } - sprintf(holder, "%s.dll", fname); + snprintf(holder, holderlen, "%s.dll", fname); } else { dll_build_name(holder, holderlen, pname, fname); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java index ccc36c3e166..71e98a4b2ac 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java @@ -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 @@ -195,6 +195,24 @@ protected void initLibProvidersLookup( Map params, LibProvidersLookup libProvidersLookup) { + libProvidersLookup.setPackageLookup(file -> { + Path realPath = file.toRealPath(); + + try { + // Try the real path first as it works better on newer Ubuntu versions + return findProvidingPackages(realPath); + } catch (IOException ex) { + // Try the default path if differ + if (!realPath.toString().equals(file.toString())) { + return findProvidingPackages(file); + } else { + throw ex; + } + } + }); + } + + private static Stream findProvidingPackages(Path file) throws IOException { // // `dpkg -S` command does glob pattern lookup. If not the absolute path // to the file is specified it might return mltiple package names. @@ -237,32 +255,30 @@ protected void initLibProvidersLookup( // 4. Arch suffix should be stripped from accepted package names. // - libProvidersLookup.setPackageLookup(file -> { - Set archPackages = new HashSet<>(); - Set otherPackages = new HashSet<>(); - - Executor.of(TOOL_DPKG, "-S", file.toString()) - .saveOutput(true).executeExpectSuccess() - .getOutput().forEach(line -> { - Matcher matcher = PACKAGE_NAME_REGEX.matcher(line); - if (matcher.find()) { - String name = matcher.group(1); - if (name.endsWith(":" + DEB_ARCH)) { - // Strip arch suffix - name = name.substring(0, - name.length() - (DEB_ARCH.length() + 1)); - archPackages.add(name); - } else { - otherPackages.add(name); - } + Set archPackages = new HashSet<>(); + Set otherPackages = new HashSet<>(); + + Executor.of(TOOL_DPKG, "-S", file.toString()) + .saveOutput(true).executeExpectSuccess() + .getOutput().forEach(line -> { + Matcher matcher = PACKAGE_NAME_REGEX.matcher(line); + if (matcher.find()) { + String name = matcher.group(1); + if (name.endsWith(":" + DEB_ARCH)) { + // Strip arch suffix + name = name.substring(0, + name.length() - (DEB_ARCH.length() + 1)); + archPackages.add(name); + } else { + otherPackages.add(name); } - }); + } + }); - if (!archPackages.isEmpty()) { - return archPackages.stream(); - } - return otherPackages.stream(); - }); + if (!archPackages.isEmpty()) { + return archPackages.stream(); + } + return otherPackages.stream(); } @Override diff --git a/src/jdk.management/share/native/libmanagement_ext/GcInfoBuilder.c b/src/jdk.management/share/native/libmanagement_ext/GcInfoBuilder.c index ebd53958055..80675177aa8 100644 --- a/src/jdk.management/share/native/libmanagement_ext/GcInfoBuilder.c +++ b/src/jdk.management/share/native/libmanagement_ext/GcInfoBuilder.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 @@ -129,7 +129,9 @@ static void setLongValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Long"; static const char* signature = "(J)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -138,7 +140,9 @@ static void setBooleanValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Boolean"; static const char* signature = "(Z)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -147,7 +151,9 @@ static void setByteValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Byte"; static const char* signature = "(B)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -156,7 +162,9 @@ static void setIntValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Integer"; static const char* signature = "(I)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -165,7 +173,9 @@ static void setShortValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Short"; static const char* signature = "(S)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -174,7 +184,9 @@ static void setDoubleValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Double"; static const char* signature = "(D)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -183,7 +195,9 @@ static void setFloatValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Float"; static const char* signature = "(D)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -192,7 +206,9 @@ static void setCharValueAtObjectArray(JNIEnv *env, jobjectArray array, static const char* class_name = "java/lang/Character"; static const char* signature = "(C)V"; jobject obj = JNU_NewObjectByName(env, class_name, signature, value); - + if ((*env)->ExceptionCheck(env)) { + return; + } (*env)->SetObjectArrayElement(env, array, index, obj); } @@ -293,6 +309,10 @@ JNIEXPORT jobject JNICALL Java_com_sun_management_internal_GcInfoBuilder_getLast if (nativeTypes != NULL) { free(nativeTypes); } + // Recognise possible Exception from the switch statement above: + if ((*env)->ExceptionCheck(env)) { + return NULL; + } return JNU_NewObjectByName(env, "com/sun/management/GcInfo", diff --git a/src/jdk.management/share/native/libmanagement_ext/management_ext.c b/src/jdk.management/share/native/libmanagement_ext/management_ext.c index 02285521042..726f1e7b5b9 100644 --- a/src/jdk.management/share/native/libmanagement_ext/management_ext.c +++ b/src/jdk.management/share/native/libmanagement_ext/management_ext.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/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java index 5cb2b4cb6c4..7678efa1fca 100644 --- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java +++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java @@ -42,7 +42,7 @@ import sun.security.krb5.*; import sun.security.jgss.krb5.Krb5Util; import sun.security.krb5.Credentials; -import sun.security.util.HexDumpEncoder; + import static sun.security.util.ResourcesMgr.getAuthResourceString; /** @@ -765,15 +765,11 @@ private void attemptAuthentication(boolean getPasswdFromSharedState) if (debug) { System.out.println("principal is " + principal); - HexDumpEncoder hd = new HexDumpEncoder(); if (ktab != null) { System.out.println("Will use keytab"); } else if (storeKey) { for (int i = 0; i < encKeys.length; i++) { - System.out.println("EncryptionKey: keyType=" + - encKeys[i].getEType() + - " keyBytes (hex dump)=" + - hd.encodeBuffer(encKeys[i].getBytes())); + System.out.println(encKeys[i].toString()); } } } @@ -874,7 +870,7 @@ private void promptForPass(boolean getPasswdFromSharedState) } if (debug) { System.out.println - ("password is " + new String(password)); + ("Get password from shared state"); } return; } diff --git a/src/utils/IdealGraphVisualizer/Bytecodes/pom.xml b/src/utils/IdealGraphVisualizer/Bytecodes/pom.xml index 188537f81d6..e87ad7fb509 100644 --- a/src/utils/IdealGraphVisualizer/Bytecodes/pom.xml +++ b/src/utils/IdealGraphVisualizer/Bytecodes/pom.xml @@ -1,6 +1,6 @@ diff --git a/test/hotspot/jtreg/vmTestbase/nsk/stress/stack/TEST.properties b/src/utils/IdealGraphVisualizer/application/src/main/resources/idealgraphvisualizer.conf similarity index 66% rename from test/hotspot/jtreg/vmTestbase/nsk/stress/stack/TEST.properties rename to src/utils/IdealGraphVisualizer/application/src/main/resources/idealgraphvisualizer.conf index 8b51b2a9115..500ed32f280 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/stress/stack/TEST.properties +++ b/src/utils/IdealGraphVisualizer/application/src/main/resources/idealgraphvisualizer.conf @@ -1,5 +1,4 @@ -# -# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. +# 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 @@ -19,6 +18,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. -# -exclusiveAccess.dirs=. +# Open/export modules still accessed by the NetBeans Platform. +# All options must be passed in a single line for multi-platform support. +default_options="-J--add-opens=java.base/java.net=ALL-UNNAMED -J--add-opens=java.desktop/javax.swing.plaf.synth=ALL-UNNAMED -J--add-opens=java.desktop/com.sun.java.swing.plaf.gtk=ALL-UNNAMED -J--add-opens=java.desktop/javax.swing=ALL-UNNAMED -J--add-exports=java.desktop/sun.awt=ALL-UNNAMED" \ No newline at end of file diff --git a/src/utils/IdealGraphVisualizer/branding/pom.xml b/src/utils/IdealGraphVisualizer/branding/pom.xml index 67821ff4621..ed33298d3e4 100644 --- a/src/utils/IdealGraphVisualizer/branding/pom.xml +++ b/src/utils/IdealGraphVisualizer/branding/pom.xml @@ -1,6 +1,6 @@ - - - - - SetFontTest - - - -

      SetFontTest
      Bug ID: 5010944

      - -

      See the dialog box (usually in upper left corner) for instructions

      - - - - diff --git a/test/jdk/java/awt/Choice/NonFocusablePopupMenuTest/NonFocusablePopupMenuTest.java b/test/jdk/java/awt/Choice/NonFocusablePopupMenuTest/NonFocusablePopupMenuTest.java deleted file mode 100644 index 4a10184e0d6..00000000000 --- a/test/jdk/java/awt/Choice/NonFocusablePopupMenuTest/NonFocusablePopupMenuTest.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. - * 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 6519005 - @summary regression: Selection the item on the choice don't work properly in vista ultimate. - @author Dmitry Cherepanov area=awt.choice - @run applet/manual=yesno NonFocusablePopupMenuTest.html -*/ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; - -public class NonFocusablePopupMenuTest extends Applet -{ - public void init() - { - Choice choice = new Choice(); - choice.add("111"); - choice.add("222"); - choice.add("333"); - choice.add("444"); - choice.setFocusable(false); - - this.add(choice); - - this.setLayout (new FlowLayout ()); - - String[] instructions = - { - "1) The applet contains a non-focusable choice, ", - "2) Click on the choice by mouse, try to change the selection of the choice, ", - "3) If it's not possible to change the selection and popup menu is always open ", - " even if you click by mouse on any item of the choice, the test failed, ", - "4) Otherwise, the test passed. " - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - - setSize (200,200); - setVisible(true); - validate(); - - }// start() -} - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/Dialog/NestedDialogs/Modal/NestedModalDialogTest.java b/test/jdk/java/awt/Dialog/NestedDialogs/Modal/NestedModalDialogTest.java index f5d85d8a2ad..c7ec4d66000 100644 --- a/test/jdk/java/awt/Dialog/NestedDialogs/Modal/NestedModalDialogTest.java +++ b/test/jdk/java/awt/Dialog/NestedDialogs/Modal/NestedModalDialogTest.java @@ -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 @@ -54,12 +54,11 @@ import java.awt.event.KeyEvent; public class NestedModalDialogTest { - private static Frame frame; + private static StartFrame frame; private static IntermediateDialog interDiag; private static TextDialog txtDiag; // Global variables so the robot thread can locate things. - private static Button[] robot_button = new Button[2]; private static TextField robot_text = null; private static Robot robot = null; @@ -78,6 +77,9 @@ private static void blockTillDisplayed(Component comp) { } private static void clickOnComp(Component comp) { + robot.waitForIdle(); + robot.delay(1000); + Rectangle bounds = new Rectangle(comp.getLocationOnScreen(), comp.getSize()); robot.mouseMove(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2); robot.waitForIdle(); @@ -94,11 +96,11 @@ public void testModalDialogs() throws Exception { // launch first frame with firstButton frame = new StartFrame(); blockTillDisplayed(frame); - clickOnComp(robot_button[0]); + clickOnComp(frame.button); // Dialog must be created and onscreen before we proceed. blockTillDisplayed(interDiag); - clickOnComp(robot_button[1]); + clickOnComp(interDiag.button); // Again, the Dialog must be created and onscreen before we proceed. blockTillDisplayed(robot_text); @@ -144,6 +146,8 @@ public void testModalDialogs() throws Exception { */ class StartFrame extends Frame { + public volatile Button button; + /** * Constructs a new instance. */ @@ -168,7 +172,7 @@ public void actionPerformed(ActionEvent e) { pan.add(but); add(pan); setVisible(true); - robot_button[0] = but; + button = but; } } @@ -177,6 +181,7 @@ public void actionPerformed(ActionEvent e) { class IntermediateDialog extends Dialog { Dialog m_parent; + public volatile Button button; public IntermediateDialog(Frame parent) { super(parent, "Intermediate Modal", true /*Modal*/); @@ -193,9 +198,7 @@ public void actionPerformed(ActionEvent e) { pan.add(but); add(pan); pack(); - - // The robot needs to know about us, so set global - robot_button[1] = but; + button = but; } } @@ -215,12 +218,12 @@ public TextDialog(Dialog parent) { } } - public static void main(String[] args) throws RuntimeException, Exception { + public static void main(String[] args) throws Exception { try { new NestedModalDialogTest().testModalDialogs(); } catch (Exception e) { throw new RuntimeException("NestedModalDialogTest object creation " - + "failed"); + + "failed", e); } } } diff --git a/test/jdk/java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.java b/test/jdk/java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.java index 959c90b6997..13a3db5bbf4 100644 --- a/test/jdk/java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.java +++ b/test/jdk/java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.java @@ -28,7 +28,7 @@ @summary namefilter is not called for file dialog on windows @library ../../regtesthelpers @build Util - @run main FilenameFilterTest + @run main/othervm FilenameFilterTest */ import java.awt.*; diff --git a/test/jdk/java/awt/Focus/6981400/Test1.java b/test/jdk/java/awt/Focus/6981400/Test1.java index 730e10804fa..ab60129ba94 100644 --- a/test/jdk/java/awt/Focus/6981400/Test1.java +++ b/test/jdk/java/awt/Focus/6981400/Test1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 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 @@ -27,8 +27,8 @@ * @bug 6981400 * @summary Tabbing between textfiled do not work properly when ALT+TAB * @author anton.tarasov - * @library ../../regtesthelpers - * @build Util + * @library /java/awt/regtesthelpers /test/lib + * @build Util jdk.test.lib.Platform * @run main Test1 */ @@ -41,12 +41,28 @@ // The FOCUS_LOST/FOCUS_GAINED events order in the original frame is tracked and should be: // b0 -> b1 -> b2 -> b3. -import java.awt.*; -import java.awt.event.*; +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.AWTEventListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import jdk.test.lib.Platform; import test.java.awt.regtesthelpers.Util; public class Test1 { @@ -72,7 +88,7 @@ public class Test1 { static boolean tracking; - public static void main(String[] args) { + public static void main(String[] args) throws Exception { Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { public void eventDispatched(AWTEvent e) { System.out.println(e); @@ -81,6 +97,7 @@ public void eventDispatched(AWTEvent e) { try { robot = new Robot(); + robot.setAutoDelay(50); } catch (AWTException ex) { throw new RuntimeException("Error: can't create Robot"); } @@ -90,13 +107,13 @@ public void eventDispatched(AWTEvent e) { f0.add(f0b2); f0.add(f0b3); f0.setLayout(new FlowLayout()); - f0.setBounds(0, 100, 400, 200); + f0.setBounds(100, 100, 400, 200); f1.add(f1b0); - f1.setBounds(0, 400, 400, 200); + f1.setBounds(100, 400, 400, 200); f2.add(f2b0); - f2.setBounds(0, 400, 400, 200); + f2.setBounds(100, 400, 400, 200); f0b0.addFocusListener(new FocusAdapter() { @Override @@ -115,6 +132,7 @@ public void focusLost(FocusEvent e) { f0.setVisible(true); Util.waitForIdle(robot); + robot.delay(500); if (!f0b0.isFocusOwner()) { Util.clickOnComp(f0b0, robot); @@ -152,28 +170,29 @@ public void focusLost(FocusEvent e) { System.out.println("\nTest passed."); } - public static void test(Component compToClick) { + public static void test(Component compToClick) throws Exception { tracking = true; robot.keyPress(KeyEvent.VK_TAB); - robot.delay(50); robot.keyRelease(KeyEvent.VK_TAB); - robot.delay(50); + robot.waitForIdle(); robot.keyPress(KeyEvent.VK_TAB); - robot.delay(50); robot.keyRelease(KeyEvent.VK_TAB); - robot.delay(50); + robot.waitForIdle(); robot.keyPress(KeyEvent.VK_TAB); - robot.delay(50); robot.keyRelease(KeyEvent.VK_TAB); + robot.waitForIdle(); - robot.delay(50); Util.clickOnComp(compToClick, robot); - robot.delay(50); - Util.clickOnTitle(f0, robot); + robot.waitForIdle(); + SwingUtilities.invokeAndWait(f0::toFront); + + if (!Platform.isOnWayland()) { + Util.clickOnTitle(f0, robot); + } Util.waitForIdle(robot); diff --git a/test/jdk/java/awt/Focus/ActualFocusedWindowTest/ActualFocusedWindowBlockingTest.java b/test/jdk/java/awt/Focus/ActualFocusedWindowTest/ActualFocusedWindowBlockingTest.java index a87b59a9388..35c6b5cdee6 100644 --- a/test/jdk/java/awt/Focus/ActualFocusedWindowTest/ActualFocusedWindowBlockingTest.java +++ b/test/jdk/java/awt/Focus/ActualFocusedWindowTest/ActualFocusedWindowBlockingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2018, 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 @@ -26,13 +26,27 @@ @key headful @bug 6314575 @summary Tests that previosly focused owned window doesn't steal focus when an owner's component requests focus. - @library ../../regtesthelpers - @build Util + @library /java/awt/regtesthelpers /test/lib + @build Util jdk.test.lib.Platform @run main ActualFocusedWindowBlockingTest */ -import java.awt.*; -import java.awt.event.*; +import java.awt.AWTEvent; +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.AWTEventListener; +import java.awt.event.FocusEvent; +import java.awt.event.WindowEvent; + +import jdk.test.lib.Platform; import test.java.awt.regtesthelpers.Util; public class ActualFocusedWindowBlockingTest { @@ -44,7 +58,7 @@ public class ActualFocusedWindowBlockingTest { Button wButton = new Button("window button") {public String toString() {return "Window_Button";}}; Button aButton = new Button("auxiliary button") {public String toString() {return "Auxiliary_Button";}}; - public static void main(String[] args) { + public static void main(String[] args) throws Exception { ActualFocusedWindowBlockingTest app = new ActualFocusedWindowBlockingTest(); app.init(); app.start(); @@ -68,12 +82,7 @@ public void eventDispatched(AWTEvent e) { tuneAndShowWindows(new Window[] {owner, win, frame}); } - public void start() { - if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { - System.out.println("No testing on Motif. Test passed."); - return; - } - + public void start() throws Exception { System.out.println("\nTest started:\n"); // Test 1. @@ -104,7 +113,12 @@ public void start() { clickOnCheckFocus(fButton); clickOnCheckFocus(aButton); - Util.clickOnTitle(owner, robot); + EventQueue.invokeAndWait(owner::toFront); + + if (!Platform.isOnWayland()) { + Util.clickOnTitle(owner, robot); + } + if (!testFocused(fButton)) { throw new TestFailedException("The owner's component [" + fButton + "] couldn't be focused as the most recent focus owner"); } @@ -122,11 +136,15 @@ void tuneAndShowWindows(Window[] arr) { y += 200; Util.waitForIdle(robot); } + robot.delay(500); } - void clickOnCheckFocus(Component c) { + void clickOnCheckFocus(Component c) throws Exception { if (c instanceof Frame) { - Util.clickOnTitle((Frame)c, robot); + EventQueue.invokeAndWait(() -> ((Frame) c).toFront()); + if (!Platform.isOnWayland()) { + Util.clickOnTitle((Frame) c, robot); + } } else { Util.clickOnComp(c, robot); } diff --git a/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java b/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java index 0a7a1afff61..45914e0670b 100644 --- a/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java +++ b/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -293,43 +293,39 @@ public void run() { // 6. Show unblocking modal Dialog. /////////////////////////////////// - if ("sun.awt.motif.MToolkit".equals(toolkitClassName)) { - System.out.println("Stage 6 - Skiping."); - } else { - System.out.println("Stage 6 in progress..."); + System.out.println("Stage 6 in progress..."); - // --- - // Testing the bug of activating invisible modal Dialog (awt_Window::SetAndActivateModalBlocker). - // Having some window not excluded from modality, so that it would be blocked. - Frame f = new Frame("Aux. Frame"); - f.setSize(100, 100); - setVisible(f, true); - // --- + // --- + // Testing the bug of activating invisible modal Dialog (awt_Window::SetAndActivateModalBlocker). + // Having some window not excluded from modality, so that it would be blocked. + Frame f = new Frame("Aux. Frame"); + f.setSize(100, 100); + setVisible(f, true); + // --- - setVisible(focusedFrame, true); + setVisible(focusedFrame, true); + if (!focusOwner.hasFocus()) { + Util.clickOnComp(focusOwner, robot); + Util.waitForIdle(robot); if (!focusOwner.hasFocus()) { - Util.clickOnComp(focusOwner, robot); - Util.waitForIdle(robot); - if (!focusOwner.hasFocus()) { - throw new Error("Test error: the frame couldn't be focused."); - } + throw new Error("Test error: the frame couldn't be focused."); } + } - dialog.setModal(true); - dialog.setAutoRequestFocus(false); - focusedFrame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); + dialog.setModal(true); + dialog.setAutoRequestFocus(false); + focusedFrame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); - TestHelper.invokeLaterAndWait(new Runnable() { - public void run() { - dialog.setVisible(true); - } - }, robot); + TestHelper.invokeLaterAndWait(new Runnable() { + public void run() { + dialog.setVisible(true); + } + }, robot); - if (dialog.isFocused()) { - throw new TestFailedException("the unblocking dialog shouldn't gain focus but it did!"); - } - setVisible(dialog, false); + if (dialog.isFocused()) { + throw new TestFailedException("the unblocking dialog shouldn't gain focus but it did!"); } + setVisible(dialog, false); System.out.println("Test passed."); } diff --git a/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java b/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java index 8ca4d4d6f7b..b1b8b8458ff 100644 --- a/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java +++ b/test/jdk/java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -202,26 +202,22 @@ public void start() { // Focused frame is excluded from modality. //////////////////////////////////////////////// - if (!"sun.awt.motif.MToolkit".equals(toolkitClassName)) { - recreateGUI(); - auxFrame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); + recreateGUI(); + auxFrame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); - Test.setWindows(modalDialog, modalDialog, new Window[] {modalDialog, frame3}); - Test.test("Test stage 6.1 in progress", modalDlgButton); - } + Test.setWindows(modalDialog, modalDialog, new Window[] {modalDialog, frame3}); + Test.test("Test stage 6.1 in progress", modalDlgButton); // 6.2. Owner Frame (with owned modal Dialog). // Focused frame is excluded from modality. //////////////////////////////////////////////// - if (!"sun.awt.motif.MToolkit".equals(toolkitClassName)) { - recreateGUI(); - auxFrame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); + recreateGUI(); + auxFrame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); - Test.setWindows(frame3, modalDialog, new Window[] {modalDialog, frame3}); - Test.test("Test stage 6.2 in progress", modalDlgButton, true); - } + Test.setWindows(frame3, modalDialog, new Window[] {modalDialog, frame3}); + Test.test("Test stage 6.2 in progress", modalDlgButton, true); /////////////////////////////////////////////////// // 7. Calling setVisible(true) for the shown Frame. @@ -422,4 +418,3 @@ class TestFailedException extends RuntimeException { super("Test failed: " + msg); } } - diff --git a/test/jdk/java/awt/Focus/ModalBlockedStealsFocusTest/ModalBlockedStealsFocusTest.java b/test/jdk/java/awt/Focus/ModalBlockedStealsFocusTest/ModalBlockedStealsFocusTest.java index 9a0d476133b..27be25773f2 100644 --- a/test/jdk/java/awt/Focus/ModalBlockedStealsFocusTest/ModalBlockedStealsFocusTest.java +++ b/test/jdk/java/awt/Focus/ModalBlockedStealsFocusTest/ModalBlockedStealsFocusTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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,11 +47,6 @@ public static void main(String[] args) { } public void start() { - if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { - System.out.println("The test is not for MToolkit."); - return; - } - dialog.setBounds(800, 0, 200, 100); frame.setBounds(800, 150, 200, 100); diff --git a/test/jdk/java/awt/Focus/ModalDialogInFocusEventTest.java b/test/jdk/java/awt/Focus/ModalDialogInFocusEventTest.java index d4f0d65f071..4c659819c3c 100644 --- a/test/jdk/java/awt/Focus/ModalDialogInFocusEventTest.java +++ b/test/jdk/java/awt/Focus/ModalDialogInFocusEventTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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,11 +22,11 @@ */ /* - test + @test + @key headful @bug 4531693 4636269 4681908 4688142 4691646 4721470 @summary Showing modal dialog during dispatching SequencedEvent - @key headful - @run main AutomaticAppletTest + @run main ModalDialogInFocusEventTest */ import java.awt.AWTEvent; @@ -68,6 +68,8 @@ public class ModalDialogInFocusEventTest static final int MAX_STAGE_NUM = stages.length; static final Object stageMonitor = new Object(); + static boolean isOnWayland; + Robot robot = null; Frame frame; Frame oppositeFrame; @@ -209,18 +211,21 @@ public void windowLostFocus(WindowEvent e) { void clickOnFrameTitle(Frame frame) throws InterruptedException, InvocationTargetException { - System.out.println("click on title of " + frame.getName()); - int[] point = new int[2]; - EventQueue.invokeAndWait(() -> { - Point location = frame.getLocationOnScreen(); - Insets insets = frame.getInsets(); - int width = frame.getWidth(); - point[0] = location.x + width / 2; - point[1] = location.y + insets.top / 2; - }); - robot.mouseMove(point[0], point[1]); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + EventQueue.invokeAndWait(frame::toFront); + if (!isOnWayland) { + System.out.println("click on title of " + frame.getName()); + int[] point = new int[2]; + EventQueue.invokeAndWait(() -> { + Point location = frame.getLocationOnScreen(); + Insets insets = frame.getInsets(); + int width = frame.getWidth(); + point[0] = location.x + width / 2; + point[1] = location.y + insets.top / 2; + }); + robot.mouseMove(point[0], point[1]); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } EventQueue.invokeAndWait(frame::requestFocusInWindow); } @@ -344,6 +349,7 @@ public void focusLost(FocusEvent e) { public static void main(String[] args) throws InterruptedException, InvocationTargetException { + isOnWayland = System.getenv("WAYLAND_DISPLAY") != null; ModalDialogInFocusEventTest test = new ModalDialogInFocusEventTest(); test.start(); } diff --git a/test/jdk/java/awt/Focus/ModalExcludedWindowClickTest/ModalExcludedWindowClickTest.java b/test/jdk/java/awt/Focus/ModalExcludedWindowClickTest/ModalExcludedWindowClickTest.java index 0ee37cbce69..7cfe66834bc 100644 --- a/test/jdk/java/awt/Focus/ModalExcludedWindowClickTest/ModalExcludedWindowClickTest.java +++ b/test/jdk/java/awt/Focus/ModalExcludedWindowClickTest/ModalExcludedWindowClickTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -57,12 +57,6 @@ public void init() { } public void start() { - - if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { - System.out.println("No testing on MToolkit."); - return; - } - button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { actionPerformed = true; diff --git a/test/jdk/java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.java b/test/jdk/java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.java index b6d4c962efa..abf8d57cecd 100644 --- a/test/jdk/java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.java +++ b/test/jdk/java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -56,12 +56,6 @@ public void init() { } public void start() { - - if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { - System.out.println("No testing on MToolkit."); - return; - } - try { EventQueue.invokeLater(new Runnable() { public void run() { diff --git a/test/jdk/java/awt/Focus/NonFocusableWindowTest/NonfocusableOwnerTest.java b/test/jdk/java/awt/Focus/NonFocusableWindowTest/NonfocusableOwnerTest.java index cfabc697f85..7b552424f8d 100644 --- a/test/jdk/java/awt/Focus/NonFocusableWindowTest/NonfocusableOwnerTest.java +++ b/test/jdk/java/awt/Focus/NonFocusableWindowTest/NonfocusableOwnerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2018, 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 @@ -21,20 +21,29 @@ * questions. */ -/* - @test - @key headful - @bug 6182359 - @summary Tests that Window having non-focusable owner can't be a focus owner. - @library ../../regtesthelpers - @build Util - @run main NonfocusableOwnerTest -*/ - -import java.awt.*; -import java.awt.event.*; import test.java.awt.regtesthelpers.Util; +import java.awt.AWTEvent; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.AWTEventListener; +import java.awt.event.FocusEvent; +import java.awt.event.WindowEvent; + +/* + ( @test + * @key headful + * @bug 6182359 + * @summary Tests that Window having non-focusable owner can't be a focus owner. + * @library ../../regtesthelpers + * @build Util + * @run main NonfocusableOwnerTest + */ public class NonfocusableOwnerTest { Robot robot = Util.createRobot(); Frame frame; @@ -55,7 +64,7 @@ public void eventDispatched(AWTEvent e) { } }, FocusEvent.FOCUS_EVENT_MASK | WindowEvent.WINDOW_FOCUS_EVENT_MASK | WindowEvent.WINDOW_EVENT_MASK); - frame = new Frame("Frame"); + frame = new Frame("NonfocusableOwnerTest"); frame.setName("Frame-owner"); frame.setBounds(100, 0, 100, 100); dialog = new Dialog(frame, "Dialog"); @@ -92,9 +101,11 @@ void test1(Window owner, Window child) { owner.setFocusableWindowState(false); owner.setVisible(true); + robot.waitForIdle(); child.add(button); child.setVisible(true); + robot.waitForIdle(); Util.waitTillShown(child); @@ -111,12 +122,15 @@ void test2(Window owner, Window child1, Window child2) { owner.setFocusableWindowState(false); owner.setVisible(true); + robot.waitForIdle(); child1.setFocusableWindowState(true); child1.setVisible(true); + robot.waitForIdle(); child2.add(button); child2.setVisible(true); + robot.waitForIdle(); Util.waitTillShown(child2); @@ -134,13 +148,16 @@ void test3(Window owner, Window child1, Window child2) { owner.setFocusableWindowState(true); owner.setVisible(true); + robot.waitForIdle(); child1.setFocusableWindowState(false); child1.setVisible(true); + robot.waitForIdle(); child2.setFocusableWindowState(true); child2.add(button); child2.setVisible(true); + robot.waitForIdle(); Util.waitTillShown(child2); diff --git a/test/jdk/java/awt/Focus/OwnedWindowFocusIMECrashTest/OwnedWindowFocusIMECrashTest.java b/test/jdk/java/awt/Focus/OwnedWindowFocusIMECrashTest/OwnedWindowFocusIMECrashTest.java index 6fe6b6a1fa5..f81b25cb1d4 100644 --- a/test/jdk/java/awt/Focus/OwnedWindowFocusIMECrashTest/OwnedWindowFocusIMECrashTest.java +++ b/test/jdk/java/awt/Focus/OwnedWindowFocusIMECrashTest/OwnedWindowFocusIMECrashTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -64,6 +64,7 @@ public void start() { window.setVisible(true); Util.waitForIdle(robot); + robot.delay(1000); test(); diff --git a/test/jdk/java/awt/Focus/SimpleWindowActivationTest/SimpleWindowActivationTest.java b/test/jdk/java/awt/Focus/SimpleWindowActivationTest/SimpleWindowActivationTest.java index 0a2ed310c17..2e386fd1c6d 100644 --- a/test/jdk/java/awt/Focus/SimpleWindowActivationTest/SimpleWindowActivationTest.java +++ b/test/jdk/java/awt/Focus/SimpleWindowActivationTest/SimpleWindowActivationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -56,12 +56,6 @@ public class SimpleWindowActivationTest { private static Robot robot; public static void main(String[] args) throws Exception { - - if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { - System.out.println("No testing on Motif. Test passed."); - return; - } - robot = new Robot(); robot.setAutoDelay(50); diff --git a/test/jdk/java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java b/test/jdk/java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java index 962f6b4665c..6a6cfc93939 100644 --- a/test/jdk/java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java +++ b/test/jdk/java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -63,11 +63,6 @@ public void init() { } public void start() { - if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { - System.out.println("No testing on Motif."); - return; - } - test(new Frame("Frame owner")); Frame dialog_owner = new Frame("dialog's owner"); test(new Dialog(dialog_owner)); diff --git a/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java new file mode 100644 index 00000000000..caa365a3f21 --- /dev/null +++ b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java @@ -0,0 +1,118 @@ +/* + * 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. + */ + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +/* + * @test + * @bug 8328896 + * @summary test that using very large font sizes used don't break later uses + */ + +public class ExtremeFontSizeTest { + + static BufferedImage bi = new BufferedImage(1,1,1); + static Graphics2D g2d = bi.createGraphics(); + static String testString = "M"; + static Font font = new Font("SansSerif", Font.PLAIN, 12); + static int fontSize = 0; + static boolean failed = false; + static int[] fontSizes = { 10, 12, 1000, 2000, 20000, 100000, 8 }; + static double[] scales = { 1.0, 900.0}; + static boolean[] fms = { false, true }; + + public static void main(String[] args) { + + /* run tests validating bounds etc are non-zero + * then run with extreme scales for which zero is allowed - but not required + * then run the first tests again to be sure they are still reasonable. + */ + runTests(); + test(5_000_000, 10_000, false, testString, false); + test(5_000_000, 10_000, true, testString, false); + test(0, 0.00000001, false, testString, false); + runTests(); + + if (failed) { + throw new RuntimeException("Test failed. Check stdout log."); + } + } + + static void runTests() { + for (int fontSize : fontSizes) { + for (double scale : scales) { + for (boolean fm : fms) { + test(fontSize, scale, fm, testString, true); + } + } + } + } + + static void test(int size, double scale, boolean fm, String str, boolean checkAll) { + + AffineTransform at = AffineTransform.getScaleInstance(scale, scale); + FontRenderContext frc = new FontRenderContext(at, false, fm); + font = font.deriveFont((float)size); + g2d.setTransform(at); + g2d.setFont(font); + FontMetrics metrics = g2d.getFontMetrics(); + int height = metrics.getHeight(); + double width = font.getStringBounds(str, frc).getWidth(); + + GlyphVector gv = font.createGlyphVector(frc, str.toCharArray()); + Rectangle pixelBounds = gv.getPixelBounds(frc, 0, 0); + Rectangle2D visualBounds = gv.getVisualBounds(); + + System.out.println("Test parameters: size="+size+" scale="+scale+" fm="+fm+" str="+str); + System.out.println("font height="+metrics.getHeight()); + System.out.println("string bounds width="+width); + System.out.println("GlyphVector Pixel Bounds="+ pixelBounds); + System.out.println("GlyphVector Visual Bounds="+ visualBounds); + + + if (height < 0 || width < 0 || pixelBounds.getWidth() < 0 || visualBounds.getWidth() < 0) { + failed = true; + System.out.println(" *** Unexpected negative size reported *** "); + } + if (!checkAll) { + System.out.println(); + return; + } + + if (height == 0 || width == 0 || (pixelBounds.isEmpty()) || visualBounds.isEmpty() ) { + failed = true; + System.out.println("Pixel bounds empty="+pixelBounds.isEmpty()); + System.out.println("Visual bounds empty="+visualBounds.isEmpty()); + System.out.println(" *** RESULTS NOT AS EXPECTED *** "); + } + System.out.println(); + } +} diff --git a/test/jdk/java/awt/Frame/DefaultSizeTest.java b/test/jdk/java/awt/Frame/DefaultSizeTest.java new file mode 100644 index 00000000000..dff1ff147c5 --- /dev/null +++ b/test/jdk/java/awt/Frame/DefaultSizeTest.java @@ -0,0 +1,68 @@ +/* + * 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 + * 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.awt.EventQueue; +import java.awt.Frame; + +/* + * @test 4033151 + * @summary Test that frame default size is minimum possible size + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultSizeTest + */ + +public class DefaultSizeTest { + + private static final String INSTRUCTIONS = """ + An empty frame is created. + It should be located to the right of this window + and should be the minimum size allowed by the window manager. + For any WM, the frame should be very small. + If the frame is not large, click Pass or Fail otherwise. + """; + + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("DefaultSizeTest Instructions Frame") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(10) + .columns(45) + .screenCapture() + .build(); + + EventQueue.invokeAndWait(() -> { + Frame frame = new Frame("DefaultSize"); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame + .positionTestWindow(frame, PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Frame/FrameRepackTest.java b/test/jdk/java/awt/Frame/FrameRepackTest.java new file mode 100644 index 00000000000..4f77d195ca6 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameRepackTest.java @@ -0,0 +1,175 @@ +/* + * 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 + * 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.awt.BorderLayout; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @key headful + * @summary Test dynamically changing frame component visibility and repacking + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameRepackTest + */ + +public class FrameRepackTest { + + private static final String INSTRUCTIONS = """ + There is a green frame with a menubar. + The menubar has one menu, labelled 'Flip'. + The menu has two items, labelled 'visible' and 'not visible'. + The frame also contains a red panel that contains two line labels, + 'This panel is always displayed' and 'it is a test'. + + If you select the menu item 'Flip->visible', then another panel is + added below the red panel. + The added panel is blue and has yellow horizontal and vertical scrollbars. + + If you select menu item 'Flip->not visible', the second panel + is removed and the frame appears as it did originally. + + You can repeatedly add and remove the second panel in this way. + After such an addition or removal, the frame's location on the screen + should not change, while the size changes to accommodate + either just the red panel or both the red and the blue panels. + + If you resize the frame larger, the red panel remains at the + top of the frame with its height fixed and its width adjusted + to the width of the frame. + + Similarly, if it is present, the blue panel and its yellow scroolbars + remain at the bottom of the frame with fixed height and width adjusted + to the size of the frame. But selecting 'visible' or 'not visible' + repacks the frame, thereby adjusting its size tightly to its panel(s). + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("FrameRepackTest Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(30) + .columns(45) + .build(); + + EventQueue.invokeAndWait(() -> { + FrameRepack frame = new FrameRepack(); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } + +} + +class FrameRepack extends Frame implements ActionListener { + + Panel south; + + public FrameRepack() { + super("FrameRepack"); + + // create the menubar + MenuBar menubar = new MenuBar(); + this.setMenuBar(menubar); + // create the options + Menu flip = new Menu("Flip"); + MenuItem mi; + mi = new MenuItem("visible"); + mi.addActionListener(this); + flip.add(mi); + mi = new MenuItem("not visible"); + mi.addActionListener(this); + flip.add(mi); + + menubar.add(flip); + + setLayout(new BorderLayout(2, 2)); + setBackground(Color.green); + + // north panel is always displayed + Panel north = new Panel(); + north.setBackground(Color.red); + north.setLayout(new BorderLayout(2, 2)); + north.add("North", new Label("This panel is always displayed")); + north.add("Center", new Label("it is a test")); + north.setSize(200, 200); + add("North", north); + + // south panel can be visible or not... + // The problem seems to occur when I put this panel not visible + south = new Panel(); + south.setBackground(Color.white); + south.setLayout(new BorderLayout()); + + ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); + scroller.setBackground(Color.yellow); + Panel pan1 = new Panel(); + pan1.setBackground(Color.blue); + pan1.setLayout(new BorderLayout()); + + pan1.setSize(400, 150); + scroller.add("Center", pan1); + + south.add("South", scroller); + + add("South", south); + + south.setVisible(false); + + setSize(350, 300); + + pack(); + } + + @Override + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() instanceof MenuItem) { + if (evt.getActionCommand().equals("visible")) { + south.setVisible(true); + pack(); + } else if (evt.getActionCommand().equals("not visible")) { + south.setVisible(false); + pack(); + } + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_1.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_1.java new file mode 100644 index 00000000000..7503672fccd --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_1.java @@ -0,0 +1,92 @@ +/* + * 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 + * 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.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; + +/* + * @test + * @bug 4041442 + * @key headful + * @summary Test resizing a frame containing a canvas + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_1 + */ + +public class FrameResizeTest_1 { + + private static final String INSTRUCTIONS = """ + To the right of this frame is an all-white 200x200 frame. + + This is actually a white canvas component in the frame. + The frame itself is red. + The red should never show. + In particular, after you resize the frame, you should see all white and no red. + (During very fast window resizing, red color may appear briefly, + which is not a failure.) + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("FrameResizeTest_1 Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(12) + .columns(45) + .build(); + + EventQueue.invokeAndWait(() -> { + FrameResize_1 frame = new FrameResize_1(); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} + +class FrameResize_1 extends Frame { + + FrameResize_1() { + super("FrameResize_1"); + // Create a white canvas + Canvas canvas = new Canvas(); + canvas.setBackground(Color.white); + + setLayout(new BorderLayout()); + add("Center", canvas); + + setBackground(Color.red); + setSize(200,200); + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_2.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_2.java new file mode 100644 index 00000000000..e8fdd52522b --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_2.java @@ -0,0 +1,144 @@ +/* + * 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 + * 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.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Panel; + +/* + * @test + * @bug 4065568 + * @key headful + * @summary Test resizing a frame containing a canvas + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_2 + */ + +public class FrameResizeTest_2 { + private static final String INSTRUCTIONS = """ + There is a frame (size 300x300). + The left half is red and the right half is blue. + + When you resize the frame, it should still have a red left half + and a blue right half. + + In particular, no green should be visible after a resize. + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("FrameResizeTest_2 Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(10) + .columns(45) + .build(); + + EventQueue.invokeAndWait(() -> { + FrameResize_2 frame = new FrameResize_2(); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} + +class FrameResize_2 extends Frame { + + FrameResize_2() { + super("FrameResize_2"); + + setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + + Container dumbContainer = new DumbContainer(); + add(dumbContainer, c); + + Panel dumbPanel = new DumbPanel(); + add(dumbPanel, c); + + setSize(300, 300); + } +} + + +class Fake extends Canvas { + public Fake(String name, Color what) { + setBackground(what); + setName(name); + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(getBackground()); + g.fillRect(0, 0, d.width, d.height); + } +} + +class DumbContainer extends Container { + public DumbContainer() { + setLayout(new BorderLayout()); + add("Center", new Fake("dumbc", Color.red)); + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.green); + g.fillRect(0, 0, d.width, d.height); + super.paint(g); + } +} + +class DumbPanel extends Panel { + public DumbPanel() { + setLayout(new BorderLayout()); + add("Center", new Fake("dumbp", Color.blue)); + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.green); + g.fillRect(0, 0, d.width, d.height); + super.paint(g); + } +} diff --git a/test/jdk/java/awt/Frame/GetBoundsResizeTest.java b/test/jdk/java/awt/Frame/GetBoundsResizeTest.java new file mode 100644 index 00000000000..d3c2ffb0bad --- /dev/null +++ b/test/jdk/java/awt/Frame/GetBoundsResizeTest.java @@ -0,0 +1,97 @@ +/* + * 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 + * 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.awt.Button; +import java.awt.Frame; +import java.awt.EventQueue; +import java.awt.Robot; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +/* + * @test + * @bug 4103095 + * @summary Test for getBounds() after a Frame resize. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual GetBoundsResizeTest +*/ + +public class GetBoundsResizeTest { + private static final String INSTRUCTIONS = """ + 0. There is a test window with a "Press" button, + Its original bounds should be printed in the text area below. + 1. Resize the test window using the upper left corner. + 2. Press the button to print the result of getBounds() to the text area. + 3. Previously, a window could report an incorrect position on the + screen after resizing the window in this way. + If getBounds() prints the appropriate values for the window, + click Pass, otherwise click Fail. + """; + + private static JTextArea textArea; + private static Frame frame; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + + PassFailJFrame passFailJFrame = PassFailJFrame + .builder() + .title("GetBoundsResizeTest Instructions") + .instructions(INSTRUCTIONS) + .splitUIBottom(() -> { + textArea = new JTextArea("", 8, 55); + textArea.setEditable(false); + return new JScrollPane(textArea); + }) + .testUI(GetBoundsResizeTest::getFrame) + .rows((int) (INSTRUCTIONS.lines().count() + 2)) + .columns(40) + .build(); + + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(() -> + logFrameBounds("Original Frame.getBounds() = %s\n")); + + passFailJFrame.awaitAndCheck(); + } + + private static Frame getFrame() { + frame = new Frame("GetBoundsResizeTest"); + + Button button = new Button("Press"); + button.addActionListener((e) -> + logFrameBounds("Current Frame.getBounds() = %s\n")); + + frame.add(button); + frame.setSize(200, 100); + + return frame; + } + + private static void logFrameBounds(String format) { + textArea.append(format.formatted(frame.getBounds())); + } +} diff --git a/test/jdk/java/awt/Frame/GetBoundsResizeTest/GetBoundsResizeTest.java b/test/jdk/java/awt/Frame/GetBoundsResizeTest/GetBoundsResizeTest.java deleted file mode 100644 index 9dad65ddd2c..00000000000 --- a/test/jdk/java/awt/Frame/GetBoundsResizeTest/GetBoundsResizeTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. - * 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 4103095 - @summary Test for getBounds() after a Frame resize. - @author andrei.dmitriev : area=awt.toplevel - @run main/manual GetBoundsResizeTest -*/ - -import java.applet.Applet; -import java.lang.*; -import java.awt.*; -import java.awt.event.*; - -class Globals { - static boolean testPassed=false; - static Thread mainThread=null; -} - -public class GetBoundsResizeTest extends Applet { - - public static void main(String args[]) throws Exception { - GetBoundsResizeTest app = new GetBoundsResizeTest(); - app.start(); - Globals.mainThread = Thread.currentThread(); - try { - Thread.sleep(300000); - } catch (InterruptedException e) { - if (!Globals.testPassed) - throw new Exception("GetBoundsResizeTest failed."); - } - } - - public void start() - { - String[] message = { - "Resize the window using the upper left corner.", - "Press the button to print the result of getBounds() to the terminal.", - "If getBounds() prints the correct values for the window", - "then click Pass, else click Fail." - }; - new TestDialog(new Frame(), "GetBoundsResizeTest", message).start(); - new GetBoundsResizeTester("GetBoundsResizeTester").start(); - } -} - -//////////////////////////////////////////////////////////////////////// -// Test Dialog -//////////////////////////////////////////////////////////////////////// - -class TestDialog extends Dialog - implements ActionListener { - - static TextArea output; - Button passButton; - Button failButton; - String name; - - public TestDialog(Frame frame, String name, String[] message) - { - super(frame, name + " Pass/Fail Dialog"); - this.name = name; - int maxStringLength = 0; - for (int i=0; i frame = new WindowMove()); + + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING))); + + if (!WindowMove.latch.await(2, TimeUnit.SECONDS)) { + throw new RuntimeException("Test timeout."); + } + + if (WindowMove.failMessage != null) { + throw new RuntimeException(WindowMove.failMessage); + } + } +} + +class WindowMove extends Frame implements WindowListener { + static final Rectangle expectedBounds = + new Rectangle(100, 100, 300, 300); + + static CountDownLatch latch = new CountDownLatch(1); + static String failMessage = null; + + private boolean layoutCheck; + private boolean visibleCheck; + private boolean openedCheck; + private boolean closingCheck; + private boolean closedCheck; + + public WindowMove() { + super("WindowMove"); + addWindowListener(this); + + setSize(300, 300); + setLocation(100, 100); + setBackground(Color.white); + + setLayout(null); + if (checkBounds()) { + layoutCheck = true; + } + System.out.println("setLayout bounds: " + getBounds()); + + setVisible(true); + if (checkBounds()) { + visibleCheck = true; + } + System.out.println("setVisible bounds: " + getBounds()); + } + + private boolean checkBounds() { + return getBounds().equals(expectedBounds); + } + + public void checkResult() { + if (layoutCheck + && visibleCheck + && openedCheck + && closingCheck + && closedCheck) { + System.out.println("Test passed."); + } else { + failMessage = """ + Some of the checks failed: + layoutCheck %s + visibleCheck %s + openedCheck %s + closingCheck %s + closedCheck %s + """ + .formatted( + layoutCheck, + visibleCheck, + openedCheck, + closingCheck, + closedCheck + ); + } + + latch.countDown(); + } + + public void windowClosing(WindowEvent evt) { + if (checkBounds()) { + closingCheck = true; + } + System.out.println("Closing bounds: " + getBounds()); + + setVisible(false); + dispose(); + } + + public void windowClosed(WindowEvent evt) { + if (checkBounds()) { + closedCheck = true; + } + System.out.println("Closed bounds: " + getBounds()); + + checkResult(); + } + + public void windowOpened(WindowEvent evt) { + if (checkBounds()) { + openedCheck = true; + } + System.out.println("Opening bounds: " + getBounds()); + } + + public void windowActivated(WindowEvent evt) {} + + public void windowIconified(WindowEvent evt) {} + + public void windowDeactivated(WindowEvent evt) {} + + public void windowDeiconified(WindowEvent evt) {} +} diff --git a/test/jdk/java/awt/LightweightComponent/LightweightCliprect.java b/test/jdk/java/awt/LightweightComponent/LightweightCliprect.java new file mode 100644 index 00000000000..a6b1e3bc95c --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/LightweightCliprect.java @@ -0,0 +1,120 @@ +/* + * 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 + * 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.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +/* + * @test + * @bug 4116029 + * @summary drawString does not honor clipping regions for lightweight components + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LightweightCliprect + */ + +public class LightweightCliprect { + + private static final String INSTRUCTIONS = """ + If some text is drawn outside the red rectangle, press "Fail" button. + Otherwise, press "Pass" button. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("LightweightCliprect Instructions Frame") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(10) + .columns(45) + .build(); + + EventQueue.invokeAndWait(() -> { + Frame frame = new Frame("DefaultSize"); + + Container panel = new MyContainer(); + MyComponent c = new MyComponent(); + panel.add(c); + + frame.add(panel); + frame.setSize(400, 300); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame + .positionTestWindow(frame, PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} + +class MyComponent extends Component { + + public void paint(Graphics g) { + Color c = g.getColor(); + g.setColor(Color.red); + g.fillRect(20, 20, 400, 200); + Shape clip = g.getClip(); + g.setClip(20, 20, 400, 200); + //draw the current java version in the component + g.setColor(Color.black); + String version = System.getProperty("java.version"); + String vendor = System.getProperty("java.vendor"); + int y = 10; + for(int i = 0; i < 30; i++) { + g.drawString("Lightweight: Java version: " + version + + ", Vendor: " + vendor, 10, y += 20); + } + g.setColor(c); + g.setClip(clip); + super.paint(g); + } + + public Dimension getPreferredSize() { + return new Dimension(300, 300); + } +} + +class MyContainer extends Container { + public MyContainer() { + super(); + setLayout(new FlowLayout()); + } + + public void paint(Graphics g) { + Rectangle bounds = new Rectangle(getSize()); + g.setColor(Color.cyan); + g.drawRect(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1); + super.paint(g); + } +} diff --git a/test/jdk/java/awt/List/ActionEventTest/ActionEventTest.java b/test/jdk/java/awt/List/ActionEventTest/ActionEventTest.java index 758f9ff479f..ecda27e4249 100644 --- a/test/jdk/java/awt/List/ActionEventTest/ActionEventTest.java +++ b/test/jdk/java/awt/List/ActionEventTest/ActionEventTest.java @@ -56,6 +56,7 @@ public ActionEventTest() { add(list); setSize(400,400); setLayout(new FlowLayout()); + setLocationRelativeTo(null); pack(); setVisible(true); } @@ -70,9 +71,9 @@ public void actionPerformed(ActionEvent ae) { if ((md & expectedMask) != expectedMask) { - robot.keyRelease(KeyEvent.VK_ALT); - robot.keyRelease(KeyEvent.VK_SHIFT); robot.keyRelease(KeyEvent.VK_CONTROL); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_ALT); dispose(); throw new RuntimeException("Action Event modifiers are not" + " set correctly."); @@ -87,9 +88,9 @@ public void actionPerformed(ActionEvent ae) { // Press Enter on list item, to generate action event. robot.keyPress(KeyEvent.VK_ENTER); robot.keyRelease(KeyEvent.VK_ENTER); - robot.keyRelease(KeyEvent.VK_ALT); - robot.keyRelease(KeyEvent.VK_SHIFT); robot.keyRelease(KeyEvent.VK_CONTROL); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_ALT); } public static void main(String args[]) { diff --git a/test/jdk/java/awt/List/MouseDraggedOutCauseScrollingTest/MouseDraggedOutCauseScrollingTest.html b/test/jdk/java/awt/List/MouseDraggedOutCauseScrollingTest/MouseDraggedOutCauseScrollingTest.html deleted file mode 100644 index 7049e827033..00000000000 --- a/test/jdk/java/awt/List/MouseDraggedOutCauseScrollingTest/MouseDraggedOutCauseScrollingTest.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - ManualYesNoTest - - - -

      ManualYesNoTest
      Bug ID:

      - -

      See the dialog box (usually in upper left corner) for instructions

      - - - - diff --git a/test/jdk/java/awt/List/MouseDraggedOutCauseScrollingTest/MouseDraggedOutCauseScrollingTest.java b/test/jdk/java/awt/List/MouseDraggedOutCauseScrollingTest/MouseDraggedOutCauseScrollingTest.java index 8b509a12311..09ea5bc11cc 100644 --- a/test/jdk/java/awt/List/MouseDraggedOutCauseScrollingTest/MouseDraggedOutCauseScrollingTest.java +++ b/test/jdk/java/awt/List/MouseDraggedOutCauseScrollingTest/MouseDraggedOutCauseScrollingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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 @@ -22,29 +22,29 @@ */ /* - test + @test @bug 6243382 8006070 @summary Dragging of mouse outside of a List and Choice area don't work properly on XAWT - @author Dmitry.Cherepanov@SUN.COM area=awt.list - @run applet/manual=yesno MouseDraggedOutCauseScrollingTest.html + @requires (os.family == "linux") + @library /java/awt/regtesthelpers + @run main/manual MouseDraggedOutCauseScrollingTest */ -import java.applet.Applet; -import java.awt.*; +import java.awt.Choice; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Toolkit; -public class MouseDraggedOutCauseScrollingTest extends Applet -{ - Choice choice; - List singleList; - List multipleList; +public class MouseDraggedOutCauseScrollingTest { - public void init() - { - this.setLayout (new GridLayout (1, 3)); + static Frame createUI() { + Frame frame = new Frame("MouseDraggedOutCausesScrollingTest"); + frame.setLayout(new GridLayout(1, 3)); - choice = new Choice(); - singleList = new List(3, false); - multipleList = new List(3, true); + Choice choice = new Choice(); + List singleList = new List(3, false); + List multipleList = new List(3, true); choice.add("Choice"); for (int i = 1; i < 100; i++){ @@ -59,188 +59,68 @@ public void init() for (int i = 1; i < 100; i++) multipleList.add(""+i); - this.add(choice); - this.add(singleList); - this.add(multipleList); + frame.add(choice); + frame.add(singleList); + frame.add(multipleList); + frame.setSize(400, 100); + return frame; + } + public static void main(String[] args) throws Exception { String toolkitName = Toolkit.getDefaultToolkit().getClass().getName(); + if (!toolkitName.equals("sun.awt.X11.XToolkit")) { - String[] instructions = - { - "This test is not applicable to the current platform. Press PASS" - }; - Sysout.createDialogWithInstructions( instructions ); - } else { - String[] instructions = - { - "0) Please note, that this is only Motif/XAWT test. At first, make the applet active", - "1.1) Click on the choice", - "1.2) Press the left button of the mouse and keep on any item of the choice, for example 5", - "1.3) Drag mouse out of the area of the unfurled list, at the same time hold the X coordinate of the mouse position about the same", - "1.4) To make sure, that when the Y coordinate of the mouse position higher of the upper bound of the list then scrolling UP of the list and selected item changes on the upper. If not, the test failed", - "1.5) To make sure, that when the Y coordinate of the mouse position under of the lower bound of the list then scrolling DOWN of the list and selected item changes on the lower. If not, the test failed", - "-----------------------------------", - "2.1) Click on the single list", - "2.2) Press the left button of the mouse and keep on any item of the list, for example 5", - "2.3) Drag mouse out of the area of the unfurled list, at the same time hold the X coordinate of the mouse position about the same", - "2.4) To make sure, that when the Y coordinate of the mouse position higher of the upper bound of the list then scrolling UP of the list and selected item changes on the upper. If not, the test failed", - "2.5) To make sure, that when the Y coordinate of the mouse position under of the lower bound of the list then scrolling DOWN of the list and selected item changes on the lower. If not, the test failed", - "-----------------------------------", - "3.1) Click on the multiple list", - "3.2) Press the left button of the mouse and keep on any item of the list, for example 5", - "3.3) Drag mouse out of the area of the unfurled list, at the same time hold the X coordinate of the mouse position about the same", - "3.4) To make sure, that when the Y coordinate of the mouse position higher of the upper bound of the list then scrolling of the list NO OCCURED and selected item NO CHANGES on the upper. If not, the test failed", - "3.5) To make sure, that when the Y coordinate of the mouse position under of the lower bound of the list then scrolling of the list NO OCCURED and selected item NO CHANGES on the lower. If not, the test failed", - "4) Test passed." - }; - Sysout.createDialogWithInstructions( instructions ); + System.out.println(INAPPLICABLE); + return; } - }//End init() - - public void start () - { - setSize (400,100); - setVisible(true); - validate(); - - }// start() - -}// class ManualYesNoTest - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); + PassFailJFrame + .builder() + .instructions(INSTRUCTIONS) + .rows(40) + .columns(70) + .testUI(MouseDraggedOutCauseScrollingTest::createUI) + .build() + .awaitAndCheck(); } -}// TestDialog class + static final String INAPPLICABLE = "The test is not applicable to the current platform. Test PASSES."; + static final String INSTRUCTIONS = """ + 0) Please note, that this is an XAWT/Linux only test. First, make the test window is active. + ----------------------------------- + 1.1) Click on the Choice. + 1.2) Press and hold down the left button of the mouse to select (eg) item 5 in the choice. + 1.3) Drag the mouse vertically out of the area of the open list, + keeping the X coordinate of the mouse position about the same. + 1.4) Check that when the Y coordinate of the mouse position is higher than the upper bound of the list + then the list continues to scrolls UP and the selected item changes at the top until you reach the topmost item. + If not, the test failed. Press FAIL. + 1.5) Check that when the Y coordinate of the mouse position is lower than the lower bound of the list + then the list continues to scroll DOWN and the selected item changes at the bottom until you reach the bottommost item. + If not, the test failed. Press FAIL. + ----------------------------------- + 2.1) Click on the Single List. + 2.2) Press and hold down the left button of the mouse to select (eg) item 5 in the list. + 2.3) Drag the mouse vertically out of the area of the open list, + keeping the X coordinate of the mouse position about the same. + 2.4) Check that when the Y coordinate of the mouse position is higher than the upper bound of the list + then the list continues to scrolls UP and the selected item changes at the top until you reach the topmost item. + If not, the test failed. Press FAIL. + 2.5) Check that when the Y coordinate of the mouse position is lower than the lower bound of the list + then the list continues to scroll DOWN and the selected item changes at the bottom until you reach the bottommost item. + If not, the test failed. Press FAIL. + ----------------------------------- + 3.1) Click on the Multiple List. + 3.2) Press and hold down the left button of the mouse to select (eg) item 5 in the list. + 3.3) Drag the mouse vertically out of the area of the open list, + keeping the X coordinate of the mouse position about the same. + 3.4) Check that when the Y coordinate of the mouse is higher than the upper bound of the list + that scrolling of the list DOES NOT OCCUR and the selected item IS UNCHANGED at the top. + If not, the test failed. Press FAIL. + 3.5) Check that when the Y coordinate of the mouse is below the lower bound of the list + that scrolling of the list DOES NOT OCCUR and the selected item IS UNCHANGED at the bottom. + If not, the test failed. Press FAIL. + ----------------------------------- + 4) The test has now passed. Press PASS. + """; +} diff --git a/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_1.java b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_1.java new file mode 100644 index 00000000000..808bb598cec --- /dev/null +++ b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_1.java @@ -0,0 +1,104 @@ +/* + * 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 + * 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.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4028130 + * @summary Test dynamically adding and removing a menu bar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AddRemoveMenuBarTest_1 + */ + +public class AddRemoveMenuBarTest_1 { + + private static final String INSTRUCTIONS = """ + An initially empty frame should appear. + + Click anywhere in the frame to add a menu bar at the top of the frame. + + Click again to replace the menu bar with another menu bar. + + Each menu bar has one (empty) menu, labelled with the + number of the menu bar appearing. + + After a menubar is added, the frame should not be resized nor repositioned + on the screen; + + it should have the same size and position. + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("AddRemoveMenuBarTest_1 Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(18) + .columns(45) + .build(); + + SwingUtilities.invokeAndWait(() -> { + AddRemoveMenuBar_1 frame = new AddRemoveMenuBar_1(); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} + +class AddRemoveMenuBar_1 extends Frame { + int menuCount; + + AddRemoveMenuBar_1() { + super("AddRemoveMenuBar_1"); + setSize(200, 200); + menuCount = 0; + + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + setMenuBar(); + } + }); + } + + void setMenuBar() { + MenuBar bar = new MenuBar(); + bar.add(new Menu(Integer.toString(menuCount++))); + setMenuBar(bar); + } +} diff --git a/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_2.java b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_2.java new file mode 100644 index 00000000000..c0c98a5ca95 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_2.java @@ -0,0 +1,99 @@ +/* + * 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 + * 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.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4028130 + * @key headful + * @summary Test dynamically adding and removing a menu bar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AddRemoveMenuBarTest_2 + */ + +public class AddRemoveMenuBarTest_2 { + private static final String INSTRUCTIONS = """ + A frame with a menu bar appears. + + Click anywhere in the frame to replace the menu bar with + another one. + + Each menu bar has one (empty) menu, 'foo'. + + After the menu bar replacement, the containing frame + should not be resized nor repositioned on the screen. + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("AddRemoveMenuBarTest_2 Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(15) + .columns(45) + .build(); + + SwingUtilities.invokeAndWait(() -> { + AddRemoveMenuBar_2 frame = new AddRemoveMenuBar_2(); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} + +class AddRemoveMenuBar_2 extends Frame { + AddRemoveMenuBar_2() { + super("AddRemoveMenuBar_2"); + setSize(200, 200); + setMenuBar(); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + setMenuBar(); + } + }); + } + + int count = 0; + + void setMenuBar() { + MenuBar bar = new MenuBar(); + bar.add(new Menu("foo " + count++)); + super.setMenuBar(bar); + } +} diff --git a/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_3.java b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_3.java new file mode 100644 index 00000000000..a01ddd9925b --- /dev/null +++ b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_3.java @@ -0,0 +1,195 @@ +/* + * 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 + * 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.awt.BorderLayout; +import java.awt.Checkbox; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.TextField; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4017504 + * @summary Test dynamically adding and removing a menu bar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AddRemoveMenuBarTest_3 + */ + +public class AddRemoveMenuBarTest_3 { + private static final String INSTRUCTIONS = """ + A frame at (100,100) contains two (2) rows of three (3) text + fields each, and under this, a checkbox labelled 'Use menubar'. + + The first row's text fields pertain to the x coordinates and + the second row's text fields pertain to the y coordinates. + + The first column, 'request', is an input only field for frame + location. (press enter to apply). + + The second column, 'reported', is an output only + field reporting frame location. + + The third column, 'inset', is an output only field reporting + the frame's inset values. + + You can click the 'Use menubar' checkbox to alternately add + and remove a menu bar containing an (empty) 'Help' menu. + + After a menubar is added or removed, the frame should not + have been resized nor repositioned on the screen and the + y inset should accurately reflect the presence or absence + of the menubar within the inset. + + The insets always include the window manager's title and border + decorations, if any. + + Upon test completion, click Pass or Fail appropriately. + """; + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("AddRemoveMenuBarTest_3 Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(30) + .columns(38) + .build(); + + SwingUtilities.invokeAndWait(() -> { + AddRemoveMenuBar_3 frame = new AddRemoveMenuBar_3(); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(null, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} + +class AddRemoveMenuBar_3 extends Frame { + TextField xfield; + TextField yfield; + + TextField xfield_out; + TextField yfield_out; + TextField xinset_out; + TextField yinset_out; + + Checkbox menu_checkbox; + MenuBar menubar; + + public AddRemoveMenuBar_3() { + super("AddRemoveMenuBar_3"); + + menubar = new MenuBar(); + menubar.setHelpMenu(new Menu("Help")); + + setLayout(new BorderLayout()); + Panel p = new Panel(); + add("Center", p); + p.setLayout(new GridLayout(3, 3)); + + menu_checkbox = new Checkbox("Use menubar"); + add("South", menu_checkbox); + + xfield = new TextField(); + yfield = new TextField(); + xfield_out = new TextField(); + xfield_out.setEditable(false); + xfield_out.setFocusable(false); + yfield_out = new TextField(); + yfield_out.setEditable(false); + yfield_out.setFocusable(false); + + xinset_out = new TextField(); + xinset_out.setEditable(false); + xinset_out.setFocusable(false); + yinset_out = new TextField(); + yinset_out.setEditable(false); + yinset_out.setFocusable(false); + + p.add(new Label("request")); + p.add(new Label("reported")); + p.add(new Label("inset")); + + p.add(xfield); + p.add(xfield_out); + p.add(xinset_out); + + p.add(yfield); + p.add(yfield_out); + p.add(yinset_out); + + setSize(200, 200); + setLocation(100, 100); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentMoved(ComponentEvent e) { + xfield_out.setText(Integer.toString(getLocation().x)); + yfield_out.setText(Integer.toString(getLocation().y)); + + xinset_out.setText(Integer.toString(getInsets().left)); + yinset_out.setText(Integer.toString(getInsets().top)); + } + }); + + ActionListener setLocationListener = e -> { + Rectangle r = getBounds(); + try { + r.x = Integer.parseInt(xfield.getText()); + r.y = Integer.parseInt(yfield.getText()); + } catch (java.lang.NumberFormatException ignored) { + } + + setLocation(r.x, r.y); + }; + + xfield.addActionListener(setLocationListener); + yfield.addActionListener(setLocationListener); + + menu_checkbox.addItemListener(e -> { + if (menu_checkbox.getState()) { + setMenuBar(menubar); + } else { + setMenuBar(null); + } + + validate(); + xinset_out.setText(Integer.toString(getInsets().left)); + yinset_out.setText(Integer.toString(getInsets().top)); + }); + } +} diff --git a/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_4.java b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_4.java new file mode 100644 index 00000000000..571fce7fba5 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/AddRemoveMenuBarTests/AddRemoveMenuBarTest_4.java @@ -0,0 +1,116 @@ +/* + * 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 + * 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.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4071086 + * @key headful + * @summary Test dynamically adding and removing a menu bar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AddRemoveMenuBarTest_4 + */ + +public class AddRemoveMenuBarTest_4 { + + private static final String INSTRUCTIONS = """ + There is a frame with a menubar and a single button. + + The button is labelled 'Add new MenuBar'. + + If you click the button, the menubar is replaced with another menubar. + This can be done repeatedly. + + The -th menubar contains one menu, 'TestMenu', + with two items, 'one ' and 'two '. + + Click again to replace the menu bar with another menu bar. + + After a menubar has been replaced with another menubar, + the frame should not be resized nor repositioned on the screen. + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("AddRemoveMenuBarTest_4 Instructions") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(18) + .columns(45) + .build(); + + SwingUtilities.invokeAndWait(() -> { + AddRemoveMenuBar_4 frame = new AddRemoveMenuBar_4(); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} + +class AddRemoveMenuBar_4 extends Frame { + int count = 1; + MenuBar mb = null; + + AddRemoveMenuBar_4() { + super("AddRemoveMenuBar_4"); + setLayout(new FlowLayout()); + + Button b = new Button("Add new MenuBar"); + b.addActionListener((e) -> createMenuBar()); + add(b); + + createMenuBar(); + + setSize(300, 300); + } + + void createMenuBar() { + if (mb != null) { + remove(mb); + } + + mb = new MenuBar(); + Menu m = new Menu("TestMenu" + count); + m.add(new MenuItem("one " + count)); + m.add(new MenuItem("two " + count)); + count++; + mb.add(m); + setMenuBar(mb); + } +} diff --git a/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java b/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java index 0bd5dd19272..c11f0476bc7 100644 --- a/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java +++ b/test/jdk/java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.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 @@ -21,12 +21,33 @@ * questions. */ - -import java.awt.*; -import java.awt.event.*; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.List; +import java.awt.Panel; +import java.awt.Robot; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.InputEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import javax.swing.SwingUtilities; -import java.io.*; + +import jdk.test.lib.Platform; /** * AWT Mixing test for HierarchyBoundsListener ancestors. @@ -37,7 +58,8 @@ * @key headful * @bug 6768230 8221823 * @summary Mixing test for HierarchyBoundsListener ancestors - * @build FrameBorderCounter + * @library /test/lib + * @build FrameBorderCounter jdk.test.lib.Platform * @run main HierarchyBoundsListenerMixingTest */ public class HierarchyBoundsListenerMixingTest { @@ -137,9 +159,9 @@ protected boolean performTest() { robot.mouseMove((int) components[0].getLocationOnScreen().x + components[0].getSize().width / 2, (int) components[0].getLocationOnScreen().y + components[0].getSize().height / 2); robot.delay(delay); - robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.delay(delay); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.delay(delay); resetValues(); @@ -177,45 +199,54 @@ public void run() { robot.delay(delay * 5); resetValues(); - int x = (int) frame.getLocationOnScreen().x; - int y = (int) frame.getLocationOnScreen().y; - int w = frame.getSize().width; - int h = frame.getSize().height; - - robot.mouseMove(x + w + BORDER_SHIFT, y + h / 2); - robot.delay(delay); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.delay(delay); - for (int i = 0; i < 20; i++) { - robot.mouseMove(x + w + i + BORDER_SHIFT, y + h / 2); - robot.delay(50); - } - robot.delay(delay); - robot.mouseRelease(InputEvent.BUTTON1_MASK); - if (! resizeTriggered) { - synchronized (resizeLock) { - try { - resizeLock.wait(delay * 10); - } catch (Exception e) { + int x; + int y; + int w; + int h; + + if (!Platform.isOnWayland()) { + x = frame.getLocationOnScreen().x; + y = frame.getLocationOnScreen().y; + w = frame.getSize().width; + h = frame.getSize().height; + + robot.mouseMove(x + w + BORDER_SHIFT, y + h / 2); + robot.delay(delay); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(delay); + for (int i = 0; i < 20; i++) { + robot.mouseMove(x + w + i + BORDER_SHIFT, y + h / 2); + robot.delay(50); + } + robot.delay(delay); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (!resizeTriggered) { + synchronized (resizeLock) { + try { + resizeLock.wait(delay * 10); + } catch (Exception e) { + } } } - } - for (int i = 0; i < components.length; i++) { - if (! ancestorResized[i]) { - System.err.println("FAIL: Frame resized using mouse action. " + - "Ancestor resized event did not occur for " + - components[i].getClass()); + for (int i = 0; i < components.length; i++) { + if (!ancestorResized[i]) { + System.err.println("FAIL: Frame resized using mouse action. " + + "Ancestor resized event did not occur for " + + components[i].getClass()); + passed = false; + } + } + if (moveCount > 0) { + System.err.println("FAIL: Ancestor moved event occurred when Frame resized using mouse"); passed = false; } - } - if (moveCount > 0) { - System.err.println("FAIL: Ancestor moved event occured when Frame resized using mouse"); - passed = false; + + resetValues(); } - resetValues(); try { EventQueue.invokeAndWait(new Runnable() { public void run() { @@ -250,52 +281,55 @@ public void run() { robot.delay(delay * 10); resetValues(); - x = (int) frame.getLocationOnScreen().x; - y = (int) frame.getLocationOnScreen().y; - w = frame.getSize().width; - h = frame.getSize().height; - - //Click on the dummy frame so that the test frame loses focus. This is to workaround - //a bug in Linux AS. - robot.mouseMove((int) dummy.getLocationOnScreen().x + dummy.getSize().width / 2, - (int) dummy.getLocationOnScreen().y + dummy.getSize().height / 2); - robot.delay(delay); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.delay(delay); - robot.mouseRelease(InputEvent.BUTTON1_MASK); - robot.delay(delay); - robot.mouseMove(x + w / 2, y + 10); - robot.delay(delay); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.delay(delay); - for (int i = 1; i <= 20; i++) { - robot.mouseMove(x + w / 2 + i, y + 10); - robot.delay(50); - } - robot.delay(delay); - robot.mouseRelease(InputEvent.BUTTON1_MASK); - - if (! moveTriggered) { - synchronized (moveLock) { - try { - moveLock.wait(delay * 10); - } catch (Exception e) { + if (!Platform.isOnWayland()) { + x = frame.getLocationOnScreen().x; + y = frame.getLocationOnScreen().y; + w = frame.getSize().width; + h = frame.getSize().height; + + //Click on the dummy frame so that the test frame loses focus. This is to workaround + //a bug in Linux AS. + robot.mouseMove((int) dummy.getLocationOnScreen().x + dummy.getSize().width / 2, + (int) dummy.getLocationOnScreen().y + dummy.getSize().height / 2); + robot.delay(delay); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(delay); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(delay); + + robot.mouseMove(x + w / 2, y + 10); + robot.delay(delay); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(delay); + for (int i = 1; i <= 20; i++) { + robot.mouseMove(x + w / 2 + i, y + 10); + robot.delay(50); + } + robot.delay(delay); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (! moveTriggered) { + synchronized (moveLock) { + try { + moveLock.wait(delay * 10); + } catch (Exception e) { + } } } - } - for (int i = 0; i < components.length; i++) { - if (! ancestorMoved[i]) { - System.err.println("FAIL: Frame moved using mouse action. " + - "Ancestor moved event did not occur for " + components[i].getClass()); + for (int i = 0; i < components.length; i++) { + if (! ancestorMoved[i]) { + System.err.println("FAIL: Frame moved using mouse action. " + + "Ancestor moved event did not occur for " + components[i].getClass()); + passed = false; + } + } + if (resizeCount > 0) { + System.err.println("FAIL: Ancestor resized event occured when Frame moved using mouse"); passed = false; } } - if (resizeCount > 0) { - System.err.println("FAIL: Ancestor resized event occured when Frame moved using mouse"); - passed = false; - } return passed; } @@ -450,7 +484,7 @@ private static void init() throws InterruptedException { // instantiated in the same VM. Being static (and using // static vars), it aint gonna work. Not worrying about // it for now. - public static void main(String args[]) throws InterruptedException { + public static void main(String[] args) throws InterruptedException { mainThread = Thread.currentThread(); try { init(); diff --git a/test/jdk/java/awt/Modal/ToFront/FrameToFrontModelessTest.java b/test/jdk/java/awt/Modal/ToFront/FrameToFrontModelessTest.java index 03ceb6e022d..00c67cc1fc6 100644 --- a/test/jdk/java/awt/Modal/ToFront/FrameToFrontModelessTest.java +++ b/test/jdk/java/awt/Modal/ToFront/FrameToFrontModelessTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, 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 @@ -35,6 +35,9 @@ public class FrameToFrontModelessTest { private boolean isModeless; + private static final boolean IS_ON_WAYLAND = + System.getenv("WAYLAND_DISPLAY") != null; + public FrameToFrontModelessTest(boolean modeless) throws Exception { isModeless = modeless; robot = new ExtendedRobot(); @@ -76,6 +79,9 @@ public void doTest() throws Exception { robot.waitForIdle(delay); // show the right frame appear on top of the dialog + if (IS_ON_WAYLAND) { + rightFrame.toFront(); + } rightFrame.clickDummyButton(robot); robot.waitForIdle(delay); diff --git a/test/jdk/java/awt/Modal/helpers/TestDialog.java b/test/jdk/java/awt/Modal/helpers/TestDialog.java index 6252fe46b7a..2ae93f835b9 100644 --- a/test/jdk/java/awt/Modal/helpers/TestDialog.java +++ b/test/jdk/java/awt/Modal/helpers/TestDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -45,6 +45,9 @@ public class TestDialog extends Dialog implements ActionListener, public static int delay = 500; public static int keyDelay = 100; + private static final boolean IS_ON_WAYLAND = + System.getenv("WAYLAND_DISPLAY") != null; + public TestDialog(Frame frame) { super(frame); initializeGUI(); @@ -287,6 +290,9 @@ public void transferFocusToDialog(ExtendedRobot robot, String message, Button b) throws Exception { focusGained.reset(); + if (IS_ON_WAYLAND) { + toFront(); + } clickInside(robot); focusGained.waitForFlagTriggered(); assertTrue(focusGained.flag(), @@ -303,6 +309,9 @@ public void transferFocusToBlockedDialog(ExtendedRobot robot, String message, Button b) throws Exception { focusGained.reset(); + if (IS_ON_WAYLAND) { + toFront(); + } clickInside(robot); robot.waitForIdle(delay); diff --git a/test/jdk/java/awt/Modal/helpers/TestFrame.java b/test/jdk/java/awt/Modal/helpers/TestFrame.java index 925b216b963..dd8ed1530ef 100644 --- a/test/jdk/java/awt/Modal/helpers/TestFrame.java +++ b/test/jdk/java/awt/Modal/helpers/TestFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -43,6 +43,8 @@ public class TestFrame extends Frame implements ActionListener, public static int delay = 500; public static int keyDelay = 100; + private static final boolean IS_ON_WAYLAND = + System.getenv("WAYLAND_DISPLAY") != null; public TestFrame() { super(); @@ -251,7 +253,7 @@ public void clickDummyButton(ExtendedRobot robot, String message) throws Exception { dummyClicked.reset(); clickButton(dummyButton, robot); - dummyClicked.waitForFlagTriggered(); + dummyClicked.waitForFlagTriggered(attempts); String msg = "Clicking the frame Dummy button " + (refState ? "did not trigger an action." : @@ -277,6 +279,9 @@ public void transferFocusToFrame(ExtendedRobot robot, String message, Button b) throws Exception { focusGained.reset(); + if (IS_ON_WAYLAND) { + toFront(); + } clickInside(robot); focusGained.waitForFlagTriggered(); @@ -293,6 +298,9 @@ public void transferFocusToBlockedFrame(ExtendedRobot robot, String message, Button b) throws Exception { focusGained.reset(); + if (IS_ON_WAYLAND) { + toFront(); + } clickInside(robot); robot.waitForIdle(delay); diff --git a/test/jdk/java/awt/Mouse/MouseModifiersUnitTest/ExtraButtonDrag.java b/test/jdk/java/awt/Mouse/MouseModifiersUnitTest/ExtraButtonDrag.java index 29b90c9d430..3f8eeb17b38 100644 --- a/test/jdk/java/awt/Mouse/MouseModifiersUnitTest/ExtraButtonDrag.java +++ b/test/jdk/java/awt/Mouse/MouseModifiersUnitTest/ExtraButtonDrag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2016, 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 @@ -105,7 +105,7 @@ public void mouseReleased(MouseEvent e) { //XToolkit: extra buttons should report MOVED events only //WToolkit: extra buttons should report DRAGGED events only if (i > 2){ //extra buttons only - if (tk.equals("sun.awt.X11.XToolkit") || tk.equals("sun.awt.motif.MToolkit")) { + if (tk.equals("sun.awt.X11.XToolkit")) { if (!moved || dragged) { throw new RuntimeException("Test failed."+ tk +" Button = " +(i+1) + " moved = "+moved +" : dragged = " +dragged); } @@ -152,4 +152,3 @@ public static void dragMouse(int button, int x0, int y0, int x1, int y1){ } } - diff --git a/test/jdk/java/awt/print/PaintSetEnabledDeadlock/PaintSetEnabledDeadlock.java b/test/jdk/java/awt/Paint/PaintSetEnabledDeadlock/PaintSetEnabledDeadlock.java similarity index 100% rename from test/jdk/java/awt/print/PaintSetEnabledDeadlock/PaintSetEnabledDeadlock.java rename to test/jdk/java/awt/Paint/PaintSetEnabledDeadlock/PaintSetEnabledDeadlock.java diff --git a/test/jdk/java/awt/PrintJob/ConstrainedPrintingTest/ConstrainedPrintingTest.java b/test/jdk/java/awt/PrintJob/ConstrainedPrintingTest/ConstrainedPrintingTest.java index 179b4594652..b284a033316 100644 --- a/test/jdk/java/awt/PrintJob/ConstrainedPrintingTest/ConstrainedPrintingTest.java +++ b/test/jdk/java/awt/PrintJob/ConstrainedPrintingTest/ConstrainedPrintingTest.java @@ -24,6 +24,7 @@ /* @test @bug 4116029 4300383 + @key printer @summary verify that child components can draw only inside their visible bounds @author das@sparc.spb.su area=awt.print diff --git a/test/jdk/java/awt/PrintJob/EdgeTest/EdgeTest.java b/test/jdk/java/awt/PrintJob/EdgeTest/EdgeTest.java index f7bf5e72a60..58f84b43fb2 100644 --- a/test/jdk/java/awt/PrintJob/EdgeTest/EdgeTest.java +++ b/test/jdk/java/awt/PrintJob/EdgeTest/EdgeTest.java @@ -24,9 +24,9 @@ /** * @test * @bug 4092755 + * @key printer * @summary Verifies that (0, 0) is the upper-left corner of the page, not * the upper-left corner adjusted for the margins. - * @author dpm * @run main/manual EdgeTest */ diff --git a/test/jdk/java/awt/PrintJob/HighResTest.java b/test/jdk/java/awt/PrintJob/HighResTest.java index e802214fdaf..dbf7d4a2ce6 100644 --- a/test/jdk/java/awt/PrintJob/HighResTest.java +++ b/test/jdk/java/awt/PrintJob/HighResTest.java @@ -26,7 +26,7 @@ @test @bug 4227128 8066139 @summary Test printing at resolutions > 72dpi - @author dpm: area=awt.print + @key printer @run main/manual HighResTest */ import java.awt.Button; diff --git a/test/jdk/java/awt/PrintJob/JobAttrUpdateTest.java b/test/jdk/java/awt/PrintJob/JobAttrUpdateTest.java index 186b9a6d8ba..cbc8d6a7ae8 100644 --- a/test/jdk/java/awt/PrintJob/JobAttrUpdateTest.java +++ b/test/jdk/java/awt/PrintJob/JobAttrUpdateTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 6357905 + * @key printer * @summary JobAttributes.getFromPage() and getToPage() always returns 1 * @run main/manual JobAttrUpdateTest */ diff --git a/test/jdk/java/awt/PrintJob/MultipleEnd/MultipleEnd.java b/test/jdk/java/awt/PrintJob/MultipleEnd/MultipleEnd.java index b115e8d4a91..88b6a4720d9 100644 --- a/test/jdk/java/awt/PrintJob/MultipleEnd/MultipleEnd.java +++ b/test/jdk/java/awt/PrintJob/MultipleEnd/MultipleEnd.java @@ -23,7 +23,7 @@ /** * @test - * @key headful + * @key headful printer * @bug 4112758 * @summary Checks that a second invocation of PrintJob.end() does not cause * an exception or segmentation violation. diff --git a/test/jdk/java/awt/PrintJob/PageSetupDlgBlockingTest/PageSetupDlgBlockingTest.java b/test/jdk/java/awt/PrintJob/PageSetupDlgBlockingTest/PageSetupDlgBlockingTest.java index 805a2fa5c2c..9fbf48f3eb4 100644 --- a/test/jdk/java/awt/PrintJob/PageSetupDlgBlockingTest/PageSetupDlgBlockingTest.java +++ b/test/jdk/java/awt/PrintJob/PageSetupDlgBlockingTest/PageSetupDlgBlockingTest.java @@ -25,7 +25,7 @@ @test @bug 4507585 @summary Native modal dialog shouldn't block event dispatching when called on EventDispatchThread. - @author tav@sparc.spb.su: area=awt.PrintJob + @key printer @run main/manual=yesno PageSetupDlgBlockingTest */ diff --git a/test/jdk/java/awt/PrintJob/PrintArcTest/PrintArcTest.java b/test/jdk/java/awt/PrintJob/PrintArcTest/PrintArcTest.java index 15cf121607c..ab969f68eb7 100644 --- a/test/jdk/java/awt/PrintJob/PrintArcTest/PrintArcTest.java +++ b/test/jdk/java/awt/PrintJob/PrintArcTest/PrintArcTest.java @@ -23,10 +23,9 @@ /* * @test - * @key headful + * @key headful printer * @bug 4105609 * @summary Test printing of drawArc preceded by drawString - * @author robi.khan */ import java.awt.*; diff --git a/test/jdk/java/awt/PrintJob/PrintCheckboxTest/PrintCheckboxManualTest.java b/test/jdk/java/awt/PrintJob/PrintCheckboxTest/PrintCheckboxManualTest.java index 33ef83bfddd..4fd3ee8ef16 100644 --- a/test/jdk/java/awt/PrintJob/PrintCheckboxTest/PrintCheckboxManualTest.java +++ b/test/jdk/java/awt/PrintJob/PrintCheckboxTest/PrintCheckboxManualTest.java @@ -26,7 +26,7 @@ @bug 5045936 5055171 @summary Tests that there is no ClassCastException thrown in printing checkbox and scrollbar with XAWT - @author art@sparc.spb.su + @key printer @run applet/manual=yesno PrintCheckboxManualTest.html */ diff --git a/test/jdk/java/awt/PrintJob/QuoteAndBackslashTest/QuoteAndBackslashTest.java b/test/jdk/java/awt/PrintJob/QuoteAndBackslashTest/QuoteAndBackslashTest.java index dcc720de23e..c84671b6dc2 100644 --- a/test/jdk/java/awt/PrintJob/QuoteAndBackslashTest/QuoteAndBackslashTest.java +++ b/test/jdk/java/awt/PrintJob/QuoteAndBackslashTest/QuoteAndBackslashTest.java @@ -23,11 +23,10 @@ /** * @test - * @key headful + * @key headful printer * @bug 4040668 * @summary Checks that banner titles which contain double quotation marks * or backslashes still print correctly. - * @author dpm */ import java.awt.*; diff --git a/test/jdk/java/awt/PrintJob/RoundedRectTest/RoundedRectTest.java b/test/jdk/java/awt/PrintJob/RoundedRectTest/RoundedRectTest.java index a8af7b585f1..650203889c7 100644 --- a/test/jdk/java/awt/PrintJob/RoundedRectTest/RoundedRectTest.java +++ b/test/jdk/java/awt/PrintJob/RoundedRectTest/RoundedRectTest.java @@ -23,10 +23,9 @@ /** * @test - * @key headful + * @key headful printer * @bug 4061440 * @summary Checks that rounded rectangles print correctly. - * @author dpm */ import java.awt.*; diff --git a/test/jdk/java/awt/PrintJob/SaveDialogTitleTest.java b/test/jdk/java/awt/PrintJob/SaveDialogTitleTest.java index 4e502290f94..b0d7c9cd238 100644 --- a/test/jdk/java/awt/PrintJob/SaveDialogTitleTest.java +++ b/test/jdk/java/awt/PrintJob/SaveDialogTitleTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 4851363 8025988 8025990 + * @key printer * @summary Tests the save to file dialog has a title. * @run main/manual=yesno/othervm SaveDialogTitleTest */ diff --git a/test/jdk/java/awt/PrintJob/Security/SecurityDialogTest.java b/test/jdk/java/awt/PrintJob/Security/SecurityDialogTest.java index 3bda65e0a71..b84afc6eb9b 100644 --- a/test/jdk/java/awt/PrintJob/Security/SecurityDialogTest.java +++ b/test/jdk/java/awt/PrintJob/Security/SecurityDialogTest.java @@ -23,7 +23,7 @@ /** * @test - * @key headful + * @key headful printer * @bug 6195901 6195923 6195928 6195933 6491273 6888734 * @summary No SecurityException should be thrown when printing to a file using the given policy. diff --git a/test/jdk/java/awt/PrintJob/TestPrintJobFrameAssociation.java b/test/jdk/java/awt/PrintJob/TestPrintJobFrameAssociation.java index 1dd861cf325..2a58cb724d7 100644 --- a/test/jdk/java/awt/PrintJob/TestPrintJobFrameAssociation.java +++ b/test/jdk/java/awt/PrintJob/TestPrintJobFrameAssociation.java @@ -23,6 +23,7 @@ /* * @test * @bug 8154218 + * @key printer * @summary Verifies if owner Frame is associated with print dialog * @run main/manual TestPrintJobFrameAssociation */ diff --git a/test/jdk/java/awt/PrintJob/Text/stringwidth.sh b/test/jdk/java/awt/PrintJob/Text/stringwidth.sh index 32bce9e728b..fea11e5ba5f 100644 --- a/test/jdk/java/awt/PrintJob/Text/stringwidth.sh +++ b/test/jdk/java/awt/PrintJob/Text/stringwidth.sh @@ -23,6 +23,7 @@ # # @test # @bug 4692562 +# @key printer # @summary Requirement: Windows with printer installed. It should print with text "Hello World". # @compile StringWidth.java # @run shell/manual stringwidth.sh diff --git a/test/jdk/java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java b/test/jdk/java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java index 3ab3ba3ff87..ee83a328f0f 100644 --- a/test/jdk/java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java +++ b/test/jdk/java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, 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 @@ -57,7 +57,7 @@ public static void main(String []s){ //MouseInfo.getNumberOfButtons() reports two more buttons on XToolkit //as they reserved for wheel (both directions). - if (tk.equals("sun.awt.X11.XToolkit") || tk.equals("sun.awt.motif.MToolkit")) { + if (tk.equals("sun.awt.X11.XToolkit")) { buttonsNum = buttonsNum - 2; } System.out.println("Number Of Buttons = "+ buttonsNum); diff --git a/test/jdk/java/awt/Robot/ManualInstructions/ManualInstructions.java b/test/jdk/java/awt/Robot/ManualInstructions/ManualInstructions.java deleted file mode 100644 index df842ab841e..00000000000 --- a/test/jdk/java/awt/Robot/ManualInstructions/ManualInstructions.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2008, 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 - * 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 %W% %E% %I%, %G% - @bug 6315717 - @summary manual control over the Robot - @author Andrei Dmitriev : area=awt.robot - @run applet/manual=yesno ManualInstructions.html -*/ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; -import java.util.Timer; -import java.util.TimerTask; - -public class ManualInstructions extends Applet -{ - final static long SEND_DELAY = 1000; - - public static void main(String s[]){ - ManualInstructions mi = new ManualInstructions(); - mi.init(); - mi.start(); - } - - static Robot robot; - Point mouseLocation; //where mouse should be pressed each time - Panel target = new Panel(); - Button pressOn = new Button("press on ..."); - Button releaseOn = new Button("release on ..."); - Button clickOn = new Button("click on ..."); - Choice buttonNumber = new Choice(); - - public void init() - { - try { - robot = new Robot(); - } catch (AWTException ex) { - ex.printStackTrace(); - throw new RuntimeException(ex); - } - this.setLayout (new BorderLayout ()); - - target.setBackground(Color.green); - target.setName("GreenBox");//for the ease of debug - target.setPreferredSize(new Dimension(100, 100)); - String toolkit = Toolkit.getDefaultToolkit().getClass().getName(); - - // on X systems two buttons are reserved for wheel though they are countable by MouseInfo. - int buttonsNumber = toolkit.equals("sun.awt.windows.WToolkit")?MouseInfo.getNumberOfButtons():MouseInfo.getNumberOfButtons()-2; - - for (int i = 0; i < 8; i++){ - buttonNumber.add("BUTTON"+(i+1)+"_MASK"); - } - - pressOn.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent e){ - System.out.println("Now pressing : " + (buttonNumber.getSelectedIndex()+1)); - - Timer timer = new Timer(); - TimerTask robotInteraction = new TimerTask(){ - public void run(){ - robot.mouseMove(updateTargetLocation().x, updateTargetLocation().y); - robot.mousePress(getMask(buttonNumber.getSelectedIndex()+1)); - } - }; - timer.schedule(robotInteraction, SEND_DELAY); - } - }); - - releaseOn.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent e){ - System.out.println("Now releasing : " + (buttonNumber.getSelectedIndex()+1)); - Timer timer = new Timer(); - TimerTask robotInteraction = new TimerTask(){ - public void run(){ - robot.mouseMove(updateTargetLocation().x, updateTargetLocation().y); - robot.mouseRelease(getMask(buttonNumber.getSelectedIndex()+1)); - } - }; - timer.schedule(robotInteraction, SEND_DELAY); - } - }); - - clickOn.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent e){ - System.out.println("Now clicking : " + (buttonNumber.getSelectedIndex()+1)); - Timer timer = new Timer(); - TimerTask robotInteraction = new TimerTask(){ - public void run(){ - robot.mouseMove(updateTargetLocation().x, updateTargetLocation().y); - robot.mousePress(getMask(buttonNumber.getSelectedIndex()+1)); - robot.mouseRelease(getMask(buttonNumber.getSelectedIndex()+1)); - } - }; - timer.schedule(robotInteraction, SEND_DELAY); - } - - }); - target.addMouseListener(new MouseAdapter(){ - public void mousePressed(MouseEvent e){ - Sysout.println(""+e); - } - public void mouseReleased(MouseEvent e){ - Sysout.println(""+e); - } - public void mouseClicked(MouseEvent e){ - Sysout.println(""+e); - } - }); - - String[] instructions = - { - "Do provide an instruction to the robot by", - "choosing the button number to act and ", - "pressing appropriate java.awt.Button on the left.", - "Inspect an output in the TextArea below.", - "Please don't generate non-natural sequences like Release-Release, etc.", - "If you use keyboard be sure that you released the keyboard shortly.", - "If events are generated well press Pass, otherwise Fail." - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - private int getMask(int button){ - return InputEvent.getMaskForButton(button); - - /* - //this only works for standard buttons and for old JDK builds - int mask = 0; - switch (button){ - case 1: { - mask = InputEvent.BUTTON1_MASK; - break; - } - case 2: { - mask = InputEvent.BUTTON2_MASK; - break; - } - case 3: { - mask = InputEvent.BUTTON3_MASK; - break; - } - } - return mask; - */ - } - - private Point updateTargetLocation() { - return new Point(target.getLocationOnScreen().x + target.getWidth()/2, target.getLocationOnScreen().y + target.getHeight()/2); - } - - public void start () - { - //Get things going. Request focus, set size, et cetera - setSize (200,200); - setVisible(true); - validate(); - Frame f = new Frame ("Set action for Robot here."); - f.setLayout(new FlowLayout()); - f.add(buttonNumber); - f.add(pressOn); - f.add(releaseOn); - f.add(clickOn); - f.add(target); - f.pack(); - f.setVisible(true); - }// start() -}// class - -/* Place other classes related to the test after this line */ - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - This is part of the standard test machinery. - It creates a dialog (with the instructions), and is the interface - for sending text messages to the user. - To print the instructions, send an array of strings to Sysout.createDialog - WithInstructions method. Put one line of instructions per array entry. - To display a message for the tester to see, simply call Sysout.println - with the string to be displayed. - This mimics System.out.println but works within the test harness as well - as standalone. - */ - -class Sysout -{ - private static TestDialog dialog; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - This is part of the standard test machinery. It provides a place for the - test instructions to be displayed, and a place for interactive messages - to the user to be displayed. - To have the test instructions displayed, see Sysout. - To have a message to the user be displayed, see Sysout. - Do not call anything in this dialog directly. - */ -class TestDialog extends Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 120; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - }// while - }// for - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/ScrollPane/ScrollPaneTest.java b/test/jdk/java/awt/ScrollPane/ScrollPaneTest.java new file mode 100644 index 00000000000..7b628d900a5 --- /dev/null +++ b/test/jdk/java/awt/ScrollPane/ScrollPaneTest.java @@ -0,0 +1,216 @@ +/* + * 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 + * 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.awt.BorderLayout; +import java.awt.Button; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.ScrollPane; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; + +/* + * @test + * @bug 4124460 + * @key headful + * @summary Test for initializing a Motif peer component causes a crash. +*/ + +public class ScrollPaneTest { + private static volatile Point p1 = null; + private static volatile Point p2 = null; + private static Robot robot = null; + + private static Point getClickPoint(Component component) { + Point locationOnScreen = component.getLocationOnScreen(); + Dimension size = component.getSize(); + locationOnScreen.x += size.width / 2; + locationOnScreen.y += size.height / 2; + return locationOnScreen; + } + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.setAutoDelay(100); + + try { + doTest(); + } finally { + ScrollPaneTester.disposeAll(); + } + } + + private static void doTest() throws Exception { + EventQueue.invokeAndWait(ScrollPaneTester::initAndShowGui); + + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + p1 = getClickPoint(ScrollPaneTester.st1.buttonRight); + p2 = getClickPoint(ScrollPaneTester.st1.buttonSwap); + }); + + robot.mouseMove(p1.x, p1.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + robot.mouseMove(p2.x, p2.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + p1 = getClickPoint(ScrollPaneTester.st2.buttonRight); + p2 = getClickPoint(ScrollPaneTester.st2.buttonSwap); + }); + + robot.mouseMove(p1.x, p1.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + robot.mouseMove(p2.x, p2.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } +} + +class ScrollPaneTester implements ActionListener { + static ScrollPaneTester st1, st2; + final Button buttonLeft, buttonRight, buttonQuit, buttonSwap; + protected ScrollPane sp; + protected Frame f; + + public static void initAndShowGui() { + ScrollPaneTester.st1 = new ScrollPaneTester(true); + ScrollPaneTester.st2 = new ScrollPaneTester(false); + } + + public ScrollPaneTester(boolean large) { + sp = new ScrollPane(ScrollPane.SCROLLBARS_NEVER); + + Panel p = new Panel(); + + if (large) { + p.setLayout(new GridLayout(10, 10)); + for (int i = 0; i < 10; i++) + for (int j = 0; j < 10; j++) { + TextField tf = new TextField("I am " + i + j); + tf.setSize(100, 20); + p.add(tf); + } + } else { + TextField tf = new TextField("Smallness:"); + tf.setSize(150, 50); + p.add(tf); + } + + sp.add(p); + + // Button panel + buttonLeft = new Button("Left"); + buttonLeft.addActionListener(this); + buttonQuit = new Button("Quit"); + buttonQuit.addActionListener(this); + buttonSwap = new Button("Swap"); + buttonSwap.addActionListener(this); + buttonRight = new Button("Right"); + buttonRight.addActionListener(this); + + Panel bp = new Panel(); + bp.add(buttonLeft); + bp.add(buttonSwap); + bp.add(buttonQuit); + bp.add(buttonRight); + + // Window w/ button panel and scrollpane + f = new Frame("ScrollPane Test " + (large ? "large" : "small")); + f.setLayout(new BorderLayout()); + f.add("South", bp); + f.add("Center", sp); + + if (large) { + f.setSize(300, 200); + f.setLocation(100, 100); + } else { + f.setSize(200, 100); + f.setLocation(500, 100); + } + + f.setVisible(true); + } + + public static void disposeAll() { + ScrollPaneTester.st1.f.dispose(); + ScrollPaneTester.st2.f.dispose(); + } + + public static void + swapPanels() { + ScrollPane sss = st1.sp; + + st1.f.add("Center", st2.sp); + st1.sp = st2.sp; + + st2.f.add("Center", sss); + st2.sp = sss; + } + + public void + actionPerformed(ActionEvent ev) { + Object s = ev.getSource(); + + if (s == buttonLeft) { + scroll(true); + } else if (s == buttonRight) { + scroll(false); + } else if (s == buttonSwap) { + swapPanels(); + } else if (s == buttonQuit) { + disposeAll(); + } + } + + private void + scroll(boolean scroll_left) { + Point p = sp.getScrollPosition(); + + if (scroll_left) + p.x = Math.max(0, p.x - 20); + else { + int cwidth = sp.getComponent(0).getSize().width; + p.x = Math.min(p.x + 20, cwidth); + } + + sp.setScrollPosition(p); + } +} diff --git a/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java b/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java index 6018d044a61..1f2de081cce 100644 --- a/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java +++ b/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.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 @@ -120,6 +120,7 @@ static void testSplash(ImageInfo test) throws Exception { static void testFocus() throws Exception { Robot robot = new Robot(); + robot.setAutoWaitForIdle(true); robot.setAutoDelay(50); Frame frame = new Frame(); @@ -130,6 +131,7 @@ static void testFocus() throws Exception { frame.add(textField); frame.setVisible(true); robot.waitForIdle(); + robot.delay(1000); robot.keyPress(KeyEvent.VK_A); robot.keyRelease(KeyEvent.VK_A); diff --git a/test/jdk/java/awt/TextArea/Length.java b/test/jdk/java/awt/TextArea/Length.java new file mode 100644 index 00000000000..1ea99659383 --- /dev/null +++ b/test/jdk/java/awt/TextArea/Length.java @@ -0,0 +1,61 @@ +/* + * 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 + * 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.awt.EventQueue; +import java.awt.TextArea; + +/* + * @test + * @bug 4120876 + * @key headful + * @summary Ensure that getText can handle strings of various lengths, + * in particular strings longer than 255 characters + */ + +public class Length { + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(() -> { + TextArea ta = new TextArea(); + StringBuffer sb = new StringBuffer("x"); + + for (int i = 0; i < 14; i++) { + String s = sb.toString(); + check(ta, s.substring(1)); + check(ta, s); + check(ta, s + "y"); + sb.append(s); + } + }); + } + + static void check(TextArea ta, String s) { + ta.setText(s); + String s2 = ta.getText(); + System.err.println(s.length() + " " + s2.length()); + if (s.length() != s2.length()) { + throw new RuntimeException("Expected " + s.length() + + "chars, got " + s2.length()); + } + } +} diff --git a/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.java b/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.java index aa69f8fa4b7..784dc0ff18b 100644 --- a/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.java +++ b/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -30,6 +30,7 @@ * @test * @key headful * @bug 4758438 + * @requires os.family == "linux" * @summary Testcase to check the implementation of RFE 4758438 * The RFE suggests that the GNOME desktop properties * should be made accessible through the @@ -43,12 +44,14 @@ public class rfe4758438 implements PropertyChangeListener { enum PROPS { drag_threshold( + "org.gnome.desktop.peripherals.mouse drag-threshold", "org.gnome.settings-daemon.peripherals.mouse drag-threshold", "/desktop/gnome/peripherals/mouse/drag_threshold", "gnome.Net/DndDragThreshold", "int", new String[]{"5", "6"}), double_click( + "org.gnome.desktop.peripherals.mouse double-click", "org.gnome.settings-daemon.peripherals.mouse double-click", "/desktop/gnome/peripherals/mouse/double_click", "gnome.Net/DoubleClickTime", @@ -56,31 +59,43 @@ enum PROPS { new String[]{"200","300"}), cursor_blink( "org.gnome.desktop.interface cursor-blink", + null, "/desktop/gnome/interface/cursor_blink", "gnome.Net/CursorBlink", "bool", new String[]{"true","false"}), cursor_blink_time( "org.gnome.desktop.interface cursor-blink-time", + null, "/desktop/gnome/interface/cursor_blink_time", "gnome.Net/CursorBlinkTime", "int", new String[]{"1000","1500"}), gtk_theme( "org.gnome.desktop.interface gtk-theme", + null, "/desktop/gnome/interface/gtk_theme", "gnome.Net/ThemeName", "string", new String[]{"Crux","Simple"}); public final String gsettings; + public final String gsettingsFallback; public final String gconftool; public final String java; public final String type; public final String[] values; - PROPS(String gsettings, String gconftool, String java, String type, String[] values){ + PROPS( + String gsettings, + String gsettingsFallback, + String gconftool, + String java, + String type, + String[] values + ){ this.gsettings = gsettings; + this.gsettingsFallback = gsettingsFallback; this.gconftool = gconftool; this.java = java; this.type = type; @@ -90,10 +105,10 @@ enum PROPS { static boolean useGsettings; static String tool; - Toolkit toolkit = Toolkit.getDefaultToolkit(); + final Toolkit toolkit = Toolkit.getDefaultToolkit(); String changedProperty; Object changedValue; - Object lock = new Object(); + final Object lock = new Object(); /** * Implementation of PropertyChangeListener method @@ -105,7 +120,7 @@ public void propertyChange(PropertyChangeEvent event) { synchronized(lock) { try { lock.notifyAll(); - } catch (Exception e) { + } catch (Exception ignored) { } } } @@ -132,6 +147,34 @@ void doTest() throws Exception { System.out.println("Test passed"); } + String prepareCommand(PROPS property, boolean useMain) { + //Create the command to execute + StringBuffer sb = new StringBuffer(tool); + if (useGsettings) { + sb.append(" set "); + sb.append( useMain + ? property.gsettings + : property.gsettingsFallback + ); + sb.append(" "); + } else { + sb.append(" --set --type="); + sb.append(property.type); + sb.append(" "); + sb.append(property.gconftool); + sb.append(" "); + } + return sb.toString(); + } + + int doTestCommand(String cmd) throws Exception { + //Initialize the variables and execute the command + changedProperty = ""; + changedValue = null; + + return executeCommand(cmd); + } + /** * Do the test for each property. Find the current value * of the property, set the property to a value not equal @@ -146,10 +189,10 @@ void doTest(PROPS property) throws Exception { //For boolean type values, getDesktopProperty method returns Integer objects if (property.type.equals("bool")) { - if (obj.equals(new Integer(1))) { - obj = new String("true"); + if (obj.equals(1)) { + obj = "true"; } else { - obj = new String("false"); + obj = "false"; } } Object value = property.values[0]; @@ -157,56 +200,53 @@ void doTest(PROPS property) throws Exception { value = property.values[1]; } - //Create the command to execute - StringBuffer sb = new StringBuffer(tool); - if (useGsettings) { - sb.append(" set "); - sb.append(property.gsettings); - sb.append(" "); - } else { - sb.append(" --set --type="); - sb.append(property.type); - sb.append(" "); - sb.append(property.gconftool); - sb.append(" "); - } - String tempCommand = sb.toString(); - sb.append(value.toString()); + String tempCommand = prepareCommand(property, true); - //Initialize the variables and execute the command - changedProperty = ""; - changedValue = null; - if (executeCommand(sb.toString()) != 0) - throw new RuntimeException("Could not execute the command"); + int retVal = doTestCommand(tempCommand + value); + + if (retVal != 0) { + if (useGsettings && property.gsettingsFallback != null) { + System.out.printf("Failed:\n\t%s\nTrying fallback:\n\t", tempCommand); + tempCommand = prepareCommand(property, false); + System.out.println(tempCommand); + + retVal = doTestCommand(tempCommand + value); + } + + if (retVal != 0) { + throw new RuntimeException("Could not execute the command"); + } + } synchronized(lock) { try { lock.wait(5000); - } catch (Exception e) { + } catch (Exception ignored) { } } + if (property.type.equals("bool")) { - if (changedValue.equals(new Integer(1))) { - changedValue = new String("true"); + if (changedValue.equals(1)) { + changedValue = "true"; } else { - changedValue = new String("false"); + changedValue = "false"; } } //Check if the event got triggered if (!changedProperty.equals(property.java)) { //Reset the property - executeCommand(tempCommand + obj.toString()); + executeCommand(tempCommand + obj); throw new RuntimeException("PropertyChangedEvent did not occur for " + property.java); } else if (!changedValue.toString().equals(value.toString())) { //Reset the property - executeCommand(tempCommand + obj.toString()); + executeCommand(tempCommand + obj); throw new RuntimeException("New value of the property is different from " + "the value supplied"); } //Reset the property - executeCommand(tempCommand + obj.toString()); + executeCommand(tempCommand + obj); } /** @@ -231,9 +271,9 @@ int executeCommand(String command) throws Exception { stderr.append((char) es.read()); if (stdout.length() > 0) - System.out.println(stdout.toString()); + System.out.println(stdout); if (stderr.length() > 0) - System.err.println(stderr.toString()); + System.err.println(stderr); return process.exitValue(); } } diff --git a/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.sh b/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.sh index 6a55d727c7b..ce5628ac9d4 100644 --- a/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.sh +++ b/test/jdk/java/awt/Toolkit/DesktopProperties/rfe4758438.sh @@ -1,4 +1,4 @@ -# 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 @@ -28,18 +28,20 @@ OS=`uname` case "$OS" in Linux* ) - GNOMESID=`pgrep gnome-session` + GNOMESID=`pgrep gnome-session | head -n1` + + printf "\n/* gnome-session environ\n" + cat "/proc/$GNOMESID/environ" | tr '\0' '\n' + printf "\n*/\n\n" + DBUS_SESSION_BUS_ADDRESS=`grep -z DBUS_SESSION_BUS_ADDRESS /proc/$GNOMESID/environ | cut -d= -f2-` export DBUS_SESSION_BUS_ADDRESS + DISPLAY=`grep -z DISPLAY /proc/$GNOMESID/environ | cut -d= -f2-` export DISPLAY - ;; - Sun* ) - GNOMESID=`pgrep gnome-session` - DBUS_SESSION_BUS_ADDRESS=`pargs -e $GNOMESID | grep DBUS_SESSION_BUS_ADDRESS | cut -d= -f2-` - export DBUS_SESSION_BUS_ADDRESS - DISPLAY=`pargs -e $GNOMESID | grep DISPLAY | cut -d= -f2-` - export DISPLAY + + XDG_CURRENT_DESKTOP=`grep -z XDG_CURRENT_DESKTOP /proc/$GNOMESID/environ | cut -d= -f2-` + export XDG_CURRENT_DESKTOP ;; * ) echo "This Feature is not to be tested on $OS" @@ -47,13 +49,18 @@ case "$OS" in ;; esac -if [ ${GNOME_DESKTOP_SESSION_ID:-nonset} = "nonset" ]; +printf "\n/* Test env:\n\n" +env +printf "\n*/\n\n" + +XDG_GNOME=$(echo $XDG_CURRENT_DESKTOP | grep -i gnome) + +if [ -z "$XDG_GNOME" ] \ + && [ ${GNOME_DESKTOP_SESSION_ID:-nonset} = "nonset" ] \ + && [ ${GNOME_SESSION_NAME:-nonset} = "nonset" ] then - if [ ${GNOME_SESSION_NAME:-nonset} = "nonset" ]; - then - echo "This test should run under Gnome" - exit 0 - fi + echo "This test should run under Gnome" + exit 0 fi SCHEMAS=`gsettings list-schemas | wc -l` @@ -70,7 +77,11 @@ fi cd ${TESTSRC} echo $PWD echo "${TESTJAVA}/bin/javac -d ${TESTCLASSES} rfe4758438.java" + +set -e ${TESTJAVA}/bin/javac -d ${TESTCLASSES} rfe4758438.java +set +e + cd ${TESTCLASSES} ${TESTJAVA}/bin/java -DuseGsettings=${USE_GSETTINGS} -Dtool=${TOOL} ${TESTVMOPTS} rfe4758438 diff --git a/test/jdk/java/awt/Window/FindOwner/FindOwnerTest.html b/test/jdk/java/awt/Window/FindOwner/FindOwnerTest.html deleted file mode 100644 index 38846d9285f..00000000000 --- a/test/jdk/java/awt/Window/FindOwner/FindOwnerTest.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Testing Menus - - - - - - diff --git a/test/jdk/java/awt/Window/FindOwner/FindOwnerTest.java b/test/jdk/java/awt/Window/FindOwner/FindOwnerTest.java deleted file mode 100644 index 0d4f0ccd60e..00000000000 --- a/test/jdk/java/awt/Window/FindOwner/FindOwnerTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ - -/* @bug 8139227 - @summary Text fields in JPopupMenu structure do not receive focus in hosted - Applets - @author Semyon Sadetsky -*/ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; - -public class FindOwnerTest extends Applet -{ - - private boolean gained; - - public void init() { - super.init(); - } - - @Override - public void start() { - Window owner = SwingUtilities.windowForComponent(this); - - Window window1 = new Window(owner); - window1.setVisible(true); - - Window window2 = new Window(window1); - window2.setFocusable(true); - JTextField field = new JTextField("JTextField"); - field.addFocusListener(new FocusListener() { - @Override - public void focusGained(FocusEvent e) { - gained = true; - } - - @Override - public void focusLost(FocusEvent e) { - } - }); - window2.setBounds(100, 100, 200, 200); - window2.add(field); - window2.setVisible(true); - - try { - gained = false; - Robot robot = new Robot(); - robot.setAutoDelay(50); - robot.waitForIdle(); - robot.delay(200); - - Point p = field.getLocationOnScreen(); - System.out.println(p); - robot.mouseMove(p.x + 1, p.y + 1); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.mouseRelease(InputEvent.BUTTON1_MASK); - robot.waitForIdle(); - robot.delay(200); - - if (!gained) { - throw new Exception("Focus is not gained upon mouse click"); - } - System.out.println("ok"); - } catch (SecurityException e) { - - JOptionPane optionPane = new JOptionPane( - "You are in the browser so test is manual. Try to " + - "click \"JTextField\" in the opened window then press OK " + - "below", - JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); - JDialog dialog = - optionPane.createDialog(null,"FindOwnerTest instruction"); - dialog.setModalityType(Dialog.ModalityType.DOCUMENT_MODAL); - dialog.setVisible(true); - if (!gained) { - throw new RuntimeException( - "Focus is not gained upon mouse click"); - } - System.out.println("ok"); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - window1.dispose(); - stop(); - } - } -} diff --git a/test/jdk/java/awt/Window/Grab/GrabTest.java b/test/jdk/java/awt/Window/Grab/GrabTest.java index 5db28cb15b5..b173477c164 100644 --- a/test/jdk/java/awt/Window/Grab/GrabTest.java +++ b/test/jdk/java/awt/Window/Grab/GrabTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, 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 @@ -22,19 +22,34 @@ */ /* - @test - @key headful - @bug 7124430 - @summary Tests that SunToolkit.grab API works - @author anton.tarasov@oracle.com: area=awt.toolkit - @library ../../regtesthelpers - @modules java.desktop/sun.awt - @build Util - @run main GrabTest -*/ - -import java.awt.*; -import java.awt.event.*; + * @test + * @key headful + * @bug 7124430 + * @summary Tests that SunToolkit.grab API works + * @library ../../regtesthelpers + * @modules java.desktop/sun.awt + * @build Util + * @run main GrabTest + */ + + +import java.awt.AWTEvent; +import java.awt.Button; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.AWTEventListener; +import java.awt.event.MouseAdapter; + +import javax.swing.SwingUtilities; import test.java.awt.regtesthelpers.Util; public class GrabTest { @@ -56,9 +71,13 @@ public class GrabTest { static volatile boolean passed = true; - public static void main(String[] args) { + public static void main(String[] args) throws Exception { + + robot = new Robot(); + robot.setAutoDelay(100); - Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { + SwingUtilities.invokeAndWait(() -> { + Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { public void eventDispatched(AWTEvent e) { System.out.println(e); if (e instanceof sun.awt.UngrabEvent) { @@ -67,61 +86,56 @@ public void eventDispatched(AWTEvent e) { } }, sun.awt.SunToolkit.GRAB_EVENT_MASK); - f = new Frame("Frame"); - f.setBounds(0, 0, 300, 300); - f.addMouseListener(new MouseAdapter() { + f = new Frame("Frame"); + f.setBounds(0, 0, 300, 300); + f.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { System.out.println(e); framePressed = true; } }); - f1 = new Frame("OtherFrame"); - f1.setBounds(700, 100, 300, 300); + f1 = new Frame("OtherFrame"); + f1.setBounds(700, 100, 300, 300); - w = new Window(f); - w.setLayout(new FlowLayout()); - b = new Button("Press"); - b.addActionListener(new ActionListener() { + w = new Window(f); + w.setLayout(new FlowLayout()); + b = new Button("Press"); + b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println(e); buttonPressed = true; } }); - w.add(b); - w.setBounds(400, 100, 300, 300); - w.setBackground(Color.blue); - w.addMouseListener(new MouseAdapter() { + w.add(b); + w.setBounds(400, 100, 300, 300); + w.setBackground(Color.blue); + w.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { System.out.println(e); windowPressed = true; } }); - f.setVisible(true); - w.setVisible(true); + f.setVisible(true); + w.setVisible(true); - frame = new Frame(); - window1 = new Window(frame); - window1.setSize(200, 200); - window1.setLocationRelativeTo(null); - window1.setBackground(Color.blue); + frame = new Frame(); + window1 = new Window(frame); + window1.setSize(200, 200); + window1.setLocationRelativeTo(null); + window1.setBackground(Color.blue); - window2 = new Window(window1); - window2.setSize(100, 100); - window2.setLocationRelativeTo(null); - window2.setBackground(Color.green); + window2 = new Window(window1); + window2.setSize(100, 100); + window2.setLocationRelativeTo(null); + window2.setBackground(Color.green); - tk = (sun.awt.SunToolkit)Toolkit.getDefaultToolkit(); - - try { - robot = new Robot(); - } catch (AWTException ex) { - throw new RuntimeException(ex); - } + tk = (sun.awt.SunToolkit)Toolkit.getDefaultToolkit(); + }); Util.waitForIdle(robot); - + robot.delay(500); test(); } @@ -131,6 +145,7 @@ public static void test() { // 1. Check that button press doesn't cause ungrab Util.clickOnComp(b, robot); Util.waitForIdle(robot); + checkAndThrow(buttonPressed, "Error: Button can not be pressed"); if (ungrabbed) { passed = false; @@ -151,6 +166,7 @@ public static void test() { // 3. Check that press on the frame causes ungrab, event must be dispatched Util.clickOnComp(f, robot); Util.waitForIdle(robot); + checkAndThrow(framePressed, "Error: Frame can't be pressed"); if (!ungrabbed) { passed = false; @@ -173,28 +189,33 @@ public static void test() { // 5. Check that press on the other frame's title causes ungrab f1.setVisible(true); Util.waitForIdle(robot); + robot.delay(500); + Util.clickOnTitle(f1, robot); + Util.waitForIdle(robot); + if (!ungrabbed) { passed = false; System.err.println("Failure: [5] Press inside of other Frame's title didn't cause ungrab"); } f.requestFocus(); // restore focus Util.waitForIdle(robot); + if (!f.hasFocus()) { System.err.println("Error: Frame can't be focused"); } ungrabbed = false; tk.grab(w); - // 6. Check that press on the outside area causes ungrab Point loc = f.getLocationOnScreen(); robot.mouseMove(loc.x + 100, loc.y + f.getSize().height + 10); Util.waitForIdle(robot); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.delay(50); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); Util.waitForIdle(robot); + if (!ungrabbed) { passed = false; System.err.println("Failure: [6] Press on the outside area didn't cause ungrab"); @@ -218,6 +239,7 @@ public static void test() { window1.setVisible(true); window2.setVisible(true); Util.waitForIdle(robot); + robot.delay(500); tk.grab(window1); diff --git a/test/jdk/java/awt/Window/GrabSequence/GrabSequence.java b/test/jdk/java/awt/Window/GrabSequence/GrabSequence.java index 686cae28010..da419e10ef1 100644 --- a/test/jdk/java/awt/Window/GrabSequence/GrabSequence.java +++ b/test/jdk/java/awt/Window/GrabSequence/GrabSequence.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -40,11 +40,6 @@ public class GrabSequence { private static void init() { - String toolkit = Toolkit.getDefaultToolkit().getClass().getName(); - if ( toolkit.equals("sun.awt.motif.MToolkit")){ - System.out.println("This test is for XToolkit and WToolkit only. Now using " + toolkit + ". Automatically passed."); - return; - } Frame frame = new Frame("Frame"); frame.setBackground(Color.green); frame.setForeground(Color.green); diff --git a/test/jdk/java/awt/Window/WindowOwner.java b/test/jdk/java/awt/Window/WindowOwner.java new file mode 100644 index 00000000000..82cb35a1faa --- /dev/null +++ b/test/jdk/java/awt/Window/WindowOwner.java @@ -0,0 +1,155 @@ +/* + * 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 + * 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.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Window; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @key headful + * @summary automated test for window-ownership on Windows, Frames, and Dialogs + */ + +public class WindowOwner extends Panel { + + Label status = null; + static List windowsToDispose = new ArrayList<>(); + + public static void main(String[] args) throws Exception { + WindowOwner windowOwner = new WindowOwner(); + try { + EventQueue.invokeAndWait(windowOwner::init); + Thread.sleep(2000); + } finally { + EventQueue.invokeAndWait( + () -> windowsToDispose.forEach(Window::dispose) + ); + } + } + + public void init() { + status = new Label(); + add(status); + + statusMessage("Testing Window Ownership..."); + + // Test Frame as owner + Frame frame0 = new Frame("WindowOwner Test"); + windowsToDispose.add(frame0); + frame0.add("Center", new Label("Frame Level0")); + + Dialog dialog1 = new Dialog(frame0, "WindowOwner Test"); + windowsToDispose.add(dialog1); + dialog1.add("Center", new Label("Dialog Level1")); + verifyOwner(dialog1, frame0); + + Window window1 = new Window(frame0); + windowsToDispose.add(window1); + window1.add("Center", new Label("Window Level1")); + window1.setBounds(10, 10, 140, 70); + verifyOwner(window1, frame0); + + verifyOwnee(frame0, dialog1); + verifyOwnee(frame0, window1); + + // Test Dialog as owner + Dialog dialog2 = new Dialog(dialog1, "WindowOwner Test"); + windowsToDispose.add(dialog2); + dialog2.add("Center", new Label("Dialog Level2")); + verifyOwner(dialog2, dialog1); + + Window window2 = new Window(dialog1); + windowsToDispose.add(window2); + window2.add("Center", new Label("Window Level2")); + window2.setBounds(110, 110, 140, 70); + verifyOwner(window2, dialog1); + + verifyOwnee(dialog1, window2); + verifyOwnee(dialog1, dialog2); + + // Test Window as owner + Window window3 = new Window(window2); + windowsToDispose.add(window3); + window3.add("Center", new Label("Window Level3")); + window3.setBounds(210, 210, 140, 70); + verifyOwner(window3, window2); + verifyOwnee(window2, window3); + + // Ensure native peers handle ownership without errors + frame0.pack(); + frame0.setVisible(true); + + dialog1.pack(); + dialog1.setVisible(true); + + window1.setLocation(50, 50); + window1.setVisible(true); + + dialog2.pack(); + dialog2.setVisible(true); + + window2.setLocation(100, 100); + window2.setVisible(true); + + window3.setLocation(150, 150); + window3.setVisible(true); + + statusMessage("Window Ownership test completed successfully."); + } + + public void statusMessage(String msg) { + status.setText(msg); + status.invalidate(); + validate(); + } + + public static void verifyOwner(Window ownee, Window owner) { + if (ownee.getOwner() != owner) { + throw new RuntimeException("Window owner not valid for " + + ownee.getName()); + } + } + + public static void verifyOwnee(Window owner, Window ownee) { + Window[] ownedWins = owner.getOwnedWindows(); + if (!windowInList(ownedWins, ownee)) { + throw new RuntimeException("Ownee " + ownee.getName() + + " not found in owner list for " + owner.getName()); + } + } + + public static boolean windowInList(Window[] windows, Window target) { + for (Window window : windows) { + if (window == target) { + return true; + } + } + return false; + } +} diff --git a/test/jdk/java/awt/color/ICC_Profile/CustomCMMID.java b/test/jdk/java/awt/color/ICC_Profile/CustomCMMID.java new file mode 100644 index 00000000000..5eb6221d39e --- /dev/null +++ b/test/jdk/java/awt/color/ICC_Profile/CustomCMMID.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; +import java.util.Arrays; + +/** + * @test + * @bug 8321489 + * @summary tests that the cmm id is not ignored + */ +public final class CustomCMMID { + + private static final byte[] JAVA_ID = { + (byte) 'j', (byte) 'a', (byte) 'v', (byte) 'a', + }; + + private static final int[] CS = { + ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, + ColorSpace.CS_PYCC, ColorSpace.CS_sRGB + }; + + public static void main(String[] args) { + for (int cs : CS) { + ICC_Profile p = createProfile(cs); + validate(p); + } + } + + private static ICC_Profile createProfile(int type) { + byte[] data = ICC_Profile.getInstance(type).getData(); + System.arraycopy(JAVA_ID, 0, data, ICC_Profile.icHdrCmmId, + JAVA_ID.length); + return ICC_Profile.getInstance(data); + } + + private static void validate(ICC_Profile p) { + byte[] header = p.getData(ICC_Profile.icSigHead); + byte[] id = new byte[JAVA_ID.length]; + System.arraycopy(header, ICC_Profile.icHdrCmmId, id, 0, JAVA_ID.length); + + if (!java.util.Arrays.equals(id, JAVA_ID)) { + System.err.println("Expected: " + Arrays.toString(JAVA_ID)); + System.err.println("Actual: " + Arrays.toString(id)); + throw new RuntimeException("Wrong cmm id"); + } + } +} diff --git a/test/jdk/java/awt/dnd/MouseEventAfterStartDragTest/MouseEventAfterStartDragTest.java b/test/jdk/java/awt/dnd/MouseEventAfterStartDragTest/MouseEventAfterStartDragTest.java new file mode 100644 index 00000000000..faa98c43b75 --- /dev/null +++ b/test/jdk/java/awt/dnd/MouseEventAfterStartDragTest/MouseEventAfterStartDragTest.java @@ -0,0 +1,214 @@ +/* + * 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 + * 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.awt.AWTEvent; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseMotionListener; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4613903 + * @summary verifies that mouse events are not dispatched during drag + * @key headful + * @run main MouseEventAfterStartDragTest + */ + +public final class MouseEventAfterStartDragTest implements AWTEventListener { + final Frame frame = new Frame(); + volatile Point srcPoint; + volatile Dimension d; + volatile MouseEvent lastMouseEvent = null; + volatile boolean passed = true; + final DragSource dragSource = DragSource.getDefaultDragSource(); + final Transferable transferable = new StringSelection("TEXT"); + + final MouseMotionListener mouseMotionListener = new MouseMotionAdapter() { + public void mouseDragged(MouseEvent e) { + System.out.println("mouseDragged: " + e + + ", hash:" + e.hashCode()); + if (lastMouseEvent != null && !e.equals(lastMouseEvent)) { + System.out.println("Unexpected: " + e + + ", hash:" + e.hashCode()); + passed = false; + } + } + }; + + final DragSourceListener dragSourceListener = new DragSourceAdapter() { + public void dragDropEnd(DragSourceDragEvent dsde) { + System.out.println("dragDropEnd: " + dsde); + lastMouseEvent = null; + } + }; + + final DragGestureListener dragGestureListener = new DragGestureListener() { + public void dragGestureRecognized(DragGestureEvent dge) { + System.out.println("dragGestureRecognized: " + dge); + Object[] events = dge.toArray(); + Object lastEvent = events[events.length - 1]; + if (lastEvent instanceof MouseEvent) { + lastMouseEvent = (MouseEvent) lastEvent; + } + System.out.println("The last mouse event: " + lastMouseEvent + + ", hash:" + lastMouseEvent.hashCode()); + dge.startDrag(null, transferable, dragSourceListener); + } + }; + + static final Object SYNC_LOCK = new Object(); + static final int MOUSE_RELEASE_TIMEOUT = 1000; + volatile Component clickedComponent = null; + + public static void main(String[] args) throws Exception { + System.setProperty("awt.dnd.drag.threshold", "0"); + MouseEventAfterStartDragTest app = new MouseEventAfterStartDragTest(); + try { + app.createAndShowGUI(); + app.test(); + } finally { + app.dispose(); + } + } + + public void createAndShowGUI() throws Exception { + SwingUtilities.invokeAndWait(() -> { + frame.setTitle("Test frame"); + frame.setBounds(100, 100, 200, 200); + frame.setLocationRelativeTo(null); + frame.addMouseMotionListener(mouseMotionListener); + dragSource.createDefaultDragGestureRecognizer(frame, DnDConstants.ACTION_COPY_OR_MOVE, + dragGestureListener); + + frame.getToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); + frame.setVisible(true); + }); + } + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + public void test() throws Exception { + final Robot robot = new Robot(); + robot.setAutoDelay(45); + robot.waitForIdle(); + + SwingUtilities.invokeAndWait(() -> { + srcPoint = frame.getLocationOnScreen(); + d = frame.getSize(); + }); + srcPoint.translate(d.width / 2, d.height / 2); + + if (!pointInComponent(robot, srcPoint, frame)) { + System.err.println("WARNING: Couldn't locate source frame."); + return; + } + + final Point dstPoint = new Point(srcPoint); + dstPoint.translate(d.width / 4, d.height / 4); + + if (!pointInComponent(robot, dstPoint, frame)) { + System.err.println("WARNING: Couldn't locate target frame."); + return; + } + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.delay(250); + System.out.println("srcPoint = " + srcPoint); + for (; !srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + System.out.println("srcPoint = " + srcPoint); + } + + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + System.out.println("done"); + robot.waitForIdle(); + robot.delay(MOUSE_RELEASE_TIMEOUT); + + if (!passed) { + throw new RuntimeException("Test failed"); + } + } + + public void dispose() throws Exception { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + public void reset() { + clickedComponent = null; + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component) e.getSource(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + } + + boolean pointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + robot.waitForIdle(); + reset(); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + synchronized (SYNC_LOCK) { + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + SYNC_LOCK.wait(MOUSE_RELEASE_TIMEOUT); + } + + Component c = clickedComponent; + + while (c != null && c != comp) { + c = c.getParent(); + } + + return c == comp; + } +} diff --git a/test/jdk/java/awt/dnd/RecognizedActionTest/RecognizedActionTest.java b/test/jdk/java/awt/dnd/RecognizedActionTest/RecognizedActionTest.java index 399236f17d1..ecd8709721f 100644 --- a/test/jdk/java/awt/dnd/RecognizedActionTest/RecognizedActionTest.java +++ b/test/jdk/java/awt/dnd/RecognizedActionTest/RecognizedActionTest.java @@ -90,6 +90,7 @@ public void init() { dragGestureListener); frame.getToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); + frame.setLocationRelativeTo(null); frame.setVisible(true); Thread.sleep(100); @@ -165,10 +166,10 @@ public void init() { break; case InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK: - robot.keyRelease(KeyEvent.VK_CONTROL); - robot.waitForIdle(); robot.keyRelease(KeyEvent.VK_SHIFT); robot.waitForIdle(); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); break; default: diff --git a/test/jdk/java/awt/event/KeyEvent/FunctionKeyTest.java b/test/jdk/java/awt/event/KeyEvent/FunctionKeyTest.java new file mode 100644 index 00000000000..02e7a890d83 --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/FunctionKeyTest.java @@ -0,0 +1,136 @@ +/* + * 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 + * 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.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Event; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Robot; +import java.awt.TextArea; +import java.awt.event.KeyEvent; + +/* + * @test + * @bug 4011219 + * @summary Test for function key press/release received by Java client. + * @key headful + */ + +public class FunctionKeyTest { + private static FunctionKeyTester frame; + private static Robot robot; + + static volatile boolean keyPressReceived; + static volatile boolean keyReleaseReceived; + + static final StringBuilder failures = new StringBuilder(); + + private static void testKey(int keyCode, String keyText) { + keyPressReceived = false; + keyReleaseReceived = false; + + robot.keyPress(keyCode); + + if (!keyPressReceived) { + failures.append(keyText).append(" key press is not received\n"); + } + + robot.keyRelease(keyCode); + + if (!keyReleaseReceived) { + failures.append(keyText).append(" key release is not received\n"); + } + } + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.setAutoDelay(150); + + try { + EventQueue.invokeAndWait(() -> { + frame = new FunctionKeyTester(); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(1000); + + testKey(KeyEvent.VK_F11, "F11"); + testKey(KeyEvent.VK_F12, "F12"); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + if (failures.isEmpty()) { + System.out.println("Passed"); + } else { + throw new RuntimeException(failures.toString()); + } + } +} + +class FunctionKeyTester extends Frame { + Label l = new Label ("NULL"); + Button b = new Button(); + TextArea log = new TextArea(); + + FunctionKeyTester() { + super("Function Key Test"); + this.setLayout(new BorderLayout()); + this.add(BorderLayout.NORTH, l); + this.add(BorderLayout.SOUTH, b); + this.add(BorderLayout.CENTER, log); + log.setFocusable(false); + log.setEditable(false); + l.setBackground(Color.red); + setSize(200, 200); + } + + public boolean handleEvent(Event e) { + String message = "e.id=" + e.id + "\n"; + System.out.print(message); + log.append(message); + + switch (e.id) { + case 403 -> FunctionKeyTest.keyPressReceived = true; + case 404 -> FunctionKeyTest.keyReleaseReceived = true; + } + + return super.handleEvent(e); + } + + public boolean keyDown(Event e, int key) { + l.setText("e.key=" + Integer.valueOf(e.key).toString()); + return false; + } +} diff --git a/test/jdk/java/awt/event/MouseEvent/ClickDuringKeypress/ClickDuringKeypress.java b/test/jdk/java/awt/event/MouseEvent/ClickDuringKeypress/ClickDuringKeypress.java index 2f0affd5620..ae757bb7d4e 100644 --- a/test/jdk/java/awt/event/MouseEvent/ClickDuringKeypress/ClickDuringKeypress.java +++ b/test/jdk/java/awt/event/MouseEvent/ClickDuringKeypress/ClickDuringKeypress.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 @@ -21,116 +21,89 @@ * questions. */ +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + /* - @test 1.2 98/08/05 + @test @key headful @bug 4515763 @summary Tests that clicking mouse and pressing keys generates correct amount of click-counts - @author andrei.dmitriev: area=awt.mouse @run main ClickDuringKeypress */ -/** - * ClickDuringKeypress.java - * - * summary: - */ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; +public class ClickDuringKeypress implements MouseListener { -public class ClickDuringKeypress implements MouseListener - { - //Declare things used in the test, like buttons and labels here final static int CLICKCOUNT = 10; - final static int DOUBLE_CLICK_AUTO_DELAY = 10; - volatile int lastClickCount = 0; - volatile boolean clicked = false; - volatile boolean ready = false; - - Frame frame; - Robot robot; - - public void init() - { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - + final static int DOUBLE_CLICK_AUTO_DELAY = 20; + static volatile int lastClickCount = 0; + static volatile boolean clicked = false; + static volatile boolean ready = false; + + static volatile Frame frame; + static volatile Robot robot; + static final ClickDuringKeypress clicker = new ClickDuringKeypress(); + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(ClickDuringKeypress::createUI); + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.delay(2000); + robot.mouseMove(200, 200); + robot.delay(2000); + EventQueue.invokeAndWait(() -> frame.setVisible(true)); + doTest(); + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + } + + static void createUI() { frame = new Frame("ClickDuringKeypress"); - frame.addMouseListener(this); + frame.addMouseListener(clicker); frame.addWindowListener(new WindowAdapter() { public void windowActivated(WindowEvent e) { - synchronized(ClickDuringKeypress.this) { ready = true; - ClickDuringKeypress.this.notifyAll(); - } } }); frame.setBounds(0, 0, 400, 400); + } - start(); - - }//End init() - - public void start () - { - try { - robot = new Robot(); - } catch (AWTException e) { - System.out.println("Could not create Robot."); - throw new RuntimeException("Couldn't create Robot. Test fails"); - } - - robot.mouseMove(200, 200); - frame.show(); - - synchronized(this) { - try { - if (!ready) { - wait(10000); - } - } catch (InterruptedException ex) { - } - if (!ready) { - System.out.println("Not Activated. Test fails"); - throw new RuntimeException("Not Activated. Test fails"); - } + static void doTest() throws Exception { + robot.waitForIdle(); + robot.delay(1000); + if (!ready) { + System.out.println("Not Activated. Test fails"); + throw new RuntimeException("Not Activated. Test fails"); } - - doTest(); - - //What would normally go into main() will probably go here. - //Use System.out.println for diagnostic messages that you want - //to read after the test is done. - //Use Sysout.println for messages you want the tester to read. - - }// start() - - // Mouse should be over the Frame by this point - private void doTest() { + // Mouse should be over the Frame by this point robot.setAutoDelay(2000); robot.waitForIdle(); robot.keyPress(KeyEvent.VK_B); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.delay(10); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); // Should trigger mouseClicked robot.keyRelease(KeyEvent.VK_B); robot.delay(1000); robot.setAutoDelay(DOUBLE_CLICK_AUTO_DELAY); for (int i = 0; i < CLICKCOUNT / 2; i++) { - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.delay(10); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.keyPress(KeyEvent.VK_B); - robot.delay(10); robot.keyRelease(KeyEvent.VK_B); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.delay(10); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); } robot.waitForIdle(); // check results @@ -156,9 +129,4 @@ public void mouseClicked(MouseEvent e) { clicked = true; lastClickCount = e.getClickCount(); } - - public static void main(String[] args) { - new ClickDuringKeypress().init(); - } - - }// class ClickDuringKeypress +} diff --git a/test/jdk/java/awt/font/JNICheck/JNICheck.sh b/test/jdk/java/awt/font/JNICheck/JNICheck.sh index 4244b510b23..cc019e72d56 100644 --- a/test/jdk/java/awt/font/JNICheck/JNICheck.sh +++ b/test/jdk/java/awt/font/JNICheck/JNICheck.sh @@ -49,7 +49,7 @@ else fi $JAVA_HOME/bin/java ${TESTVMOPTS} \ - -cp "${CP}" -Xcheck:jni JNICheck | grep -v SIG | grep -v Signal | grep -v CallStatic > "${CP}"/log.txt + -cp "${CP}" -Xcheck:jni JNICheck | grep -v SIG | grep -v Signal | grep -v Handler | grep -v jsig | grep -v CallStatic > "${CP}"/log.txt # any messages logged may indicate a failure. if [ -s "${CP}"/log.txt ]; then diff --git a/test/jdk/java/awt/font/Rotate/RotateTest3.java b/test/jdk/java/awt/font/Rotate/RotateTest3.java new file mode 100644 index 00000000000..0241e65e1eb --- /dev/null +++ b/test/jdk/java/awt/font/Rotate/RotateTest3.java @@ -0,0 +1,114 @@ +/* + * 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 + * 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.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/* + * @test + * @key headful + * @bug 4240228 + * @summary This test is designed to test for a crashing bug in the zh + * locale on Solaris. Rotated text should be displayed, but + * anything other than a crash passes the specific test. + * For example, the missing glyph empty box may be displayed + * in some locales, or no text at all. + */ + +public class RotateTest3 extends Panel { + static JFrame frame; + + protected Java2DView java2DView; + + public RotateTest3(){ + this.setLayout(new GridLayout(1, 1)); + this.setSize(300, 300); + this.java2DView = new Java2DView(); + this.add(this.java2DView); + } + + public static void main(String[] s) throws Exception { + try { + SwingUtilities.invokeAndWait(RotateTest3::initAndShowGui); + Thread.sleep(1000); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void initAndShowGui() { + RotateTest3 panel = new RotateTest3(); + + frame = new JFrame("RotateTest3"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + frame.dispose(); + } + }); + frame.getContentPane().setLayout(new GridLayout(1, 1)); + frame.getContentPane().add("Center", panel); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + static public class Java2DView extends Component { + + public void paint(Graphics g){ + Graphics2D g2d = (Graphics2D) g; + Dimension d = this.getSize(); + g.setColor(this.getBackground()); + g.fillRect(0, 0, d.width, d.height); + g2d.setPaint(Color.black); + + g2d.translate(150,150); + g2d.rotate(-Math.PI / 3); + + String testString = + "\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341"; + g2d.drawString(testString, 0, 0); + } + + public Dimension getMinimumSize(){ + return new Dimension(300, 300); + } + + public Dimension getPreferredSize(){ + return new Dimension(300, 300); + } + } +} diff --git a/test/jdk/java/awt/font/TextLayout/ArabicBox.java b/test/jdk/java/awt/font/TextLayout/ArabicBox.java new file mode 100644 index 00000000000..e03c4cf918e --- /dev/null +++ b/test/jdk/java/awt/font/TextLayout/ArabicBox.java @@ -0,0 +1,103 @@ +/* + * 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 + * 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.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; + +import javax.swing.JPanel; + +/* + * @test + * @bug 4427483 + * @summary Arabic text followed by newline should have no missing glyphs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ArabicBox + */ +public final class ArabicBox { + + private static final String TEXT = + "\u0627\u0644\u0639\u0631\u0628\u064A\u0629\n"; + + private static final String FONT_NAME = Font.DIALOG; + + private static final String INSTRUCTIONS = """ + In the below panel, you should see the following text: + + """ + + TEXT + """ + (It's \u2018Arabic\u2019 in Arabic.) + + If there are no 'box glyphs' for missing glyphs, + press Pass; otherwise, press Fail."""; + + public static void main(String[] args) throws Exception { + final Font font = new Font(FONT_NAME, Font.PLAIN, 24); + System.out.println("asked for " + FONT_NAME + " and got: " + font.getFontName()); + + PassFailJFrame.builder() + .title("Arabic Box") + .instructions(INSTRUCTIONS) + .rows(7) + .columns(40) + .splitUIBottom(() -> createPanel(font)) + .build() + .awaitAndCheck(); + } + + private static JPanel createPanel(Font font) { + return new TextPanel(font); + } + + private static final class TextPanel extends JPanel { + private TextLayout layout; + + private TextPanel(Font font) { + setForeground(Color.black); + setBackground(Color.white); + setFont(font); + setPreferredSize(new Dimension(300, 150)); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + Graphics2D g2d = (Graphics2D)g; + if (layout == null) { + Font font = g2d.getFont(); + FontRenderContext frc = g2d.getFontRenderContext(); + + layout = new TextLayout(TEXT, font, frc); + System.out.println(layout.getBounds()); + } + + layout.draw(g2d, 10, 50); + g2d.drawString(TEXT, 10, 100); + } + } +} diff --git a/test/jdk/java/awt/font/TextLayout/TestJustification.html b/test/jdk/java/awt/font/TextLayout/TestJustification.html deleted file mode 100644 index c9e79f44cb2..00000000000 --- a/test/jdk/java/awt/font/TextLayout/TestJustification.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - -Test Justification - - - -

      Test Justification

      -
      -

      Five lines of text should appear, all justified to the same width, -followed by a sixth line containing only roman characters and no spaces -which is not justified, and instead is centered. -Carets should appear between all characters. Pass the test if this is -true. -

      - -alt="Your browser understands the <APPLET> tag but isn't running the applet, for some reason." -Your browser is completely ignoring the <APPLET> tag! - - - - diff --git a/test/jdk/java/awt/font/TextLayout/TestJustification.java b/test/jdk/java/awt/font/TextLayout/TestJustification.java index 417ddd5adfc..62377531e9d 100644 --- a/test/jdk/java/awt/font/TextLayout/TestJustification.java +++ b/test/jdk/java/awt/font/TextLayout/TestJustification.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, 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 @@ -21,229 +21,241 @@ * questions. */ +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + /* - * - * See TestJustification.html for main test. + * @test + * @bug 4211728 4178140 8145542 + * @summary Justify several lines of text and verify that the lines are the same + length and cursor positions are correct. + Bug 4211728: TextLayout.draw() draws characters at wrong position. + Bug 4178140: TextLayout does not justify. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestJustification */ -import java.applet.*; -import java.awt.*; -import java.awt.event.*; -import java.awt.font.*; -import java.awt.geom.*; -import java.text.*; - -public class TestJustification extends Applet { - JustificationPanel panel; - - public void init() { - setLayout(new BorderLayout()); - panel = new JustificationPanel("Bitstream Cyberbit"); - add("Center", panel); - } - - public void destroy() { - remove(panel); - } - - // calls system.exit, not for use in tests. - public static void main(String args[]) { - TestJustification justificationTest = new TestJustification(); - justificationTest.init(); - justificationTest.start(); - - Frame f = new Frame("Test Justification"); - f.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - System.exit(0); - } - }); - - f.add("Center", justificationTest); - f.setSize(500, 500); - f.show(); - } - - public String getAppletInfo() { - return "Test TextLayout.getJustifiedLayout()"; - } - - static class JustificationPanel extends Panel { - TextLayout[] layouts; - String fontname; - float height; - float oldfsize; - - AttributedCharacterIterator lineText; - TextLayout[] lines; - int linecount; - float oldwidth; - - JustificationPanel(String fontname) { - this.fontname = fontname; +public class TestJustification { + private static final String INSTRUCTIONS = """ + Five lines of text should appear, all justified to the same width, + followed by a sixth line containing only roman characters and + no spaces which is not justified, and instead is centered. + Carets should appear between all characters. + + PASS the test if this is true, else press FAIL. + """; + + public static void main(String[] args) throws Exception { + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(TestJustification::createUI) + .build() + .awaitAndCheck(); } - private static final String[] texts = { - "This is an english Highlighting demo.", "Highlighting", - "This is an arabic \u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e demo.", "arabic \u0627\u0628\u062a\u062c", - "This is a hebrew \u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5 demo.", "hebrew \u05d0\u05d1\u05d2", - "This is a cjk \u4e00\u4e01\u4e02\uac00\uac01\uc4fa\uf900\uf901\uf902 demo.", "cjk", - "NoSpaceCJK:\u4e00\u4e01\u4e02and\uac00\uac01\uc4faand\uf900\uf901\uf902", "No", - "NoSpaceRoman", "Space" - }; + private static Frame createUI() { + Frame frame= new Frame("Test Text Justification"); + JustificationPanel panel = new JustificationPanel("Bitstream Cyberbit"); + frame.add(panel); + frame.add("Center", panel); + frame.setSize(500, 450); + return frame; + } - public void paint(Graphics g) { - Graphics2D g2d = (Graphics2D)g; + static class JustificationPanel extends Panel { + TextLayout[] layouts; + String fontname; + float height; + float oldfsize; - Dimension d = getSize(); - Insets insets = getInsets(); + AttributedCharacterIterator lineText; + TextLayout[] lines; + int linecount; + float oldwidth; - float w = d.width - insets.left - insets.right; - float h = d.height - insets.top - insets.bottom; - int fsize = (int)w/25; + JustificationPanel(String fontname) { + this.fontname = fontname; + } - FontRenderContext frc = g2d.getFontRenderContext(); + private static final String[] texts = { + "This is an english Highlighting demo.", "Highlighting", + "This is an arabic \u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e demo.", "arabic \u0627\u0628\u062a\u062c", + "This is a hebrew \u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5 demo.", "hebrew \u05d0\u05d1\u05d2", + "This is a cjk \u4e00\u4e01\u4e02\uac00\uac01\uc4fa\uf900\uf901\uf902 demo.", "cjk", + "NoSpaceCJK:\u4e00\u4e01\u4e02and\uac00\uac01\uc4faand\uf900\uf901\uf902", "No", + "NoSpaceRoman", "Space" + }; - if (layouts == null || fsize != oldfsize) { - oldfsize = fsize; + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D)g; - Font f0 = new Font(fontname, Font.PLAIN, fsize); - Font f1 = new Font(fontname, Font.ITALIC, (int)(fsize * 1.5)); + Dimension d = getSize(); + Insets insets = getInsets(); - if (layouts == null) { - layouts = new TextLayout[texts.length / 2]; - } + float w = d.width - insets.left - insets.right; + float h = d.height - insets.top - insets.bottom; + int fsize = (int)w/25; - height = 0; - for (int i = 0; i < layouts.length; ++i) { - String text = texts[i*2]; - String target = texts[i*2+1]; + FontRenderContext frc = g2d.getFontRenderContext(); - AttributedString astr = new AttributedString(text); - astr.addAttribute(TextAttribute.FONT, f0, 0, text.length()); + if (layouts == null || fsize != oldfsize) { + oldfsize = fsize; - int start = text.indexOf(target); - int limit = start + target.length(); - astr.addAttribute(TextAttribute.FONT, f1, start, limit); + Font f0 = new Font(fontname, Font.PLAIN, fsize); + Font f1 = new Font(fontname, Font.ITALIC, (int)(fsize * 1.5)); - TextLayout layout = new TextLayout(astr.getIterator(), frc); + if (layouts == null) { + layouts = new TextLayout[texts.length / 2]; + } - layout = layout.getJustifiedLayout(w - 20); + height = 0; + for (int i = 0; i < layouts.length; ++i) { + String text = texts[i*2]; + String target = texts[i*2+1]; - layouts[i] = layout; + AttributedString astr = new AttributedString(text); + astr.addAttribute(TextAttribute.FONT, f0, 0, text.length()); + + int start = text.indexOf(target); + int limit = start + target.length(); + astr.addAttribute(TextAttribute.FONT, f1, start, limit); + + TextLayout layout = new TextLayout(astr.getIterator(), frc); - height += layout.getAscent() + layout.getDescent() + layout.getLeading(); - } - } + layout = layout.getJustifiedLayout(w - 20); + + layouts[i] = layout; - g2d.setColor(Color.white); - g2d.fill(new Rectangle.Float(insets.left, insets.top, w, h)); + height += layout.getAscent() + layout.getDescent() + layout.getLeading(); + } + } - float basey = 20; + g2d.setColor(Color.white); + g2d.fill(new Rectangle.Float(insets.left, insets.top, w, h)); - for (int i = 0; i < layouts.length; ++i) { - TextLayout layout = layouts[i]; + float basey = 20; - float la = layout.getAscent(); - float ld = layout.getDescent(); - float ll = layout.getLeading(); - float lw = layout.getAdvance(); - float lh = la + ld + ll; - float lx = (w - lw) / 2f; - float ly = basey + layout.getAscent(); + for (TextLayout layout : layouts) { + float la = layout.getAscent(); + float ld = layout.getDescent(); + float ll = layout.getLeading(); + float lw = layout.getAdvance(); + float lh = la + ld + ll; + float lx = (w - lw) / 2f; + float ly = basey + layout.getAscent(); - g2d.setColor(Color.black); - g2d.translate(insets.left + lx, insets.top + ly); + g2d.setColor(Color.black); + g2d.translate(insets.left + lx, insets.top + ly); - Rectangle2D bounds = new Rectangle2D.Float(0, -la, lw, lh); - g2d.draw(bounds); + Rectangle2D bounds = new Rectangle2D.Float(0, -la, lw, lh); + g2d.draw(bounds); - layout.draw(g2d, 0, 0); + layout.draw(g2d, 0, 0); - g2d.setColor(Color.red); - for (int j = 0, e = layout.getCharacterCount(); j <= e; ++j) { - Shape[] carets = layout.getCaretShapes(j, bounds); - g2d.draw(carets[0]); - } + g2d.setColor(Color.red); + for (int j = 0, e = layout.getCharacterCount(); j <= e; ++j) { + Shape[] carets = layout.getCaretShapes(j, bounds); + g2d.draw(carets[0]); + } - g2d.translate(-insets.left - lx, -insets.top - ly); - basey += layout.getAscent() + layout.getDescent() + layout.getLeading(); - } - - // add LineBreakMeasurer-generated layouts - - if (lineText == null) { - String text = "This is a long line of text that should be broken across multiple " - + "lines and then justified to fit the break width. This test should pass if " - + "these lines are justified to the same width, and fail otherwise. It should " - + "also format the hebrew (\u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5) and arabic " - + "(\u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e) and CJK " - + "(\u4e00\u4e01\u4e02\uac00\uac01\uc4fa\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7" - + "\u67b8\u67b9) text correctly."; - - Float regular = new Float(16.0); - Float big = new Float(24.0); - AttributedString astr = new AttributedString(text); - astr.addAttribute(TextAttribute.SIZE, regular, 0, text.length()); - astr.addAttribute(TextAttribute.FAMILY, fontname, 0, text.length()); - - int ix = text.indexOf("broken"); - astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6); - ix = text.indexOf("hebrew"); - astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6); - ix = text.indexOf("arabic"); - astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6); - ix = text.indexOf("CJK"); - astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 3); - - lineText = astr.getIterator(); - } - - float width = w - 20; - if (lines == null || width != oldwidth) { - oldwidth = width; - - lines = new TextLayout[10]; - linecount = 0; - - LineBreakMeasurer measurer = new LineBreakMeasurer(lineText, frc); - - for (;;) { - TextLayout layout = measurer.nextLayout(width); - if (layout == null) { - break; - } - - // justify all but last line - if (linecount > 0) { - lines[linecount - 1] = lines[linecount - 1].getJustifiedLayout(width); - } - - if (linecount == lines.length) { - TextLayout[] nlines = new TextLayout[lines.length * 2]; - System.arraycopy(lines, 0, nlines, 0, lines.length); - lines = nlines; - } - - lines[linecount++] = layout; - } - } + g2d.translate(-insets.left - lx, -insets.top - ly); + basey += layout.getAscent() + layout.getDescent() + layout.getLeading(); + } - float basex = insets.left + 10; - basey += 10; - g2d.setColor(Color.black); + // add LineBreakMeasurer-generated layouts - for (int i = 0; i < linecount; ++i) { - TextLayout layout = lines[i]; + if (lineText == null) { + String text = "This is a long line of text that should be broken across multiple " + + "lines and then justified to fit the break width. This test should pass if " + + "these lines are justified to the same width, and fail otherwise. It should " + + "also format the hebrew (\u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5) and arabic " + + "(\u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e) and CJK " + + "(\u4e00\u4e01\u4e02\uac00\uac01\uc4fa\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7" + + "\u67b8\u67b9) text correctly."; - basey += layout.getAscent(); - float adv = layout.getAdvance(); - float dx = layout.isLeftToRight() ? 0 : width - adv; + Float regular = 16.0F; + Float big = 24.0F; + AttributedString astr = new AttributedString(text); + astr.addAttribute(TextAttribute.SIZE, regular, 0, text.length()); + astr.addAttribute(TextAttribute.FAMILY, fontname, 0, text.length()); - layout.draw(g2d, basex + dx, basey); + int ix = text.indexOf("broken"); + astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6); + ix = text.indexOf("hebrew"); + astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6); + ix = text.indexOf("arabic"); + astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6); + ix = text.indexOf("CJK"); + astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 3); - basey += layout.getDescent() + layout.getLeading(); - } + lineText = astr.getIterator(); + } + + float width = w - 20; + if (lines == null || width != oldwidth) { + oldwidth = width; + + lines = new TextLayout[10]; + linecount = 0; + + LineBreakMeasurer measurer = new LineBreakMeasurer(lineText, frc); + + for (;;) { + TextLayout layout = measurer.nextLayout(width); + if (layout == null) { + break; + } + + // justify all but last line + if (linecount > 0) { + lines[linecount - 1] = lines[linecount - 1].getJustifiedLayout(width); + } + + if (linecount == lines.length) { + TextLayout[] nlines = new TextLayout[lines.length * 2]; + System.arraycopy(lines, 0, nlines, 0, lines.length); + lines = nlines; + } + + lines[linecount++] = layout; + } + } + + float basex = insets.left + 10; + basey += 10; + g2d.setColor(Color.black); + + for (int i = 0; i < linecount; ++i) { + TextLayout layout = lines[i]; + + basey += layout.getAscent(); + float adv = layout.getAdvance(); + float dx = layout.isLeftToRight() ? 0 : width - adv; + + layout.draw(g2d, basex + dx, basey); + + basey += layout.getDescent() + layout.getLeading(); + } + } } - } } diff --git a/test/jdk/java/awt/print/Dialog/DestinationTest.java b/test/jdk/java/awt/print/Dialog/DestinationTest.java index 8bb9403b566..933fba3042b 100644 --- a/test/jdk/java/awt/print/Dialog/DestinationTest.java +++ b/test/jdk/java/awt/print/Dialog/DestinationTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 4846344 4851365 4851321 4851316 4863656 5046198 6293139 + * @key printer * @summary Confirm that cancelling the dialog will not prompt for file. * @run main/manual DestinationTest */ diff --git a/test/jdk/java/awt/print/Dialog/DialogCopies.java b/test/jdk/java/awt/print/Dialog/DialogCopies.java index 39d15461de8..1fb874a9d57 100644 --- a/test/jdk/java/awt/print/Dialog/DialogCopies.java +++ b/test/jdk/java/awt/print/Dialog/DialogCopies.java @@ -24,6 +24,7 @@ /* * @test * @bug 6357858 + * @key printer * @summary Job must reports the number of copies set in the dialog. * @run main/manual DialogCopies */ diff --git a/test/jdk/java/awt/print/Dialog/DialogOrient.java b/test/jdk/java/awt/print/Dialog/DialogOrient.java index 92b3c65e642..982e51548bf 100644 --- a/test/jdk/java/awt/print/Dialog/DialogOrient.java +++ b/test/jdk/java/awt/print/Dialog/DialogOrient.java @@ -24,6 +24,7 @@ /* @test @bug 6594374 + @key printer @summary Confirm that the orientation is as specified. @run main/manual DialogOrient */ diff --git a/test/jdk/java/awt/print/Dialog/DialogOwnerTest.java b/test/jdk/java/awt/print/Dialog/DialogOwnerTest.java index 45156e8590a..11c854ecfc3 100644 --- a/test/jdk/java/awt/print/Dialog/DialogOwnerTest.java +++ b/test/jdk/java/awt/print/Dialog/DialogOwnerTest.java @@ -23,6 +23,7 @@ /* @test @bug 8203796 + @key printer @run main/manual DialogOwnerTest @summary Test DialogOwner API */ diff --git a/test/jdk/java/awt/print/Dialog/DialogType.java b/test/jdk/java/awt/print/Dialog/DialogType.java index 00b07930cf0..472b89e44f2 100644 --- a/test/jdk/java/awt/print/Dialog/DialogType.java +++ b/test/jdk/java/awt/print/Dialog/DialogType.java @@ -24,6 +24,7 @@ /** * @test * @bug 6568874 + * @key printer * @summary Verify the native dialog works with attribute sets. * @run main/manual=yesno DialogType */ diff --git a/test/jdk/java/awt/print/Dialog/MediaInPrintable.java b/test/jdk/java/awt/print/Dialog/MediaInPrintable.java index a800f69c74d..cb74cb140f5 100644 --- a/test/jdk/java/awt/print/Dialog/MediaInPrintable.java +++ b/test/jdk/java/awt/print/Dialog/MediaInPrintable.java @@ -24,6 +24,7 @@ /** * @test * @bug 4869575 6361766 + * @key printer * @summary Setting orientation in the page format does not have any effect on the printout. To test 6361766, the application must exit. * @run main/manual MediaInPrintable */ diff --git a/test/jdk/java/awt/print/Dialog/PaperSizeError.java b/test/jdk/java/awt/print/Dialog/PaperSizeError.java index 970c7df2fcc..fea7bb85af8 100644 --- a/test/jdk/java/awt/print/Dialog/PaperSizeError.java +++ b/test/jdk/java/awt/print/Dialog/PaperSizeError.java @@ -24,6 +24,7 @@ /** * @test * @bug 6360339 + * @key printer * @summary Test for fp error in paper size calculations. * @run main/manual PaperSizeError */ diff --git a/test/jdk/java/awt/print/Dialog/PrintApplet.java b/test/jdk/java/awt/print/Dialog/PrintApplet.java index ecd9920571c..c8a3cd955fe 100644 --- a/test/jdk/java/awt/print/Dialog/PrintApplet.java +++ b/test/jdk/java/awt/print/Dialog/PrintApplet.java @@ -24,6 +24,7 @@ /* @test @bug 5024549 + @key printer @summary Pass if dialogs are modal. @run applet/manual PrintApplet.html */ diff --git a/test/jdk/java/awt/print/Dialog/PrintDialog.java b/test/jdk/java/awt/print/Dialog/PrintDialog.java index 870db01a985..c81ebfbc452 100644 --- a/test/jdk/java/awt/print/Dialog/PrintDialog.java +++ b/test/jdk/java/awt/print/Dialog/PrintDialog.java @@ -24,6 +24,7 @@ /* @test @bug 6342748 + @key printer @summary Pass if dialogs display correctly @run main/manual PrintDialog */ diff --git a/test/jdk/java/awt/print/Dialog/PrintDlgPageable.java b/test/jdk/java/awt/print/Dialog/PrintDlgPageable.java index d447874f72a..eadd3cf9cb6 100644 --- a/test/jdk/java/awt/print/Dialog/PrintDlgPageable.java +++ b/test/jdk/java/awt/print/Dialog/PrintDlgPageable.java @@ -24,6 +24,7 @@ /** * @test * @bug 4869502 4869539 + * @key printer * @summary Confirm that ToPage is populated for argument =2. Range is disabled for argument = 0. * @run main/manual PrintDlgPageable */ diff --git a/test/jdk/java/awt/print/Dialog/RestoreActiveWindowTest/RestoreActiveWindowTest.java b/test/jdk/java/awt/print/Dialog/RestoreActiveWindowTest/RestoreActiveWindowTest.java index 8d29dbb5849..2d2e00f2ed7 100644 --- a/test/jdk/java/awt/print/Dialog/RestoreActiveWindowTest/RestoreActiveWindowTest.java +++ b/test/jdk/java/awt/print/Dialog/RestoreActiveWindowTest/RestoreActiveWindowTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 6365992 6379599 8137137 + * @key printer * @summary REG: Showing and disposing a native print dialog makes the main * frame inactive, Win32 * @run main/manual RestoreActiveWindowTest diff --git a/test/jdk/java/awt/print/Headless/HeadlessPrinterJob.java b/test/jdk/java/awt/print/Headless/HeadlessPrinterJob.java index ff7a22ec8a3..143c898e32a 100644 --- a/test/jdk/java/awt/print/Headless/HeadlessPrinterJob.java +++ b/test/jdk/java/awt/print/Headless/HeadlessPrinterJob.java @@ -31,6 +31,7 @@ /* * @test + * @key printer * @summary Check that PrinterJob constructor and methods do not throw unexpected * exceptions in headless mode * @run main/othervm -Djava.awt.headless=true HeadlessPrinterJob diff --git a/test/jdk/java/awt/print/MissedFontFamilyName/PrintFontWithMissedFontFamilyTest.java b/test/jdk/java/awt/print/MissedFontFamilyName/PrintFontWithMissedFontFamilyTest.java index a6808282f37..af655f2d69e 100644 --- a/test/jdk/java/awt/print/MissedFontFamilyName/PrintFontWithMissedFontFamilyTest.java +++ b/test/jdk/java/awt/print/MissedFontFamilyName/PrintFontWithMissedFontFamilyTest.java @@ -25,6 +25,7 @@ /** * @test * @bug 8265761 + * @key printer * @requires (os.family == "windows") * @summary Font with missed font family name is not properly printed on Windows * @run main/othervm/manual PrintFontWithMissedFontFamilyTest diff --git a/test/jdk/java/awt/print/PageFormat/CustomPaper.java b/test/jdk/java/awt/print/PageFormat/CustomPaper.java index 3b8e83d5c1b..1976a20c01f 100644 --- a/test/jdk/java/awt/print/PageFormat/CustomPaper.java +++ b/test/jdk/java/awt/print/PageFormat/CustomPaper.java @@ -25,7 +25,7 @@ * @test * @bug 4355514 * @bug 4385157 - * @author Jennifer Godinez + * @key printer * @summary Prints a rectangle to show the imageable area of a * 12in x 14in custom paper size. * @run main/manual CustomPaper diff --git a/test/jdk/java/awt/print/PageFormat/ImageableAreaTest.java b/test/jdk/java/awt/print/PageFormat/ImageableAreaTest.java index 7bf37415e6e..07f458704a5 100644 --- a/test/jdk/java/awt/print/PageFormat/ImageableAreaTest.java +++ b/test/jdk/java/awt/print/PageFormat/ImageableAreaTest.java @@ -47,7 +47,7 @@ * @test * @bug 8044444 8081491 * @summary The output's 'Page-n' footer does not show completely - * @author Alexandr Scherbatiy + * @key printer * @run main/manual ImageableAreaTest */ public class ImageableAreaTest { diff --git a/test/jdk/java/awt/print/PageFormat/NullPaper.java b/test/jdk/java/awt/print/PageFormat/NullPaper.java index 0bae3aa9592..1a9000b6135 100644 --- a/test/jdk/java/awt/print/PageFormat/NullPaper.java +++ b/test/jdk/java/awt/print/PageFormat/NullPaper.java @@ -23,13 +23,11 @@ /* @test - @key headful @bug 4199506 @summary java.awt.print.PageFormat.setpaper(Paper paper) assertion test fails by not throwing NullPointerException when a null paper instance is passed as argument and this is specified in the doc. - @author rbi: area=PageFormat @run main NullPaper */ @@ -46,9 +44,6 @@ */ -import java.awt.*; -import java.awt.event.*; -import java.awt.geom.*; import java.awt.print.*; // This test is a "main" test as applets would need Runtime permission @@ -154,7 +149,6 @@ public static synchronized void setTimeoutTo( int seconds ) public static synchronized void pass() { System.out.println( "The test passed." ); - System.out.println( "The test is over, hit Ctl-C to stop Java VM" ); //first check if this is executing in main thread if ( mainThread == Thread.currentThread() ) { @@ -180,7 +174,6 @@ public static synchronized void fail() public static synchronized void fail( String whyFailed ) { System.out.println( "The test failed: " + whyFailed ); - System.out.println( "The test is over, hit Ctl-C to stop Java VM" ); //check if this called from main thread if ( mainThread == Thread.currentThread() ) { diff --git a/test/jdk/java/awt/print/PageFormat/Orient.java b/test/jdk/java/awt/print/PageFormat/Orient.java index ac3bb1ed840..ea6facaf570 100644 --- a/test/jdk/java/awt/print/PageFormat/Orient.java +++ b/test/jdk/java/awt/print/PageFormat/Orient.java @@ -27,7 +27,7 @@ @summary Confirm that the you get three pages of output, one each in portrait, landscape, and reverse landscape orientations. - @author rbi: area=PageFormat + @key printer @run main/manual Orient */ diff --git a/test/jdk/java/awt/print/PageFormat/PageFormatFromAttributes.java b/test/jdk/java/awt/print/PageFormat/PageFormatFromAttributes.java index e552db507de..b5ac83969f2 100644 --- a/test/jdk/java/awt/print/PageFormat/PageFormatFromAttributes.java +++ b/test/jdk/java/awt/print/PageFormat/PageFormatFromAttributes.java @@ -25,6 +25,7 @@ * @test * @bug 4500750 6848799 8028584 * @summary Tests creating page format from attributes + * @key printer * @run main PageFormatFromAttributes */ import java.awt.print.*; diff --git a/test/jdk/java/awt/print/PageFormat/PageSetupDialog.java b/test/jdk/java/awt/print/PageFormat/PageSetupDialog.java index 98da138ac22..5284793ef01 100644 --- a/test/jdk/java/awt/print/PageFormat/PageSetupDialog.java +++ b/test/jdk/java/awt/print/PageFormat/PageSetupDialog.java @@ -28,7 +28,7 @@ * @bug 6358747 * @bug 6574633 * @summary Page setup dialog settings - * @author prr + * @key printer * @run main/manual PageSetupDialog */ diff --git a/test/jdk/java/awt/print/PageFormat/PrintContentCutOffTest.java b/test/jdk/java/awt/print/PageFormat/PrintContentCutOffTest.java index d2362cdc815..df07741c907 100644 --- a/test/jdk/java/awt/print/PageFormat/PrintContentCutOffTest.java +++ b/test/jdk/java/awt/print/PageFormat/PrintContentCutOffTest.java @@ -24,6 +24,7 @@ /** * @test + * @key printer * @bug 8295737 * @summary macOS: Print content cut off when width > height with portrait orientation * @run main/othervm/manual PrintContentCutOffTest diff --git a/test/jdk/java/awt/print/PageFormat/ReverseLandscapeTest.java b/test/jdk/java/awt/print/PageFormat/ReverseLandscapeTest.java index 2c6544f4af1..850123aa9a8 100644 --- a/test/jdk/java/awt/print/PageFormat/ReverseLandscapeTest.java +++ b/test/jdk/java/awt/print/PageFormat/ReverseLandscapeTest.java @@ -23,7 +23,7 @@ /* * @test - * @key headful + * @key headful printer * @bug 4254954 * @summary PageFormat would fail on solaris when setting orientation */ diff --git a/test/jdk/java/awt/print/PageFormat/SetOrient.html b/test/jdk/java/awt/print/PageFormat/SetOrient.html index e500872ff05..422d64e4610 100644 --- a/test/jdk/java/awt/print/PageFormat/SetOrient.html +++ b/test/jdk/java/awt/print/PageFormat/SetOrient.html @@ -27,6 +27,7 @@ @bug 4186119 @summary Confirm that the clip and transform of the Graphics2D is affected by the landscape orientation of the PageFormat. + @key printer @run applet/manual=yesno SetOrient.html --> diff --git a/test/jdk/java/awt/print/PageFormat/SmallPaperPrinting.java b/test/jdk/java/awt/print/PageFormat/SmallPaperPrinting.java index e03e9f283b2..e936cd047de 100644 --- a/test/jdk/java/awt/print/PageFormat/SmallPaperPrinting.java +++ b/test/jdk/java/awt/print/PageFormat/SmallPaperPrinting.java @@ -21,43 +21,56 @@ * questions. */ - import java.awt.*; - import java.awt.print.*; +import java.awt.Graphics; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; - public class SmallPaperPrinting - { - public static void main(String args[]) - { - System.out.println("----------------- Instructions --------------------"); - System.out.println("Arguments: (none) - paper width=1, height=.0001"); - System.out.println(" 1 - paper width=.0001, height=1"); - System.out.println(" 2 - paper width=-1, height=1"); - System.out.println("A passing test should catch a PrinterException"); - System.out.println("and should display \"Print error: (exception msg)\"."); - System.out.println("---------------------------------------------------\n"); - PrinterJob job = PrinterJob.getPrinterJob(); - PageFormat format = job.defaultPage(); - Paper paper = format.getPaper(); +/* + * @test + * @key printer + * @run main/othervm SmallPaperPrinting + * @run main/othervm SmallPaperPrinting 1 + * @run main/othervm SmallPaperPrinting 2 + */ + +public class SmallPaperPrinting +{ + public static void main(String args[]) { + System.out.println("----------------- Instructions --------------------"); + System.out.println("Arguments: (none) - paper width=1, height=.0001"); + System.out.println(" 1 - paper width=.0001, height=1"); + System.out.println(" 2 - paper width=-1, height=1"); + System.out.println("A passing test should catch a PrinterException"); + System.out.println("and should display \"Print error: (exception msg)\"."); + System.out.println("---------------------------------------------------\n"); + PrinterJob job = PrinterJob.getPrinterJob(); + PageFormat format = job.defaultPage(); + Paper paper = format.getPaper(); - double w = 1, h = .0001; // Generates ArithmeticException: / by zero. - if(args.length > 0 && args[0].equals("1")) { - w = .0001; h = 1; } // Generates IllegalArgumentException. - else if(args.length > 0 && args[0].equals("2")) { - w = -1; h = 1; } // Generates NegativeArraySizeException. - paper.setSize(w, h); - paper.setImageableArea(0, 0, w, h); - format.setPaper(paper); - job.setPrintable( - new Printable() { - public int print(Graphics g, PageFormat page_format, int page) { - return NO_SUCH_PAGE; - } - }, format); + double w = 1, h = .0001; // Generates ArithmeticException: / by zero. + if (args.length > 0 && args[0].equals("1")) { + w = .0001; h = 1; + } // Generates IllegalArgumentException. + else if (args.length > 0 && args[0].equals("2")) { + w = -1; h = 1; + } // Generates NegativeArraySizeException. + paper.setSize(w, h); + paper.setImageableArea(0, 0, w, h); + format.setPaper(paper); + job.setPrintable( + new Printable() { + public int print(Graphics g, PageFormat page_format, int page) { + return NO_SUCH_PAGE; + } + }, format); - try { - job.print(); } - catch(PrinterException e) { - System.err.println("Print error:\n" + e.getMessage()); // Passing test! - } + try { + job.print(); + } catch (PrinterException e) { + System.err.println("Print error:\n" + e.getMessage()); // Passing test! } } +} diff --git a/test/jdk/java/awt/print/PageFormat/ValidateCustom.java b/test/jdk/java/awt/print/PageFormat/ValidateCustom.java index e15eebf9bc4..2521609ee4f 100644 --- a/test/jdk/java/awt/print/PageFormat/ValidateCustom.java +++ b/test/jdk/java/awt/print/PageFormat/ValidateCustom.java @@ -24,7 +24,7 @@ /* * @test * @bug 4414987 - * @author Jennifer Godinez + * @key printer * @summary Displays width & height of validated custom paper size * @run main/manual ValidateCustom */ diff --git a/test/jdk/java/awt/print/PageFormat/WrongPaperForBookPrintingTest.java b/test/jdk/java/awt/print/PageFormat/WrongPaperForBookPrintingTest.java index 5c4410e0ee4..1015fab4ed6 100644 --- a/test/jdk/java/awt/print/PageFormat/WrongPaperForBookPrintingTest.java +++ b/test/jdk/java/awt/print/PageFormat/WrongPaperForBookPrintingTest.java @@ -22,7 +22,7 @@ */ /* @test - @key headful + @key printer @bug 8201818 @summary Printing attributes break page size set via "java.awt.print.Book" object diff --git a/test/jdk/java/awt/print/PageFormat/WrongPaperPrintingTest.java b/test/jdk/java/awt/print/PageFormat/WrongPaperPrintingTest.java index 77b46031487..d96d4a4c336 100644 --- a/test/jdk/java/awt/print/PageFormat/WrongPaperPrintingTest.java +++ b/test/jdk/java/awt/print/PageFormat/WrongPaperPrintingTest.java @@ -23,6 +23,7 @@ /* @test @bug 8167102 8181659 + @key printer @summary PrintRequestAttributeSet breaks page size set using PageFormat @run main/manual WrongPaperPrintingTest */ diff --git a/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorShapeTest.java b/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorShapeTest.java index 3b90d1e2068..9a626eae364 100644 --- a/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorShapeTest.java +++ b/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorShapeTest.java @@ -25,6 +25,7 @@ /** * @test * @bug 8262470 + * @key printer * @requires (os.family == "windows") * @summary Check thay shapes are properly painted with the precision scale factor * @run main/othervm/manual PathPrecisionScaleFactorShapeTest diff --git a/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorTextTest.java b/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorTextTest.java index 9b20ab4748a..f959ec9ca7c 100644 --- a/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorTextTest.java +++ b/test/jdk/java/awt/print/PathPrecisionScaleFactor/PathPrecisionScaleFactorTextTest.java @@ -25,6 +25,7 @@ /** * @test * @bug 8262470 + * @key printer * @requires (os.family == "windows") * @summary Check that a GlyphVector outline is printed with good quility on low dpi printers * @run main/othervm/manual PathPrecisionScaleFactorTextTest diff --git a/test/jdk/java/awt/print/PrinterJob/BannerTest.java b/test/jdk/java/awt/print/PrinterJob/BannerTest.java index e061e240400..e98bfaaf8d6 100644 --- a/test/jdk/java/awt/print/PrinterJob/BannerTest.java +++ b/test/jdk/java/awt/print/PrinterJob/BannerTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 6575247 8170579 + * @key printer * @summary Verifies if Banner page is printed * @requires os.family == "solaris" * @run main/manual BannerTest diff --git a/test/jdk/java/awt/print/PrinterJob/Cancel/PrinterJobCancel.java b/test/jdk/java/awt/print/PrinterJob/Cancel/PrinterJobCancel.java index 0ad27cb5a53..20197224f56 100644 --- a/test/jdk/java/awt/print/PrinterJob/Cancel/PrinterJobCancel.java +++ b/test/jdk/java/awt/print/PrinterJob/Cancel/PrinterJobCancel.java @@ -25,7 +25,7 @@ * @test * @bug 4245280 * @summary PrinterJob not cancelled when PrinterJob.cancel() is used - * @author prr + * @key printer * @run main/manual PrinterJobCancel */ diff --git a/test/jdk/java/awt/print/PrinterJob/Collate2DPrintingTest.java b/test/jdk/java/awt/print/PrinterJob/Collate2DPrintingTest.java index 8d08da6a4a5..754030c4e9e 100644 --- a/test/jdk/java/awt/print/PrinterJob/Collate2DPrintingTest.java +++ b/test/jdk/java/awt/print/PrinterJob/Collate2DPrintingTest.java @@ -25,6 +25,7 @@ * @test * @bug 6362683 8012381 * @summary Collation should work. + * @key printer * @run main/manual Collate2DPrintingTest */ import java.awt.*; diff --git a/test/jdk/java/awt/print/PrinterJob/CompareImageable.java b/test/jdk/java/awt/print/PrinterJob/CompareImageable.java index 79a78c3b5a8..aec27d09d93 100644 --- a/test/jdk/java/awt/print/PrinterJob/CompareImageable.java +++ b/test/jdk/java/awt/print/PrinterJob/CompareImageable.java @@ -24,6 +24,7 @@ /* @test @bug 4748055 + @key printer @summary PASS if the values are same in both cases (2 and 3) below. @run main/manual CompareImageable */ diff --git a/test/jdk/java/awt/print/PrinterJob/CustomFont/CustomFont.java b/test/jdk/java/awt/print/PrinterJob/CustomFont/CustomFont.java index 2b6c91fc053..e12b7fe1689 100644 --- a/test/jdk/java/awt/print/PrinterJob/CustomFont/CustomFont.java +++ b/test/jdk/java/awt/print/PrinterJob/CustomFont/CustomFont.java @@ -25,7 +25,7 @@ @test @bug 4386025 8231243 @summary fonts not in win32 font directory print incorrectly. - @author prr: area=PrinterJob + @key printer @run main/manual CustomFont */ import java.io.*; diff --git a/test/jdk/java/awt/print/PrinterJob/CustomPrintService/PrintDialog.java b/test/jdk/java/awt/print/PrinterJob/CustomPrintService/PrintDialog.java index d18ee86878d..cf37cf0cfa7 100644 --- a/test/jdk/java/awt/print/PrinterJob/CustomPrintService/PrintDialog.java +++ b/test/jdk/java/awt/print/PrinterJob/CustomPrintService/PrintDialog.java @@ -30,7 +30,6 @@ * @bug 6870661 * @summary Verify that no native dialog is opened for a custom PrintService * @run main/manual PrintDialog - * @author reinhapa */ public class PrintDialog { diff --git a/test/jdk/java/awt/print/PrinterJob/CustomPrintService/SetPrintServiceTest.java b/test/jdk/java/awt/print/PrinterJob/CustomPrintService/SetPrintServiceTest.java index 9c3ebe7d921..5a7fb526e9d 100644 --- a/test/jdk/java/awt/print/PrinterJob/CustomPrintService/SetPrintServiceTest.java +++ b/test/jdk/java/awt/print/PrinterJob/CustomPrintService/SetPrintServiceTest.java @@ -28,7 +28,6 @@ * @test * @bug 6870661 * @summary tests setPrintService() with a custom implementation - * @author reinhapa */ public class SetPrintServiceTest { diff --git a/test/jdk/java/awt/print/PrinterJob/DeviceScale.java b/test/jdk/java/awt/print/PrinterJob/DeviceScale.java index 7320eda70c9..008b5d2f0fa 100644 --- a/test/jdk/java/awt/print/PrinterJob/DeviceScale.java +++ b/test/jdk/java/awt/print/PrinterJob/DeviceScale.java @@ -21,7 +21,7 @@ * questions. */ -/* @test 1.2 02/05/15 +/* @test @bug 4810363 4924441 @key printer @run main DeviceScale diff --git a/test/jdk/java/awt/print/PrinterJob/DlgAttrsBug.java b/test/jdk/java/awt/print/PrinterJob/DlgAttrsBug.java index dc31ffda1fd..93fecd486a3 100644 --- a/test/jdk/java/awt/print/PrinterJob/DlgAttrsBug.java +++ b/test/jdk/java/awt/print/PrinterJob/DlgAttrsBug.java @@ -23,6 +23,7 @@ /* * @test * @bug 8061258 + * @key printer * @summary PrinterJob's native Print Dialog does not reflect * specified Copies or Page Ranges * @run main/manual DlgAttrsBug diff --git a/test/jdk/java/awt/print/PrinterJob/DrawImage.java b/test/jdk/java/awt/print/PrinterJob/DrawImage.java index 977dc946a92..2fcc710719c 100644 --- a/test/jdk/java/awt/print/PrinterJob/DrawImage.java +++ b/test/jdk/java/awt/print/PrinterJob/DrawImage.java @@ -24,8 +24,8 @@ /** * @test * @bug 4329866 + * @key printer * @summary Confirm that no printing exception is generated. - * @author jgodinez * @run main/manual DrawImage */ diff --git a/test/jdk/java/awt/print/PrinterJob/DrawStringMethods.java b/test/jdk/java/awt/print/PrinterJob/DrawStringMethods.java index 37bb3e045a5..f5417ebae4d 100644 --- a/test/jdk/java/awt/print/PrinterJob/DrawStringMethods.java +++ b/test/jdk/java/awt/print/PrinterJob/DrawStringMethods.java @@ -24,6 +24,7 @@ /** * @test * @bug 4185019 + * @key printer * @summary Confirm that all of the drawString methods on Graphics2D * work for printer graphics objects. * @run main/manual DrawStringMethods diff --git a/test/jdk/java/awt/print/PrinterJob/EmptyFill.java b/test/jdk/java/awt/print/PrinterJob/EmptyFill.java index ddf8ebb0150..3da1689bc3d 100644 --- a/test/jdk/java/awt/print/PrinterJob/EmptyFill.java +++ b/test/jdk/java/awt/print/PrinterJob/EmptyFill.java @@ -66,11 +66,11 @@ public static void main(String arg[]) throws Exception { } ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); StreamPrintService svc = spfs[0].getPrintService(baos); - - PrinterJob pj = PrinterJob.getPrinterJob(); if (svc == null) { - return; + throw new RuntimeException("Could not create postscript stream"); } + + PrinterJob pj = PrinterJob.getPrinterJob(); pj.setPrintService(svc); pj.setPrintable(new EmptyFill()); pj.print(); diff --git a/test/jdk/java/awt/print/PrinterJob/GetMediasTest.java b/test/jdk/java/awt/print/PrinterJob/GetMediasTest.java index c70d377c6ef..f4ff1643b00 100644 --- a/test/jdk/java/awt/print/PrinterJob/GetMediasTest.java +++ b/test/jdk/java/awt/print/PrinterJob/GetMediasTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 6653384 + * @key printer * @summary No exception should be thrown. * @run main GetMediasTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/ImagePrinting/ImageTypes.java b/test/jdk/java/awt/print/PrinterJob/ImagePrinting/ImageTypes.java index c33dc0f6718..af655b75d36 100644 --- a/test/jdk/java/awt/print/PrinterJob/ImagePrinting/ImageTypes.java +++ b/test/jdk/java/awt/print/PrinterJob/ImagePrinting/ImageTypes.java @@ -25,7 +25,7 @@ * @test * @bug 4521945 7006865 * @summary Test printing images of different types. - * @author prr + * @key printer * @run main/manual ImageTypes */ diff --git a/test/jdk/java/awt/print/PrinterJob/ImagePrinting/PrintARGBImage.java b/test/jdk/java/awt/print/PrinterJob/ImagePrinting/PrintARGBImage.java index 4bef1cce0f5..e16dc17c24f 100644 --- a/test/jdk/java/awt/print/PrinterJob/ImagePrinting/PrintARGBImage.java +++ b/test/jdk/java/awt/print/PrinterJob/ImagePrinting/PrintARGBImage.java @@ -34,6 +34,7 @@ /* * @test * @bug 6581756 + * @key printer * @library ../../../regtesthelpers * @build PassFailJFrame * @summary Test printing of images which need to have src area clipped diff --git a/test/jdk/java/awt/print/PrinterJob/InitToBlack.java b/test/jdk/java/awt/print/PrinterJob/InitToBlack.java index a4d7dd0a958..7ca5846fd47 100644 --- a/test/jdk/java/awt/print/PrinterJob/InitToBlack.java +++ b/test/jdk/java/awt/print/PrinterJob/InitToBlack.java @@ -24,6 +24,7 @@ /** * @test * @bug 4184565 + * @key printer * @summary Confirm that the default foreground color on a printer * graphics object is black so that rendering will appear * without having to execute setColor first. diff --git a/test/jdk/java/awt/print/PrinterJob/InvalidPage.java b/test/jdk/java/awt/print/PrinterJob/InvalidPage.java index f84bd2c0d78..fd013032052 100644 --- a/test/jdk/java/awt/print/PrinterJob/InvalidPage.java +++ b/test/jdk/java/awt/print/PrinterJob/InvalidPage.java @@ -25,7 +25,7 @@ * @test InvalidPage.java * @bug 4671634 6506286 * @summary Invalid page format can crash win32 JRE - * @author prr + * @key printer * @run main/manual InvalidPage */ diff --git a/test/jdk/java/awt/print/PrinterJob/JobName/PrinterJobName.java b/test/jdk/java/awt/print/PrinterJob/JobName/PrinterJobName.java index a6c65445912..f1b99487788 100644 --- a/test/jdk/java/awt/print/PrinterJob/JobName/PrinterJobName.java +++ b/test/jdk/java/awt/print/PrinterJob/JobName/PrinterJobName.java @@ -25,7 +25,7 @@ * @test * @bug 4205601 * @summary setJobName should be used by PrinterJob - * @author prr + * @key printer * @run main/manual PrinterJobName */ diff --git a/test/jdk/java/awt/print/PrinterJob/LandscapeStackOverflow.java b/test/jdk/java/awt/print/PrinterJob/LandscapeStackOverflow.java index fc65dc9ddab..398939d125d 100644 --- a/test/jdk/java/awt/print/PrinterJob/LandscapeStackOverflow.java +++ b/test/jdk/java/awt/print/PrinterJob/LandscapeStackOverflow.java @@ -22,7 +22,7 @@ */ /* * @test - * @key headful printer + * @key printer * @bug 6842011 8158758 * @summary Test if StackOverflowError occurs during printing landscape with * scale and transform. diff --git a/test/jdk/java/awt/print/PrinterJob/Legal/PrintTest.java b/test/jdk/java/awt/print/PrinterJob/Legal/PrintTest.java index b4c708e2785..f5ee1c4d65a 100644 --- a/test/jdk/java/awt/print/PrinterJob/Legal/PrintTest.java +++ b/test/jdk/java/awt/print/PrinterJob/Legal/PrintTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 4886069 8023045 + * @key printer * @summary Confirm that printer recognizes the Legal selection either by * prompting the user to put Legal paper or automatically selecting * the tray containing Legal Paper. The printout image should not diff --git a/test/jdk/java/awt/print/PrinterJob/LinearGradientPrintingTest.java b/test/jdk/java/awt/print/PrinterJob/LinearGradientPrintingTest.java index ab1ab17aa7f..456b77614a3 100644 --- a/test/jdk/java/awt/print/PrinterJob/LinearGradientPrintingTest.java +++ b/test/jdk/java/awt/print/PrinterJob/LinearGradientPrintingTest.java @@ -24,6 +24,7 @@ * @test * @bug 8162796 * @summary Verifies if LinearGradientPaint is printed in osx + * @key printer * @run main/manual LinearGradientPrintingTest */ import java.awt.BorderLayout; diff --git a/test/jdk/java/awt/print/PrinterJob/MultiMonPrintDlgTest.java b/test/jdk/java/awt/print/PrinterJob/MultiMonPrintDlgTest.java index 4086f663a5c..9da6e64fedc 100644 --- a/test/jdk/java/awt/print/PrinterJob/MultiMonPrintDlgTest.java +++ b/test/jdk/java/awt/print/PrinterJob/MultiMonPrintDlgTest.java @@ -37,6 +37,7 @@ /** * @test * @bug 8138749 + * @key printer multimon * @summary PrinterJob.printDialog() does not support multi-mon, * always displayed on primary * @run main/manual MultiMonPrintDlgTest diff --git a/test/jdk/java/awt/print/PrinterJob/MultiThread/MultiThreadTest.java b/test/jdk/java/awt/print/PrinterJob/MultiThread/MultiThreadTest.java index 8282a052ee1..609cc6f988b 100644 --- a/test/jdk/java/awt/print/PrinterJob/MultiThread/MultiThreadTest.java +++ b/test/jdk/java/awt/print/PrinterJob/MultiThread/MultiThreadTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 4922036 + * @key printer * @summary Confirm that no Exception is thrown and 2 identical output is produced. * @run main/manual MultiThreadTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/NumCopies.java b/test/jdk/java/awt/print/PrinterJob/NumCopies.java index c851e2403d0..119bcab6f7a 100644 --- a/test/jdk/java/awt/print/PrinterJob/NumCopies.java +++ b/test/jdk/java/awt/print/PrinterJob/NumCopies.java @@ -25,7 +25,7 @@ * @test * @bug 4258003 * @summary Checks the right number of copies are printed - * @author prr + * @key printer * @run main/manual NumCopies */ diff --git a/test/jdk/java/awt/print/PrinterJob/PageDialogMarginTest.java b/test/jdk/java/awt/print/PrinterJob/PageDialogMarginTest.java index de47e70f639..1931de988d1 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageDialogMarginTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PageDialogMarginTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 6801613 + * @key printer * @summary Verifies if cross-platform pageDialog and printDialog top margin * entry is working * @run main/manual PageDialogMarginTest diff --git a/test/jdk/java/awt/print/PrinterJob/PageDialogMarginValidation.java b/test/jdk/java/awt/print/PrinterJob/PageDialogMarginValidation.java index d71f84f1879..674266e7fc8 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageDialogMarginValidation.java +++ b/test/jdk/java/awt/print/PrinterJob/PageDialogMarginValidation.java @@ -23,6 +23,7 @@ /* * @test * @bug 6509729 + * @key printer * @summary Verifies pageDialog margin validation is correct * @run main/manual PageDialogMarginValidation */ diff --git a/test/jdk/java/awt/print/PrinterJob/PageDialogTest.java b/test/jdk/java/awt/print/PrinterJob/PageDialogTest.java index 8a8f9839ddf..eea118733de 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageDialogTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PageDialogTest.java @@ -24,6 +24,7 @@ /* @test @bug 6302514 + @key printer @run main/manual PageDialogTest @summary A toolkit modal dialog should not be blocked by Page/Print dialog. */ diff --git a/test/jdk/java/awt/print/PrinterJob/PageDlgApp.java b/test/jdk/java/awt/print/PrinterJob/PageDlgApp.java index e07e8ffd6cf..96d5f64486d 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageDlgApp.java +++ b/test/jdk/java/awt/print/PrinterJob/PageDlgApp.java @@ -31,6 +31,7 @@ /** * @test * @bug 8067059 + * @key printer * @run main/manual PageDlgApp * @summary Test if cancelling dialog returns null when * PrinterJob.pageDialog() with DialogSelectionType.NATIVE is called diff --git a/test/jdk/java/awt/print/PrinterJob/PageDlgPrnButton.java b/test/jdk/java/awt/print/PrinterJob/PageDlgPrnButton.java index 45a1c71a44a..6c000dc7f5f 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageDlgPrnButton.java +++ b/test/jdk/java/awt/print/PrinterJob/PageDlgPrnButton.java @@ -24,6 +24,7 @@ /** * @test * @bug 4956397 + * @key printer * @run main/manual PageDlgPrnButton */ diff --git a/test/jdk/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java b/test/jdk/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java index ffe695d6f48..048b81f273d 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PageDlgStackOverflowTest.java @@ -28,6 +28,7 @@ /** * @test * @bug 8039412 + * @key printer * @run main/manual PageDlgStackOverflowTest * @summary Calling pageDialog() after printDialog with * DialogTypeSelection.NATIVE should not result in StackOverflowError diff --git a/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java b/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java index 7cb9a2bd3ff..3cb4254c64f 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java +++ b/test/jdk/java/awt/print/PrinterJob/PageFormatChange.java @@ -24,6 +24,7 @@ /** * @test * @bug 6359283 + * @key printer * @summary pagedialog needs to update based on change of printer. * @run main/manual PageFormatChange */ diff --git a/test/jdk/java/awt/print/PrinterJob/PageRanges.java b/test/jdk/java/awt/print/PrinterJob/PageRanges.java index 9bfd79eacce..accde99ae95 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageRanges.java +++ b/test/jdk/java/awt/print/PrinterJob/PageRanges.java @@ -24,6 +24,7 @@ /** * @test * @bug 6575331 + * @key printer * @summary The specified pages should be printed. * @run main/manual=yesno PageRanges */ diff --git a/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java b/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java index 14a04d5542f..7d8568c01f9 100644 --- a/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PolylinePrintingTest.java @@ -23,6 +23,7 @@ /** * @bug 8041902 + * @key printer * @summary Test printing of wide poly lines. * @run main/manual=yesno PolylinePrintingTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintAWTImage.java b/test/jdk/java/awt/print/PrinterJob/PrintAWTImage.java index 2612ca6bdad..2397931b4fc 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintAWTImage.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintAWTImage.java @@ -23,6 +23,7 @@ /** * @test * @bug 4257262 6708509 + * @key printer * @summary Image should be sent to printer. * @run main/manual PrintAWTImage */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintAllFonts.java b/test/jdk/java/awt/print/PrinterJob/PrintAllFonts.java index 59ebf21ee5b..0c9f8afb769 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintAllFonts.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintAllFonts.java @@ -23,9 +23,11 @@ /** * + * test * @bug 4884389 7183516 + * @key printer * @summary Font specified with face name loses style on printing - * @run main/manual PrintRotatedText + * @run main/manual PrintAllFonts */ import java.awt.*; diff --git a/test/jdk/java/awt/print/PrinterJob/PrintAttributeUpdateTest.java b/test/jdk/java/awt/print/PrinterJob/PrintAttributeUpdateTest.java index 1b69e0b8b36..fd92f5deda8 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintAttributeUpdateTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintAttributeUpdateTest.java @@ -24,6 +24,7 @@ /* @test @bug 8042713 8170578 + @key printer @summary Print Dialog does not update attribute set with page range @run main/manual PrintAttributeUpdateTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintBadImage.java b/test/jdk/java/awt/print/PrinterJob/PrintBadImage.java index ad3b3fd5d83..48e4503db70 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintBadImage.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintBadImage.java @@ -25,7 +25,7 @@ * @test * @bug 4398853 * @summary Printing shouldn't hang on bad images - * @author prr + * @key printer * @run main/manual PrintBadImage */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintCompoundString.java b/test/jdk/java/awt/print/PrinterJob/PrintCompoundString.java index 82560d8964b..95cebd99765 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintCompoundString.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintCompoundString.java @@ -25,7 +25,7 @@ * @test * @bug 4396835 * @summary Compound font string not printing. - * @author prr + * @key printer * @run main/manual PrintCompoundString */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintDialog.java b/test/jdk/java/awt/print/PrinterJob/PrintDialog.java index 9ab5d63f641..fa9569e1aa7 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintDialog.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintDialog.java @@ -25,7 +25,7 @@ @test PrintDialog.java @bug 4257903 @summary Confirm that the you see the print dialog. - @author prr: area=PrinterJob + @key printer @run main/manual PrintDialog */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintDialogCancel.java b/test/jdk/java/awt/print/PrinterJob/PrintDialogCancel.java index 4fe3d29fe5b..b2d3d3a2230 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintDialogCancel.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintDialogCancel.java @@ -25,7 +25,7 @@ @test @bug 4398231 @summary Confirm that the print dialog returns false when cancelled. - @author prr: area=PrinterJob + @key printer @run main/manual PrintDialogCancel */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintDlgPageable.java b/test/jdk/java/awt/print/PrinterJob/PrintDlgPageable.java index f6a74f9f965..6d1d8bcf6df 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintDlgPageable.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintDlgPageable.java @@ -23,6 +23,7 @@ /* * @test * @bug 4885375 + * @key printer * @summary Verifies if PageRanges To Field is populated based on Pageable * for COMMON print dialog * @run main/manual PrintDlgPageable diff --git a/test/jdk/java/awt/print/PrinterJob/PrintDlgSelectionAttribTest.java b/test/jdk/java/awt/print/PrinterJob/PrintDlgSelectionAttribTest.java index 98585478626..65adee3f4f9 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintDlgSelectionAttribTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintDlgSelectionAttribTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 6529030 8159134 + * @key printer * @summary Verifies if Java Printing: Selection radiobutton gets enabled. * @requires (os.family == "windows") * @run main/manual PrintDlgSelectionAttribTest diff --git a/test/jdk/java/awt/print/PrinterJob/PrintFontStyle.java b/test/jdk/java/awt/print/PrinterJob/PrintFontStyle.java index 3d67246eac2..f9997d443a2 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintFontStyle.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintFontStyle.java @@ -21,6 +21,13 @@ * questions. */ +/* + * Not enabled as a test. Needs some work. + * test + * @key printer + * @run main/manual PrintFontStyle + */ + import java.awt.*; import java.awt.print.*; import java.awt.GraphicsEnvironment; diff --git a/test/jdk/java/awt/print/PrinterJob/PrintGlyphVectorTest.java b/test/jdk/java/awt/print/PrinterJob/PrintGlyphVectorTest.java index bd921b5162d..696cf1b7445 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintGlyphVectorTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintGlyphVectorTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 8029204 + * @key printer * @library ../../regtesthelpers * @build PassFailJFrame * @summary Tests GlyphVector is printed in the correct location diff --git a/test/jdk/java/awt/print/PrinterJob/PrintImage.java b/test/jdk/java/awt/print/PrinterJob/PrintImage.java index 7f632bfa94c..7eed8c2c276 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintImage.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintImage.java @@ -25,7 +25,7 @@ * @test %I %W * @bug 4298489 * @summary Confirm that output is same as screen. - * @author jgodinez + * @key printer * @run main/manual PrintImage */ import java.awt.*; diff --git a/test/jdk/java/awt/print/PrinterJob/PrintLatinCJKTest.java b/test/jdk/java/awt/print/PrinterJob/PrintLatinCJKTest.java index f6221aa5706..5ffb34540ce 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintLatinCJKTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintLatinCJKTest.java @@ -27,6 +27,7 @@ * @library ../../regtesthelpers * @build PassFailJFrame * @summary JDK7 Printing: CJK and Latin Text in string overlap + * @key printer * @run main/manual PrintLatinCJKTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintNullString.java b/test/jdk/java/awt/print/PrinterJob/PrintNullString.java index 7e3101c6b30..b46db6a1447 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintNullString.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintNullString.java @@ -25,7 +25,7 @@ * @test * @bug 4223328 * @summary Printer graphics must behave the same as screen graphics - * @author prr + * @key printer * @run main/manual PrintNullString */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintParenString.java b/test/jdk/java/awt/print/PrinterJob/PrintParenString.java index dc6d544d126..f3ed7ec1f3e 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintParenString.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintParenString.java @@ -25,7 +25,7 @@ * @test * @bug 4399442 * @summary Brackets should be quoted in Postscript output - * @author prr + * @key printer * @run main/manual PrintParenString */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintRotatedText.java b/test/jdk/java/awt/print/PrinterJob/PrintRotatedText.java index 5f5a6fa69d6..deabc0e6999 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintRotatedText.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintRotatedText.java @@ -26,7 +26,7 @@ * @bug 4271596 * @bug 4460699 * @summary Rotated text printing - * @author prr + * @key printer * @run main/manual PrintRotatedText */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintTest.java b/test/jdk/java/awt/print/PrinterJob/PrintTest.java index 3c3febe414c..321293877ab 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 8151590 + * @key printer * @summary All radio button should be selected when we call * setDefaultSelection(JobAttributes.DefaultSelectionType.ALL); * @run main/manual PrintTest diff --git a/test/jdk/java/awt/print/PrinterJob/PrintTestLexmarkIQ.java b/test/jdk/java/awt/print/PrinterJob/PrintTestLexmarkIQ.java index cb82100efb0..a5c7110c5d5 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintTestLexmarkIQ.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintTestLexmarkIQ.java @@ -25,6 +25,7 @@ * @bug 6966350 8160882 * @summary Verifies if Empty pages are printed on Lexmark E352dn PS3 * with "1200 IQ" setting + * @key printer * @run main/manual PrintTestLexmarkIQ */ import java.awt.BorderLayout; diff --git a/test/jdk/java/awt/print/PrinterJob/PrintTextLayout.java b/test/jdk/java/awt/print/PrinterJob/PrintTextLayout.java index 2dfd10ccd32..9560c6b86ff 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintTextLayout.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintTextLayout.java @@ -25,7 +25,7 @@ * @test * @bug 4480930 * @summary TextLayout prints as filled shapes - * @author prr + * @key printer * @run main/manual PrintTextLayout */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintTextTest.java b/test/jdk/java/awt/print/PrinterJob/PrintTextTest.java index c7d9c984b64..95bf177aa2f 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintTextTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintTextTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 6425068 7157659 8132890 + * @key printer * @summary Confirm that text prints where we expect to the length we expect. * @run main/manual=yesno PrintTextTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintTranslatedFont.java b/test/jdk/java/awt/print/PrinterJob/PrintTranslatedFont.java index fefa737990b..51ff6ce57a7 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintTranslatedFont.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintTranslatedFont.java @@ -24,8 +24,8 @@ /** * @test * @bug 6359734 + * @key printer * @summary Test that fonts with a translation print where they should. - * @author prr * @run main/manual PrintTranslatedFont */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrintVolatileImage.java b/test/jdk/java/awt/print/PrinterJob/PrintVolatileImage.java index 82a7c9ed83d..d594b5a31dd 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintVolatileImage.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintVolatileImage.java @@ -25,7 +25,7 @@ * @test * @bug 4511023 * @summary Image should be sent to printer, no exceptions thrown - * @author prr + * @key printer * @run main/manual PrintVolatileImage */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.html b/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.html index 3b6b87f6b03..9cfb86bd479 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.html +++ b/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.html @@ -25,8 +25,8 @@ diff --git a/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.java b/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.java index 2178697b170..bb40832e36f 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.java +++ b/test/jdk/java/awt/print/PrinterJob/PrinterDialogsModalityTest/PrinterDialogsModalityTest.java @@ -24,8 +24,8 @@ /* test @bug 4784285 4785920 + @key printer @summary check whether Print- and Page- dialogs are modal and correct window activated after their closing - @author son@sparc.spb.su: area=PrinterJob.modality @run applet/manual=yesno PrinterDialogsModalityTest.html */ diff --git a/test/jdk/java/awt/print/PrinterJob/PrinterJobDialogBugDemo.java b/test/jdk/java/awt/print/PrinterJob/PrinterJobDialogBugDemo.java index d06d08dcbe3..d7a18300bd7 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrinterJobDialogBugDemo.java +++ b/test/jdk/java/awt/print/PrinterJob/PrinterJobDialogBugDemo.java @@ -24,6 +24,7 @@ /** * @test * @bug 4775862 + * @key printer * @run main/manual PrinterJobDialogBugDemo */ import java.awt.BorderLayout; diff --git a/test/jdk/java/awt/print/PrinterJob/RadialGradientPrintingTest.java b/test/jdk/java/awt/print/PrinterJob/RadialGradientPrintingTest.java index 61c9faeda24..8cd538f1d62 100644 --- a/test/jdk/java/awt/print/PrinterJob/RadialGradientPrintingTest.java +++ b/test/jdk/java/awt/print/PrinterJob/RadialGradientPrintingTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 8162796 + * @key printer * @summary Verifies if RadialGradientPaint is printed in osx * @run main/manual RadialGradientPrintingTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/RemoveListener.java b/test/jdk/java/awt/print/PrinterJob/RemoveListener.java index 2a467a438a3..0818cd7e7f4 100644 --- a/test/jdk/java/awt/print/PrinterJob/RemoveListener.java +++ b/test/jdk/java/awt/print/PrinterJob/RemoveListener.java @@ -22,8 +22,9 @@ */ /* - * @test 1.1 01/05/17 + * @test * @bug 4459889 + * @key printer * @summary No NullPointerException should occur. * @run main RemoveListener */ diff --git a/test/jdk/java/awt/print/PrinterJob/SameService.java b/test/jdk/java/awt/print/PrinterJob/SameService.java index 4073b55fb92..611cea9ecdb 100644 --- a/test/jdk/java/awt/print/PrinterJob/SameService.java +++ b/test/jdk/java/awt/print/PrinterJob/SameService.java @@ -24,6 +24,7 @@ /** * @test * @bug 6446094 + * @key printer * @summary Don't re-create print services. * @run main SameService */ diff --git a/test/jdk/java/awt/print/PrinterJob/ScaledText/ScaledText.java b/test/jdk/java/awt/print/PrinterJob/ScaledText/ScaledText.java index a440db4f2f9..22da4431117 100644 --- a/test/jdk/java/awt/print/PrinterJob/ScaledText/ScaledText.java +++ b/test/jdk/java/awt/print/PrinterJob/ScaledText/ScaledText.java @@ -25,7 +25,7 @@ @test @bug 4291373 @summary Printing of scaled text is wrong / disappearing - @author prr: area=PrinterJob + @key printer @run main/manual ScaledText */ import java.awt.*; diff --git a/test/jdk/java/awt/print/PrinterJob/SecurityDialogTest.java b/test/jdk/java/awt/print/PrinterJob/SecurityDialogTest.java index c5d356c6118..d6a9efeb97c 100644 --- a/test/jdk/java/awt/print/PrinterJob/SecurityDialogTest.java +++ b/test/jdk/java/awt/print/PrinterJob/SecurityDialogTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 4937672 5100706 6252456 + * @key printer * @run main/othervm/manual -Djava.security.manager=allow SecurityDialogTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/SetCopies/Test.java b/test/jdk/java/awt/print/PrinterJob/SetCopies/Test.java index 7b8d9ce7c00..b32d716bbee 100644 --- a/test/jdk/java/awt/print/PrinterJob/SetCopies/Test.java +++ b/test/jdk/java/awt/print/PrinterJob/SetCopies/Test.java @@ -24,6 +24,7 @@ /** * @test * @bug 4694495 + * @key printer * @summary Check that the dialog shows copies = 3. * @run main/manual Test */ diff --git a/test/jdk/java/awt/print/PrinterJob/SwingUIText.java b/test/jdk/java/awt/print/PrinterJob/SwingUIText.java index c1e47638023..5fcd5e39158 100644 --- a/test/jdk/java/awt/print/PrinterJob/SwingUIText.java +++ b/test/jdk/java/awt/print/PrinterJob/SwingUIText.java @@ -24,6 +24,7 @@ /** * @test * @bug 6488219 6560738 7158350 8017469 + * @key printer * @summary Test that text printed in Swing UI measures and looks OK. * @run main/manual=yesno PrintTextTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/TestCheckSystemDefaultBannerOption.java b/test/jdk/java/awt/print/PrinterJob/TestCheckSystemDefaultBannerOption.java index b9e18d34393..ca07c05a6ef 100644 --- a/test/jdk/java/awt/print/PrinterJob/TestCheckSystemDefaultBannerOption.java +++ b/test/jdk/java/awt/print/PrinterJob/TestCheckSystemDefaultBannerOption.java @@ -23,6 +23,7 @@ /* * @test * @bug 8165947 8170579 + * @key printer * @summary Verifies System default banner page option is honoured by jdk * @requires os.family == "linux" * @run main/manual TestCheckSystemDefaultBannerOption diff --git a/test/jdk/java/awt/print/PrinterJob/TestMediaTraySelection.java b/test/jdk/java/awt/print/PrinterJob/TestMediaTraySelection.java index f75336c2c0d..6a5a91ac89e 100644 --- a/test/jdk/java/awt/print/PrinterJob/TestMediaTraySelection.java +++ b/test/jdk/java/awt/print/PrinterJob/TestMediaTraySelection.java @@ -24,6 +24,7 @@ * @bug 6357887 8165146 8234393 * @summary Verifies if selected printertray is used * @requires (os.family == "linux" | os.family == "mac") + * @key printer * @run main/manual TestMediaTraySelection */ diff --git a/test/jdk/java/awt/print/PrinterJob/TestPageDlgFrameAssociation.java b/test/jdk/java/awt/print/PrinterJob/TestPageDlgFrameAssociation.java index 32ee61ad1c6..5ca2cda4b60 100644 --- a/test/jdk/java/awt/print/PrinterJob/TestPageDlgFrameAssociation.java +++ b/test/jdk/java/awt/print/PrinterJob/TestPageDlgFrameAssociation.java @@ -23,6 +23,7 @@ /* * @test * @bug 7064425 6948907 + * @key printer * @summary Verifies if owner Frame is associated with page dialog of PrinterJob * @run main/manual TestPageDlgFrameAssociation */ diff --git a/test/jdk/java/awt/print/PrinterJob/TestPrintDlgFrameAssociation.java b/test/jdk/java/awt/print/PrinterJob/TestPrintDlgFrameAssociation.java index d1d54307ab5..e5d58c8baea 100644 --- a/test/jdk/java/awt/print/PrinterJob/TestPrintDlgFrameAssociation.java +++ b/test/jdk/java/awt/print/PrinterJob/TestPrintDlgFrameAssociation.java @@ -23,6 +23,7 @@ /* * @test * @bug 7064425 6948907 + * @key printer * @summary Verifies if owner Frame is associated with print dialog of PrinterJob * @run main/manual TestPrintDlgFrameAssociation */ diff --git a/test/jdk/java/awt/print/PrinterJob/TexturePaintPrintingTest.java b/test/jdk/java/awt/print/PrinterJob/TexturePaintPrintingTest.java index 8ac81c8bbad..3d8c6815f95 100644 --- a/test/jdk/java/awt/print/PrinterJob/TexturePaintPrintingTest.java +++ b/test/jdk/java/awt/print/PrinterJob/TexturePaintPrintingTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 8040635 + * @key printer * @summary Verifies if TexturePaint is printed in osx * @run main/manual TexturePaintPrintingTest */ diff --git a/test/jdk/java/awt/print/PrinterJob/ThinLines.java b/test/jdk/java/awt/print/PrinterJob/ThinLines.java index 2d880a44006..f8e4e60b319 100644 --- a/test/jdk/java/awt/print/PrinterJob/ThinLines.java +++ b/test/jdk/java/awt/print/PrinterJob/ThinLines.java @@ -25,7 +25,7 @@ @test @bug 4190081 @summary Confirm that the you see "Z" shapes on the printed page. - @author prr/rbi: area=PrinterJob + @key printer @run main/manual ThinLines */ diff --git a/test/jdk/java/awt/print/PrinterJob/ValidatePage/ValidatePage.java b/test/jdk/java/awt/print/PrinterJob/ValidatePage/ValidatePage.java index 8c9f81b11df..ccf0bed43bb 100644 --- a/test/jdk/java/awt/print/PrinterJob/ValidatePage/ValidatePage.java +++ b/test/jdk/java/awt/print/PrinterJob/ValidatePage/ValidatePage.java @@ -24,8 +24,8 @@ /** * @test * @bug 4252108 6229507 + * @key printer * @summary PrinterJob.validatePage() is unimplemented. - * @author prr * @run main/manual ValidatePage */ diff --git a/test/jdk/java/awt/print/PrinterJob/XparColor.java b/test/jdk/java/awt/print/PrinterJob/XparColor.java index dd39578f2cb..9a85a78af55 100644 --- a/test/jdk/java/awt/print/PrinterJob/XparColor.java +++ b/test/jdk/java/awt/print/PrinterJob/XparColor.java @@ -24,6 +24,7 @@ /** * @test * @bug 4179262 + @ @key printer * @summary Confirm that transparent colors are printed correctly. The * printout should show transparent rings with increasing darkness toward * the center. diff --git a/test/jdk/java/awt/print/PrinterJob/raster/RasterTest.java b/test/jdk/java/awt/print/PrinterJob/raster/RasterTest.java index 56adc8552f7..29709429913 100644 --- a/test/jdk/java/awt/print/PrinterJob/raster/RasterTest.java +++ b/test/jdk/java/awt/print/PrinterJob/raster/RasterTest.java @@ -25,7 +25,7 @@ * @test * @bug 4242639 * @summary Printing quality problem on Canon and NEC - * @author prr + * @key printer * @run main/manual RasterTest */ import java.awt.*; diff --git a/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java b/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java index 3bfedb463a7..7768c54481a 100644 --- a/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java +++ b/test/jdk/java/awt/print/RemotePrinterStatusRefresh/RemotePrinterStatusRefresh.java @@ -23,6 +23,7 @@ /* * @test + * @key printer * @bug 8153732 8212202 8221263 8221412 8222108 8263311 * @requires (os.family == "Windows") * @summary Windows remote printer changes do not reflect in lookupPrintServices() diff --git a/test/jdk/java/awt/print/bug8023392/bug8023392.html b/test/jdk/java/awt/print/bug8023392/bug8023392.html index c51ae001e2e..178fba9b230 100644 --- a/test/jdk/java/awt/print/bug8023392/bug8023392.html +++ b/test/jdk/java/awt/print/bug8023392/bug8023392.html @@ -26,7 +26,7 @@ @test @bug 8023392 @summary Swing text components printed with spaces between chars - @author Anton Nashatyrev + @key printer @modules java.desktop/sun.swing @run applet/manual=yesno bug8023392.html --> diff --git a/test/jdk/java/awt/print/bug8023392/bug8023392.java b/test/jdk/java/awt/print/bug8023392/bug8023392.java index 071c9021ddf..d792562ea25 100644 --- a/test/jdk/java/awt/print/bug8023392/bug8023392.java +++ b/test/jdk/java/awt/print/bug8023392/bug8023392.java @@ -25,7 +25,7 @@ test @bug 8023392 8259232 @summary Swing text components printed with spaces between chars - @author Anton Nashatyrev + @key printer @run applet/manual=yesno bug8023392.html */ diff --git a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java index 15a914934e7..6290d14b428 100644 --- a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java +++ b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -63,6 +63,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.Timer; import javax.swing.text.JTextComponent; @@ -114,6 +115,13 @@ * or a list of windows if the test needs multiple windows, * or directly a single window, an array of windows or a list of windows. *

      + * For simple test UI, use {@code Builder.splitUI}, or explicitly + * {@code Builder.splitUIRight} or {@code Builder.splitUIBottom} with + * a {@code PanelCreator}. The framework will call the provided + * {@code createUIPanel} to create the component with test UI and + * will place it as the right or bottom component in a split pane + * along with instruction UI. + *

      * Alternatively, use one of the {@code PassFailJFrame} constructors to * create an object, then create secondary test UI, register it * with {@code PassFailJFrame}, position it and make it visible. @@ -166,6 +174,14 @@ public final class PassFailJFrame { */ private static final String EMPTY_REASON = "(no reason provided)"; + /** + * List of windows or frames managed by the {@code PassFailJFrame} + * framework. These windows are automatically disposed of when the + * test is finished. + *

      + * Note: access to this field has to be synchronized by + * {@code PassFailJFrame.class}. + */ private static final List windowList = new ArrayList<>(); private static final CountDownLatch latch = new CountDownLatch(1); @@ -276,10 +292,33 @@ public PassFailJFrame(String title, String instructions, long testTimeOut, enableScreenCapture)); } - private PassFailJFrame(Builder builder) throws InterruptedException, - InvocationTargetException { - this(builder.title, builder.instructions, builder.testTimeOut, - builder.rows, builder.columns, builder.screenCapture); + /** + * Configures {@code PassFailJFrame} using the builder. + * It creates test UI specified using {@code testUI} or {@code splitUI} + * methods on EDT. + * @param builder the builder with the parameters + * @throws InterruptedException if the current thread is interrupted while + * waiting for EDT to complete a task + * @throws InvocationTargetException if an exception is thrown while + * running a task on EDT + */ + private PassFailJFrame(final Builder builder) + throws InterruptedException, InvocationTargetException { + invokeOnEDT(() -> createUI(builder)); + + if (!builder.splitUI && builder.panelCreator != null) { + JComponent content = builder.panelCreator.createUIPanel(); + String title = content.getName(); + if (title == null) { + title = "Test UI"; + } + JDialog dialog = new JDialog(frame, title, false); + dialog.addWindowListener(windowClosingHandler); + dialog.add(content, BorderLayout.CENTER); + dialog.pack(); + addTestWindow(dialog); + positionTestWindow(dialog, builder.position); + } if (builder.windowListCreator != null) { invokeOnEDT(() -> @@ -299,11 +338,10 @@ private PassFailJFrame(Builder builder) throws InterruptedException, if (builder.positionWindows != null) { positionInstructionFrame(builder.position); - invokeOnEDT(() -> { - builder.positionWindows - .positionTestWindows(unmodifiableList(builder.testWindows), - builder.instructionUIHandler); - }); + invokeOnEDT(() -> + builder.positionWindows + .positionTestWindows(unmodifiableList(builder.testWindows), + builder.instructionUIHandler)); } else if (builder.testWindows.size() == 1) { Window window = builder.testWindows.get(0); positionTestWindow(window, builder.position); @@ -341,16 +379,61 @@ private static void createUI(String title, String instructions, frame = new JFrame(title); frame.setLayout(new BorderLayout()); + frame.addWindowListener(windowClosingHandler); + + frame.add(createInstructionUIPanel(instructions, + testTimeOut, + rows, columns, + enableScreenCapture), + BorderLayout.CENTER); + frame.pack(); + frame.setLocationRelativeTo(null); + addTestWindow(frame); + } + + private static void createUI(Builder builder) { + frame = new JFrame(builder.title); + frame.setLayout(new BorderLayout()); + + frame.addWindowListener(windowClosingHandler); + + JComponent instructionUI = + createInstructionUIPanel(builder.instructions, + builder.testTimeOut, + builder.rows, builder.columns, + builder.screenCapture); + + if (builder.splitUI) { + JSplitPane splitPane = new JSplitPane( + builder.splitUIOrientation, + instructionUI, + builder.panelCreator.createUIPanel()); + frame.add(splitPane, BorderLayout.CENTER); + } else { + frame.add(instructionUI, BorderLayout.CENTER); + } + + frame.pack(); + frame.setLocationRelativeTo(null); + addTestWindow(frame); + } + + private static JComponent createInstructionUIPanel(String instructions, + long testTimeOut, + int rows, int columns, + boolean enableScreenCapture) { + JPanel main = new JPanel(new BorderLayout()); + JLabel testTimeoutLabel = new JLabel("", JLabel.CENTER); timeoutHandler = new TimeoutHandler(testTimeoutLabel, testTimeOut); - frame.add(testTimeoutLabel, BorderLayout.NORTH); + main.add(testTimeoutLabel, BorderLayout.NORTH); JTextComponent text = instructions.startsWith("") ? configureHTML(instructions, rows, columns) : configurePlainText(instructions, rows, columns); text.setEditable(false); - frame.add(new JScrollPane(text), BorderLayout.CENTER); + main.add(new JScrollPane(text), BorderLayout.CENTER); JButton btnPass = new JButton("Pass"); btnPass.addActionListener((e) -> { @@ -372,12 +455,10 @@ private static void createUI(String title, String instructions, buttonsPanel.add(createCapturePanel()); } - frame.addWindowListener(windowClosingHandler); + main.add(buttonsPanel, BorderLayout.SOUTH); + main.setMinimumSize(main.getPreferredSize()); - frame.add(buttonsPanel, BorderLayout.SOUTH); - frame.pack(); - frame.setLocationRelativeTo(null); - addTestWindow(frame); + return main; } private static JTextComponent configurePlainText(String instructions, @@ -433,6 +514,22 @@ public interface WindowListCreator { List createTestUI(); } + /** + * Creates a component (panel) with test UI + * to be hosted in a split pane or a frame. + */ + @FunctionalInterface + public interface PanelCreator { + /** + * Creates a component which hosts test UI. This component + * is placed into a split pane or into a frame to display the UI. + *

      + * This method is called by the framework on the EDT. + * @return a component (panel) with test UI + */ + JComponent createUIPanel(); + } + /** * Positions test UI windows. */ @@ -634,10 +731,12 @@ private static void captureScreen(CaptureType type) { break; case WINDOWS: - windowList.stream() - .filter(Window::isShowing) - .map(Window::getBounds) - .forEach(PassFailJFrame::captureScreen); + synchronized (PassFailJFrame.class) { + windowList.stream() + .filter(Window::isShowing) + .map(Window::getBounds) + .forEach(PassFailJFrame::captureScreen); + } break; default: @@ -950,6 +1049,9 @@ public static final class Builder { private List testWindows; private WindowListCreator windowListCreator; + private PanelCreator panelCreator; + private boolean splitUI; + private int splitUIOrientation; private PositionWindows positionWindows; private InstructionUI instructionUIHandler; @@ -1090,8 +1192,102 @@ private void checkWindowsLists() { } } - public Builder positionTestUI(PositionWindows positionWindows) { - this.positionWindows = positionWindows; + /** + * Adds a {@code PanelCreator} which the framework will use + * to create a component and place it into a dialog. + * + * @param panelCreator a {@code PanelCreator} to create a component + * with test UI + * @return this builder + * @throws IllegalStateException if split UI was enabled using + * a {@code splitUI} method + */ + public Builder testUI(PanelCreator panelCreator) { + if (splitUI) { + throw new IllegalStateException("Can't combine splitUI and " + + "testUI with panelCreator"); + } + this.panelCreator = panelCreator; + return this; + } + + /** + * Adds a {@code PanelCreator} which the framework will use + * to create a component with test UI and display it in a split pane. + *

      + * By default, horizontal orientation is used, + * and test UI is displayed to the right of the instruction UI. + * + * @param panelCreator a {@code PanelCreator} to create a component + * with test UI + * @return this builder + * + * @throws IllegalStateException if a {@code PanelCreator} is + * already set + * @throws IllegalArgumentException if {panelCreator} is {@code null} + */ + public Builder splitUI(PanelCreator panelCreator) { + return splitUIRight(panelCreator); + } + + /** + * Adds a {@code PanelCreator} which the framework will use + * to create a component with test UI and display it + * to the right of instruction UI. + * + * @param panelCreator a {@code PanelCreator} to create a component + * with test UI + * @return this builder + * + * @throws IllegalStateException if a {@code PanelCreator} is + * already set + * @throws IllegalArgumentException if {panelCreator} is {@code null} + */ + public Builder splitUIRight(PanelCreator panelCreator) { + return splitUI(panelCreator, JSplitPane.HORIZONTAL_SPLIT); + } + + /** + * Adds a {@code PanelCreator} which the framework will use + * to create a component with test UI and display it + * in the bottom of instruction UI. + * + * @param panelCreator a {@code PanelCreator} to create a component + * with test UI + * @return this builder + * + * @throws IllegalStateException if a {@code PanelCreator} is + * already set + * @throws IllegalArgumentException if {panelCreator} is {@code null} + */ + public Builder splitUIBottom(PanelCreator panelCreator) { + return splitUI(panelCreator, JSplitPane.VERTICAL_SPLIT); + } + + /** + * Enables split UI and stores the orientation of the split pane. + * + * @param panelCreator a {@code PanelCreator} to create a component + * with test UI + * @param splitUIOrientation orientation of the split pane + * @return this builder + * + * @throws IllegalStateException if a {@code PanelCreator} is + * already set + * @throws IllegalArgumentException if {panelCreator} is {@code null} + */ + private Builder splitUI(PanelCreator panelCreator, + int splitUIOrientation) { + if (panelCreator == null) { + throw new IllegalArgumentException("A PanelCreator cannot be null"); + } + if (this.panelCreator != null) { + throw new IllegalStateException("A PanelCreator is already set"); + } + + splitUI = true; + this.splitUIOrientation = splitUIOrientation; + this.panelCreator = panelCreator; return this; } @@ -1129,7 +1325,8 @@ private void validate() { } if (position == null - && (testWindows != null || windowListCreator != null)) { + && (testWindows != null || windowListCreator != null + || (!splitUI && panelCreator != null))) { position = Position.HORIZONTAL; } @@ -1137,7 +1334,7 @@ private void validate() { if (positionWindows != null) { if (testWindows == null && windowListCreator == null) { throw new IllegalStateException("To position windows, " - + "provide an a list of windows to the builder"); + + "provide a list of windows to the builder"); } instructionUIHandler = new InstructionUIHandler(); } @@ -1176,6 +1373,11 @@ public Position getPosition() { } } + /** + * Creates a builder for configuring {@code PassFailJFrame}. + * + * @return the builder for configuring {@code PassFailJFrame} + */ public static Builder builder() { return new Builder(); } diff --git a/test/jdk/java/awt/regtesthelpers/Util.java b/test/jdk/java/awt/regtesthelpers/Util.java index 90739ded406..d0ec8bcc5c9 100644 --- a/test/jdk/java/awt/regtesthelpers/Util.java +++ b/test/jdk/java/awt/regtesthelpers/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -410,8 +410,7 @@ public void windowClosing(WindowEvent e) { } /* - * The values directly map to the ones of - * sun.awt.X11.XWM & sun.awt.motif.MToolkit classes. + * The values directly map to the ones of sun.awt.X11.XWM class. */ public final static int UNDETERMINED_WM = 1, @@ -438,8 +437,6 @@ public static int getWMID() { try { if ("sun.awt.X11.XToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { clazz = Class.forName("sun.awt.X11.XWM"); - } else if ("sun.awt.motif.MToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName())) { - clazz = Class.forName("sun.awt.motif.MToolkit"); } } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); @@ -451,7 +448,6 @@ public static int getWMID() { try { final Class _clazz = clazz; Method m_addExports = Class.forName("java.awt.Helper").getDeclaredMethod("addExports", String.class, java.lang.Module.class); - // No MToolkit anymore: nothing to do about it. // We may be called from non-X11 system, and this permission cannot be delegated to a test. m_addExports.invoke(null, "sun.awt.X11", Util.class.getModule()); Method m_getWMID = (Method)AccessController.doPrivileged(new PrivilegedAction() { diff --git a/test/jdk/java/io/Serializable/concurrentClassDescLookup/ConcurrentClassDescLookup.java b/test/jdk/java/io/Serializable/concurrentClassDescLookup/ConcurrentClassDescLookup.java index 48cbd1b0ffe..196a27b52ba 100644 --- a/test/jdk/java/io/Serializable/concurrentClassDescLookup/ConcurrentClassDescLookup.java +++ b/test/jdk/java/io/Serializable/concurrentClassDescLookup/ConcurrentClassDescLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, 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 @@ -28,6 +28,7 @@ */ import java.io.*; +import java.util.concurrent.CountDownLatch; class Good implements Serializable { private static final long serialVersionUID = 6319710844400051132L; @@ -51,19 +52,20 @@ class Bad implements Serializable { class SuccessfulLookup extends Thread { Class cl; long suid; - Object barrier; + final CountDownLatch lookupLatch; boolean ok; - SuccessfulLookup(Class cl, long suid, Object barrier) { + SuccessfulLookup(Class cl, long suid, CountDownLatch lookupLatch) { this.cl = cl; this.suid = suid; - this.barrier = barrier; + this.lookupLatch = lookupLatch; } public void run() { - synchronized (barrier) { - try { barrier.wait(); } catch (InterruptedException ex) {} - } + lookupLatch.countDown(); // let others know we are ready + try { + lookupLatch.await(); // await for others + } catch (InterruptedException ex) {} for (int i = 0; i < 100; i++) { if (ObjectStreamClass.lookup(cl).getSerialVersionUID() != suid) { return; @@ -75,18 +77,19 @@ public void run() { class FailingLookup extends Thread { Class cl; - final Object barrier; + final CountDownLatch lookupLatch; boolean ok; - FailingLookup(Class cl, Object barrier) { + FailingLookup(Class cl, CountDownLatch lookupLatch) { this.cl = cl; - this.barrier = barrier; + this.lookupLatch = lookupLatch; } public void run() { - synchronized (barrier) { - try { barrier.wait(); } catch (InterruptedException ex) {} - } + lookupLatch.countDown(); // let others know we are ready + try { + lookupLatch.await(); // await for others + } catch (InterruptedException ex) {} for (int i = 0; i < 100; i++) { try { ObjectStreamClass.lookup(cl); @@ -102,39 +105,36 @@ public class ConcurrentClassDescLookup { public static void main(String[] args) throws Exception { ClassLoader loader = ConcurrentClassDescLookup.class.getClassLoader(); Class cl = Class.forName("Good", false, loader); - Object barrier = new Object(); - SuccessfulLookup[] slookups = new SuccessfulLookup[50]; + int numSuccessfulLookups = 50; + CountDownLatch sLookupLatch = new CountDownLatch(numSuccessfulLookups); + SuccessfulLookup[] slookups = new SuccessfulLookup[numSuccessfulLookups]; for (int i = 0; i < slookups.length; i++) { - slookups[i] = - new SuccessfulLookup(cl, 6319710844400051132L, barrier); + slookups[i] = new SuccessfulLookup(cl, 6319710844400051132L, sLookupLatch); slookups[i].start(); } - Thread.sleep(1000); - synchronized (barrier) { - barrier.notifyAll(); - } + System.out.println("awaiting completion of " + slookups.length + " SuccessfulLookup"); for (int i = 0; i < slookups.length; i++) { slookups[i].join(); if (!slookups[i].ok) { throw new Error(); } } - + System.out.println("all " + slookups.length + " SuccessfulLookup completed"); cl = Class.forName("Bad", false, loader); - FailingLookup[] flookups = new FailingLookup[50]; + int numFailingLookups = 50; + CountDownLatch fLookupLatch = new CountDownLatch(numFailingLookups); + FailingLookup[] flookups = new FailingLookup[numFailingLookups]; for (int i = 0; i < flookups.length; i++) { - flookups[i] = new FailingLookup(cl, barrier); + flookups[i] = new FailingLookup(cl, fLookupLatch); flookups[i].start(); } - Thread.sleep(1000); - synchronized (barrier) { - barrier.notifyAll(); - } - for (int i = 0; i < slookups.length; i++) { + System.out.println("awaiting completion of " + flookups.length + " FailingLookup"); + for (int i = 0; i < flookups.length; i++) { flookups[i].join(); if (!flookups[i].ok) { throw new Error(); } } + System.out.println("all " + flookups.length + " FailingLookup completed"); } } diff --git a/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java b/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java index a0374522567..045e26bf3c3 100644 --- a/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.java +++ b/test/jdk/java/lang/ClassLoader/forNameLeak/ClassForNameLeak.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 @@ -28,15 +28,14 @@ * from a custom classloader. * @library /test/lib * @build jdk.test.lib.Utils + * jdk.test.lib.util.ForceGC * jdk.test.lib.util.JarUtils * @build ClassForName ClassForNameLeak * @run main/othervm/policy=test.policy -Djava.security.manager ClassForNameLeak */ import java.io.IOException; -import java.lang.ref.PhantomReference; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -51,6 +50,7 @@ import java.util.stream.Stream; import jdk.test.lib.Utils; +import jdk.test.lib.util.ForceGC; import jdk.test.lib.util.JarUtils; /* @@ -60,35 +60,32 @@ public class ClassForNameLeak { private static final long TIMEOUT = (long)(5000.0 * Utils.TIMEOUT_FACTOR); private static final int THREADS = 10; private static final Path jarFilePath = Paths.get("cfn.jar"); - private static final ReferenceQueue rq = new ReferenceQueue<>(); - static class TestLoader { - private final PhantomReference ref; - TestLoader() { - this.ref = loadAndRun(); - } - - // Use a new classloader to load the ClassForName class, then run its - // Runnable. - PhantomReference loadAndRun() { + static class TestLoader extends URLClassLoader { + static URL[] toURLs() { try { - ClassLoader classLoader = - new URLClassLoader("LeakedClassLoader", - new URL[]{jarFilePath.toUri().toURL()}, - ClassLoader.getPlatformClassLoader()); - - Class loadClass = Class.forName("ClassForName", true, classLoader); - ((Runnable) loadClass.newInstance()).run(); - - return new PhantomReference<>(classLoader, rq); - } catch (MalformedURLException|ReflectiveOperationException e) { + return new URL[]{jarFilePath.toUri().toURL()}; + } catch (MalformedURLException e) { throw new RuntimeException(e); } } - PhantomReference getRef() { - return ref; + TestLoader() { + super("LeakedClassLoader", toURLs(), ClassLoader.getPlatformClassLoader()); + } + } + + // Use a new classloader to load the ClassForName class, then run its + // Runnable. + static WeakReference loadAndRun() { + TestLoader classLoader = new TestLoader(); + try { + Class loadClass = Class.forName("ClassForName", true, classLoader); + ((Runnable) loadClass.newInstance()).run(); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); } + return new WeakReference<>(classLoader); } public static void main(String... args) throws Exception { @@ -98,30 +95,19 @@ public static void main(String... args) throws Exception { // Make simultaneous calls to the test method, to stress things a bit ExecutorService es = Executors.newFixedThreadPool(THREADS); - List> callables = + List>> callables = Stream.generate(() -> { - Callable cprcl = TestLoader::new; + Callable> cprcl = ClassForNameLeak::loadAndRun; return cprcl; }).limit(THREADS).collect(Collectors.toList()); - List> futures = es.invokeAll(callables); - - // Give the GC a chance to enqueue the PhantomReferences - for (int i = 0; i < 10; i++) { - System.gc(); - } - - // Make sure all PhantomReferences to the leaked classloader are enqueued - for (int j = 0; j < futures.size(); j++) { - Reference rmRef = rq.remove(TIMEOUT); - if (rmRef == null) { - throw new RuntimeException("ClassLoader was never enqueued!"); - } else { - System.out.println("Enqueued " + rmRef); + for (Future> future : es.invokeAll(callables)) { + WeakReference ref = future.get(); + if (!ForceGC.wait(() -> ref.refersTo(null))) { + throw new RuntimeException(ref.get() + " not unloaded"); } } es.shutdown(); - System.out.println("All ClassLoaders successfully enqueued"); } private static final String CLASSFILENAME = "ClassForName.class"; diff --git a/test/jdk/java/lang/Math/CubeRootTests.java b/test/jdk/java/lang/Math/CubeRootTests.java index a6350702d90..9347160d719 100644 --- a/test/jdk/java/lang/Math/CubeRootTests.java +++ b/test/jdk/java/lang/Math/CubeRootTests.java @@ -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 @@ -25,6 +25,7 @@ * @test * @library /test/lib * @build jdk.test.lib.RandomFactory + * @build Tests * @run main CubeRootTests * @bug 4347132 4939441 8078672 * @summary Tests for {Math, StrictMath}.cbrt (use -Dseed=X to set PRNG seed) @@ -32,6 +33,7 @@ */ import jdk.test.lib.RandomFactory; +import static java.lang.Double.longBitsToDouble; public class CubeRootTests { private CubeRootTests(){} @@ -42,7 +44,7 @@ private CubeRootTests(){} // Initialize shared random number generator static java.util.Random rand = RandomFactory.getRandom(); - static int testCubeRootCase(double input, double expected) { + private static int testCubeRootCase(double input, double expected) { int failures=0; failures+=Tests.test("Math.cbrt", input, Math::cbrt, expected); @@ -53,22 +55,17 @@ static int testCubeRootCase(double input, double expected) { return failures; } - static int testCubeRoot() { + private static int testCubeRoot() { int failures = 0; + + for(double nan : Tests.NaNs) { + failures += testCubeRootCase(nan, NaNd); + } + double [][] testCases = { - {NaNd, NaNd}, - {Double.longBitsToDouble(0x7FF0000000000001L), NaNd}, - {Double.longBitsToDouble(0xFFF0000000000001L), NaNd}, - {Double.longBitsToDouble(0x7FF8555555555555L), NaNd}, - {Double.longBitsToDouble(0xFFF8555555555555L), NaNd}, - {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0x7FFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0xFFFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0x7FFCafeBabe00000L), NaNd}, - {Double.longBitsToDouble(0xFFFCafeBabe00000L), NaNd}, {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY}, + {+0.0, +0.0}, {-0.0, -0.0}, {+1.0, +1.0}, diff --git a/test/jdk/java/lang/Math/Expm1Tests.java b/test/jdk/java/lang/Math/Expm1Tests.java index 204fe44ef15..61f02acfe21 100644 --- a/test/jdk/java/lang/Math/Expm1Tests.java +++ b/test/jdk/java/lang/Math/Expm1Tests.java @@ -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 @@ -24,9 +24,14 @@ /* * @test * @bug 4851638 4900189 4939441 + * @build Tests + * @build Expm1Tests + * @run main Expm1Tests * @summary Tests for {Math, StrictMath}.expm1 */ +import static java.lang.Double.longBitsToDouble; + /* * The Taylor expansion of expxm1(x) = exp(x) -1 is * @@ -48,21 +53,14 @@ private Expm1Tests(){} static final double infinityD = Double.POSITIVE_INFINITY; static final double NaNd = Double.NaN; - static int testExpm1() { + private static int testExpm1() { int failures = 0; + for(double nan : Tests.NaNs) { + failures += testExpm1Case(nan, NaNd); + } + double [][] testCases = { - {Double.NaN, NaNd}, - {Double.longBitsToDouble(0x7FF0000000000001L), NaNd}, - {Double.longBitsToDouble(0xFFF0000000000001L), NaNd}, - {Double.longBitsToDouble(0x7FF8555555555555L), NaNd}, - {Double.longBitsToDouble(0xFFF8555555555555L), NaNd}, - {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0x7FFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0xFFFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0x7FFCafeBabe00000L), NaNd}, - {Double.longBitsToDouble(0xFFFCafeBabe00000L), NaNd}, {infinityD, infinityD}, {-infinityD, -1.0}, {-0.0, -0.0}, diff --git a/test/jdk/java/lang/Math/HyperbolicTests.java b/test/jdk/java/lang/Math/HyperbolicTests.java index 8309606f63d..c0ce8f49ef3 100644 --- a/test/jdk/java/lang/Math/HyperbolicTests.java +++ b/test/jdk/java/lang/Math/HyperbolicTests.java @@ -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 @@ -24,9 +24,14 @@ /* * @test * @bug 4851625 4900189 4939441 + * @build Tests + * @build HyperbolicTests + * @run main HyperbolicTests * @summary Tests for {Math, StrictMath}.{sinh, cosh, tanh} */ +import static java.lang.Double.longBitsToDouble; + public class HyperbolicTests { private HyperbolicTests(){} @@ -248,19 +253,12 @@ static int testSinh() { 3.0); } + for(double nan : Tests.NaNs) { + failures += testSinhCaseWithUlpDiff(nan, NaNd, 0); + } + double [][] specialTestCases = { {0.0, 0.0}, - {NaNd, NaNd}, - {Double.longBitsToDouble(0x7FF0000000000001L), NaNd}, - {Double.longBitsToDouble(0xFFF0000000000001L), NaNd}, - {Double.longBitsToDouble(0x7FF8555555555555L), NaNd}, - {Double.longBitsToDouble(0xFFF8555555555555L), NaNd}, - {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0x7FFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0xFFFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0x7FFCafeBabe00000L), NaNd}, - {Double.longBitsToDouble(0xFFFCafeBabe00000L), NaNd}, {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY} }; @@ -590,19 +588,12 @@ static int testCosh() { 3.0); } + for(double nan : Tests.NaNs) { + failures += testCoshCaseWithUlpDiff(nan, NaNd, 0); + } + double [][] specialTestCases = { {0.0, 1.0}, - {NaNd, NaNd}, - {Double.longBitsToDouble(0x7FF0000000000001L), NaNd}, - {Double.longBitsToDouble(0xFFF0000000000001L), NaNd}, - {Double.longBitsToDouble(0x7FF8555555555555L), NaNd}, - {Double.longBitsToDouble(0xFFF8555555555555L), NaNd}, - {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0x7FFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0xFFFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0x7FFCafeBabe00000L), NaNd}, - {Double.longBitsToDouble(0xFFFCafeBabe00000L), NaNd}, {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY} }; @@ -938,19 +929,12 @@ static int testTanh() { 3.0); } + for(double nan : Tests.NaNs) { + failures += testTanhCaseWithUlpDiff(nan, NaNd, 0); + } + double [][] specialTestCases = { {0.0, 0.0}, - {NaNd, NaNd}, - {Double.longBitsToDouble(0x7FF0000000000001L), NaNd}, - {Double.longBitsToDouble(0xFFF0000000000001L), NaNd}, - {Double.longBitsToDouble(0x7FF8555555555555L), NaNd}, - {Double.longBitsToDouble(0xFFF8555555555555L), NaNd}, - {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0x7FFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0xFFFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0x7FFCafeBabe00000L), NaNd}, - {Double.longBitsToDouble(0xFFFCafeBabe00000L), NaNd}, {Double.POSITIVE_INFINITY, 1.0} }; diff --git a/test/jdk/java/lang/Math/InverseTrigTests.java b/test/jdk/java/lang/Math/InverseTrigTests.java new file mode 100644 index 00000000000..f533b509d3d --- /dev/null +++ b/test/jdk/java/lang/Math/InverseTrigTests.java @@ -0,0 +1,187 @@ +/* + * 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 + * 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 8302026 + * @build Tests + * @build InverseTrigTests + * @run main InverseTrigTests + * @summary Tests for {Math, StrictMath}.{asin, acos, atan} + */ + +import static java.lang.Double.longBitsToDouble; + +public class InverseTrigTests { + private InverseTrigTests(){} + + public static void main(String... args) { + int failures = 0; + + failures += testAsinSpecialCases(); + failures += testAcosSpecialCases(); + failures += testAtanSpecialCases(); + + if (failures > 0) { + System.err.println("Testing inverse trig mthods incurred " + + failures + " failures."); + throw new RuntimeException(); + } + } + + private static final double InfinityD = Double.POSITIVE_INFINITY; + private static final double NaNd = Double.NaN; + + /** + * From the spec for Math.asin: + * + * "Special cases: + * + * If the argument is NaN or its absolute value is greater than 1, + * then the result is NaN. + * + * If the argument is zero, then the result is a zero with the + * same sign as the argument." + */ + private static int testAsinSpecialCases() { + int failures = 0; + + for(double nan : Tests.NaNs) { + failures += testAsinCase(nan, NaNd); + } + + double [][] testCases = { + {Math.nextUp(1.0), NaNd}, + {Math.nextDown(-1.0), NaNd}, + { InfinityD, NaNd}, + {-InfinityD, NaNd}, + + {-0.0, -0.0}, + {+0.0, +0.0}, + }; + + for(int i = 0; i < testCases.length; i++) { + failures += testAsinCase(testCases[i][0], + testCases[i][1]); + } + + return failures; + } + + private static int testAsinCase(double input, double expected) { + int failures=0; + + failures += Tests.test("Math.asin", input, Math::asin, expected); + failures += Tests.test("StrictMath.asin", input, StrictMath::asin, expected); + + return failures; + } + + /** + * From the spec for Math.acos: + * + * "Special case: + * + * If the argument is NaN or its absolute value is greater than 1, + * then the result is NaN. + * + * If the argument is 1.0, the result is positive zero." + */ + private static int testAcosSpecialCases() { + int failures = 0; + + for(double nan : Tests.NaNs) { + failures += testAcosCase(nan, NaNd); + } + + double [][] testCases = { + {Math.nextUp(1.0), NaNd}, + {Math.nextDown(-1.0), NaNd}, + {InfinityD, NaNd}, + {-InfinityD, NaNd}, + + {1.0, +0.0}, + }; + + for(int i = 0; i < testCases.length; i++) { + failures += testAcosCase(testCases[i][0], + testCases[i][1]); + } + + return failures; + } + + private static int testAcosCase(double input, double expected) { + int failures=0; + + failures += Tests.test("Math.acos", input, Math::acos, expected); + failures += Tests.test("StrictMath.acos", input, StrictMath::acos, expected); + + return failures; + } + + /** + * From the spec for Math.atan: + * + * "Special cases: + * + * If the argument is NaN, then the result is NaN. + * + * If the argument is zero, then the result is a zero with the + * same sign as the argument. + * + * If the argument is infinite, then the result is the closest + * value to pi/2 with the same sign as the input." + */ + private static int testAtanSpecialCases() { + int failures = 0; + + for(double nan : Tests.NaNs) { + failures += testAtanCase(nan, NaNd); + } + + double [][] testCases = { + {-0.0, -0.0}, + {+0.0, +0.0}, + + { InfinityD, +Math.PI/2.0}, + {-InfinityD, -Math.PI/2.0}, + }; + + for(int i = 0; i < testCases.length; i++) { + failures += testAtanCase(testCases[i][0], + testCases[i][1]); + } + + return failures; + } + + private static int testAtanCase(double input, double expected) { + int failures=0; + + failures += Tests.test("Math.atan", input, Math::atan, expected); + failures += Tests.test("StrictMath.atan", input, StrictMath::atan, expected); + + return failures; + } +} diff --git a/test/jdk/java/lang/Math/Log10Tests.java b/test/jdk/java/lang/Math/Log10Tests.java index dbdd7c14179..f53004ca0bc 100644 --- a/test/jdk/java/lang/Math/Log10Tests.java +++ b/test/jdk/java/lang/Math/Log10Tests.java @@ -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 @@ -24,43 +24,41 @@ /* * @test * @bug 4074599 4939441 + * @build Tests + * @build Log10Tests + * @run main Log10Tests * @summary Tests for {Math, StrictMath}.log10 */ +import static java.lang.Double.longBitsToDouble; + public class Log10Tests { private Log10Tests(){} - static final double infinityD = Double.POSITIVE_INFINITY; - static final double NaNd = Double.NaN; - static final double LN_10 = StrictMath.log(10.0); + private static final double infinityD = Double.POSITIVE_INFINITY; + private static final double NaNd = Double.NaN; + private static final double LN_10 = StrictMath.log(10.0); // Initialize shared random number generator static java.util.Random rand = new java.util.Random(0L); - static int testLog10Case(double input, double expected) { + private static int testLog10Case(double input, double expected) { int failures=0; - failures+=Tests.test("Math.log10", input, Math::log10, expected); - failures+=Tests.test("StrictMath.log10", input, StrictMath::log10, expected); + failures += Tests.test("Math.log10", input, Math::log10, expected); + failures += Tests.test("StrictMath.log10", input, StrictMath::log10, expected); return failures; } - static int testLog10() { + private static int testLog10() { int failures = 0; + for(double nan : Tests.NaNs) { + failures += testLog10Case(nan, NaNd); + } + double [][] testCases = { - {Double.NaN, NaNd}, - {Double.longBitsToDouble(0x7FF0000000000001L), NaNd}, - {Double.longBitsToDouble(0xFFF0000000000001L), NaNd}, - {Double.longBitsToDouble(0x7FF8555555555555L), NaNd}, - {Double.longBitsToDouble(0xFFF8555555555555L), NaNd}, - {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0x7FFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0xFFFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0x7FFCafeBabe00000L), NaNd}, - {Double.longBitsToDouble(0xFFFCafeBabe00000L), NaNd}, {Double.NEGATIVE_INFINITY, NaNd}, {-8.0, NaNd}, {-1.0, NaNd}, diff --git a/test/jdk/java/lang/Math/Log1pTests.java b/test/jdk/java/lang/Math/Log1pTests.java index 2fa7ddec442..89412fc971e 100644 --- a/test/jdk/java/lang/Math/Log1pTests.java +++ b/test/jdk/java/lang/Math/Log1pTests.java @@ -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 @@ -25,6 +25,8 @@ * @test * @library /test/lib * @build jdk.test.lib.RandomFactory + * @build Tests + * @build Log1pTests * @run main Log1pTests * @bug 4851638 4939441 8078672 * @summary Tests for {Math, StrictMath}.log1p (use -Dseed=X to set PRNG seed) @@ -59,21 +61,14 @@ static double hp15cLogp(double x) { * Also x/(x+1) < ln(1+x) < x */ - static int testLog1p() { + private static int testLog1p() { int failures = 0; + for(double nan : Tests.NaNs) { + failures += testLog1pCase(nan, NaNd); + } + double [][] testCases = { - {Double.NaN, NaNd}, - {Double.longBitsToDouble(0x7FF0000000000001L), NaNd}, - {Double.longBitsToDouble(0xFFF0000000000001L), NaNd}, - {Double.longBitsToDouble(0x7FF8555555555555L), NaNd}, - {Double.longBitsToDouble(0xFFF8555555555555L), NaNd}, - {Double.longBitsToDouble(0x7FFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0xFFFFFFFFFFFFFFFFL), NaNd}, - {Double.longBitsToDouble(0x7FFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0xFFFDeadBeef00000L), NaNd}, - {Double.longBitsToDouble(0x7FFCafeBabe00000L), NaNd}, - {Double.longBitsToDouble(0xFFFCafeBabe00000L), NaNd}, {Double.NEGATIVE_INFINITY, NaNd}, {-8.0, NaNd}, {-1.0, -infinityD}, diff --git a/test/jdk/java/lang/Math/Tests.java b/test/jdk/java/lang/Math/Tests.java index f1de6319a9e..60981e318e2 100644 --- a/test/jdk/java/lang/Math/Tests.java +++ b/test/jdk/java/lang/Math/Tests.java @@ -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 @@ -24,6 +24,7 @@ import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleUnaryOperator; import java.util.function.DoubleToIntFunction; +import static java.lang.Double.longBitsToDouble; /* * Shared static test methods for numerical tests. Sharing these @@ -37,6 +38,38 @@ public class Tests { private Tests(){}; // do not instantiate + // Used to create a NaN value at runtime; mark as volatile to foil + // compile-time constant folding. + static volatile double zero = 0.0; + + private static final double PLATFORM_NAN = zero / zero; + + public static final double[] NaNs = { + Double.NaN, + PLATFORM_NAN, + bitwiseNegate(PLATFORM_NAN), + // Exotic NaN bit patterns. Includes values that would + // *not* be considered a NaN if only the high-order + // 32-bits were examined. + longBitsToDouble(0x7FF0_0000_0000_0001L), + longBitsToDouble(0xFFF0_0000_0000_0001L), + longBitsToDouble(0x7FF8_5555_5555_5555L), + longBitsToDouble(0xFFF8_5555_5555_5555L), + longBitsToDouble(0x7FFF_FFFF_FFFF_FFFFL), + longBitsToDouble(0xFFFF_FFFF_FFFF_FFFFL), + longBitsToDouble(0x7FF0_0000_7FFF_FFFFL), + longBitsToDouble(0xFFF0_0000_7FFF_FFFFL), + longBitsToDouble(0x7FF0_Dead_Beef_0000L), + longBitsToDouble(0xFFF0_Dead_Beef_0000L), + longBitsToDouble(0x7FF0_Cafe_Babe_0000L), + longBitsToDouble(0xFFF0_Cafe_Babe_0000L), + }; + + public static double bitwiseNegate(double d) { + long SIGNBIT = 0x8000_0000_0000_0000L; + return longBitsToDouble(Double.doubleToRawLongBits(d) ^ SIGNBIT ); + } + public static String toHexString(float f) { if (!Float.isNaN(f)) return Float.toHexString(f); diff --git a/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java new file mode 100644 index 00000000000..daffb4b8c84 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8325567 8325621 + * @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac") + * @library /test/lib + * @run driver JspawnhelperWarnings + */ + +import java.nio.file.Paths; +import java.util.Arrays; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class JspawnhelperWarnings { + + private static void tryWithNArgs(int nArgs) throws Exception { + System.out.println("Running jspawnhelper with " + nArgs + " args"); + String[] args = new String[nArgs + 1]; + Arrays.fill(args, "1"); + args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString(); + Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args)); + OutputAnalyzer oa = new OutputAnalyzer(p); + oa.shouldHaveExitValue(1); + oa.shouldContain("This command is not for general use"); + if (nArgs != 2) { + oa.shouldContain("Incorrect number of arguments"); + } else { + oa.shouldContain("Incorrect Java version"); + } + } + + private static void testVersion() throws Exception { + String[] args = new String[3]; + args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString(); + args[1] = "wrongVersion"; + args[2] = "1:1:1"; + Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args)); + OutputAnalyzer oa = new OutputAnalyzer(p); + oa.shouldHaveExitValue(1); + oa.shouldContain("This command is not for general use"); + oa.shouldContain("Incorrect Java version: wrongVersion"); + } + + public static void main(String[] args) throws Exception { + for (int nArgs = 0; nArgs < 10; nArgs++) { + tryWithNArgs(nArgs); + } + + testVersion(); + } +} diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index 4f572610b9d..d3c44bc9279 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java @@ -25,10 +25,10 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; @@ -37,9 +37,9 @@ /* * @test - * @bug 8289643 + * @bug 8289643 8291760 * @requires (os.family == "linux" & !vm.musl) - * @summary file descriptor leak with ProcessBuilder.startPipeline + * @summary File descriptor leak detection with ProcessBuilder.startPipeline * @run testng/othervm PipelineLeaksFD */ @@ -68,26 +68,27 @@ void checkForLeaks(List builders) throws IOException { Assert.fail("There should be at least 3 pipes before, (0, 1, 2)"); } - // Redirect all of the error streams to stdout (except the last) - // so those file descriptors are not left open - for (int i = 0; i < builders.size() - 1; i++) { - builders.get(i).redirectErrorStream(true); - } - List processes = ProcessBuilder.startPipeline(builders); // Write something through the pipeline - try (OutputStream out = processes.get(0).getOutputStream()) { - out.write('a'); + final String text = "xyz"; + try (Writer out = processes.get(0).outputWriter()) { + out.write(text); } - Process last = processes.get(processes.size() - 1); - try (InputStream inputStream = last.getInputStream(); - InputStream errorStream = last.getErrorStream()) { - byte[] bytes = inputStream.readAllBytes(); - Assert.assertEquals(bytes.length, 1, "stdout bytes read"); - byte[] errBytes = errorStream.readAllBytes(); - Assert.assertEquals(errBytes.length, 0, "stderr bytes read"); + // Read, check, and close all streams + for (int i = 0; i < processes.size(); i++) { + final Process p = processes.get(i); + String expectedOut = (i == processes.size() - 1) ? text : null; + String expectedErr = null; // EOF + try (BufferedReader inputStream = p.inputReader(); + BufferedReader errorStream = p.errorReader()) { + String outActual = inputStream.readLine(); + Assert.assertEquals(outActual, expectedOut, "stdout, process[ " + i + "]: " + p); + + String errActual = errorStream.readLine(); + Assert.assertEquals(errActual, expectedErr, "stderr, process[ " + i + "]: " + p); + } } processes.forEach(p -> waitForQuiet(p)); diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java index 2e5c4837282..b016b2f6899 100644 --- a/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java +++ b/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -222,7 +222,7 @@ void unload() { } boolean tryUnload() { - return ForceGC.wait(() -> weakRef.refersTo(null)); + return ForceGC.waitFor(() -> weakRef.refersTo(null), 2000L); } } diff --git a/test/jdk/java/lang/management/ClassLoadingMXBean/TestVerboseClassLoading.java b/test/jdk/java/lang/management/ClassLoadingMXBean/TestVerboseClassLoading.java new file mode 100644 index 00000000000..c951578fac5 --- /dev/null +++ b/test/jdk/java/lang/management/ClassLoadingMXBean/TestVerboseClassLoading.java @@ -0,0 +1,84 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8338139 + * @summary Basic unit test of ClassLoadingMXBean.set/isVerbose() when + * related unified logging is enabled. + * + * @run main/othervm -Xlog:class+load=trace:file=vm.log TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load=debug:file=vm.log TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load=info:file=vm.log TestVerboseClassLoading false + * + * @run main/othervm -Xlog:class+load=trace TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load=debug TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load=info TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load=warning TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load=error TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load=off TestVerboseClassLoading false + * + * @run main/othervm -Xlog:class+load*=trace TestVerboseClassLoading true + * @run main/othervm -Xlog:class+load*=debug TestVerboseClassLoading true + * @run main/othervm -Xlog:class+load*=info TestVerboseClassLoading true + * @run main/othervm -Xlog:class+load*=warning TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load*=error TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load*=off TestVerboseClassLoading false + * + * @run main/othervm -Xlog:class+load*=info,class+load=trace TestVerboseClassLoading true + * @run main/othervm -Xlog:class+load*=info,class+load=debug TestVerboseClassLoading true + * @run main/othervm -Xlog:class+load*=info,class+load=info TestVerboseClassLoading true + * @run main/othervm -Xlog:class+load*=info,class+load=warning TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load*=info,class+load=error TestVerboseClassLoading false + * @run main/othervm -Xlog:class+load*=info,class+load=off TestVerboseClassLoading false + * + * @run main/othervm -Xlog:all=trace:file=vm.log TestVerboseClassLoading false + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.ClassLoadingMXBean; + +public class TestVerboseClassLoading { + + public static void main(String[] args) throws Exception { + ClassLoadingMXBean mxBean = ManagementFactory.getClassLoadingMXBean(); + boolean expected = Boolean.parseBoolean(args[0]); + boolean initial = mxBean.isVerbose(); + if (expected != initial) { + throw new Error("Initial verbosity setting was unexpectedly " + initial); + } + mxBean.setVerbose(false); + if (mxBean.isVerbose()) { + throw new Error("Verbosity was still enabled"); + } + mxBean.setVerbose(true); + if (!mxBean.isVerbose()) { + throw new Error("Verbosity was still disabled"); + } + // Turn off again as a double-check and also to avoid excessive logging + mxBean.setVerbose(false); + if (mxBean.isVerbose()) { + throw new Error("Verbosity was still enabled"); + } + } +} diff --git a/test/jdk/java/lang/management/MemoryMXBean/TestVerboseMemory.java b/test/jdk/java/lang/management/MemoryMXBean/TestVerboseMemory.java new file mode 100644 index 00000000000..7d34c45036b --- /dev/null +++ b/test/jdk/java/lang/management/MemoryMXBean/TestVerboseMemory.java @@ -0,0 +1,79 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8338139 + * @summary Basic unit test of TestVerboseMemory.set/isVerbose() when + * related unified logging is enabled. + * + * @run main/othervm -Xlog:gc=trace:file=vm.log TestVerboseMemory false + * @run main/othervm -Xlog:gc=debug:file=vm.log TestVerboseMemory false + * @run main/othervm -Xlog:gc=info:file=vm.log TestVerboseMemory false + * + * @run main/othervm -Xlog:gc=off TestVerboseMemory false + * @run main/othervm -Xlog:gc=error TestVerboseMemory false + * @run main/othervm -Xlog:gc=warning TestVerboseMemory false + * + * @run main/othervm -Xlog:gc=info TestVerboseMemory true + * @run main/othervm -Xlog:gc=trace TestVerboseMemory true + * @run main/othervm -Xlog:gc=debug TestVerboseMemory true + * + * @run main/othervm -Xlog:gc*=info TestVerboseMemory true + * @run main/othervm -Xlog:gc*=debug TestVerboseMemory true + * @run main/othervm -Xlog:gc*=trace TestVerboseMemory true + * + * @run main/othervm -Xlog:gc=info,gc+init=off TestVerboseMemory true + * @run main/othervm -Xlog:gc=off,gc+init=info TestVerboseMemory false + * @run main/othervm -Xlog:gc,gc+init TestVerboseMemory true + * + * @run main/othervm -Xlog:all=trace:file=vm.log TestVerboseMemory false + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; + +public class TestVerboseMemory { + + public static void main(String[] args) throws Exception { + MemoryMXBean mxBean = ManagementFactory.getMemoryMXBean(); + boolean expected = Boolean.parseBoolean(args[0]); + boolean initial = mxBean.isVerbose(); + if (expected != initial) { + throw new Error("Initial verbosity setting was unexpectedly " + initial); + } + mxBean.setVerbose(false); + if (mxBean.isVerbose()) { + throw new Error("Verbosity was still enabled"); + } + mxBean.setVerbose(true); + if (!mxBean.isVerbose()) { + throw new Error("Verbosity was still disabled"); + } + // Turn off again as a double-check and also to avoid excessive logging + mxBean.setVerbose(false); + if (mxBean.isVerbose()) { + throw new Error("Verbosity was still enabled"); + } + } +} diff --git a/test/jdk/java/net/MulticastSocket/IPMulticastIF.java b/test/jdk/java/net/MulticastSocket/IPMulticastIF.java index 566e3603fa6..3909f6d6276 100644 --- a/test/jdk/java/net/MulticastSocket/IPMulticastIF.java +++ b/test/jdk/java/net/MulticastSocket/IPMulticastIF.java @@ -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 @@ -63,9 +63,25 @@ public Object[][] positive() throws Exception { InetAddress.getLoopbackAddress()); List list = new ArrayList<>(); NetworkConfiguration nc = NetworkConfiguration.probe(); + // retains only network interface whose bound addresses match addrs.stream().forEach(a -> nc.multicastInterfaces(true) - .map(nif -> new Object[] { new InetSocketAddress(a, 0), nif }) - .forEach(list::add) ); + .filter(nif -> nif.inetAddresses().toList().contains(a)) + .map(nif -> new Object[] { new InetSocketAddress(a, 0), nif }) + .forEach(list::add) ); + // any network interface should work with the wildcard address + nc.multicastInterfaces(true) + .map(nif -> new Object[] {new InetSocketAddress(0), nif}) + .forEach(list::add); + return list.stream().toArray(Object[][]::new); + } + + @DataProvider(name = "interfaces") + public Object[][] interfaces() throws Exception { + List list = new ArrayList<>(); + NetworkConfiguration nc = NetworkConfiguration.probe(); + nc.multicastInterfaces(true) + .map(nif -> new Object[] {nif}) + .forEach(list::add); return list.stream().toArray(Object[][]::new); } @@ -82,8 +98,8 @@ public void testSetGetInterfaceBound(InetSocketAddress bindAddr, NetworkInterfac } } - @Test(dataProvider = "scenarios") - public void testSetGetInterfaceUnbound(InetSocketAddress ignore, NetworkInterface nif) + @Test(dataProvider = "interfaces") + public void testSetGetInterfaceUnbound(NetworkInterface nif) throws Exception { out.println(format("\n\n--- testSetGetInterfaceUnbound nif=[%s]", nif)); @@ -106,8 +122,8 @@ public void testSetGetOptionBound(InetSocketAddress bindAddr, NetworkInterface n } } - @Test(dataProvider = "scenarios") - public void testSetGetOptionUnbound(InetSocketAddress ignore, NetworkInterface nif) + @Test(dataProvider = "interfaces") + public void testSetGetOptionUnbound(NetworkInterface nif) throws Exception { out.println(format("\n\n--- testSetGetOptionUnbound nif=[%s]", nif)); @@ -139,8 +155,8 @@ public void testGetInterfaceBound(InetSocketAddress bindAddr) } @Test - public void testGettInterfaceUnbound() throws Exception { - out.println("\n\n--- testGettInterfaceUnbound "); + public void testGetInterfaceUnbound() throws Exception { + out.println("\n\n--- testGetInterfaceUnbound "); try (MulticastSocket ms = new MulticastSocket()) { assertPlaceHolder(ms.getNetworkInterface()); } diff --git a/test/jdk/java/net/ProxySelector/LoopbackAddresses.java b/test/jdk/java/net/ProxySelector/LoopbackAddresses.java index cd5eb919b97..4af9f4db866 100644 --- a/test/jdk/java/net/ProxySelector/LoopbackAddresses.java +++ b/test/jdk/java/net/ProxySelector/LoopbackAddresses.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 @@ -27,15 +27,25 @@ * @summary PIT: Can no launch jnlp application via 127.0.0.1 address on the web server. * This test might fail intermittently as it needs a server that * binds to the wildcard address. - * @modules java.base/sun.net.www - * @library ../../../sun/net/www/httptest/ /test/lib - * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback + * @library /test/lib * @compile LoopbackAddresses.java * @run main/othervm LoopbackAddresses */ -import java.net.*; -import java.io.*; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.concurrent.Executors; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.URIBuilder; /** @@ -43,17 +53,8 @@ * addresses when selecting proxies. This is the existing behaviour. */ -public class LoopbackAddresses implements HttpCallback { - static TestHttpServer server; - - public void request (HttpTransaction req) { - req.setResponseEntityBody ("Hello ."); - try { - req.sendResponse (200, "Ok"); - req.orderlyClose(); - } catch (IOException e) { - } - } +public class LoopbackAddresses { + static HttpServer server; public static void main(String[] args) { try { @@ -63,15 +64,18 @@ public static void main(String[] args) { // to answer both for the loopback and "localhost". // Though "localhost" usually point to the loopback there is no // hard guarantee. - server = new TestHttpServer (new LoopbackAddresses(), 1, 10, 0); - ProxyServer pserver = new ProxyServer(InetAddress.getByName("localhost"), server.getLocalPort()); + server = HttpServer.create(new InetSocketAddress(loopback, 0), 10); + server.createContext("/", new LoopbackAddressesHandler()); + server.setExecutor(Executors.newSingleThreadExecutor()); + server.start(); + ProxyServer pserver = new ProxyServer(InetAddress.getByName("localhost"), server.getAddress().getPort()); // start proxy server new Thread(pserver).start(); System.setProperty("http.proxyHost", loopback.getHostAddress()); System.setProperty("http.proxyPort", pserver.getPort()+""); - URL url = new URL("http://localhost:"+server.getLocalPort()); + URL url = new URL("http://localhost:"+server.getAddress().getPort()); try { HttpURLConnection urlc = (HttpURLConnection)url.openConnection (); @@ -85,7 +89,7 @@ public static void main(String[] args) { url = URIBuilder.newBuilder() .scheme("http") .host(loopback.getHostAddress()) - .port(server.getLocalPort()) + .port(server.getAddress().getPort()) .toURL(); HttpURLConnection urlc = (HttpURLConnection)url.openConnection (); int respCode = urlc.getResponseCode(); @@ -97,7 +101,7 @@ public static void main(String[] args) { throw new RuntimeException(e); } finally { if (server != null) { - server.terminate(); + server.stop(1); } } @@ -151,3 +155,18 @@ public int getPort() { } } } + +class LoopbackAddressesHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + exchange.sendResponseHeaders(200, 0); + } catch (IOException e) { + e.printStackTrace(); + } + try(PrintWriter pw = new PrintWriter(exchange.getResponseBody(), false, Charset.forName("UTF-8"))) { + pw.print("Hello ."); + } + } +} diff --git a/test/jdk/java/net/ProxySelector/ProxyTest.java b/test/jdk/java/net/ProxySelector/ProxyTest.java index 8debe931469..4459f796b91 100644 --- a/test/jdk/java/net/ProxySelector/ProxyTest.java +++ b/test/jdk/java/net/ProxySelector/ProxyTest.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 @@ -25,33 +25,37 @@ * @test * @bug 4696512 * @summary HTTP client: Improve proxy server configuration and selection - * @modules java.base/sun.net.www - * @library ../../../sun/net/www/httptest/ /test/lib - * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback + * @library /test/lib * @compile ProxyTest.java * @run main/othervm -Dhttp.proxyHost=inexistant -Dhttp.proxyPort=8080 ProxyTest */ -import java.net.*; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URL; +import java.nio.charset.Charset; import java.util.List; +import java.util.concurrent.Executors; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.URIBuilder; -public class ProxyTest implements HttpCallback { - static TestHttpServer server; +public class ProxyTest { + static HttpServer server; public ProxyTest() { } - public void request(HttpTransaction req) { - req.setResponseEntityBody("Hello ."); - try { - req.sendResponse(200, "Ok"); - req.orderlyClose(); - } catch (IOException e) { - } - } - static public class MyProxySelector extends ProxySelector { private static volatile URI lastURI; private final static List NO_PROXY = List.of(Proxy.NO_PROXY); @@ -75,11 +79,14 @@ public static void main(String[] args) { ProxySelector.setDefault(new MyProxySelector()); try { InetAddress loopback = InetAddress.getLoopbackAddress(); - server = new TestHttpServer(new ProxyTest(), 1, 10, loopback, 0); + server = HttpServer.create(new InetSocketAddress(loopback, 0), 10); + server.createContext("/", new ProxyTestHandler()); + server.setExecutor(Executors.newSingleThreadExecutor()); + server.start(); URL url = URIBuilder.newBuilder() .scheme("http") .loopback() - .port(server.getLocalPort()) + .port(server.getAddress().getPort()) .toURL(); System.out.println("client opening connection to: " + url); HttpURLConnection urlc = (HttpURLConnection)url.openConnection(); @@ -93,8 +100,23 @@ public static void main(String[] args) { throw new RuntimeException(e); } finally { if (server != null) { - server.terminate(); + server.stop(1); } } } } + +class ProxyTestHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + exchange.sendResponseHeaders(200, 0); + } catch (IOException e) { + e.printStackTrace(); + } + try(PrintWriter pw = new PrintWriter(exchange.getResponseBody(), false, Charset.forName("UTF-8"))) { + pw.print("Hello ."); + } + } +} diff --git a/test/jdk/java/net/URL/PerConnectionProxy.java b/test/jdk/java/net/URL/PerConnectionProxy.java index 6ce25b9a50e..6c66c10d12e 100644 --- a/test/jdk/java/net/URL/PerConnectionProxy.java +++ b/test/jdk/java/net/URL/PerConnectionProxy.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 @@ -24,42 +24,48 @@ /* @test * @bug 4920526 * @summary Needs per connection proxy support for URLs - * @modules java.base/sun.net.www - * @library ../../../sun/net/www/httptest/ /test/lib - * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback + * @library /test/lib * @compile PerConnectionProxy.java * @run main/othervm -Dhttp.proxyHost=inexistant -Dhttp.proxyPort=8080 PerConnectionProxy */ -import java.net.*; -import java.io.*; - +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.concurrent.Executors; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import jdk.test.lib.net.URIBuilder; -public class PerConnectionProxy implements HttpCallback { - static TestHttpServer server; - - public void request (HttpTransaction req) { - req.setResponseEntityBody ("Hello ."); - try { - req.sendResponse (200, "Ok"); - req.orderlyClose(); - } catch (IOException e) { - } - } +public class PerConnectionProxy { + static HttpServer server; public static void main(String[] args) { try { InetAddress loopbackAddress = InetAddress.getLoopbackAddress(); - server = new TestHttpServer(new PerConnectionProxy(), 1, 10, loopbackAddress, 0); - ProxyServer pserver = new ProxyServer(loopbackAddress, server.getLocalPort()); + server = HttpServer.create(new InetSocketAddress(loopbackAddress, 0), 10); + server.createContext("/", new PerConnectionProxyHandler()); + server.setExecutor(Executors.newSingleThreadExecutor()); + server.start(); + ProxyServer pserver = new ProxyServer(loopbackAddress, server.getAddress().getPort()); // start proxy server new Thread(pserver).start(); URL url = URIBuilder.newBuilder() .scheme("http") .loopback() - .port(server.getLocalPort()) + .port(server.getAddress().getPort()) .toURLUnchecked(); // for non existing proxy expect an IOException @@ -73,7 +79,6 @@ public static void main(String[] args) { } catch (IOException ioex) { // expected } - // for NO_PROXY, expect direct connection try { HttpURLConnection urlc = (HttpURLConnection)url.openConnection (Proxy.NO_PROXY); @@ -82,7 +87,6 @@ public static void main(String[] args) { } catch (IOException ioex) { throw new RuntimeException("direct connection should succeed :"+ioex.getMessage()); } - // for a normal proxy setting expect to see connection // goes through that proxy try { @@ -101,10 +105,9 @@ public static void main(String[] args) { throw new RuntimeException(e); } finally { if (server != null) { - server.terminate(); + server.stop(1); } } - } static class ProxyServer extends Thread { @@ -145,7 +148,6 @@ public void run() { private void processRequests() throws Exception { // connection set to the tunneling mode - Socket serverSocket = new Socket(serverInetAddr, serverPort); ProxyTunnel clientToServer = new ProxyTunnel( clientSocket, serverSocket); @@ -161,7 +163,6 @@ private void processRequests() throws Exception { clientToServer.close(); serverToClient.close(); - } /** @@ -221,6 +222,20 @@ public void close() { } catch (IOException ignored) { } } } + } +} +class PerConnectionProxyHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + exchange.sendResponseHeaders(200, 0); + } catch (IOException e) { + } + try(PrintWriter pw = new PrintWriter(exchange.getResponseBody(), false, Charset.forName("UTF-8"))) { + pw.print("Hello ."); + } } } + diff --git a/test/jdk/java/net/URLConnection/B5052093.java b/test/jdk/java/net/URLConnection/B5052093.java index f5434f9528b..cb1b591d322 100644 --- a/test/jdk/java/net/URLConnection/B5052093.java +++ b/test/jdk/java/net/URLConnection/B5052093.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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,21 +23,31 @@ /* * @test + * @modules java.base/sun.net.www.protocol.file * @bug 5052093 - * @modules java.base/sun.net.www java.base/sun.net.www.protocol.file - * @library ../../../sun/net/www/httptest/ - * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction - * @run main B5052093 + * @library /test/lib + * @run main/othervm B5052093 * @summary URLConnection doesn't support large files */ -import java.net.*; -import java.io.*; -import sun.net.www.protocol.file.FileURLConnection; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.Executors; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; import static java.net.Proxy.NO_PROXY; +import jdk.test.lib.net.URIBuilder; +import sun.net.www.protocol.file.FileURLConnection; -public class B5052093 implements HttpCallback { - private static TestHttpServer server; - private static long testSize = ((long) (Integer.MAX_VALUE)) + 2; +public class B5052093 { + private static HttpServer server; + static long testSize = ((long) (Integer.MAX_VALUE)) + 2; public static class LargeFile extends File { public LargeFile() { @@ -55,20 +65,19 @@ public LargeFileURLConnection(LargeFile f) throws IOException { } } - public void request(HttpTransaction req) { - try { - req.setResponseHeader("content-length", Long.toString(testSize)); - req.sendResponse(200, "OK"); - } catch (IOException e) { - e.printStackTrace(); - } - } - public static void main(String[] args) throws Exception { InetAddress loopback = InetAddress.getLoopbackAddress(); - server = new TestHttpServer(new B5052093(), 1, 10, loopback, 0); + server = HttpServer.create(new InetSocketAddress(loopback, 0), 10); + server.createContext("/", new B5052093Handler()); + server.setExecutor(Executors.newSingleThreadExecutor()); + server.start(); try { - URL url = new URL("http://" + server.getAuthority() + "/foo"); + URL url = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(server.getAddress().getPort()) + .path("/foo") + .build().toURL(); URLConnection conn = url.openConnection(NO_PROXY); int i = conn.getContentLength(); long l = conn.getContentLengthLong(); @@ -89,7 +98,20 @@ public static void main(String[] args) throws Exception { throw new RuntimeException("Wrong content-length from file"); } } finally { - server.terminate(); + server.stop(1); + } + } +} + +class B5052093Handler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + exchange.getResponseHeaders().set("content-length", Long.toString(B5052093.testSize)); + exchange.sendResponseHeaders(200, 0); + exchange.close(); + } catch (IOException e) { + e.printStackTrace(); } } } diff --git a/test/jdk/java/net/httpclient/ForbiddenHeadTest.java b/test/jdk/java/net/httpclient/ForbiddenHeadTest.java index 0c39b44d7f5..1498aa118b3 100644 --- a/test/jdk/java/net/httpclient/ForbiddenHeadTest.java +++ b/test/jdk/java/net/httpclient/ForbiddenHeadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, 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 @@ -205,18 +205,18 @@ static final void printFailedTests(ITestContext context) { @DataProvider(name = "all") public Object[][] allcases() { List result = new ArrayList<>(); - for (var client : List.of(authClient, noAuthClient)) { + for (boolean useAuth : List.of(true, false)) { for (boolean async : List.of(true, false)) { for (int code : List.of(UNAUTHORIZED, PROXY_UNAUTHORIZED)) { var srv = code == PROXY_UNAUTHORIZED ? "/proxy" : "/server"; for (var auth : List.of("/auth", "/noauth")) { var pcode = code; if (auth.equals("/noauth")) { - if (client == authClient) continue; + if (useAuth) continue; pcode = FORBIDDEN; } for (var uri : List.of(httpURI, httpsURI, http2URI, https2URI)) { - result.add(new Object[]{uri + srv + auth, pcode, async, client}); + result.add(new Object[]{uri + srv + auth, pcode, async, useAuth}); } } } @@ -237,12 +237,13 @@ protected PasswordAuthentication getPasswordAuthentication() { static final AtomicLong sleepCount = new AtomicLong(); @Test(dataProvider = "all") - void test(String uriString, int code, boolean async, HttpClient client) throws Throwable { + void test(String uriString, int code, boolean async, boolean useAuth) throws Throwable { checkSkip(); + HttpClient client = useAuth ? authClient : noAuthClient; var name = String.format("test(%s, %d, %s, %s)", uriString, code, async ? "async" : "sync", client.authenticator().isPresent() ? "authClient" : "noAuthClient"); out.printf("%n---- starting %s ----%n", name); - assert client.authenticator().isPresent() ? client == authClient : client == noAuthClient; + assert client.authenticator().isPresent() == useAuth; uriString = uriString + "/ForbiddenTest"; for (int i=0; i 1) out.printf("---- ITERATION %d%n",i); @@ -380,14 +381,17 @@ public void setup() throws Exception { public void teardown() throws Exception { authClient = noAuthClient = null; Thread.sleep(100); - AssertionError fail = TRACKER.check(500); - - proxy.stop(); - authproxy.stop(); - httpTestServer.stop(); - httpsTestServer.stop(); - http2TestServer.stop(); - https2TestServer.stop(); + AssertionError fail = TRACKER.check(1500); + try { + proxy.stop(); + authproxy.stop(); + httpTestServer.stop(); + httpsTestServer.stop(); + http2TestServer.stop(); + https2TestServer.stop(); + } finally { + if (fail != null) throw fail; + } } static class TestProxySelector extends ProxySelector { diff --git a/test/jdk/java/net/httpclient/ProxySelectorTest.java b/test/jdk/java/net/httpclient/ProxySelectorTest.java index bb3ddc84017..fd8f85fa6d7 100644 --- a/test/jdk/java/net/httpclient/ProxySelectorTest.java +++ b/test/jdk/java/net/httpclient/ProxySelectorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, 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 @@ -377,15 +377,18 @@ public void teardown() throws Exception { client = null; Thread.sleep(100); AssertionError fail = TRACKER.check(500); - - proxy.stop(); - authproxy.stop(); - httpTestServer.stop(); - proxyHttpTestServer.stop(); - authProxyHttpTestServer.stop(); - httpsTestServer.stop(); - http2TestServer.stop(); - https2TestServer.stop(); + try { + proxy.stop(); + authproxy.stop(); + httpTestServer.stop(); + proxyHttpTestServer.stop(); + authProxyHttpTestServer.stop(); + httpsTestServer.stop(); + http2TestServer.stop(); + https2TestServer.stop(); + } finally { + if (fail != null) throw fail; + } } class TestProxySelector extends ProxySelector { diff --git a/test/jdk/java/net/httpclient/Response1xxTest.java b/test/jdk/java/net/httpclient/Response1xxTest.java new file mode 100644 index 00000000000..f370ba81060 --- /dev/null +++ b/test/jdk/java/net/httpclient/Response1xxTest.java @@ -0,0 +1,497 @@ +/* + * 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.ProtocolException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpTimeoutException; +import java.nio.charset.StandardCharsets; +import java.time.Duration; + +import javax.net.ssl.SSLContext; + +import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.httpclient.test.lib.http2.Http2TestServer; +import jdk.test.lib.net.SimpleSSLContext; +import jdk.test.lib.net.URIBuilder; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * @test + * @bug 8292044 + * @summary Tests behaviour of HttpClient when server responds with 102 or 103 status codes + * @modules java.base/sun.net.www.http + * java.net.http/jdk.internal.net.http.common + * java.net.http/jdk.internal.net.http.frame + * java.net.http/jdk.internal.net.http.hpack + * java.logging + * jdk.httpserver + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext + * @run testng/othervm -Djdk.internal.httpclient.debug=true + * * -Djdk.httpclient.HttpClient.log=headers,requests,responses,errors Response1xxTest + */ +public class Response1xxTest implements HttpServerAdapters { + private static final String EXPECTED_RSP_BODY = "Hello World"; + + private ServerSocket serverSocket; + private Http11Server server; + private String http1RequestURIBase; + + + private HttpTestServer http2Server; // h2c + private String http2RequestURIBase; + + + private SSLContext sslContext; + private HttpTestServer https2Server; // h2 + private String https2RequestURIBase; + + private final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + + @BeforeClass + public void setup() throws Exception { + serverSocket = new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); + server = new Http11Server(serverSocket); + new Thread(server).start(); + http1RequestURIBase = URIBuilder.newBuilder().scheme("http").loopback() + .port(serverSocket.getLocalPort()).build().toString(); + + http2Server = HttpTestServer.of(new Http2TestServer("localhost", false, 0)); + http2Server.addHandler(new Http2Handler(), "/http2/102"); + http2Server.addHandler(new Http2Handler(), "/http2/103"); + http2Server.addHandler(new Http2Handler(), "/http2/100"); + http2Server.addHandler(new Http2Handler(), "/http2/101"); + http2Server.addHandler(new OKHandler(), "/http2/200"); + http2Server.addHandler(new OnlyInformationalHandler(), "/http2/only-informational"); + http2RequestURIBase = URIBuilder.newBuilder().scheme("http").loopback() + .port(http2Server.getAddress().getPort()) + .path("/http2").build().toString(); + + http2Server.start(); + System.out.println("Started HTTP2 server at " + http2Server.getAddress()); + + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) { + throw new AssertionError("Unexpected null sslContext"); + } + https2Server = HttpTestServer.of(new Http2TestServer("localhost", + true, sslContext)); + https2Server.addHandler(new Http2Handler(), "/http2/101"); + https2RequestURIBase = URIBuilder.newBuilder().scheme("https").loopback() + .port(https2Server.getAddress().getPort()) + .path("/http2").build().toString(); + https2Server.start(); + System.out.println("Started (https) HTTP2 server at " + https2Server.getAddress()); + + } + + @AfterClass + public void teardown() throws Throwable { + try { + assertNoOutstandingClientOps(); + } finally { + if (server != null) { + server.stop = true; + System.out.println("(HTTP 1.1) Server stop requested"); + } + if (serverSocket != null) { + serverSocket.close(); + System.out.println("Closed (HTTP 1.1) server socket"); + } + if (http2Server != null) { + http2Server.stop(); + System.out.println("Stopped HTTP2 server"); + } + if (https2Server != null) { + https2Server.stop(); + System.out.println("Stopped (https) HTTP2 server"); + } + } + } + + private static final class Http11Server implements Runnable { + private static final int CONTENT_LENGTH = EXPECTED_RSP_BODY.getBytes(StandardCharsets.UTF_8).length; + + private static final String HTTP_1_1_RSP_200 = "HTTP/1.1 200 OK\r\n" + + "Content-Length: " + CONTENT_LENGTH + "\r\n\r\n" + + EXPECTED_RSP_BODY; + + private static final String REQ_LINE_FOO = "GET /test/foo HTTP/1.1\r\n"; + private static final String REQ_LINE_BAR = "GET /test/bar HTTP/1.1\r\n"; + private static final String REQ_LINE_HELLO = "GET /test/hello HTTP/1.1\r\n"; + private static final String REQ_LINE_BYE = "GET /test/bye HTTP/1.1\r\n"; + + + private final ServerSocket serverSocket; + private volatile boolean stop; + + private Http11Server(final ServerSocket serverSocket) { + this.serverSocket = serverSocket; + } + + @Override + public void run() { + System.out.println("Server running at " + serverSocket); + while (!stop) { + Socket socket = null; + try { + // accept a connection + socket = serverSocket.accept(); + System.out.println("Accepted connection from client " + socket); + // read request + final String requestLine; + try { + requestLine = readRequestLine(socket); + } catch (Throwable t) { + // ignore connections from potential rogue client + System.err.println("Ignoring connection/request from client " + socket + + " due to exception:"); + t.printStackTrace(); + // close the socket + safeClose(socket); + continue; + } + System.out.println("Received following request line from client " + socket + + " :\n" + requestLine); + final int informationalResponseCode; + if (requestLine.startsWith(REQ_LINE_FOO)) { + // we will send intermediate/informational 102 response + informationalResponseCode = 102; + } else if (requestLine.startsWith(REQ_LINE_BAR)) { + // we will send intermediate/informational 103 response + informationalResponseCode = 103; + } else if (requestLine.startsWith(REQ_LINE_HELLO)) { + // we will send intermediate/informational 100 response + informationalResponseCode = 100; + } else if (requestLine.startsWith(REQ_LINE_BYE)) { + // we will send intermediate/informational 101 response + informationalResponseCode = 101; + } else { + // unexpected client. ignore and close the client + System.err.println("Ignoring unexpected request from client " + socket); + safeClose(socket); + continue; + } + try (final OutputStream os = socket.getOutputStream()) { + // send informational response headers a few times (spec allows them to + // be sent multiple times) + for (int i = 0; i < 3; i++) { + // send 1xx response header + if (informationalResponseCode == 101) { + os.write(("HTTP/1.1 " + informationalResponseCode + "\r\n" + + "Connection: upgrade\r\n" + + "Upgrade: websocket\r\n\r\n") + .getBytes(StandardCharsets.UTF_8)); + } else { + os.write(("HTTP/1.1 " + informationalResponseCode + "\r\n\r\n") + .getBytes(StandardCharsets.UTF_8)); + } + os.flush(); + System.out.println("Sent response code " + informationalResponseCode + + " to client " + socket); + } + // now send a final response + System.out.println("Now sending 200 response code to client " + socket); + os.write(HTTP_1_1_RSP_200.getBytes(StandardCharsets.UTF_8)); + os.flush(); + System.out.println("Sent 200 response code to client " + socket); + } + } catch (Throwable t) { + // close the client connection + safeClose(socket); + // continue accepting any other client connections until we are asked to stop + System.err.println("Ignoring exception in server:"); + t.printStackTrace(); + } + } + } + + static String readRequestLine(final Socket sock) throws IOException { + final InputStream is = sock.getInputStream(); + final StringBuilder sb = new StringBuilder(""); + byte[] buf = new byte[1024]; + while (!sb.toString().endsWith("\r\n\r\n")) { + final int numRead = is.read(buf); + if (numRead == -1) { + return sb.toString(); + } + final String part = new String(buf, 0, numRead, StandardCharsets.ISO_8859_1); + sb.append(part); + } + return sb.toString(); + } + + private static void safeClose(final Socket socket) { + try { + socket.close(); + } catch (Throwable t) { + // ignore + } + } + } + + private static class Http2Handler implements HttpTestHandler { + + @Override + public void handle(final HttpTestExchange exchange) throws IOException { + final URI requestURI = exchange.getRequestURI(); + final int informationResponseCode; + if (requestURI.getPath().endsWith("/102")) { + informationResponseCode = 102; + } else if (requestURI.getPath().endsWith("/103")) { + informationResponseCode = 103; + } else if (requestURI.getPath().endsWith("/100")) { + informationResponseCode = 100; + } else if (requestURI.getPath().endsWith("/101")) { + informationResponseCode = 101; + } else { + // unexpected request + System.err.println("Unexpected request " + requestURI + " from client " + + exchange.getRemoteAddress()); + exchange.sendResponseHeaders(400, -1); + return; + } + // send informational response headers a few times (spec allows them to + // be sent multiple times) + for (int i = 0; i < 3; i++) { + exchange.sendResponseHeaders(informationResponseCode, -1); + System.out.println("Sent " + informationResponseCode + " response code from H2 server"); + } + // now send 200 response + try { + final byte[] body = EXPECTED_RSP_BODY.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, body.length); + System.out.println("Sent 200 response from H2 server"); + try (OutputStream os = exchange.getResponseBody()) { + os.write(body); + } + System.out.println("Sent response body from H2 server"); + } catch (Throwable e) { + System.err.println("Failed to send response from HTTP2 handler:"); + e.printStackTrace(); + throw e; + } + } + } + + private static class OnlyInformationalHandler implements HttpTestHandler { + + @Override + public void handle(final HttpTestExchange exchange) throws IOException { + // we only send informational response and then return + for (int i = 0; i < 5; i++) { + exchange.sendResponseHeaders(102, -1); + System.out.println("Sent 102 response code from H2 server"); + // wait for a while before sending again + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + // just return + System.err.println("Handler thread interrupted"); + } + } + } + } + + private static class OKHandler implements HttpTestHandler { + + @Override + public void handle(final HttpTestExchange exchange) throws IOException { + exchange.sendResponseHeaders(200, -1); + } + } + + /** + * Tests that when a HTTP/1.1 server sends intermediate 1xx response codes and then the final + * response, the client (internally) will ignore those intermediate informational response codes + * and only return the final response to the application + */ + @Test + public void test1xxForHTTP11() throws Exception { + final HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .proxy(HttpClient.Builder.NO_PROXY).build(); + TRACKER.track(client); + final URI[] requestURIs = new URI[]{ + new URI(http1RequestURIBase + "/test/foo"), + new URI(http1RequestURIBase + "/test/bar"), + new URI(http1RequestURIBase + "/test/hello")}; + for (final URI requestURI : requestURIs) { + final HttpRequest request = HttpRequest.newBuilder(requestURI).build(); + System.out.println("Issuing request to " + requestURI); + final HttpResponse response = client.send(request, + HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + Assert.assertEquals(response.version(), HttpClient.Version.HTTP_1_1, + "Unexpected HTTP version in response"); + Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); + Assert.assertEquals(response.body(), EXPECTED_RSP_BODY, "Unexpected response body"); + } + } + + /** + * Tests that when a HTTP2 server sends intermediate 1xx response codes and then the final + * response, the client (internally) will ignore those intermediate informational response codes + * and only return the final response to the application + */ + @Test + public void test1xxForHTTP2() throws Exception { + final HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .proxy(HttpClient.Builder.NO_PROXY).build(); + TRACKER.track(client); + final URI[] requestURIs = new URI[]{ + new URI(http2RequestURIBase + "/102"), + new URI(http2RequestURIBase + "/103"), + new URI(http2RequestURIBase + "/100")}; + for (final URI requestURI : requestURIs) { + final HttpRequest request = HttpRequest.newBuilder(requestURI).build(); + System.out.println("Issuing request to " + requestURI); + final HttpResponse response = client.send(request, + HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + Assert.assertEquals(response.version(), HttpClient.Version.HTTP_2, + "Unexpected HTTP version in response"); + Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); + Assert.assertEquals(response.body(), EXPECTED_RSP_BODY, "Unexpected response body"); + } + } + + + /** + * Tests that when a request is issued with a specific request timeout and the server + * responds with intermediate 1xx response code but doesn't respond with a final response within + * the timeout duration, then the application fails with a request timeout + */ + @Test + public void test1xxRequestTimeout() throws Exception { + final HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .proxy(HttpClient.Builder.NO_PROXY).build(); + TRACKER.track(client); + final URI requestURI = new URI(http2RequestURIBase + "/only-informational"); + final Duration requestTimeout = Duration.ofSeconds(2); + final HttpRequest request = HttpRequest.newBuilder(requestURI).timeout(requestTimeout) + .build(); + System.out.println("Issuing request to " + requestURI); + // we expect the request to timeout + Assert.assertThrows(HttpTimeoutException.class, () -> { + client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + }); + } + + /** + * Tests that when the HTTP/1.1 server sends a 101 response when the request hasn't asked + * for an "Upgrade" then the request fails. + */ + @Test + public void testHTTP11Unexpected101() throws Exception { + final HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .proxy(HttpClient.Builder.NO_PROXY).build(); + TRACKER.track(client); + final URI requestURI = new URI(http1RequestURIBase + "/test/bye"); + final HttpRequest request = HttpRequest.newBuilder(requestURI).build(); + System.out.println("Issuing request to " + requestURI); + // we expect the request to fail because the server sent an unexpected 101 + Assert.assertThrows(ProtocolException.class, + () -> client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))); + } + + + /** + * Tests that when the HTTP2 server (over HTTPS) sends a 101 response when the request + * hasn't asked for an "Upgrade" then the request fails. + */ + @Test + public void testSecureHTTP2Unexpected101() throws Exception { + final HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .sslContext(sslContext) + .proxy(HttpClient.Builder.NO_PROXY).build(); + TRACKER.track(client); + final URI requestURI = new URI(https2RequestURIBase + "/101"); + final HttpRequest request = HttpRequest.newBuilder(requestURI).build(); + System.out.println("Issuing request to " + requestURI); + // we expect the request to fail because the server sent an unexpected 101 + Assert.assertThrows(ProtocolException.class, + () -> client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))); + } + + /** + * Tests that when the HTTP2 server (over plain HTTP) sends a 101 response when the request + * hasn't asked for an "Upgrade" then the request fails. + */ + @Test + public void testPlainHTTP2Unexpected101() throws Exception { + final HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .proxy(HttpClient.Builder.NO_PROXY).build(); + TRACKER.track(client); + // when using HTTP2 version against a "http://" (non-secure) URI + // the HTTP client (implementation) internally initiates a HTTP/1.1 connection + // and then does an "Upgrade:" to "h2c". This it does when there isn't already a + // H2 connection against the target/destination server. So here we initiate a dummy request + // using the client instance against the same target server and just expect it to return + // back successfully. Once that connection is established (and internally pooled), the client + // will then reuse that connection and won't issue an "Upgrade:" and thus we can then + // start our testing + warmupH2Client(client); + // start the actual testing + final URI requestURI = new URI(http2RequestURIBase + "/101"); + final HttpRequest request = HttpRequest.newBuilder(requestURI).build(); + System.out.println("Issuing request to " + requestURI); + // we expect the request to fail because the server sent an unexpected 101 + Assert.assertThrows(ProtocolException.class, + () -> client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))); + } + + // sends a request and expects a 200 response back + private void warmupH2Client(final HttpClient client) throws Exception { + final URI requestURI = new URI(http2RequestURIBase + "/200"); + final HttpRequest request = HttpRequest.newBuilder(requestURI).build(); + System.out.println("Issuing (warmup) request to " + requestURI); + final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.discarding()); + Assert.assertEquals(response.statusCode(), 200, "Unexpected response code"); + } + + // verifies that the HttpClient being tracked has no outstanding operations + private void assertNoOutstandingClientOps() throws AssertionError { + System.gc(); + final AssertionError refCheckFailure = TRACKER.check(1000); + if (refCheckFailure != null) { + throw refCheckFailure; + } + // successful test completion + } +} diff --git a/test/jdk/java/net/httpclient/http2/BadHeadersTest.java b/test/jdk/java/net/httpclient/http2/BadHeadersTest.java index 0179317d6e5..062b4c89e09 100644 --- a/test/jdk/java/net/httpclient/http2/BadHeadersTest.java +++ b/test/jdk/java/net/httpclient/http2/BadHeadersTest.java @@ -23,6 +23,7 @@ /* * @test + * @bug 8303965 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer jdk.test.lib.net.SimpleSSLContext * @run testng/othervm -Djdk.internal.httpclient.debug=true BadHeadersTest @@ -202,20 +203,26 @@ void testAsync(String uri, // Assertions based on implementation specific detail messages. Keep in // sync with implementation. static void assertDetailMessage(Throwable throwable, int iterationIndex) { - assertTrue(throwable instanceof IOException, - "Expected IOException, got, " + throwable); - assertTrue(throwable.getMessage().contains("protocol error"), - "Expected \"protocol error\" in: " + throwable.getMessage()); - - if (iterationIndex == 0) { // unknown - assertTrue(throwable.getMessage().contains("Unknown pseudo-header"), - "Expected \"Unknown pseudo-header\" in: " + throwable.getMessage()); - } else if (iterationIndex == 4) { // unexpected - assertTrue(throwable.getMessage().contains(" Unexpected pseudo-header"), - "Expected \" Unexpected pseudo-header\" in: " + throwable.getMessage()); - } else { - assertTrue(throwable.getMessage().contains("Bad header"), - "Expected \"Bad header\" in: " + throwable.getMessage()); + try { + assertTrue(throwable instanceof IOException, + "Expected IOException, got, " + throwable); + assertTrue(throwable.getMessage().contains("malformed response"), + "Expected \"malformed response\" in: " + throwable.getMessage()); + + if (iterationIndex == 0) { // unknown + assertTrue(throwable.getMessage().contains("Unknown pseudo-header"), + "Expected \"Unknown pseudo-header\" in: " + throwable.getMessage()); + } else if (iterationIndex == 4) { // unexpected + assertTrue(throwable.getMessage().contains(" Unexpected pseudo-header"), + "Expected \" Unexpected pseudo-header\" in: " + throwable.getMessage()); + } else { + assertTrue(throwable.getMessage().contains("Bad header"), + "Expected \"Bad header\" in: " + throwable.getMessage()); + } + } catch (AssertionError e) { + System.out.println("Exception does not match expectation: " + throwable); + throwable.printStackTrace(System.out); + throw e; } } diff --git a/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java b/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java new file mode 100644 index 00000000000..cc6bc1a9515 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/PushPromiseContinuation.java @@ -0,0 +1,367 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8263031 + * @summary Tests that the HttpClient can correctly receive a Push Promise + * Frame with the END_HEADERS flag unset followed by one or more + * Continuation Frames. + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.http2.Http2TestServer + * jdk.httpclient.test.lib.http2.BodyOutputStream + * jdk.httpclient.test.lib.http2.OutgoingPushPromise + * @run testng/othervm PushPromiseContinuation + */ + + +import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.frame.ContinuationFrame; +import jdk.internal.net.http.frame.HeaderFrame; +import org.testng.TestException; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import javax.net.ssl.SSLSession; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiPredicate; +import jdk.httpclient.test.lib.http2.Http2TestServer; +import jdk.httpclient.test.lib.http2.Http2TestExchange; +import jdk.httpclient.test.lib.http2.Http2TestExchangeImpl; +import jdk.httpclient.test.lib.http2.Http2Handler; +import jdk.httpclient.test.lib.http2.BodyOutputStream; +import jdk.httpclient.test.lib.http2.OutgoingPushPromise; +import jdk.httpclient.test.lib.http2.Http2TestServerConnection; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.*; + +public class PushPromiseContinuation { + + static volatile HttpHeaders testHeaders; + static volatile HttpHeadersBuilder testHeadersBuilder; + static volatile int continuationCount; + static final String mainPromiseBody = "Main Promise Body"; + static final String mainResponseBody = "Main Response Body"; + Http2TestServer server; + URI uri; + + // Set up simple client-side push promise handler + ConcurrentMap>> pushPromiseMap = new ConcurrentHashMap<>(); + HttpResponse.PushPromiseHandler pph = (initial, pushRequest, acceptor) -> { + HttpResponse.BodyHandler s = HttpResponse.BodyHandlers.ofString(UTF_8); + pushPromiseMap.put(pushRequest, acceptor.apply(s)); + }; + + @BeforeMethod + public void beforeMethod() { + pushPromiseMap = new ConcurrentHashMap<>(); + } + + @BeforeTest + public void setup() throws Exception { + server = new Http2TestServer(false, 0); + server.addHandler(new ServerPushHandler(), "/"); + + // Need to have a custom exchange supplier to manage the server's push + // promise with continuation flow + server.setExchangeSupplier(Http2PushPromiseContinuationExchangeImpl::new); + + System.err.println("PushPromiseContinuation: Server listening on port " + server.getAddress().getPort()); + server.start(); + int port = server.getAddress().getPort(); + uri = new URI("http://localhost:" + port + "/"); + } + + @AfterTest + public void teardown() { + pushPromiseMap = null; + server.stop(); + } + + /** + * Tests that when the client receives PushPromise Frame with the END_HEADERS + * flag set to 0x0 and subsequently receives a continuation frame, no exception + * is thrown and all headers from the PushPromise and Continuation Frames sent + * by the server arrive at the client. + */ + @Test + public void testOneContinuation() { + continuationCount = 1; + HttpClient client = HttpClient.newHttpClient(); + + // Carry out request + HttpRequest hreq = HttpRequest.newBuilder(uri).version(HttpClient.Version.HTTP_2).GET().build(); + CompletableFuture> cf = + client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph); + HttpResponse resp = cf.join(); + + // Verify results + verify(resp); + } + + /** + * Same as above, but tests for the case where two Continuation Frames are sent + * with the END_HEADERS flag set only on the last frame. + */ + @Test + public void testTwoContinuations() { + continuationCount = 2; + HttpClient client = HttpClient.newHttpClient(); + + // Carry out request + HttpRequest hreq = HttpRequest.newBuilder(uri).version(HttpClient.Version.HTTP_2).GET().build(); + CompletableFuture> cf = + client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph); + HttpResponse resp = cf.join(); + + // Verify results + verify(resp); + } + + @Test + public void testThreeContinuations() { + continuationCount = 3; + HttpClient client = HttpClient.newHttpClient(); + + // Carry out request + HttpRequest hreq = HttpRequest.newBuilder(uri).version(HttpClient.Version.HTTP_2).GET().build(); + CompletableFuture> cf = + client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph); + HttpResponse resp = cf.join(); + + // Verify results + verify(resp); + } + + @Test + public void testSendHeadersOnPushPromiseStream() throws Exception { + // This test server sends a push promise that should be followed by a continuation but + // incorrectly sends on Response Headers while the client awaits the continuation. + Http2TestServer faultyServer = new Http2TestServer(false, 0); + faultyServer.addHandler(new ServerPushHandler(), "/"); + faultyServer.setExchangeSupplier(Http2PushPromiseHeadersExchangeImpl::new); + System.err.println("PushPromiseContinuation: FaultyServer listening on port " + faultyServer.getAddress().getPort()); + faultyServer.start(); + + int faultyPort = faultyServer.getAddress().getPort(); + URI faultyUri = new URI("http://localhost:" + faultyPort + "/"); + + HttpClient client = HttpClient.newHttpClient(); + // Server is making a request to an incorrect URI + HttpRequest hreq = HttpRequest.newBuilder(faultyUri).version(HttpClient.Version.HTTP_2).GET().build(); + CompletableFuture> cf = + client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph); + + CompletionException t = expectThrows(CompletionException.class, () -> cf.join()); + assertEquals(t.getCause().getClass(), ProtocolException.class, + "Expected a ProtocolException but got " + t.getCause()); + System.err.println("Client received the following expected exception: " + t.getCause()); + faultyServer.stop(); + } + + private void verify(HttpResponse resp) { + assertEquals(resp.statusCode(), 200); + assertEquals(resp.body(), mainResponseBody); + if (pushPromiseMap.size() > 1) { + System.err.println(pushPromiseMap.entrySet()); + throw new TestException("Results map size is greater than 1"); + } else { + // This will only iterate once + for (HttpRequest r : pushPromiseMap.keySet()) { + HttpResponse serverPushResp = pushPromiseMap.get(r).join(); + // Received headers should be the same as the combined PushPromise + // frame headers combined with the Continuation frame headers + assertEquals(testHeaders, r.headers()); + // Check status code and push promise body are as expected + assertEquals(serverPushResp.statusCode(), 200); + assertEquals(serverPushResp.body(), mainPromiseBody); + } + } + } + + static class Http2PushPromiseHeadersExchangeImpl extends Http2TestExchangeImpl { + + Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders, + HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, + SSLSession sslSession, BodyOutputStream os, + Http2TestServerConnection conn, boolean pushAllowed) { + super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed); + } + + + @Override + public void serverPush(URI uri, HttpHeaders headers, InputStream content) { + HttpHeadersBuilder headersBuilder = new HttpHeadersBuilder(); + headersBuilder.setHeader(":method", "GET"); + headersBuilder.setHeader(":scheme", uri.getScheme()); + headersBuilder.setHeader(":authority", uri.getAuthority()); + headersBuilder.setHeader(":path", uri.getPath()); + for (Map.Entry> entry : headers.map().entrySet()) { + for (String value : entry.getValue()) + headersBuilder.addHeader(entry.getKey(), value); + } + HttpHeaders combinedHeaders = headersBuilder.build(); + OutgoingPushPromise pp = new OutgoingPushPromise(streamid, uri, combinedHeaders, content); + // Indicates to the client that a continuation should be expected + pp.setFlag(0x0); + try { + conn.addToOutputQ(pp); + // writeLoop will spin up thread to read the InputStream + } catch (IOException ex) { + System.err.println("TestServer: pushPromise exception: " + ex); + } + } + } + + static class Http2PushPromiseContinuationExchangeImpl extends Http2TestExchangeImpl { + + HttpHeadersBuilder pushPromiseHeadersBuilder; + List cfs; + + Http2PushPromiseContinuationExchangeImpl(int streamid, String method, HttpHeaders reqheaders, + HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, + SSLSession sslSession, BodyOutputStream os, + Http2TestServerConnection conn, boolean pushAllowed) { + super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed); + } + + private void setPushHeaders(String name, String value) { + pushPromiseHeadersBuilder.setHeader(name, value); + testHeadersBuilder.setHeader(name, value); + } + + private void assembleContinuations() { + for (int i = 0; i < continuationCount; i++) { + HttpHeadersBuilder builder = new HttpHeadersBuilder(); + for (int j = 0; j < 10; j++) { + String name = "x-cont-" + i + "-" + j; + builder.setHeader(name, "data_" + j); + testHeadersBuilder.setHeader(name, "data_" + j); + } + + ContinuationFrame cf = new ContinuationFrame(streamid, 0x0, conn.encodeHeaders(builder.build())); + // If this is the last Continuation Frame, set the END_HEADERS flag. + if (i >= continuationCount - 1) { + cf.setFlag(HeaderFrame.END_HEADERS); + } + cfs.add(cf); + } + } + + @Override + public void serverPush(URI uri, HttpHeaders headers, InputStream content) { + pushPromiseHeadersBuilder = new HttpHeadersBuilder(); + testHeadersBuilder = new HttpHeadersBuilder(); + cfs = new ArrayList<>(); + + setPushHeaders(":method", "GET"); + setPushHeaders(":scheme", uri.getScheme()); + setPushHeaders(":authority", uri.getAuthority()); + setPushHeaders(":path", uri.getPath()); + for (Map.Entry> entry : headers.map().entrySet()) { + for (String value : entry.getValue()) { + setPushHeaders(entry.getKey(), value); + } + } + + for (int i = 0; i < 10; i++) { + setPushHeaders("x-push-header-" + i, "data_" + i); + } + + // Create the Continuation Frame/s, done before Push Promise Frame for test purposes + // as testHeaders contains all headers used in all frames + assembleContinuations(); + + HttpHeaders pushPromiseHeaders = pushPromiseHeadersBuilder.build(); + testHeaders = testHeadersBuilder.build(); + // Create the Push Promise Frame + OutgoingPushPromise pp = new OutgoingPushPromise(streamid, uri, pushPromiseHeaders, content, cfs); + + // Indicates to the client that a continuation should be expected + pp.setFlag(0x0); + + try { + // Schedule push promise and continuation for sending + conn.addToOutputQ(pp); + System.err.println("Server: Scheduled a Push Promise to Send"); + } catch (IOException ex) { + System.err.println("Server: pushPromise exception: " + ex); + } + } + } + + static class ServerPushHandler implements Http2Handler { + + public void handle(Http2TestExchange exchange) throws IOException { + System.err.println("Server: handle " + exchange); + try (InputStream is = exchange.getRequestBody()) { + is.readAllBytes(); + } + + if (exchange.serverPushAllowed()) { + pushPromise(exchange); + } + + // response data for the main response + try (OutputStream os = exchange.getResponseBody()) { + byte[] bytes = mainResponseBody.getBytes(UTF_8); + exchange.sendResponseHeaders(200, bytes.length); + os.write(bytes); + } + } + + static final BiPredicate ACCEPT_ALL = (x, y) -> true; + + + private void pushPromise(Http2TestExchange exchange) throws IOException { + URI requestURI = exchange.getRequestURI(); + URI uri = requestURI.resolve("/promise"); + InputStream is = new ByteArrayInputStream(mainPromiseBody.getBytes(UTF_8)); + Map> map = new HashMap<>(); + map.put("x-promise", List.of("promise-header")); + HttpHeaders headers = HttpHeaders.of(map, ACCEPT_ALL); + exchange.serverPush(uri, headers, is); + System.err.println("Server: Push Promise complete"); + } + } +} diff --git a/test/jdk/java/net/httpclient/http2/TrailingHeadersTest.java b/test/jdk/java/net/httpclient/http2/TrailingHeadersTest.java new file mode 100644 index 00000000000..c6eb7f709fa --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/TrailingHeadersTest.java @@ -0,0 +1,325 @@ +/* + * 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 Trailing headers should be ignored by the client when using HTTP/2 + * and not affect the rest of the exchange. + * @bug 8296410 + * @library /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.http2.Http2TestServer + * @run testng/othervm -Djdk.httpclient.HttpClient.log=all TrailingHeadersTest + */ + +import jdk.httpclient.test.lib.http2.OutgoingPushPromise; +import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.frame.DataFrame; +import jdk.internal.net.http.frame.HeaderFrame; +import jdk.internal.net.http.frame.HeadersFrame; +import org.testng.TestException; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import javax.net.ssl.SSLSession; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.function.BiPredicate; + +import jdk.httpclient.test.lib.http2.Http2TestServer; +import jdk.httpclient.test.lib.http2.Http2TestServerConnection; +import jdk.httpclient.test.lib.http2.Http2TestExchangeImpl; +import jdk.httpclient.test.lib.http2.Http2TestExchange; +import jdk.httpclient.test.lib.http2.Http2Handler; +import jdk.httpclient.test.lib.http2.BodyOutputStream; + +import static java.net.http.HttpClient.Version.HTTP_2; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertEquals; + +public class TrailingHeadersTest { + + Http2TestServer http2TestServer; + URI trailingURI, trailng1xxURI, trailingPushPromiseURI, warmupURI; + static PrintStream testLog = System.err; + + // Set up simple client-side push promise handler + ConcurrentMap>> pushPromiseMap = new ConcurrentHashMap<>(); + + @BeforeMethod + public void beforeMethod() { + pushPromiseMap = new ConcurrentHashMap<>(); + } + + @BeforeTest + public void setup() throws Exception { + Properties props = new Properties(); + // For triggering trailing headers to send after Push Promise Response headers are sent + props.setProperty("sendTrailingHeadersAfterPushPromise", "1"); + + http2TestServer = new Http2TestServer("Test_Server", + false, + 0, + null, + 0, + props, + null); + http2TestServer.setExchangeSupplier(TrailingHeadersExchange::new); + http2TestServer.addHandler(new ResponseTrailersHandler(), "/ResponseTrailingHeaders"); + http2TestServer.addHandler(new InformationalTrailersHandler(), "/InfoRespTrailingHeaders"); + http2TestServer.addHandler(new PushPromiseTrailersHandler(), "/PushPromiseTrailingHeaders"); + http2TestServer.addHandler(new WarmupHandler(), "/WarmupHandler"); + + http2TestServer.start(); + + trailingURI = URI.create("http://" + http2TestServer.serverAuthority() + "/ResponseTrailingHeaders"); + trailng1xxURI = URI.create("http://" + http2TestServer.serverAuthority() + "/InfoRespTrailingHeaders"); + trailingPushPromiseURI = URI.create("http://" + http2TestServer.serverAuthority() + "/PushPromiseTrailingHeaders"); + + // Used to ensure HTTP/2 upgrade takes place + warmupURI = URI.create("http://" + http2TestServer.serverAuthority() + "/WarmupHandler"); + } + + @AfterTest + public void teardown() { + http2TestServer.stop(); + } + + @Test(dataProvider = "httpRequests") + public void testTrailingHeaders(String description, HttpRequest hRequest, HttpResponse.PushPromiseHandler pph) { + testLog.println("testTrailingHeaders(): " + description); + HttpClient httpClient = HttpClient.newBuilder().build(); + performWarmupRequest(httpClient); + CompletableFuture> cf = httpClient.sendAsync(hRequest, BodyHandlers.ofString(UTF_8), pph); + + testLog.println("testTrailingHeaders(): Performing request: " + hRequest); + HttpResponse resp = cf.join(); + + assertEquals(resp.statusCode(), 200, "Status code of response should be 200"); + + // Verify Push Promise was successful if necessary + if (pph != null) + verifyPushPromise(); + + testLog.println("testTrailingHeaders(): Request successfully completed"); + } + + private void verifyPushPromise() { + assertEquals(pushPromiseMap.size(), 1, "Push Promise should not be greater than 1"); + // This will only iterate once + for (HttpRequest r : pushPromiseMap.keySet()) { + CompletableFuture> serverPushResp = pushPromiseMap.get(r); + // Get the push promise HttpResponse result if present + HttpResponse resp = serverPushResp.join(); + assertEquals(resp.body(), "Sample_Push_Data", "Unexpected Push Promise response body"); + assertEquals(resp.statusCode(), 200, "Status code of Push Promise response should be 200"); + } + } + + private void performWarmupRequest(HttpClient httpClient) { + HttpRequest warmupReq = HttpRequest.newBuilder(warmupURI).version(HTTP_2) + .GET() + .build(); + httpClient.sendAsync(warmupReq, BodyHandlers.discarding()).join(); + } + + @DataProvider(name = "httpRequests") + public Object[][] uris() { + HttpResponse.PushPromiseHandler pph = (initial, pushRequest, acceptor) -> { + HttpResponse.BodyHandler s = HttpResponse.BodyHandlers.ofString(UTF_8); + pushPromiseMap.put(pushRequest, acceptor.apply(s)); + }; + + HttpRequest httpGetTrailing = HttpRequest.newBuilder(trailingURI).version(HTTP_2) + .GET() + .build(); + + HttpRequest httpPost1xxTrailing = HttpRequest.newBuilder(trailng1xxURI).version(HTTP_2) + .POST(HttpRequest.BodyPublishers.ofString("Test Post")) + .expectContinue(true) + .build(); + + HttpRequest httpGetPushPromiseTrailing = HttpRequest.newBuilder(trailingPushPromiseURI).version(HTTP_2) + .GET() + .build(); + + return new Object[][] { + { "Test GET with Trailing Headers", httpGetTrailing, null }, + { "Test POST with 1xx response & Trailing Headers", httpPost1xxTrailing, null }, + { "Test Push Promise with Trailing Headers", httpGetPushPromiseTrailing, pph } + }; + } + + static class TrailingHeadersExchange extends Http2TestExchangeImpl { + + byte[] resp = "Sample_Data".getBytes(StandardCharsets.UTF_8); + + + TrailingHeadersExchange(int streamid, String method, HttpHeaders reqheaders, HttpHeadersBuilder rspheadersBuilder, + URI uri, InputStream is, SSLSession sslSession, BodyOutputStream os, + Http2TestServerConnection conn, boolean pushAllowed) { + super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed); + } + + public void sendResponseThenTrailers() throws IOException { + /* + HttpHeadersBuilder hb = this.conn.createNewHeadersBuilder(); + hb.setHeader("x-sample", "val"); + HeaderFrame headerFrame = new HeadersFrame(this.streamid, 0, this.conn.encodeHeaders(hb.build())); + */ + // TODO: see if there is a safe way to encode headers without interrupting connection thread + HeaderFrame headerFrame = new HeadersFrame(this.streamid, 0, List.of()); + headerFrame.setFlag(HeaderFrame.END_HEADERS); + headerFrame.setFlag(HeaderFrame.END_STREAM); + + this.sendResponseHeaders(200, resp.length); + DataFrame dataFrame = new DataFrame(this.streamid, 0, ByteBuffer.wrap(resp)); + this.conn.addToOutputQ(dataFrame); + this.conn.addToOutputQ(headerFrame); + } + + @Override + public void serverPush(URI uri, HttpHeaders headers, InputStream content) { + HttpHeadersBuilder headersBuilder = new HttpHeadersBuilder(); + headersBuilder.setHeader(":method", "GET"); + headersBuilder.setHeader(":scheme", uri.getScheme()); + headersBuilder.setHeader(":authority", uri.getAuthority()); + headersBuilder.setHeader(":path", uri.getPath()); + for (Map.Entry> entry : headers.map().entrySet()) { + for (String value : entry.getValue()) + headersBuilder.addHeader(entry.getKey(), value); + } + HttpHeaders combinedHeaders = headersBuilder.build(); + OutgoingPushPromise pp = new OutgoingPushPromise(streamid, uri, combinedHeaders, content); + pp.setFlag(HeaderFrame.END_HEADERS); + + try { + this.conn.addToOutputQ(pp); + } catch (IOException ex) { + testLog.println("serverPush(): pushPromise exception: " + ex); + } + } + } + + static class WarmupHandler implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + exchange.sendResponseHeaders(200, 0); + } + } + + static class ResponseTrailersHandler implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + if (exchange.getProtocol().equals("HTTP/2")) { + if (exchange instanceof TrailingHeadersExchange trailingHeadersExchange) { + trailingHeadersExchange.sendResponseThenTrailers(); + } + } else { + testLog.println("ResponseTrailersHandler: Incorrect protocol version"); + exchange.sendResponseHeaders(400, 0); + } + } + } + + static class InformationalTrailersHandler implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + if (exchange.getProtocol().equals("HTTP/2")) { + if (exchange instanceof TrailingHeadersExchange trailingHeadersExchange) { + testLog.println(this.getClass().getCanonicalName() + ": Sending status 100"); + trailingHeadersExchange.sendResponseHeaders(100, 0); + + try (InputStream is = exchange.getRequestBody()) { + is.readAllBytes(); + trailingHeadersExchange.sendResponseThenTrailers(); + } + } + } else { + testLog.println("InformationalTrailersHandler: Incorrect protocol version"); + exchange.sendResponseHeaders(400, 0); + } + } + } + + static class PushPromiseTrailersHandler implements Http2Handler { + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + if (exchange.getProtocol().equals("HTTP/2")) { + if (exchange instanceof TrailingHeadersExchange trailingHeadersExchange) { + try (InputStream is = exchange.getRequestBody()) { + is.readAllBytes(); + } + + if (exchange.serverPushAllowed()) { + pushPromise(trailingHeadersExchange); + } + + try (OutputStream os = trailingHeadersExchange.getResponseBody()) { + byte[] bytes = "Sample_Data".getBytes(UTF_8); + trailingHeadersExchange.sendResponseHeaders(200, bytes.length); + os.write(bytes); + } + } + } + } + + static final BiPredicate ACCEPT_ALL = (x, y) -> true; + + private void pushPromise(Http2TestExchange exchange) { + URI requestURI = exchange.getRequestURI(); + URI uri = requestURI.resolve("/promise"); + InputStream is = new ByteArrayInputStream("Sample_Push_Data".getBytes(UTF_8)); + Map> map = new HashMap<>(); + map.put("x-promise", List.of("promise-header")); + HttpHeaders headers = HttpHeaders.of(map, ACCEPT_ALL); + exchange.serverPush(uri, headers, is); + testLog.println("PushPromiseTrailersHandler: Push Promise complete"); + } + } +} \ No newline at end of file diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java index 623644df5a6..6bbcc4c3741 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/common/HttpServerAdapters.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 @@ -240,6 +240,7 @@ public static abstract class HttpTestExchange implements AutoCloseable { public abstract String getRequestMethod(); public abstract void close(); public abstract InetSocketAddress getRemoteAddress(); + public abstract InetSocketAddress getLocalAddress(); public void serverPush(URI uri, HttpHeaders headers, byte[] body) { ByteArrayInputStream bais = new ByteArrayInputStream(body); serverPush(uri, headers, bais); @@ -302,7 +303,10 @@ void doFilter(Filter.Chain chain) throws IOException { public InetSocketAddress getRemoteAddress() { return exchange.getRemoteAddress(); } - + @Override + public InetSocketAddress getLocalAddress() { + return exchange.getLocalAddress(); + } @Override public URI getRequestURI() { return exchange.getRequestURI(); } @Override @@ -363,6 +367,10 @@ void doFilter(Filter.Chain filter) throws IOException { public InetSocketAddress getRemoteAddress() { return exchange.getRemoteAddress(); } + @Override + public InetSocketAddress getLocalAddress() { + return exchange.getLocalAddress(); + } @Override public URI getRequestURI() { return exchange.getRequestURI(); } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java index d043f81390e..81b1d973239 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/BodyOutputStream.java @@ -130,10 +130,14 @@ public void close() { closed = true; } try { - send(EMPTY_BARRAY, 0, 0, DataFrame.END_STREAM); + sendEndStream(); } catch (IOException ex) { System.err.println("TestServer: OutputStream.close exception: " + ex); ex.printStackTrace(); } } + + protected void sendEndStream() throws IOException { + send(EMPTY_BARRAY, 0, 0, DataFrame.END_STREAM); + } } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java new file mode 100644 index 00000000000..f54a4a766b8 --- /dev/null +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/HpackTestEncoder.java @@ -0,0 +1,174 @@ +/* + * 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 + * 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.httpclient.test.lib.http2; + +import java.util.function.*; + +import jdk.internal.net.http.hpack.Encoder; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static jdk.internal.net.http.hpack.HPACK.Logger.Level.EXTRA; +import static jdk.internal.net.http.hpack.HPACK.Logger.Level.NORMAL; + +public class HpackTestEncoder extends Encoder { + + public HpackTestEncoder(int maxCapacity) { + super(maxCapacity); + } + + /** + * Sets up the given header {@code (name, value)} with possibly sensitive + * value. + * + *

      If the {@code value} is sensitive (think security, secrecy, etc.) + * this encoder will compress it using a special representation + * (see 6.2.3. Literal Header Field Never Indexed). + * + *

      Fixates {@code name} and {@code value} for the duration of encoding. + * + * @param name + * the name + * @param value + * the value + * @param sensitive + * whether or not the value is sensitive + * + * @throws NullPointerException + * if any of the arguments are {@code null} + * @throws IllegalStateException + * if the encoder hasn't fully encoded the previous header, or + * hasn't yet started to encode it + * @see #header(CharSequence, CharSequence) + * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean) + */ + public void header(CharSequence name, + CharSequence value, + boolean sensitive) throws IllegalStateException { + if (sensitive || getMaxCapacity() == 0) { + super.header(name, value, true); + } else { + header(name, value, false, (n,v) -> false); + } + } + /** + * Sets up the given header {@code (name, value)} with possibly sensitive + * value. + * + *

      If the {@code value} is sensitive (think security, secrecy, etc.) + * this encoder will compress it using a special representation + * (see 6.2.3. Literal Header Field Never Indexed). + * + *

      Fixates {@code name} and {@code value} for the duration of encoding. + * + * @param name + * the name + * @param value + * the value + * @param insertionPolicy + * a bipredicate to indicate whether a name value pair + * should be added to the dynamic table + * + * @throws NullPointerException + * if any of the arguments are {@code null} + * @throws IllegalStateException + * if the encoder hasn't fully encoded the previous header, or + * hasn't yet started to encode it + * @see #header(CharSequence, CharSequence) + * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean) + */ + public void header(CharSequence name, + CharSequence value, + BiPredicate insertionPolicy) + throws IllegalStateException { + header(name, value, false, insertionPolicy); + } + + /** + * Sets up the given header {@code (name, value)} with possibly sensitive + * value. + * + *

      If the {@code value} is sensitive (think security, secrecy, etc.) + * this encoder will compress it using a special representation + * (see + * 6.2.3. Literal Header Field Never Indexed). + * + *

      Fixates {@code name} and {@code value} for the duration of encoding. + * + * @param name + * the name + * @param value + * the value + * @param sensitive + * whether or not the value is sensitive + * @param insertionPolicy + * a bipredicate to indicate whether a name value pair + * should be added to the dynamic table + * + * @throws NullPointerException + * if any of the arguments are {@code null} + * @throws IllegalStateException + * if the encoder hasn't fully encoded the previous header, or + * hasn't yet started to encode it + * @see #header(CharSequence, CharSequence) + * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean) + */ + public void header(CharSequence name, + CharSequence value, + boolean sensitive, + BiPredicate insertionPolicy) + throws IllegalStateException { + if (sensitive == true || getMaxCapacity() == 0 || !insertionPolicy.test(name, value)) { + super.header(name, value, sensitive); + return; + } + var logger = logger(); + // Arguably a good balance between complexity of implementation and + // efficiency of encoding + requireNonNull(name, "name"); + requireNonNull(value, "value"); + var t = getHeaderTable(); + int index = tableIndexOf(name, value); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("encoding with indexing ('%s', '%s'): index:%s", + name, value, index)); + } + if (index > 0) { + indexed(index); + } else { + boolean huffmanValue = isHuffmanBetterFor(value); + if (index < 0) { + literalWithIndexing(-index, value, huffmanValue); + } else { + boolean huffmanName = isHuffmanBetterFor(name); + literalWithIndexing(name, huffmanName, value, huffmanValue); + } + } + } + + protected int calculateCapacity(int maxCapacity) { + return maxCapacity; + } + +} diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java index 208a8d54e08..1a8b5d92af5 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchange.java @@ -29,9 +29,12 @@ import java.net.URI; import java.net.InetSocketAddress; import java.net.http.HttpHeaders; +import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.function.BiPredicate; import javax.net.ssl.SSLSession; import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.frame.Http2Frame; public interface Http2TestExchange { @@ -53,6 +56,12 @@ public interface Http2TestExchange { void sendResponseHeaders(int rCode, long responseLength) throws IOException; + default void sendResponseHeaders(int rCode, long responseLength, + BiPredicate insertionPolicy) + throws IOException { + sendResponseHeaders(rCode, responseLength); + } + InetSocketAddress getRemoteAddress(); int getResponseCode(); @@ -65,6 +74,10 @@ public interface Http2TestExchange { void serverPush(URI uri, HttpHeaders headers, InputStream content); + default void sendFrames(List frames) throws IOException { + throw new UnsupportedOperationException("not implemented"); + } + /** * Send a PING on this exchanges connection, and completes the returned CF * with the number of milliseconds it took to get a valid response. diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java index 87f67e76356..5ac6ab0fd26 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java @@ -23,19 +23,22 @@ package jdk.httpclient.test.lib.http2; +import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.frame.HeaderFrame; +import jdk.internal.net.http.frame.HeadersFrame; +import jdk.internal.net.http.frame.Http2Frame; + +import javax.net.ssl.SSLSession; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; -import java.net.URI; import java.net.InetSocketAddress; +import java.net.URI; import java.net.http.HttpHeaders; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; -import javax.net.ssl.SSLSession; -import jdk.internal.net.http.common.HttpHeadersBuilder; -import jdk.internal.net.http.frame.HeaderFrame; -import jdk.internal.net.http.frame.HeadersFrame; +import java.util.function.BiPredicate; public class Http2TestExchangeImpl implements Http2TestExchange { @@ -129,8 +132,13 @@ public OutputStream getResponseBody() { return os; } - @Override public void sendResponseHeaders(int rCode, long responseLength) throws IOException { + sendResponseHeaders(rCode, responseLength, (n,v) -> false); + } + @Override + public void sendResponseHeaders(int rCode, long responseLength, + BiPredicate insertionPolicy) + throws IOException { this.responseLength = responseLength; if (responseLength !=0 && rCode != 204 && !isHeadRequest()) { long clen = responseLength > 0 ? responseLength : 0; @@ -141,7 +149,7 @@ public void sendResponseHeaders(int rCode, long responseLength) throws IOExcepti HttpHeaders headers = rspheadersBuilder.build(); Http2TestServerConnection.ResponseHeaders response - = new Http2TestServerConnection.ResponseHeaders(headers); + = new Http2TestServerConnection.ResponseHeaders(headers, insertionPolicy); response.streamid(streamid); response.setFlag(HeaderFrame.END_HEADERS); @@ -155,6 +163,11 @@ public void sendResponseHeaders(int rCode, long responseLength) throws IOExcepti System.err.println("Sent response headers " + rCode); } + @Override + public void sendFrames(List frames) throws IOException { + conn.sendFrames(frames); + } + @Override public InetSocketAddress getRemoteAddress() { return (InetSocketAddress) conn.socket.getRemoteSocketAddress(); @@ -193,6 +206,7 @@ public void serverPush(URI uri, HttpHeaders headers, InputStream content) { } HttpHeaders combinedHeaders = headersBuilder.build(); OutgoingPushPromise pp = new OutgoingPushPromise(streamid, uri, combinedHeaders, content); + pp.setFlag(HeaderFrame.END_HEADERS); try { conn.outputQ.put(pp); diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java index 912288df226..fd900f956c8 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServer.java @@ -192,7 +192,7 @@ public Http2TestServer(InetAddress localAddr, this.secure = secure; this.exec = exec == null ? getDefaultExecutor() : exec; this.handlers = Collections.synchronizedMap(new HashMap<>()); - this.properties = properties; + this.properties = properties == null ? new Properties() : properties; this.connections = new HashMap<>(); } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java index 1d48425081d..102c7a56721 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestServerConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, 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 @@ -23,34 +23,70 @@ package jdk.httpclient.test.lib.http2; +import jdk.internal.net.http.common.HttpHeadersBuilder; +import jdk.internal.net.http.common.Log; +import jdk.internal.net.http.frame.ContinuationFrame; +import jdk.internal.net.http.frame.DataFrame; +import jdk.internal.net.http.frame.ErrorFrame; +import jdk.internal.net.http.frame.FramesDecoder; +import jdk.internal.net.http.frame.FramesEncoder; +import jdk.internal.net.http.frame.GoAwayFrame; +import jdk.internal.net.http.frame.HeaderFrame; +import jdk.internal.net.http.frame.HeadersFrame; +import jdk.internal.net.http.frame.Http2Frame; +import jdk.internal.net.http.frame.PingFrame; +import jdk.internal.net.http.frame.PushPromiseFrame; +import jdk.internal.net.http.frame.ResetFrame; +import jdk.internal.net.http.frame.SettingsFrame; +import jdk.internal.net.http.frame.WindowUpdateFrame; +import jdk.internal.net.http.hpack.Decoder; +import jdk.internal.net.http.hpack.DecodingCallback; +import jdk.internal.net.http.hpack.Encoder; +import sun.net.www.http.ChunkedInputStream; +import sun.net.www.http.HttpClient; + +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIMatcher; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.StandardConstants; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.IOException; -import java.io.UncheckedIOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.net.InetAddress; import java.net.Socket; import java.net.URI; -import java.net.InetAddress; -import javax.net.ssl.*; import java.net.URISyntaxException; import java.net.http.HttpHeaders; import java.nio.ByteBuffer; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Random; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiPredicate; import java.util.function.Consumer; -import jdk.internal.net.http.common.HttpHeadersBuilder; -import jdk.internal.net.http.frame.*; -import jdk.internal.net.http.hpack.Decoder; -import jdk.internal.net.http.hpack.DecodingCallback; -import jdk.internal.net.http.hpack.Encoder; -import sun.net.www.http.ChunkedInputStream; -import sun.net.www.http.HttpClient; + import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; +import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_MAX_FRAME_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE; /** @@ -69,7 +105,7 @@ public class Http2TestServerConnection { final Http2TestExchangeSupplier exchangeSupplier; final InputStream is; final OutputStream os; - volatile Encoder hpackOut; + volatile HpackTestEncoder hpackOut; volatile Decoder hpackIn; volatile SettingsFrame clientSettings; final SettingsFrame serverSettings; @@ -362,7 +398,9 @@ private SettingsFrame getSettingsFromString(String s) throws IOException { } public int getMaxFrameSize() { - return clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE); + var max = clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE); + if (max <= 0) max = DEFAULT_MAX_FRAME_SIZE; + return max; } /** Sends a pre-canned HTTP/1.1 response. */ @@ -423,7 +461,7 @@ void run() throws Exception { //System.out.println("ServerSettings: " + serverSettings); //System.out.println("ClientSettings: " + clientSettings); - hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); + hpackOut = new HpackTestEncoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); if (!secure) { @@ -709,6 +747,14 @@ headers, rspheadersBuilder, uri, bis, getSSLSession(), } } + public void sendFrames(List frames) throws IOException { + synchronized (outputQ) { + for (var frame : frames) { + outputQ.put(frame); + } + } + } + protected HttpHeadersBuilder createNewHeadersBuilder() { return new HttpHeadersBuilder(); } @@ -819,26 +865,38 @@ static boolean isServerStreamId(int streamid) { return (streamid & 0x01) == 0x00; } + final ReentrantLock headersLock = new ReentrantLock(); + /** Encodes an group of headers, without any ordering guarantees. */ public List encodeHeaders(HttpHeaders headers) { + return encodeHeaders(headers, (n,v) -> false); + } + + public List encodeHeaders(HttpHeaders headers, + BiPredicate insertionPolicy) { List buffers = new LinkedList<>(); ByteBuffer buf = getBuffer(); boolean encoded; - for (Map.Entry> entry : headers.map().entrySet()) { - List values = entry.getValue(); - String key = entry.getKey().toLowerCase(); - for (String value : values) { - do { - hpackOut.header(key, value); - encoded = hpackOut.encode(buf); - if (!encoded) { - buf.flip(); - buffers.add(buf); - buf = getBuffer(); - } - } while (!encoded); + headersLock.lock(); + try { + for (Map.Entry> entry : headers.map().entrySet()) { + List values = entry.getValue(); + String key = entry.getKey().toLowerCase(); + for (String value : values) { + hpackOut.header(key, value, insertionPolicy); + do { + encoded = hpackOut.encode(buf); + if (!encoded && !buf.hasRemaining()) { + buf.flip(); + buffers.add(buf); + buf = getBuffer(); + } + } while (!encoded); + } } + } finally { + headersLock.unlock(); } buf.flip(); buffers.add(buf); @@ -851,18 +909,23 @@ public List encodeHeadersOrdered(List> head ByteBuffer buf = getBuffer(); boolean encoded; - for (Map.Entry entry : headers) { - String value = entry.getValue(); - String key = entry.getKey().toLowerCase(); - do { + headersLock.lock(); + try { + for (Map.Entry entry : headers) { + String value = entry.getValue(); + String key = entry.getKey().toLowerCase(); hpackOut.header(key, value); - encoded = hpackOut.encode(buf); - if (!encoded) { - buf.flip(); - buffers.add(buf); - buf = getBuffer(); - } - } while (!encoded); + do { + encoded = hpackOut.encode(buf); + if (!encoded && !buf.hasRemaining()) { + buf.flip(); + buffers.add(buf); + buf = getBuffer(); + } + } while (!encoded); + } + } finally { + headersLock.unlock(); } buf.flip(); buffers.add(buf); @@ -889,10 +952,50 @@ void writeLoop() { break; } else throw x; } - if (frame instanceof ResponseHeaders) { - ResponseHeaders rh = (ResponseHeaders)frame; - HeadersFrame hf = new HeadersFrame(rh.streamid(), rh.getFlags(), encodeHeaders(rh.headers)); - writeFrame(hf); + if (frame instanceof ResponseHeaders rh) { + var buffers = encodeHeaders(rh.headers, rh.insertionPolicy); + int maxFrameSize = Math.min(rh.getMaxFrameSize(), getMaxFrameSize() - 64); + int next = 0; + int cont = 0; + do { + // If the total size of headers exceeds the max frame + // size we need to split the headers into one + // HeadersFrame + N x ContinuationFrames + int remaining = maxFrameSize; + var list = new ArrayList(buffers.size()); + for (; next < buffers.size(); next++) { + var b = buffers.get(next); + var len = b.remaining(); + if (!b.hasRemaining()) continue; + if (len <= remaining) { + remaining -= len; + list.add(b); + } else { + if (next == 0) { + list.add(b.slice(b.position(), remaining)); + b.position(b.position() + remaining); + remaining = 0; + } + break; + } + } + int flags = rh.getFlags(); + if (next != buffers.size()) { + flags = flags & ~HeadersFrame.END_HEADERS; + } + if (cont > 0) { + flags = flags & ~HeadersFrame.END_STREAM; + } + HeaderFrame hf = cont == 0 + ? new HeadersFrame(rh.streamid(), flags, list) + : new ContinuationFrame(rh.streamid(), flags, list); + if (Log.headers()) { + // avoid too much chatter: log only if Log.headers() is enabled + System.err.println("TestServer writing " + hf); + } + writeFrame(hf); + cont++; + } while (next < buffers.size()); } else if (frame instanceof OutgoingPushPromise) { handlePush((OutgoingPushPromise)frame); } else @@ -912,7 +1015,7 @@ void writeLoop() { private void handlePush(OutgoingPushPromise op) throws IOException { int promisedStreamid = nextPushStreamId; PushPromiseFrame pp = new PushPromiseFrame(op.parentStream, - HeaderFrame.END_HEADERS, + op.getFlags(), promisedStreamid, encodeHeaders(op.headers), 0); @@ -920,17 +1023,34 @@ private void handlePush(OutgoingPushPromise op) throws IOException { nextPushStreamId += 2; pp.streamid(op.parentStream); writeFrame(pp); + // No need to check for END_HEADERS flag here to allow for tests to simulate bad server side + // behavior i.e Continuation Frames included with END_HEADERS flag set + for (Http2Frame cf : op.getContinuations()) + writeFrame(cf); + final InputStream ii = op.is; final BodyOutputStream oo = new BodyOutputStream( promisedStreamid, clientSettings.getParameter( - SettingsFrame.INITIAL_WINDOW_SIZE), this); + SettingsFrame.INITIAL_WINDOW_SIZE), this) { + + @Override + protected void sendEndStream() throws IOException { + if (properties.getProperty("sendTrailingHeadersAfterPushPromise", "0").equals("1")) { + conn.outputQ.put(getTrailingHeadersFrame(promisedStreamid, List.of())); + } else { + super.sendEndStream(); + } + } + }; + outStreams.put(promisedStreamid, oo); oo.goodToGo(); exec.submit(() -> { try { ResponseHeaders oh = getPushResponse(promisedStreamid); outputQ.put(oh); + ii.transferTo(oo); } catch (Throwable ex) { System.err.printf("TestServer: pushing response error: %s\n", @@ -943,6 +1063,11 @@ private void handlePush(OutgoingPushPromise op) throws IOException { } + private HeadersFrame getTrailingHeadersFrame(int promisedStreamid, List headerBlocks) { + // TODO: see if there is a safe way to encode headers without interrupting connection thread + return new HeadersFrame(promisedStreamid, (HeaderFrame.END_HEADERS | HeaderFrame.END_STREAM), headerBlocks); + } + // returns a minimal response with status 200 // that is the response to the push promise just sent private ResponseHeaders getPushResponse(int streamid) { @@ -1181,11 +1306,29 @@ synchronized void updateConnectionWindow(int amount) { // for the hashmap. static class ResponseHeaders extends Http2Frame { - HttpHeaders headers; + final HttpHeaders headers; + final BiPredicate insertionPolicy; + + final int maxFrameSize; + + public ResponseHeaders(HttpHeaders headers) { + this(headers, (n,v) -> false); + } + public ResponseHeaders(HttpHeaders headers, BiPredicate insertionPolicy) { + this(headers, insertionPolicy, Integer.MAX_VALUE); + } - ResponseHeaders(HttpHeaders headers) { + public ResponseHeaders(HttpHeaders headers, + BiPredicate insertionPolicy, + int maxFrameSize) { super(0, 0); this.headers = headers; + this.insertionPolicy = insertionPolicy; + this.maxFrameSize = maxFrameSize; + } + + public int getMaxFrameSize() { + return maxFrameSize; } } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/OutgoingPushPromise.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/OutgoingPushPromise.java index b5dcdea3551..908a901133a 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/OutgoingPushPromise.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/OutgoingPushPromise.java @@ -26,6 +26,9 @@ import java.io.InputStream; import java.net.URI; import java.net.http.HttpHeaders; +import java.util.List; + +import jdk.internal.net.http.frame.ContinuationFrame; import jdk.internal.net.http.frame.Http2Frame; // will be converted to a PushPromiseFrame in the writeLoop @@ -35,16 +38,29 @@ public class OutgoingPushPromise extends Http2Frame { final URI uri; final InputStream is; final int parentStream; // not the pushed streamid + private final List continuations; public OutgoingPushPromise(int parentStream, URI uri, HttpHeaders headers, InputStream is) { + this(parentStream, uri, headers, is, List.of()); + } + + public OutgoingPushPromise(int parentStream, + URI uri, + HttpHeaders headers, + InputStream is, + List continuations) { super(0,0); this.uri = uri; this.headers = headers; this.is = is; this.parentStream = parentStream; + this.continuations = List.copyOf(continuations); } + public List getContinuations() { + return continuations; + } } diff --git a/src/hotspot/share/metaprogramming/isSame.hpp b/test/jdk/java/net/httpclient/whitebox/SSLFlowDelegateTestDriver.java similarity index 66% rename from src/hotspot/share/metaprogramming/isSame.hpp rename to test/jdk/java/net/httpclient/whitebox/SSLFlowDelegateTestDriver.java index aaaa621d151..083a291751f 100644 --- a/src/hotspot/share/metaprogramming/isSame.hpp +++ b/test/jdk/java/net/httpclient/whitebox/SSLFlowDelegateTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -19,20 +19,15 @@ * 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 +/* + * @test + * @bug 8308144 + * @summary tests that the SSLFlowDelegate doesn't accumulate application data when the + * downReader doesn't request any + * @modules java.net.http/jdk.internal.net.http + * @run testng/othervm -Djdk.internal.httpclient.debug=true + * -Djavax.net.debug=ssl:handshake + * java.net.http/jdk.internal.net.http.SSLFlowDelegateTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLFlowDelegateTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLFlowDelegateTest.java new file mode 100644 index 00000000000..45af27a96a8 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLFlowDelegateTest.java @@ -0,0 +1,595 @@ +/* + * 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. + */ + +package jdk.internal.net.http; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import java.util.concurrent.Flow.Subscriber; +import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; + +import jdk.internal.net.http.common.Logger; +import jdk.internal.net.http.common.SSLFlowDelegate; +import jdk.internal.net.http.common.SubscriberWrapper; +import jdk.internal.net.http.common.Utils; +import org.testng.Assert; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +// jtreg test definition for this test resides in SSLFlowDelegateTestDriver.java +public class SSLFlowDelegateTest { + private static final String ALPN = "foobar"; + private static final String debugTag = SSLFlowDelegateTest.class.getSimpleName(); + private static final Random random = new Random(); + private static final byte DATA_BYTE = (byte) random.nextInt(); + + private ExecutorService executor; + private SSLContext sslContext; + private SSLParameters sslParams; + private SSLServerSocket sslServerSocket; + private SSLEngine clientEngine; + private CompletableFuture testCompletion; + + @BeforeTest + public void beforeTest() throws Exception { + this.executor = Executors.newCachedThreadPool(); + this.sslContext = new jdk.internal.net.http.SimpleSSLContext().get(); + this.testCompletion = new CompletableFuture<>(); + + final SSLParameters sp = new SSLParameters(); + sp.setApplicationProtocols(new String[]{ALPN}); + this.sslParams = sp; + + this.sslServerSocket = startServer(this.sslContext); + println(debugTag, "Server started at " + this.sslServerSocket.getInetAddress() + ":" + + this.sslServerSocket.getLocalPort()); + + this.clientEngine = createClientEngine(this.sslContext); + } + + @AfterTest + public void afterTest() throws Exception { + if (this.sslServerSocket != null) { + println(debugTag, "Closing server socket " + this.sslServerSocket); + this.sslServerSocket.close(); + } + if (this.executor != null) { + println(debugTag, "Shutting down the executor " + this.executor); + this.executor.shutdownNow(); + } + } + + private static void println(final String debugTag, final String msg) { + println(debugTag, msg, null); + } + + private static void println(final String debugTag, final String msg, final Throwable t) { + final Logger logger = Utils.getDebugLogger(() -> debugTag); + logger.log(msg); + if (t != null) { + t.printStackTrace(); + } + } + + private SSLServerSocket createSSLServerSocket( + final SSLContext ctx, final InetSocketAddress bindAddr) throws IOException { + final SSLServerSocketFactory fac = ctx.getServerSocketFactory(); + final SSLServerSocket sslServerSocket = (SSLServerSocket) fac.createServerSocket(); + sslServerSocket.setReuseAddress(false); + sslServerSocket.setSSLParameters(this.sslParams); + sslServerSocket.bind(bindAddr); + return sslServerSocket; + } + + private SSLServerSocket startServer(final SSLContext ctx) throws Exception { + final SSLServerSocket sslServerSocket = createSSLServerSocket(ctx, + new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + final Runnable serverResponsePusher = new ServerResponsePusher(sslServerSocket, + this.testCompletion); + final Thread serverThread = new Thread(serverResponsePusher, "serverResponsePusher"); + // start the thread which will accept() a socket connection and send data over it + serverThread.start(); + return sslServerSocket; + } + + private SSLEngine createClientEngine(final SSLContext ctx) { + final SSLEngine clientEngine = ctx.createSSLEngine(); + clientEngine.setSSLParameters(this.sslParams); + clientEngine.setUseClientMode(true); + return clientEngine; + } + + /** + * Constructs a {@code SSLFlowDelegate} with a {@code downReader} which only requests one + * item and then never requests any more. After that one item has been received by the + * {@code downReader}, this test feeds the + * {@linkplain SSLFlowDelegate#upstreamReader() upstreamReader} with (network SSL) data in + * such a manner that while + * {@link SSLEngine#unwrap(ByteBuffer, ByteBuffer) unwrapping} it in the internal implementation + * of {@code SSLFlowDelegate}, it will often trigger {@code BUFFER_UNDERFLOW} state. + * This test then verifies that the {@code SSLFlowDelegate} when it reaches such a state will + * not keep asking for more (network SSL) data and decrypting it to application data and + * accumulating it (since the {@code downReader} won't be using any of this accumulated data). + */ + @Test + public void testUnsolicitedBytes() throws Exception { + // initiate a connection to the server + try (final Socket socket = new Socket(sslServerSocket.getInetAddress(), + sslServerSocket.getLocalPort())) { + println(debugTag, "connected to server, local socket: " + socket); + // this future is completed when the AppResponseReceiver subscriber receives + // the sole item that is requests for (through just one subscription.request(1)) + final CompletableFuture soleExpectedAppData = new CompletableFuture<>(); + // the "testCompletion" CompletableFuture represents that future that's used + // in various places in this test. If the "testCompletion" completes before + // the "soleExpectedAppData" completes (typically due to some exception), + // then we complete the "soleExpectedAppData" too. + this.testCompletion.whenComplete((r, t) -> { + if (soleExpectedAppData.isDone()) { + return; + } + if (t == null) { + soleExpectedAppData.complete(-1L); // -1 indicates no item received + return; + } + soleExpectedAppData.completeExceptionally(t); + }); + // the "downReader" Subscriber which is passed to the constructor of SSLFlowDelegate. + // This subscriber receives the (decrypted) application data. This subscriber requests + // only one item (no restriction on how many bytes are received in this one item). + final AppResponseReceiver appResponseReceiver = new AppResponseReceiver( + this.testCompletion, soleExpectedAppData); + // the "downWriter" Subscriber which is passed to the constructor of the + // SSLFlowDelegate. + // This subscriber receives the (encrypted) network data and just writes it out to the + // connected socket's OutputStream. Makes no restrictions on how much items (and thus + // bytes) it receives and just keeps writing as and when it receives the data. + final SocketWriter networkDataWriter = new SocketWriter(socket, this.testCompletion); + // construct the SSLFlowDelegate + final SSLFlowDelegate sslFlowDelegate = new SSLFlowDelegate(clientEngine, executor, + appResponseReceiver, networkDataWriter); + // the SocketReader runs in a thread and it keeps reading data from the connected + // socket's InputStream. This data keeps coming from the ServerResponsePusher which too + // is running in a thread of its own and is writing it out over the connected socket's + // OutputStream. The SocketReader and ServerResponsePusher are convenience constructs + // which use the simple APIs (InputStream/OutputStream) provided by SSLServerSocket + // and (SSL)Socket to generate SSL network data. This generated data is then fed to + // the relevant subscribers of SSLFlowDelegate. The SocketReader and the + // ServerResponsePusher play no other role than just generating this SSL network data. + final SocketReader socketReader = new SocketReader(socket, executor, + sslFlowDelegate.upstreamReader(), this.testCompletion); + // start reading from the socket in separate thread + new Thread(socketReader, "socketReader").start(); + + // we use this publisher only to trigger the SSL handshake and the publisher itself + // doesn't publish any data i.e. there is no application client "request" data needed + // in this test + final Flow.Publisher> publisher = new ZeroDataPublisher<>(); + println(debugTag, "Subscribing the upstreamWriter() to trigger SSL handshake"); + // now connect all the pieces. + // this call to subscribe the upstreamWriter() triggers the SSL handshake (doesn't + // matter if our zero app data publisher publishes no app data; SSL handshake + // doesn't require app data). see SSLFlowDelegate$Writer.onSubscribe() where + // it triggers the SSL handshake when this subscription happens + publisher.subscribe(sslFlowDelegate.upstreamWriter()); + println(debugTag, "Waiting for handshake to complete"); + final String negotiatedALPN = sslFlowDelegate.alpn().join(); + println(debugTag, "handshake completed, with negotiated ALPN: " + negotiatedALPN); + Assert.assertEquals(negotiatedALPN, ALPN, "unexpected ALPN negotiated"); + try { + // now wait for the initial (and the only) chunk of application data to be + // received by the AppResponseReceiver + println(debugTag, "waiting for the sole expected chunk of application data to" + + " become available to " + appResponseReceiver); + final long numAppDataBytesReceived = soleExpectedAppData.join(); + println(debugTag, "Received " + numAppDataBytesReceived + " app data bytes," + + " no more app data expected"); + // at this point, we have received the only expected item in the downReader + // i.e. the AppResponseReceiver. We no longer expect the SSLFlowDelegate to be + // accumulating any decrypted application data (because the downReader hasn't + // requested any). + // We will now let the SocketReader and the ServerResponsePusher threads to keep + // generating and feeding the SSL network data to the SSLFlowDelegate subscribers, + // until they are "done" (either normally or exceptionally). Those threads decide + // when to stop generating the SSL network data. + this.testCompletion.join(); + } catch (CompletionException ce) { + // fail with a Assert.fail instead of throwing an exception, thus providing a + // better failure report + failTest(ce); + } + println(debugTag, "now checking if any unsolicited bytes accumulated"); + // SSL network data generation has completed, now check if the SSLFlowDelegate + // decrypted and accumulated any application data when it shouldn't have. + assertNoUnsolicitedBytes(sslFlowDelegate); + println(debugTag, "testing completed successfully, no unsolicited bytes accumulated"); + } + } + + private void failTest(final CompletionException ce) { + final Throwable cause = ce.getCause(); + Assert.fail(cause.getMessage() == null ? "test failed" : cause.getMessage(), cause); + } + + // uses reflection to get hold of the SSLFlowDelegate.reader.outputQ member field, + // which is a ConcurrentLinkedQueue holding the decrypted application data that + // is supposed to be sent to the downReader subscriber of the SSLFlowDelegate. + // Asserts that this outputQ has 0 bytes of data accumulated + private void assertNoUnsolicitedBytes(final SSLFlowDelegate sslFlowDelegate) throws Exception { + final Field readerField = SSLFlowDelegate.class.getDeclaredField("reader"); + readerField.setAccessible(true); + + final Field readerOutputQField = SubscriberWrapper.class.getDeclaredField("outputQ"); + readerOutputQField.setAccessible(true); + + final Object reader = readerField.get(sslFlowDelegate); + final ConcurrentLinkedQueue> outputQ = + ConcurrentLinkedQueue.class.cast(readerOutputQField.get(reader)); + long numUnsolicitated = 0; + List accumulations; + while ((accumulations = outputQ.poll()) != null) { + println(debugTag, "found some items in outputQ"); + for (final ByteBuffer buf : accumulations) { + if (!buf.hasRemaining()) { + continue; + } + try { + numUnsolicitated = Math.addExact(numUnsolicitated, buf.remaining()); + } catch (ArithmeticException ame) { + numUnsolicitated = Long.MAX_VALUE; + break; + } + } + println(debugTag, "num unsolicited bytes so far = " + numUnsolicitated); + } + Assert.assertEquals(numUnsolicitated, 0, + "SSLFlowDelegate has accumulated " + numUnsolicitated + " unsolicited bytes"); + } + + // A publisher which accepts only one subscriber and doesn't ever publish any data + private static final class ZeroDataPublisher implements Flow.Publisher { + private final AtomicBoolean hasSubscriber = new AtomicBoolean(); + + @Override + public void subscribe(final Subscriber subscriber) { + if (!hasSubscriber.compareAndSet(false, true)) { + // we allow only one subscriber + throw new IllegalStateException("Cannot subscribe more than once"); + } + subscriber.onSubscribe(new Flow.Subscription() { + @Override + public void request(long n) { + // no-op, we don't publish any data + } + + @Override + public void cancel() { + // no-op + } + }); + } + } + + // a Subscriber which subscribers for encrypted SSL network data that it will then + // write to a connected (SSL) Socket's OutputStream + private static final class SocketWriter implements Subscriber> { + private static final String debugTag = SocketWriter.class.getSimpleName(); + + private final Socket socket; + private final CompletableFuture completion; + private volatile Flow.Subscription subscription; + private final AtomicLong numBytesWritten = new AtomicLong(); + + private SocketWriter(final Socket socket, final CompletableFuture completion) { + this.socket = socket; + this.completion = completion; + } + + @Override + public void onSubscribe(final Flow.Subscription subscription) { + this.subscription = subscription; + println(debugTag, "onSubscribe invoked, requesting for data to write to socket"); + subscription.request(1); + } + + @Override + public void onNext(final List bufs) { + try { + final OutputStream os = + new BufferedOutputStream(this.socket.getOutputStream()); + + // these buffers contain encrypted SSL network data that we receive + // from the SSLFlowDelegate. We just write them out to the + // Socket's OutputStream. + for (final ByteBuffer buf : bufs) { + int len = buf.remaining(); + int written = writeToStream(os, buf); + assert len == written; + this.numBytesWritten.addAndGet(len); + assert !buf.hasRemaining() + : "buffer has " + buf.remaining() + " bytes left"; + this.subscription.request(1); // willing to write out more data when available + } + } catch (Throwable e) { + println(debugTag, "failed: " + e, e); + completion.completeExceptionally(e); + } + } + + @Override + public void onError(final Throwable throwable) { + println(debugTag, "error: " + throwable, throwable); + completion.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + println(debugTag, "onComplete(), total bytes written: " + this.numBytesWritten.get()); + } + + private int writeToStream(final OutputStream os, final ByteBuffer buf) throws IOException { + final byte[] b = buf.array(); + final int offset = buf.arrayOffset() + buf.position(); + final int n = buf.limit() - buf.position(); + os.write(b, offset, n); + buf.position(buf.limit()); + os.flush(); + return n; + } + } + + // a background task that keeps reading encrypted SSL network data from a connected + // (SSL) Socket and publishes this data to the SSLFlowDelegate's upstreamReader() subscriber. + // Very importantly, irrespective of how many bytes of data this SocketReader reads + // of the Socket's InputStream in one read() operation, it publishes this data to the + // upstreamReader() subscriber in very small chunks, so that when the upstreamReader() + // subscriber receives it and starts unwrapping that SSL network data, it often + // encounters a BUFFER_UNDERFLOW state. + private static final class SocketReader implements Runnable { + private static final String debugTag = SocketReader.class.getSimpleName(); + + // the size of data that will be published to the upstreamReader() subscriber. + // small enough; no other meaning to this value + private static final int VERY_SMALL_DATA_SIZE = 123; + + private final Socket socket; + private final SubmissionPublisher> publisher; + private final CompletableFuture completion; + + private SocketReader(final Socket socket, final Executor executor, + final Subscriber> incomingNetworkDataSubscriber, + final CompletableFuture completion) { + this.socket = socket; + this.completion = completion; + this.publisher = new SubmissionPublisher<>(executor, Flow.defaultBufferSize(), + (s, t) -> completion.completeExceptionally(t)); + this.publisher.subscribe(incomingNetworkDataSubscriber); + } + + @Override + public void run() { + try { + // reads off the SSLSocket the data from the "server" + final InputStream is = socket.getInputStream(); + long numBytesRead = 0; + long numBytesPublished = 0; + while (true) { + final byte[] buf = new byte[10240]; // this size doesn't matter + final int n = is.read(buf); + if (n == -1) { + println(debugTag, "got EOF, now closing resources; total read " + + numBytesRead + " bytes, total published " + numBytesPublished + + " bytes"); + closeAndComplete(is); + return; + } + println(debugTag, "read " + n + " bytes from socket"); + numBytesRead = Math.addExact(numBytesRead, n); + int remaining = n; + int index = 0; + while (remaining > 0) { + final int chunkSize = Math.min(remaining, VERY_SMALL_DATA_SIZE); + final byte[] chunk = Arrays.copyOfRange(buf, index, index + chunkSize); + index += chunkSize; + remaining -= chunkSize; + final int lagOrDrops = publisher.offer( + List.of(ByteBuffer.wrap(chunk)), 2, TimeUnit.SECONDS, null); + if (lagOrDrops < 0) { + println(debugTag, "dropped a chunk, re-offering"); + // dropped, we now re-attempt once more and if that too is dropped, + // we stop + final int newLagOrDrops = publisher.offer( + List.of(ByteBuffer.wrap(chunk)), 2, TimeUnit.SECONDS, null); + if (newLagOrDrops < 0) { + println(debugTag, "dropped the re-offered chunk; closing resources," + + " total bytes read: " + numBytesRead + + " total bytes published: " + numBytesPublished); + closeAndComplete(is); + return; + } + } + numBytesPublished += chunkSize; + println(debugTag, "published " + numBytesPublished + " bytes of total " + + numBytesRead + " bytes read"); + } + } + } catch (Throwable e) { + println(debugTag, "failed: " + e, e); + completion.completeExceptionally(e); + } + } + + private void closeAndComplete(final InputStream is) { + publisher.close(); + completion.complete(null); + Utils.close(is); + } + } + + // a background task which accepts one socket connection on a SSLServerSocket and keeps + // writing (application) data to the OutputStream of that socket. + private static final class ServerResponsePusher implements Runnable { + private static final String debugTag = ServerResponsePusher.class.getSimpleName(); + private final SSLServerSocket sslServerSocket; + private final CompletableFuture completion; + + private ServerResponsePusher(final SSLServerSocket sslServerSocket, + final CompletableFuture completion) { + this.sslServerSocket = sslServerSocket; + this.completion = completion; + } + + @Override + public void run() { + try { + // accept a connection + try (final Socket socket = this.sslServerSocket.accept()) { + println(debugTag, "Accepted connection from " + socket); + try (final OutputStream os = socket.getOutputStream()) { + final byte[] resp = new byte[10240]; // this size doesn't matter + Arrays.fill(resp, DATA_BYTE); + long numWritten = 0; + // reasonable number of times to generate enough network data + final int numTimes = 50; + for (int i = 0; i < numTimes; i++) { + println(debugTag, "writing " + resp.length + " bytes, " + + numWritten + " written so far"); + os.write(resp); + numWritten += resp.length; + os.flush(); + } + println(debugTag, "stopped writing, total bytes written: " + numWritten); + } + } + } catch (Throwable e) { + println(debugTag, "error: " + e, e); + this.completion.completeExceptionally(e); + } + } + } + + // the "downReader" Subscriber which is passed to the constructor of SSLFlowDelegate. + // This subscriber receives the (decrypted) application data. This subscriber requests + // only one item (no restriction on how many bytes are received in this one item). + private static final class AppResponseReceiver implements Subscriber> { + private static final String debugTag = AppResponseReceiver.class.getSimpleName(); + + private final byte[] expectedData = new byte[1024]; // no significance of the size + + private final AtomicLong numBytesReceived; + private volatile Flow.Subscription subscription; + private final CompletableFuture completion; + private final CompletableFuture soleExpectedAppData; + private boolean receivedOneItem; + + private AppResponseReceiver(final CompletableFuture completion, + final CompletableFuture soleExpectedAppData) { + this.numBytesReceived = new AtomicLong(0); + this.soleExpectedAppData = soleExpectedAppData; + this.completion = completion; + Arrays.fill(expectedData, DATA_BYTE); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + println(debugTag, "onSubscribe invoked"); + this.subscription = subscription; + subscription.request(1); // the sole item request this subscriber will make + } + + @Override + public void onNext(final List buffers) { + if (receivedOneItem) { + // don't throw an exception since that will go against the Subscriber's + // specification, instead complete the future exceptionally + completion.completeExceptionally(new AssertionError("onNext() called more than" + + " once, even though no request was made")); + return; + } + receivedOneItem = true; + // these buffers contain (decrypted) application data that the SSLFlowDelegate has + // forwarded to this subscriber + for (final ByteBuffer buf : buffers) { + final int numBytes = buf.remaining(); + // verify the content of the received application data + while (buf.hasRemaining()) { + final int size = Math.min(buf.remaining(), expectedData.length); + final byte[] actual = new byte[size]; + buf.get(actual); + // this is just convenience/performance optimization - instead of checking + // one byte at a time, we compare multiple bytes + final int index = Arrays.mismatch(expectedData, 0, size, actual, 0, size); + if (index != -1) { + final String errMsg = "Unexpected byte received: " + actual[index]; + println(debugTag, "Cancelling subscription due to error: " + errMsg); + subscription.cancel(); + completion.completeExceptionally(new AssertionError(errMsg)); + return; + } + } + numBytesReceived.addAndGet(numBytes); + } + println(debugTag, "Received " + numBytesReceived.get() + " bytes," + + " will not request any more data"); + soleExpectedAppData.complete(numBytesReceived.get()); + } + + @Override + public void onError(final Throwable throwable) { + completion.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + final long n = numBytesReceived.get(); + println(debugTag, "Completed: received " + n + " bytes"); + } + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java index ac251f9ba7d..114110344a4 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/SSLTubeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 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 @@ -85,16 +85,17 @@ private static class SSLLoopbackSubscriber implements FlowTube { ExecutorService exec, CountDownLatch allBytesReceived) throws IOException { SSLServerSocketFactory fac = ctx.getServerSocketFactory(); + InetAddress loopback = InetAddress.getLoopbackAddress(); SSLServerSocket serv = (SSLServerSocket) fac.createServerSocket(); serv.setReuseAddress(false); - serv.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + serv.bind(new InetSocketAddress(loopback, 0)); SSLParameters params = serv.getSSLParameters(); params.setApplicationProtocols(new String[]{"proto2"}); serv.setSSLParameters(params); int serverPort = serv.getLocalPort(); - clientSock = new Socket("localhost", serverPort); + clientSock = new Socket(loopback, serverPort); serverSock = (SSLSocket) serv.accept(); this.buffer = new LinkedBlockingQueue<>(); this.allBytesReceived = allBytesReceived; @@ -107,6 +108,7 @@ private static class SSLLoopbackSubscriber implements FlowTube { } public void start() { + System.out.println("Starting: server listening at: " + serverSock.getLocalSocketAddress()); thread1.start(); thread2.start(); thread3.start(); @@ -144,6 +146,7 @@ private void clientReader() { publisher.submit(List.of(bb)); } } catch (Throwable e) { + System.out.println("clientReader got exception: " + e); e.printStackTrace(); Utils.close(clientSock); } @@ -176,6 +179,7 @@ private void clientWriter() { clientSubscription.request(1); } } catch (Throwable e) { + System.out.println("clientWriter got exception: " + e); e.printStackTrace(); } } @@ -212,6 +216,7 @@ private void serverLoopback() { is.close(); os.close(); serverSock.close(); + System.out.println("serverLoopback exiting normally"); return; } os.write(bb, 0, n); @@ -219,7 +224,10 @@ private void serverLoopback() { loopCount.addAndGet(n); } } catch (Throwable e) { + System.out.println("serverLoopback got exception: " + e); e.printStackTrace(); + } finally { + System.out.println("serverLoopback exiting at count: " + loopCount.get()); } } diff --git a/test/jdk/java/nio/channels/AsyncCloseAndInterrupt.java b/test/jdk/java/nio/channels/AsyncCloseAndInterrupt.java index 98408f34bf8..18d27425067 100644 --- a/test/jdk/java/nio/channels/AsyncCloseAndInterrupt.java +++ b/test/jdk/java/nio/channels/AsyncCloseAndInterrupt.java @@ -129,6 +129,7 @@ private static void initFile() throws Exception { return; } fifoFile = new File("x.fifo"); + fifoFile.deleteOnExit(); if (fifoFile.exists()) { if (!fifoFile.delete()) throw new IOException("Cannot delete existing fifo " + fifoFile); diff --git a/test/jdk/java/nio/channels/DatagramChannel/Disconnect.java b/test/jdk/java/nio/channels/DatagramChannel/Disconnect.java index 1ba742b6552..cdc5882fefd 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/Disconnect.java +++ b/test/jdk/java/nio/channels/DatagramChannel/Disconnect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, 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 @@ -34,6 +34,7 @@ import java.nio.*; import java.nio.channels.*; import java.io.IOException; + import jdk.test.lib.net.IPSupport; public class Disconnect { @@ -42,43 +43,61 @@ public static void main(String[] args) throws IOException { // test with default protocol family try (DatagramChannel dc = DatagramChannel.open()) { - test(dc); - test(dc); + InetAddress lo = InetAddress.getLoopbackAddress(); + System.out.println("Testing with default family and " + lo); + test(dc, lo); + test(dc, lo); } if (IPSupport.hasIPv4()) { // test with IPv4 only try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) { - test(dc); - test(dc); + InetAddress lo4 = InetAddress.ofLiteral("127.0.0.1"); + System.out.println("Testing with INET family and " + lo4); + test(dc, lo4); + test(dc, lo4); } } if (IPSupport.hasIPv6()) { // test with IPv6 only try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET6)) { - test(dc); - test(dc); + InetAddress lo6 = InetAddress.ofLiteral("::1"); + System.out.println("Testing with INET6 family and " + lo6); + test(dc, lo6); + test(dc, lo6); } } } + static int getLocalPort(DatagramChannel ch) throws IOException { + return ((InetSocketAddress) ch.getLocalAddress()).getPort(); + } + /** * Connect DatagramChannel to a server, write a datagram and disconnect. Invoke * a second or subsequent time with the same DatagramChannel instance to check * that disconnect works as expected. */ - static void test(DatagramChannel dc) throws IOException { + static void test(DatagramChannel dc, InetAddress lo) throws IOException { try (DatagramChannel server = DatagramChannel.open()) { - server.bind(new InetSocketAddress(0)); + server.bind(new InetSocketAddress(lo, 0)); - InetAddress lh = InetAddress.getLocalHost(); - dc.connect(new InetSocketAddress(lh, server.socket().getLocalPort())); + SocketAddress dcbound = dc.getLocalAddress(); + dc.connect(new InetSocketAddress(lo, server.socket().getLocalPort())); + System.out.println("dc bound to " + dcbound + " and connected from " + + dc.getLocalAddress() + " to " + dc.getRemoteAddress()); dc.write(ByteBuffer.wrap("hello".getBytes())); - ByteBuffer bb = ByteBuffer.allocate(100); - server.receive(bb); + if (getLocalPort(dc) != getLocalPort(server)) { + ByteBuffer bb = ByteBuffer.allocate(100); + server.receive(bb); + } else { + // some systems may allow dc and server to bind to the same port. + // when that happen the datagram may never be received + System.out.println("Server and clients are bound to the same port: skipping receive"); + } dc.disconnect(); diff --git a/test/jdk/java/nio/channels/DatagramChannel/Loopback.java b/test/jdk/java/nio/channels/DatagramChannel/Loopback.java index a61a4d0b177..5562378b83f 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/Loopback.java +++ b/test/jdk/java/nio/channels/DatagramChannel/Loopback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, 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 @@ -118,7 +118,8 @@ static void test(ProtocolFamily family, InetAddress group, NetworkInterface ni) // send datagram to multicast group System.out.format("send %s -> %s%n", dc.getLocalAddress(), target); - ByteBuffer src = ByteBuffer.wrap("hello".getBytes("UTF-8")); + String str = "hello " + System.nanoTime(); + ByteBuffer src = ByteBuffer.wrap(str.getBytes("UTF-8")); dc.send(src, target); // receive datagram sent to multicast group @@ -142,6 +143,7 @@ static void test(ProtocolFamily family, InetAddress group, NetworkInterface ni) System.out.format("send %s -> %s%n", dc.getLocalAddress(), target); src.clear(); dc.send(src, target); + src.flip(); // test that we don't receive the datagram sent to multicast group dc.configureBlocking(false); @@ -157,10 +159,16 @@ static void test(ProtocolFamily family, InetAddress group, NetworkInterface ni) } else { sel.selectedKeys().clear(); SocketAddress sender = dc.receive(dst); + if (src.mismatch(dst) != -1) { + System.out.println("src: " + src + "not equal to dst: " + dst); + dst.clear(); + continue; + } if (sender != null) { System.out.format("received %s from %s%n", dst, sender); senderPort = ((InetSocketAddress) sender).getPort(); - assertTrue(senderPort != localPort, "Unexpected message"); + assertTrue(senderPort != localPort, + "Unexpected message: localPort=" + localPort); } } } diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java index 6f20df76deb..83854fe4a42 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -57,6 +57,7 @@ import static java.net.StandardProtocolFamily.INET; import static java.net.StandardProtocolFamily.INET6; import static java.net.StandardSocketOptions.SO_SNDBUF; +import static java.net.StandardSocketOptions.SO_RCVBUF; import static jdk.test.lib.net.IPSupport.hasIPv4; import static jdk.test.lib.net.IPSupport.hasIPv6; import static jdk.test.lib.net.IPSupport.preferIPv4Stack; @@ -65,8 +66,6 @@ import static org.testng.Assert.assertTrue; public class SendReceiveMaxSize { - private final static int IPV4_SNDBUF = 65507; - private final static int IPV6_SNDBUF = 65527; private final static Class IOE = IOException.class; private final static Random random = RandomFactory.getRandom(); @@ -91,12 +90,12 @@ public Object[][] invariants() throws IOException { .orElse((Inet4Address) InetAddress.getByName("127.0.0.1")); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open()), - IPV4_SNDBUF, + IPSupport.getMaxUDPSendBufSizeIPv4(), IPv4Addr }); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open(INET)), - IPV4_SNDBUF, + IPSupport.getMaxUDPSendBufSizeIPv4(), IPv4Addr }); } @@ -107,12 +106,12 @@ public Object[][] invariants() throws IOException { .orElse((Inet6Address) InetAddress.getByName("::1")); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open()), - IPV6_SNDBUF, + IPSupport.getMaxUDPSendBufSizeIPv6(), IPv6Addr }); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open(INET6)), - IPV6_SNDBUF, + IPSupport.getMaxUDPSendBufSizeIPv6(), IPv6Addr }); } @@ -134,6 +133,10 @@ public void testSendReceiveMaxSize(DatagramChannelSupplier supplier, int capacit throws IOException { try (var receiver = DatagramChannel.open()) { receiver.bind(new InetSocketAddress(host, 0)); + assertTrue(receiver.getOption(SO_RCVBUF) >= capacity, + receiver.getOption(SO_RCVBUF) + + " for UDP receive buffer too small to hold capacity " + + capacity); var port = receiver.socket().getLocalPort(); var addr = new InetSocketAddress(host, port); diff --git a/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java b/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java index b21d68ab1a9..d6d2f083eca 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java +++ b/test/jdk/java/nio/channels/DatagramChannel/StressNativeSignal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 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,68 +26,111 @@ * @summary Attempt to provoke error 316 on OS X in NativeSignal.signal() */ -import java.io.*; -import java.net.*; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.StandardSocketOptions; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; +import java.util.concurrent.CountDownLatch; public class StressNativeSignal { private UDPThread udpThread; private ServerSocketThread serverSocketThread; StressNativeSignal() { - try { - serverSocketThread = new ServerSocketThread(); + serverSocketThread = initServerSocketThread(); + if (serverSocketThread != null) { serverSocketThread.start(); + } - udpThread = new UDPThread(); + udpThread = initUDPThread(); + if (udpThread != null) { udpThread.start(); + } + } + + private UDPThread initUDPThread() { + UDPThread aUDPThread = null; + try { + aUDPThread = new UDPThread(); } catch (Exception z) { + System.err.println("failed to create and start a UDPThread"); z.printStackTrace(); } + return aUDPThread; } - public static void main(String[] args) throws Throwable { - StressNativeSignal test = new StressNativeSignal(); + private ServerSocketThread initServerSocketThread() { + ServerSocketThread aServerSocketThread = null; try { - Thread.sleep(3000); + aServerSocketThread = new ServerSocketThread(); + } catch (Exception z) { - z.printStackTrace(System.err); + System.err.println("failed to create and start a ServerSocketThread"); + z.printStackTrace(); } + return aServerSocketThread; + } + public static void main(String[] args) throws Throwable { + StressNativeSignal test = new StressNativeSignal(); + test.waitForTestThreadsToStart(); test.shutdown(); } public void shutdown() { - udpThread.terminate(); - try { - udpThread.join(); - } catch (Exception z) { - z.printStackTrace(System.err); + if ((udpThread != null) && udpThread.isAlive()) { + udpThread.terminate(); + try { + udpThread.join(); + } catch (Exception z) { + z.printStackTrace(System.err); + } + } else { + System.out.println("UDPThread test scenario was not run"); } - serverSocketThread.terminate(); - try { - serverSocketThread.join(); - } catch (Exception z) { - z.printStackTrace(System.err); + if ((serverSocketThread != null) && (serverSocketThread.isAlive())) { + serverSocketThread.terminate(); + try { + serverSocketThread.join(); + } catch (Exception z) { + z.printStackTrace(System.err); + } + } else { + System.out.println("ServerSocketThread test scenario was not run"); + } + } + + public void waitForTestThreadsToStart() { + if ((udpThread != null) && udpThread.isAlive()) { + udpThread.waitTestThreadStart(); + } + if ((serverSocketThread != null) && (serverSocketThread.isAlive())) { + serverSocketThread.waitTestThreadStart(); } } public class ServerSocketThread extends Thread { private volatile boolean shouldTerminate; private ServerSocket socket; + private final CountDownLatch threadStarted = new CountDownLatch(1); + + public ServerSocketThread () throws Exception { + socket = new ServerSocket(1122); + } public void run() { + try { - socket = new ServerSocket(1122); + threadStarted.countDown(); Socket client = socket.accept(); - BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); - shouldTerminate = false; - while (!shouldTerminate) { - String msg = reader.readLine(); - } + client.close(); + throw new RuntimeException("Unexpected return from accept call"); } catch (Exception z) { + System.err.println("ServerSocketThread: caught exception " + z.getClass().getName()); if (!shouldTerminate) { z.printStackTrace(System.err); } @@ -103,40 +146,61 @@ public void terminate() { // ignore } } + + public void waitTestThreadStart() { + try { + threadStarted.await(); + } catch (Exception z) { + z.printStackTrace(System.err); + // ignore + } + } } public class UDPThread extends Thread { private DatagramChannel channel; private volatile boolean shouldTerminate; + private final CountDownLatch threadStarted = new CountDownLatch(1); + + public UDPThread () throws Exception { + + channel = DatagramChannel.open(); + channel.setOption(StandardSocketOptions.SO_RCVBUF, 6553600); + channel.bind(new InetSocketAddress(19870)); + } @Override public void run() { - try { - channel = DatagramChannel.open(); - channel.setOption(StandardSocketOptions.SO_RCVBUF, 6553600); - channel.bind(new InetSocketAddress(19870)); - } catch (IOException z) { - z.printStackTrace(System.err); - } ByteBuffer buf = ByteBuffer.allocate(6553600); - shouldTerminate = false; - while (!shouldTerminate) { + threadStarted.countDown(); + do { try { buf.rewind(); channel.receive(buf); } catch (IOException z) { + System.err.println("UDPThread: caught exception " + z.getClass().getName()); if (!shouldTerminate) { z.printStackTrace(System.err); } } - } + } while (!shouldTerminate); } public void terminate() { shouldTerminate = true; try { channel.close(); + } catch (Exception z) { + System.err.println("UDPThread: caught exception " + z.getClass().getName()); + z.printStackTrace(System.err); + // ignore + } + } + + public void waitTestThreadStart() { + try { + threadStarted.await(); } catch (Exception z) { z.printStackTrace(System.err); // ignore diff --git a/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java b/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java index 496312256be..84e0bf0dd39 100644 --- a/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java +++ b/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java @@ -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 @@ -24,40 +24,49 @@ /* @test * @bug 8054029 * @requires (os.family == "linux") - * @summary Block devices should not report size=0 on Linux + * @summary FileChannel.size() should be equal to RandomAccessFile.size() and > 0 for block devs on Linux + * @library /test/lib */ import java.io.RandomAccessFile; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.channels.FileChannel; import java.nio.file.AccessDeniedException; import java.nio.file.NoSuchFileException; +import java.util.List; + import static java.nio.file.StandardOpenOption.*; +import jtreg.SkippedException; public class BlockDeviceSize { - private static final String BLK_FNAME = "/dev/sda1"; - private static final Path BLK_PATH = Paths.get(BLK_FNAME); + private static final List BLK_FNAMES = List.of("/dev/sda1", "/dev/nvme0n1", "/dev/xvda1") ; public static void main(String[] args) throws Throwable { - try (FileChannel ch = FileChannel.open(BLK_PATH, READ); - RandomAccessFile file = new RandomAccessFile(BLK_FNAME, "r")) { - - long size1 = ch.size(); - long size2 = file.length(); - if (size1 != size2) { - throw new RuntimeException("size differs when retrieved" + - " in different ways: " + size1 + " != " + size2); + for (String blkFname: BLK_FNAMES) { + Path blkPath = Path.of(blkFname); + try (FileChannel ch = FileChannel.open(blkPath, READ); + RandomAccessFile file = new RandomAccessFile(blkFname, "r")) { + + long size1 = ch.size(); + long size2 = file.length(); + if (size1 != size2) { + throw new RuntimeException("size differs when retrieved" + + " in different ways: " + size1 + " != " + size2); + } + if (size1 <= 0) { + throw new RuntimeException("size() for a block device size returns zero or a negative value"); + } + System.out.println("OK"); + + } catch (NoSuchFileException nsfe) { + System.err.println("File " + blkFname + " not found." + + " Skipping test"); + } catch (AccessDeniedException ade) { + throw new SkippedException("Access to " + blkFname + " is denied." + + " Run test as root.", ade); + } - System.out.println("OK"); - - } catch (NoSuchFileException nsfe) { - System.err.println("File " + BLK_FNAME + " not found." + - " Skipping test"); - } catch (AccessDeniedException ade) { - System.err.println("Access to " + BLK_FNAME + " is denied." + - " Run test as root."); } } } diff --git a/test/jdk/java/nio/channels/SocketChannel/OpenLeak.java b/test/jdk/java/nio/channels/SocketChannel/OpenLeak.java index 8d8673355ef..c7eed1f27a0 100644 --- a/test/jdk/java/nio/channels/SocketChannel/OpenLeak.java +++ b/test/jdk/java/nio/channels/SocketChannel/OpenLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2010, 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 @@ -25,28 +25,110 @@ * @bug 6548464 * @summary SocketChannel.open(SocketAddress) leaks file descriptor if * connection cannot be established + * @requires vm.flagless * @build OpenLeak - * @run main/othervm -Djava.security.manager=allow OpenLeak + * @run junit/othervm OpenLeak */ +import java.io.IOException; +import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.channels.SocketChannel; +import java.nio.channels.UnresolvedAddressException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertThrows; + public class OpenLeak { - public static void main(String[] args) throws Exception { - InetAddress lh = InetAddress.getLocalHost(); - InetSocketAddress isa = new InetSocketAddress(lh, 12345); + static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.ROOT); + static final boolean IS_WINDOWS_2016 = OS_NAME.contains("windows") && OS_NAME.contains("2016"); + + // On Windows Server 2016 trying to connect to port 47 consumes the + // whole connect timeout - which makes the test fail in timeout. + // We skip this part of the test on Windows Server 2016 + static final boolean TEST_WITH_RESERVED_PORT = !IS_WINDOWS_2016; + + private static final int MAX_LOOP = 250000; - System.setSecurityManager( new SecurityManager() ); - for (int i=0; i<100000; i++) { - try { - SocketChannel.open(isa); - throw new RuntimeException("This should not happen"); - } catch (SecurityException x) { } + + // Try to find a suitable port to provoke a "Connection Refused" + // error. + private static InetSocketAddress findSuitableRefusedAddress(InetSocketAddress isa) + throws IOException { + if (!TEST_WITH_RESERVED_PORT) return null; + var addr = isa.getAddress(); + try (SocketChannel sc1 = SocketChannel.open(isa)) { + // If we manage to connect, let's try to use some other + // port. + // port 51 is reserved too - there should be nothing there... + isa = new InetSocketAddress(addr, 51); + try (SocketChannel sc2 = SocketChannel.open(isa)) { + } + // OK, last attempt... + // port 61 is reserved too - there should be nothing there... + isa = new InetSocketAddress(addr, 61); + try (SocketChannel sc3 = SocketChannel.open(isa)) { + } + System.err.println("Could not find a suitable port"); + return null; + } catch (ConnectException x) { } + return isa; + } + + private static InetSocketAddress createUnresolved(InetSocketAddress isa, InetSocketAddress def) { + var sa = isa == null ? def : isa; + return InetSocketAddress.createUnresolved(sa.getHostString(), sa.getPort()); + } + + // Builds a list of test cases + static List testCases() throws Exception { + InetAddress lo = InetAddress.getLoopbackAddress(); + + // Try to find a suitable port that will cause a + // Connection Refused exception + // port 47 is reserved - there should be nothing there... + InetSocketAddress def = new InetSocketAddress(lo, 47); + InetSocketAddress isa = findSuitableRefusedAddress(def); + InetSocketAddress sa = createUnresolved(isa, def); + + final List cases = new ArrayList<>(); + cases.add(new Object[]{sa, UnresolvedAddressException.class}); + if (isa != null) { + cases.add(new Object[]{isa, ConnectException.class}); + } + return cases; + } + + @ParameterizedTest + @MethodSource("testCases") + public void test(SocketAddress sa, Class expectedException) throws Exception { + System.err.printf("%nExpecting %s for %s%n", expectedException, sa); + + int i = 0; + try { + for (i = 0; i < MAX_LOOP; i++) { + Throwable x = + assertThrows(expectedException, () -> SocketChannel.open(sa)); + if (i < 5 || i >= MAX_LOOP - 5) { + // print a message for the first five and last 5 exceptions + System.err.println(x); + } + } + } catch (Throwable t) { + System.err.println("Failed at " + i + " with " + t); + throw t; + } } } diff --git a/test/jdk/java/nio/charset/StandardCharsets/Standard.java b/test/jdk/java/nio/charset/StandardCharsets/Standard.java index 44c4e86a4f6..4db24b1986a 100644 --- a/test/jdk/java/nio/charset/StandardCharsets/Standard.java +++ b/test/jdk/java/nio/charset/StandardCharsets/Standard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, 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 @@ -23,102 +23,123 @@ /* * @test - * @bug 4884238 - * @summary Test standard charset name constants. + * @bug 4884238 8310047 + * @summary Test standard charset name constants and class qualities. * @author Mike Duigou - * @run main Standard + * @run junit Standard */ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.io.*; -import java.nio.charset.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class Standard { - private final static String standardCharsets[] = { - "US-ASCII", "ISO-8859-1", "UTF-8", - "UTF-16BE", "UTF-16LE", "UTF-16" }; + // These are the charsets StandardCharsets.java is expected to contain. + private static final String[] expectedCharsets = { + "US-ASCII", "ISO-8859-1", "UTF-8", + "UTF-16BE", "UTF-16LE", "UTF-16" + }; - public static void realMain(String[] args) { - check(StandardCharsets.US_ASCII instanceof Charset); - check(StandardCharsets.ISO_8859_1 instanceof Charset); - check(StandardCharsets.UTF_8 instanceof Charset); - check(StandardCharsets.UTF_16BE instanceof Charset); - check(StandardCharsets.UTF_16LE instanceof Charset); - check(StandardCharsets.UTF_16 instanceof Charset); + private static final Field[] standardCharsetFields = + StandardCharsets.class.getFields(); - check("US-ASCII".equals(StandardCharsets.US_ASCII.name())); - check("ISO-8859-1".equals(StandardCharsets.ISO_8859_1.name())); - check("UTF-8".equals(StandardCharsets.UTF_8.name())); - check("UTF-16BE".equals(StandardCharsets.UTF_16BE.name())); - check("UTF-16LE".equals(StandardCharsets.UTF_16LE.name())); - check("UTF-16".equals(StandardCharsets.UTF_16.name())); + /** + * Validates that the Charset constants from the data provider + * are of type Charset. + */ + @ParameterizedTest + @MethodSource("charsetProvider") + public void typeTest(Charset charset) { + // Doubly checked, as it is validated when passed as a param + assertTrue(charset instanceof Charset); + } + + /** + * Validates that calling .name() on a Charset constant is equal + * to the matching String value from the data provider. + */ + @ParameterizedTest + @MethodSource("charsetProvider") + public void nameMethodTest(Charset charset, String charString) { + assertEquals(charset.name(), charString); + } - check(Charset.forName("US-ASCII") == StandardCharsets.US_ASCII); - check(Charset.forName("ISO-8859-1") == StandardCharsets.ISO_8859_1); - check(Charset.forName("UTF-8") == StandardCharsets.UTF_8); - check(Charset.forName("UTF-16BE") == StandardCharsets.UTF_16BE); - check(Charset.forName("UTF-16LE") == StandardCharsets.UTF_16LE); - check(Charset.forName("UTF-16") == StandardCharsets.UTF_16); + /** + * Validates that calling Charset.forName() on a String is equal + * to the matching Charset constant from the data provider. + */ + @ParameterizedTest + @MethodSource("charsetProvider") + public void forNameMethodTest(Charset charset, String charString) { + assertEquals(Charset.forName(charString), charset); + } - Set charsets = new HashSet<>(); - Field standardCharsetFields[] = StandardCharsets.class.getFields(); + /** + * Validates the qualities of a StandardCharsets field are as expected: + * The field is final, static, public, and one can access + * the underlying value of the field. + */ + @ParameterizedTest + @MethodSource("charsetFields") + public void charsetModifiersTest(Field charsetField) throws IllegalAccessException { + // Check modifiers + assertEquals(StandardCharsets.class, charsetField.getDeclaringClass()); + assertTrue(Modifier.isFinal(charsetField.getModifiers())); + assertTrue(Modifier.isStatic(charsetField.getModifiers())); + assertTrue(Modifier.isPublic(charsetField.getModifiers())); + // Check that the value can be accessed, and it is a Charset + Object valueOfField = charsetField.get(null); + assertTrue(valueOfField instanceof Charset); + } - for(Field charsetField : standardCharsetFields) { - check(StandardCharsets.class == charsetField.getDeclaringClass()); - check(Modifier.isFinal(charsetField.getModifiers())); - check(Modifier.isStatic(charsetField.getModifiers())); - check(Modifier.isPublic(charsetField.getModifiers())); - Object value; + /** + * Validates that the Charsets contained in StandardCharsets are equal + * to the expected Charsets list defined in the test. This test should fail if + * either the actual or expected (standard) Charsets are modified, and + * the others are not. + */ + @Test + public void correctCharsetsTest() { + // Grab the value from each Standard Charset field + List actualCharsets = charsetFields().map(field -> { try { - value = charsetField.get(null); - } catch(IllegalAccessException failure) { - unexpected(failure); - continue; + return ((Charset) field.get(null)).name(); + } catch (IllegalAccessException e) { + throw new RuntimeException("Can not test correctCharsetsTest() due to %s", e); } - check(value instanceof Charset); - charsets.add(((Charset)value).name()); - } - - check(charsets.containsAll(Arrays.asList(standardCharsets))); - charsets.removeAll(Arrays.asList(standardCharsets)); - check(charsets.isEmpty()); + }).toList(); + assertEquals(actualCharsets, Arrays.asList(expectedCharsets)); } - //--------------------- Infrastructure --------------------------- - static volatile int passed = 0, failed = 0; - static void pass() { passed++; } - static void fail() { failed++; Thread.dumpStack(); } - static void fail(String msg) { System.out.println(msg); fail(); } - static void unexpected(Throwable t) { failed++; t.printStackTrace(); } - static void check(boolean cond) { if (cond) pass(); else fail(); } - static void equal(Object x, Object y) { - if (x == null ? y == null : x.equals(y)) pass(); - else {System.out.println(x + " not equal to " + y); fail();}} - static void equal2(Object x, Object y) {equal(x, y); equal(y, x);} - public static void main(String[] args) throws Throwable { - try { realMain(args); } catch (Throwable t) { unexpected(t); } - - System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); - if (failed > 0) throw new Exception("Some tests failed"); + /** + * Provides the constant Charset and associated String value of + * the standard charsets. + */ + private static Stream charsetProvider() { + return Stream.of( + Arguments.of(StandardCharsets.US_ASCII, "US-ASCII"), + Arguments.of(StandardCharsets.ISO_8859_1, "ISO-8859-1"), + Arguments.of(StandardCharsets.UTF_8, "UTF-8"), + Arguments.of(StandardCharsets.UTF_16BE, "UTF-16BE"), + Arguments.of(StandardCharsets.UTF_16LE, "UTF-16LE"), + Arguments.of(StandardCharsets.UTF_16, "UTF-16") + ); } - static byte[] serializedForm(Object obj) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - new ObjectOutputStream(baos).writeObject(obj); - return baos.toByteArray(); - } catch (IOException e) { throw new Error(e); }} - static Object readObject(byte[] bytes) - throws IOException, ClassNotFoundException { - InputStream is = new ByteArrayInputStream(bytes); - return new ObjectInputStream(is).readObject();} - @SuppressWarnings("unchecked") - static T serialClone(T obj) { - try { return (T) readObject(serializedForm(obj)); } - catch (Exception e) { throw new Error(e); }} + private static Stream charsetFields() { + return Arrays.stream(standardCharsetFields); + } } diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java index c1df9abb8b6..8d25345f9ee 100644 --- a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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 @@ -21,11 +21,23 @@ * questions. */ -/* @test - * @bug 8011536 8316304 +/* @test id=tmp + * @bug 8011536 8151430 8316304 8334339 * @summary Basic test for creationTime attribute on platforms/file systems - * that support it. - * @library ../.. /test/lib + * that support it, tests using /tmp directory. + * @library ../.. /test/lib + * @build jdk.test.lib.Platform + * @comment We see this failing with "UnsatisfiedLinkError: Native Library ...libCreationTimeHelper.so already loaded in another classloader". Thus run as othervm + * @run main/othervm CreationTime + */ + +/* @test id=cwd + * @summary Basic test for creationTime attribute on platforms/file systems + * that support it, tests using the test scratch directory, the test + * scratch directory maybe at diff disk partition to /tmp on linux. + * @library ../.. /test/lib + * @build jdk.test.lib.Platform + * @run main/native CreationTime . */ import java.nio.file.Path; @@ -35,6 +47,7 @@ import java.io.IOException; import jdk.test.lib.Platform; +import jtreg.SkippedException; public class CreationTime { @@ -65,8 +78,14 @@ static void test(Path top) throws IOException { FileTime creationTime = creationTime(file); Instant now = Instant.now(); if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) { - err.println("File creation time reported as: " + creationTime); - throw new RuntimeException("Expected to be close to: " + now); + System.out.println("creationTime.toMillis() == " + creationTime.toMillis()); + // If the file system doesn't support birth time, then skip this test + if (creationTime.toMillis() == 0) { + throw new SkippedException("birth time not support for: " + file); + } else { + err.println("File creation time reported as: " + creationTime); + throw new RuntimeException("Expected to be close to: " + now); + } } /** @@ -89,7 +108,7 @@ static void test(Path top) throws IOException { // Creation time updates are not supported on Linux supportsCreationTimeWrite = false; } - System.out.println("supportsCreationTimeRead == " + supportsCreationTimeRead); + System.out.println(top + " supportsCreationTimeRead == " + supportsCreationTimeRead); /** * If the creation-time attribute is supported then change the file's @@ -121,7 +140,12 @@ static void test(Path top) throws IOException { public static void main(String[] args) throws IOException { // create temporary directory to run tests - Path dir = TestUtil.createTemporaryDirectory(); + Path dir; + if (args.length == 0) { + dir = TestUtil.createTemporaryDirectory(); + } else { + dir = TestUtil.createTemporaryDirectory(args[0]); + } try { test(dir); } finally { diff --git a/test/jdk/java/rmi/reliability/benchmark/bench/Makefile b/test/jdk/java/rmi/reliability/benchmark/bench/Makefile index f0600d4df30..805d6216373 100644 --- a/test/jdk/java/rmi/reliability/benchmark/bench/Makefile +++ b/test/jdk/java/rmi/reliability/benchmark/bench/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 1999, 2000, 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 @@ -47,4 +47,3 @@ all: .classes clean: rm -f *.class .classes - diff --git a/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile b/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile index bb44d94f154..c142b3a2d05 100644 --- a/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile +++ b/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile @@ -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 @@ -72,4 +72,3 @@ altroot.clean: clean: altroot.clean rm -f *.class .classes - diff --git a/test/jdk/java/security/Provider/CaseSensitiveServices.java b/test/jdk/java/security/Provider/CaseSensitiveServices.java index 2a6676b4f58..a5b39bde3af 100644 --- a/test/jdk/java/security/Provider/CaseSensitiveServices.java +++ b/test/jdk/java/security/Provider/CaseSensitiveServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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,7 +23,7 @@ /* * @test - * @bug 5097015 8130181 + * @bug 5097015 8130181 8279222 8292739 * @summary make sure we correctly treat Provider string entries as case insensitive * @author Andreas Sterbenz */ @@ -36,29 +36,50 @@ public class CaseSensitiveServices extends Provider { super("Foo", "1.0", null); put("MessageDigest.Foo", "com.Foo"); put("mESSAGEdIGEST.fOO xYz", "aBc"); - put("ALg.aliaS.MESSAGEdigest.Fu", "FoO"); + // first assign the DEF alias to algorithm Foo + put("ALg.aliaS.MESSAGEdigest.DEF", "FoO"); put("messageDigest.Bar", "com.Bar"); put("MESSAGEDIGEST.BAZ", "com.Baz"); + // reassign the DEF alias to algorithm Bar + put("ALg.aliaS.MESSAGEdigest.DEF", "Bar"); + // invalid entry since it misses the corresponding impl class info + // e.g. put("MessageDigest.Invalid", "implClass"); + put("MessageDigest.Invalid xYz", "aBc"); } public static void main(String[] args) throws Exception { Provider p = new CaseSensitiveServices(); - System.out.println(p.getServices()); + + System.out.println("Services: " + p.getServices()); + if (p.getServices().size() != 3) { throw new Exception("services.size() should be 3"); } + + if (p.getService("MessageDigest", "Invalid") != null) { + throw new Exception("Invalid service returned"); + } Service s = testService(p, "MessageDigest", "fOO"); String val = s.getAttribute("Xyz"); if ("aBc".equals(val) == false) { throw new Exception("Wrong value: " + val); } - testService(p, "MessageDigest", "fU"); + if (s.toString().indexOf("DEF") != -1) { + throw new Exception("Old alias DEF should be removed"); + } + + // test Service alias DEF and its associated impl is Bar + s = testService(p, "MessageDigest", "DeF"); + if (s.getAttribute("Xyz") != null) { + throw new Exception("DEF mapped to the wrong impl"); + } testService(p, "MessageDigest", "BAR"); testService(p, "MessageDigest", "baz"); System.out.println("OK"); } - private static Service testService(Provider p, String type, String alg) throws Exception { + private static Service testService(Provider p, String type, String alg) + throws Exception { System.out.println("Getting " + type + "." + alg + "..."); Service s = p.getService(type, alg); System.out.println(s); diff --git a/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java new file mode 100644 index 00000000000..e12dabb6383 --- /dev/null +++ b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java @@ -0,0 +1,97 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8331446 + * @summary Enforce the MAX_ARGUMENT_INDEX(10,000) implementation limit for the + * ArgumentIndex element in the MessageFormat pattern syntax. This + * should be checked during construction/applyPattern/readObject and should effectively + * prevent parse/format from being invoked with values over the limit. + * @run junit MaxArgumentIndexTest + */ + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class MaxArgumentIndexTest { + + // A MessageFormat pattern that contains an ArgumentIndex value + // which violates this implementation's limit: MAX_ARGUMENT_INDEX(10,000) + // As this check is exclusive, 10,000 will violate the limit + private static final String VIOLATES_MAX_ARGUMENT_INDEX = "{10000}"; + + // Check String constructor enforces the limit + @Test + public void constructorTest() { + assertThrows(IllegalArgumentException.class, + () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX)); + } + + // Check String, Locale constructor enforces the limit + @ParameterizedTest + @MethodSource + public void constructorWithLocaleTest(Locale locale) { + assertThrows(IllegalArgumentException.class, + () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX, locale)); + } + + // Provide some basic common locale values + private static Stream constructorWithLocaleTest() { + return Stream.of(null, Locale.US, Locale.ROOT); + } + + // Edge case: Test a locale dependent subformat (with null locale) with a + // violating ArgumentIndex. In this instance, the violating ArgumentIndex + // will be caught and IAE thrown instead of the NPE + @Test + public void localeDependentSubFormatTest() { + assertThrows(IllegalArgumentException.class, + () -> new MessageFormat("{10000,number,short}", null)); + // For reference + assertThrows(NullPointerException.class, + () -> new MessageFormat("{999,number,short}", null)); + } + + // Check that the static format method enforces the limit + @Test + public void staticFormatTest() { + assertThrows(IllegalArgumentException.class, + () -> MessageFormat.format(VIOLATES_MAX_ARGUMENT_INDEX, new Object[]{1})); + } + + // Check that applyPattern(String) enforces the limit + @Test + public void applyPatternTest() { + MessageFormat mf = new MessageFormat(""); + assertThrows(IllegalArgumentException.class, + () -> mf.applyPattern(VIOLATES_MAX_ARGUMENT_INDEX)); + } +} diff --git a/test/jdk/java/text/Format/MessageFormat/SerializationTest.java b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java new file mode 100644 index 00000000000..9191c5caef3 --- /dev/null +++ b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8331446 + * @summary Check correctness of deserialization + * @run junit SerializationTest + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SerializationTest { + + // Ensure basic correctness of serialization round trip + @ParameterizedTest + @MethodSource + public void serializationRoundTrip(MessageFormat expectedMf) + throws IOException, ClassNotFoundException { + byte[] bytes = ser(expectedMf); + MessageFormat actualMf = (MessageFormat) deSer(bytes); + assertEquals(expectedMf, actualMf); + } + + // Various valid MessageFormats + private static Stream serializationRoundTrip() { + return Stream.of( + // basic pattern + new MessageFormat("{0} foo"), + // Multiple arguments + new MessageFormat("{0} {1} foo"), + // duplicate arguments + new MessageFormat("{0} {0} {1} foo"), + // Non-ascending arguments + new MessageFormat("{1} {0} foo"), + // With locale + new MessageFormat("{1} {0} foo", Locale.UK), + // With null locale. (NPE not thrown, if no format defined) + new MessageFormat("{1} {0} foo", null), + // With formats + new MessageFormat("{0,number,short} {0} {1,date,long} foo") + ); + } + + // Utility method to serialize + private static byte[] ser(Object obj) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new + ByteArrayOutputStream(); + ObjectOutputStream oos = new + ObjectOutputStream(byteArrayOutputStream); + oos.writeObject(obj); + return byteArrayOutputStream.toByteArray(); + } + + // Utility method to deserialize + private static Object deSer(byte[] bytes) throws + IOException, ClassNotFoundException { + ByteArrayInputStream byteArrayInputStream = new + ByteArrayInputStream(bytes); + ObjectInputStream ois = new + ObjectInputStream(byteArrayInputStream); + return ois.readObject(); + } +} diff --git a/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java b/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java index 2af23a4c82d..66fc44e368f 100644 --- a/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java +++ b/test/jdk/java/text/Format/NumberFormat/BigDecimalCompatibilityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, 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 @@ -24,58 +24,85 @@ /* * @test * @bug 4018937 - * @summary Confirm that DecimalFormat.parse() parses BigDecimal and BigInteger as expected. + * @summary Confirm that DecimalFormat.parse() parses BigDecimal and BigInteger + * string values as expected. Specifically, ensure a ParseException is + * not thrown as well as the parsed value being numerically correct. + * Tests large String values with combinations of multipliers and exponents. + * @run junit BigDecimalCompatibilityTest */ -import java.math.*; -import java.text.*; -import java.util.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Locale; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; public class BigDecimalCompatibilityTest { - static boolean err = false; + private static DecimalFormat df = new DecimalFormat(); + // Save JVM default Locale + private static final Locale savedLocale = Locale.getDefault(); - static final String[] input_data = { - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" - }; - static final String[] exponents = { - "E-100", "E100", "E-900", "E900", "" - }; - static final int[] multipliers = { - -1, 1, -100, 100, -9999, 9999 - }; + // ---- Used for the test data (start) ---- - public static void main(String[] args) throws Exception { - Locale loc = Locale.getDefault(); - Locale.setDefault(Locale.US); + // Both ArrayList composed of Arguments(String longString, int multiplier) + private static final ArrayList bigIntegers = new ArrayList(); + private static final ArrayList bigDecimals = new ArrayList(); - testBigDecimal(); - testBigInteger(); + // Long string data to generate combinations of test values + private static final String[] inputData = { + "0".repeat(400), + "1234567890".repeat(40)}; - Locale.setDefault(loc); + // Variety of exponents to test parse() against + private static final String[] exponents = { + "E-100", "E100", "E-900", "E900", "" + }; - if (err) { - throw new RuntimeException("Error: Unexpected value"); - } - } + // Variety of multipliers that DecimalFormat can apply + private static final int[] multipliers = { + -1, 1, -100, 100, -9999, 9999 + }; + // ---- Used for the test data (end) ---- - static private void testBigDecimal() { - DecimalFormat df = new DecimalFormat(); - df.setParseBigDecimal(true); - df.setMaximumFractionDigits(Integer.MAX_VALUE); + // Set JVM default Locale to US and populate the test arrayLists + @BeforeAll + static void initAll() { + Locale.setDefault(Locale.US); + buildTestData(); + } - for (int i = 0; i < input_data.length; i++) { - for (int j = 0; j < input_data.length; j++) { - for (int k = 0; k < input_data.length; k++) { - for (int l = 0; l < input_data.length; l++) { - for (int m = 0; m < exponents.length; m++) { - String s = input_data[i] + input_data[j] + '.' + - input_data[k] + input_data[l] + - exponents[m]; - for (int n = 0; n < multipliers.length; n++) { - test(df, s, multipliers[n]); - test(df, '-'+s, multipliers[n]); + /* + * Uses inputData and exponents to build long string + * decimal and integer values and populate bigDecimals and bigIntegers + * accordingly. Attaches a multiplier value as well to the test data. + */ + private static void buildTestData() { + for (String longString1 : inputData) { + for (String longString2 : inputData) { + String bigInteger = longString1 + longString2; + for (int multiplier : multipliers) { + bigIntegers.add(Arguments.of(bigInteger, multiplier)); + bigIntegers.add(Arguments.of('-' + bigInteger, multiplier)); + } + for (String longString3 : inputData) { + for (String longString4 : inputData) { + for (String exponent : exponents) { + String bigDecimal = longString1 + longString2 + '.' + + longString3 + longString4 + exponent; + for (int multiplier : multipliers) { + bigDecimals.add(Arguments.of(bigDecimal, multiplier)); + bigDecimals.add(Arguments.of('-' + bigDecimal, multiplier)); } } } @@ -84,51 +111,70 @@ static private void testBigDecimal() { } } - static private void testBigInteger() { - DecimalFormat df = new DecimalFormat(); - df.setParseBigDecimal(true); - df.setMaximumFractionDigits(Integer.MAX_VALUE); + // Restore JVM default Locale + @AfterAll + static void tearDownAll() { + Locale.setDefault(savedLocale); + } - for (int i = 0; i < input_data.length; i++) { - for (int j = 0; j < input_data.length; j++) { - String s = input_data[i] + input_data[j]; - for (int k = 0; k < multipliers.length; k++) { - test(df, s, multipliers[k]); - test(df, '-'+s, multipliers[k]); - } - } - } + // Tests strings with length 1600+. See test() for specific details. + @ParameterizedTest + @MethodSource("bigDecimalProvider") + public void bigDecimalParseTest(String longString, int multiplier) { + test(longString, multiplier); + } + + // Returns 960 arrangements of bigDecimal string values and multipliers + // In the form of (String, int). + private static Stream bigDecimalProvider() { + return bigDecimals.stream(); + } + + // Tests strings with length 800+. See test() for specific details. + @ParameterizedTest + @MethodSource("bigIntegerProvider") + public void bigIntegerParseTest(String longString, int multiplier) { + test(longString, multiplier); + } + + // Returns 48 arrangements of bigInteger string values and multipliers + // In the form of (String, int). + private static Stream bigIntegerProvider() { + return bigIntegers.stream(); } - static void test(DecimalFormat df, String s, int multiplier) { + /* + * Tests that parsing a large BigDecimal/BigInteger string value + * will not throw a ParseException with setParseBigDecimal as true. + * Parses with a variety of multiplier values. Then ensures that the parsed + * value is the expected number. + */ + private static void test(String longString, int multiplier) { + // Reset DecimalFormat for a clean test + df = new DecimalFormat(); + df.setParseBigDecimal(true); + // wide enough to support the long string test data + df.setMaximumFractionDigits(Integer.MAX_VALUE); df.setMultiplier(multiplier); - Number num = null; - try { - num = df.parse(s); - } - catch (ParseException e) { - err = true; - System.err.println("Failed: Exception occurred: " + e.getMessage()); - return; - } + // Check parse and returned value. This was originally intended to ensure + // a ParseException is not thrown + Number parsedValue = assertDoesNotThrow(()-> df.parse(longString), + "Should not throw an Exception"); + BigDecimal expectedValue = getExpected(longString, multiplier); + assertEquals(expectedValue, parsedValue, "With multiplier: " + multiplier); + } - BigDecimal bd = new BigDecimal(s); + // Utility to get a numerically correct value of a long string. + // Dependent on BigDecimal implementation + private static BigDecimal getExpected(String longString, int multiplier) { + BigDecimal expected = new BigDecimal(longString); try { - bd = bd.divide(new BigDecimal(multiplier)); + expected = expected.divide(new BigDecimal(multiplier)); } catch (ArithmeticException e) { - bd = bd.divide(new BigDecimal(multiplier), RoundingMode.HALF_EVEN); - } - check(num, bd, multiplier); - } - - static void check(Number got, BigDecimal expected, int multiplier) { - if (!got.equals(expected)) { - err = true; - System.err.println("Failed: got:" + got + - ", expected: " + expected + - ", multiplier=" + multiplier); + expected = expected.divide(new BigDecimal(multiplier), RoundingMode.HALF_EVEN); } + return expected; } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4208135.java b/test/jdk/java/text/Format/NumberFormat/Bug4208135.java index 1d641226ab7..301b90c89f3 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4208135.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4208135.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, 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 @@ -23,105 +23,164 @@ /* * @test - * @summary Confirm that the decimal separator is shown when explicitly requested. + * @summary Confirm that the decimal separator is shown when explicitly requested + * (or not shown if not requested). Tests against double, long, BigDecimal, + * and BigInteger with a combination of different patterns. * @bug 4208135 + * @run junit Bug4208135 */ -import java.math.*; -import java.text.*; -import java.util.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.util.Locale; +import java.util.stream.Stream; -public class Bug4208135 { +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; - static DecimalFormat df; +import static org.junit.jupiter.api.Assertions.assertEquals; - static boolean err = false; +public class Bug4208135 { - static public void main(String[] args){ + private static DecimalFormat df; + // Save JVM default Locale + private static final Locale savedLocale = Locale.getDefault(); - Locale defaultLoc = Locale.getDefault(); + // Set JVM default locale to US + @BeforeAll + static void init() { Locale.setDefault(Locale.US); + } - df = new DecimalFormat(); + // Restore JVM default locale + @AfterAll + static void tearDown() { + Locale.setDefault(savedLocale); + } + + // Confirm that decimal separator shown when formatting a number + @ParameterizedTest + @MethodSource("fractionalDigitsWithSeparatorProvider") + public void fractionalDigitsWithSeparatorTest(Number num, String expected) { + df = getDF("0.#E0", true); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.#E0", true)); + } + + // Combination of numbers and a fractional exponent pattern with a separator + private static Stream fractionalDigitsWithSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0.E0"), + Arguments.of(10.0, "1.E1"), + Arguments.of(1000.0, "1.E3"), + Arguments.of(0L, "0.E0"), + Arguments.of(10L, "1.E1"), + Arguments.of(1000L, "1.E3"), + Arguments.of(new BigDecimal("0.0"), "0.E0"), + Arguments.of(new BigDecimal("10.0"), "1.E1"), + Arguments.of(new BigDecimal("1000.0"), "1.E3"), + Arguments.of(new BigInteger("00"), "0.E0"), + Arguments.of(new BigInteger("10"), "1.E1"), + Arguments.of(new BigInteger("1000"), "1.E3") + ); + } + + // Confirm that decimal separator not shown when formatting a number + @ParameterizedTest + @MethodSource("fractionalDigitsNoSeparatorProvider") + public void fractionalDigitsNoSeparatorTest(Number num, String expected) { + df = getDF("0.#E0", false); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.#E0", false)); + } - df.applyPattern("0.#E0"); - - df.setDecimalSeparatorAlwaysShown(true); - checkFormat(0.0, "0.E0"); - checkFormat(10.0, "1.E1"); - checkFormat(1000.0, "1.E3"); - checkFormat(0L, "0.E0"); - checkFormat(10L, "1.E1"); - checkFormat(1000L, "1.E3"); - checkFormat(new BigDecimal("0.0"), "0.E0"); - checkFormat(new BigDecimal("10.0"), "1.E1"); - checkFormat(new BigDecimal("1000.0"), "1.E3"); - checkFormat(new BigInteger("00"), "0.E0"); - checkFormat(new BigInteger("10"), "1.E1"); - checkFormat(new BigInteger("1000"), "1.E3"); - - df.setDecimalSeparatorAlwaysShown(false); - checkFormat(0.0, "0E0"); - checkFormat(10.0, "1E1"); - checkFormat(1000.0, "1E3"); - checkFormat(0L, "0E0"); - checkFormat(10L, "1E1"); - checkFormat(1000L, "1E3"); - checkFormat(new BigDecimal("0.0"), "0E0"); - checkFormat(new BigDecimal("10.0"), "1E1"); - checkFormat(new BigDecimal("1000.0"), "1E3"); - checkFormat(new BigInteger("0"), "0E0"); - checkFormat(new BigInteger("10"), "1E1"); - checkFormat(new BigInteger("1000"), "1E3"); - - df.applyPattern("0.###"); - - df.setDecimalSeparatorAlwaysShown(true); - checkFormat(0.0, "0."); - checkFormat(10.0, "10."); - checkFormat(1000.0, "1000."); - checkFormat(0L, "0."); - checkFormat(10L, "10."); - checkFormat(1000L, "1000."); - checkFormat(new BigDecimal("0.0"), "0."); - checkFormat(new BigDecimal("10.0"), "10."); - checkFormat(new BigDecimal("1000.0"), "1000."); - checkFormat(new BigInteger("0"), "0."); - checkFormat(new BigInteger("10"), "10."); - checkFormat(new BigInteger("1000"), "1000."); - - df.setDecimalSeparatorAlwaysShown(false); - checkFormat(0.0, "0"); - checkFormat(10.0, "10"); - checkFormat(1000.0, "1000"); - checkFormat(0L, "0"); - checkFormat(10L, "10"); - checkFormat(1000L, "1000"); - checkFormat(new BigDecimal("0.0"), "0"); - checkFormat(new BigDecimal("10.0"), "10"); - checkFormat(new BigDecimal("1000.0"), "1000"); - checkFormat(new BigInteger("0"), "0"); - checkFormat(new BigInteger("10"), "10"); - checkFormat(new BigInteger("1000"), "1000"); - - Locale.setDefault(defaultLoc); - - if (err) { - throw new RuntimeException("Wrong format/parse with DecimalFormat"); - } + // Combination of numbers and a fractional exponent pattern with no separator + private static Stream fractionalDigitsNoSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0E0"), + Arguments.of(10.0, "1E1"), + Arguments.of(1000.0, "1E3"), + Arguments.of(0L, "0E0"), + Arguments.of(10L, "1E1"), + Arguments.of(1000L, "1E3"), + Arguments.of(new BigDecimal("0.0"), "0E0"), + Arguments.of(new BigDecimal("10.0"), "1E1"), + Arguments.of(new BigDecimal("1000.0"), "1E3"), + Arguments.of(new BigInteger("00"), "0E0"), + Arguments.of(new BigInteger("10"), "1E1"), + Arguments.of(new BigInteger("1000"), "1E3") + ); + } + + // Confirm that decimal separator shown when formatting a number + @ParameterizedTest + @MethodSource("noFractionalDigitsWithSeparatorProvider") + public void noFractionalDigitsWithSeparatorTest(Number num, String expected) { + df = getDF("0.###", true); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.###", true)); + } + + // Combination of numbers and a non-fractional exponent pattern with a separator + private static Stream noFractionalDigitsWithSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0."), + Arguments.of(10.0, "10."), + Arguments.of(1000.0, "1000."), + Arguments.of(0L, "0."), + Arguments.of(10L, "10."), + Arguments.of(1000L, "1000."), + Arguments.of(new BigDecimal("0.0"), "0."), + Arguments.of(new BigDecimal("10.0"), "10."), + Arguments.of(new BigDecimal("1000.0"), "1000."), + Arguments.of(new BigInteger("00"), "0."), + Arguments.of(new BigInteger("10"), "10."), + Arguments.of(new BigInteger("1000"), "1000.") + ); + } + + // Confirm that decimal separator not shown when formatting a number + @ParameterizedTest + @MethodSource("noFractionalDigitsNoSeparatorProvider") + public void noFractionalDigitsNoSeparatorTest(Number num, String expected) { + df = getDF("0.###", false); + String actualFormatted = df.format(num); + assertEquals(expected, actualFormatted, getErrMsg("0.###", false)); + } + + // Combination of numbers and a non-fractional exponent pattern with no separator + private static Stream noFractionalDigitsNoSeparatorProvider() { + return Stream.of( + Arguments.of(0.0, "0"), + Arguments.of(10.0, "10"), + Arguments.of(1000.0, "1000"), + Arguments.of(0L, "0"), + Arguments.of(10L, "10"), + Arguments.of(1000L, "1000"), + Arguments.of(new BigDecimal("0.0"), "0"), + Arguments.of(new BigDecimal("10.0"), "10"), + Arguments.of(new BigDecimal("1000.0"), "1000"), + Arguments.of(new BigInteger("00"), "0"), + Arguments.of(new BigInteger("10"), "10"), + Arguments.of(new BigInteger("1000"), "1000") + ); + } + + // Creates clean DF and sets the pattern and separatorShown value + private static DecimalFormat getDF(String pattern, boolean separatorShown) { + df = new DecimalFormat(); + df.applyPattern(pattern); + df.setDecimalSeparatorAlwaysShown(separatorShown); + return df; } - static void checkFormat(Number num, String expected) { - String got = df.format(num); - if (!got.equals(expected)) { - err = true; - System.err.println(" DecimalFormat format(" + - num.getClass().getName() + - ") error:" + - "\n\tnumber: " + num + - "\n\tSeparatorShown? : " + df.isDecimalSeparatorAlwaysShown() + - "\n\tgot: " + got + - "\n\texpected: " + expected); - } + // Utility to get a helpful error message when values are not as expected + private static String getErrMsg(String pattern, boolean separatorShown) { + return String.format("Fails with pattern= %s, with separatorShown = %s", + pattern, separatorShown); } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4838107.java b/test/jdk/java/text/Format/NumberFormat/Bug4838107.java index 72cad141174..8d6d2c25a62 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4838107.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4838107.java @@ -24,225 +24,260 @@ /* * @test * @bug 4838107 8008577 - * @summary Confirm that DecimalFormat can format a number with negative exponent number correctly. + * @summary Confirm that DecimalFormat can format a number with a negative + * exponent number correctly. Tests also involve using a DecimalFormat + * with a custom pattern or a custom minus sign. * @library /java/text/testlib - * @run main/othervm -Djava.locale.providers=COMPAT,SPI Bug4838107 + * @run junit/othervm -Djava.locale.providers=COMPAT,SPI Bug4838107 */ -import java.math.*; -import java.util.*; -import java.text.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; +import java.util.stream.Stream; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * This bug is about exponential formatting. But I added test cases for: + * - Double and BigDecimal numbers which don't have exponent parts. + * - Long and BigInteger numbers which don't support exponential + * notation. + * because there are few test cases for suffix and prefix. + * And also, I added test cases to guarantee further formatting and + * parsing using the same DecimalFormat instance will not change the + * Number's value anymore. + */ public class Bug4838107 extends IntlTest { - static DecimalFormat df; - static DecimalFormatSymbols dfs; - static boolean err = false; + // Save JVM default Locale + private static final Locale savedLocale = Locale.getDefault(); - static public void main(String[] args) { - Locale defaultLoc = Locale.getDefault(); + // Set JVM default Locale to US + @BeforeAll + static void init() { Locale.setDefault(Locale.US); - - /** - * This bug is about exponential formatting. But I added test cases for: - * - Double and BigDecimal numbers which don't have exponent parts. - * - Long and BigInteger numbers which don't support exponential - * notation. - * because there are few test cases for suffix and prefix. - * And also, I added test cases to guarantee further formatting and - * parsing using the same DecimalFormat instance will not change the - * Number's value anymore. - */ - - test_double(); - test_long(); - test_BigDecimal(); - test_BigInteger(); - - Locale.setDefault(defaultLoc); - - if (err) { - throw new RuntimeException("Wrong format with DecimalFormat"); - } } - static void test_double() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); - - /* Test with default pattern */ - test(1234D, "1,234"); - test(0.1234, "0.123"); // rounded - test(-1234D, "-1,234"); - test(-0.1234, "-0.123"); // rounded - - test(Double.POSITIVE_INFINITY, "\u221e"); - test(Double.NEGATIVE_INFINITY, "-\u221e"); - test(Double.NaN, "\ufffd"); // without prefix and suffix - test(0.0, "0"); - test(-0.0, "-0"); // with the minus sign - - /* Specify a pattern and the minus sign. */ - prepareFormatter("

      #.###E00", 'm'); - test(1234D, "

      1.234E03"); - test(0.1234, "

      1.234Em01"); - test(-1234D, "m

      1.234E03"); - test(-0.1234, "m

      1.234Em01"); - - prepareFormatter("

      #.###E00;#.###E00", 'm'); - test(1234D, "

      1.234E03"); - test(0.1234, "

      1.234Em01"); - test(-1234D, "1.234E03"); - test(-0.1234, "1.234Em01"); - - prepareFormatter("#.###E00;

      #.###E00", 'm'); - test(1234D, "1.234E03"); - test(0.1234, "1.234Em01"); - test(-1234D, "

      1.234E03"); - test(-0.1234, "

      1.234Em01"); - - prepareFormatter("

      #.###E00;

      -#.###E00", 'm'); - test(1234D, "

      1.234E03"); - test(0.1234, "

      1.234Em01"); - test(-1234D, "

      m1.234E03"); - test(-0.1234, "

      m1.234Em01"); - - test(Double.POSITIVE_INFINITY, "

      \u221e"); - test(Double.NEGATIVE_INFINITY, "

      m\u221e"); - test(Double.NaN, "\ufffd"); // without prefix and suffix - test(0.0, "

      0E00"); - test(-0.0, "

      m0E00"); // with the minus sign + // Restore the original JVM default locale + @AfterAll + static void tearDown() { + Locale.setDefault(savedLocale); } - static void test_BigDecimal() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); - - /* Test with default pattern */ - test(new BigDecimal("123456789012345678901234567890"), - "123,456,789,012,345,678,901,234,567,890"); - test(new BigDecimal("0.000000000123456789012345678901234567890"), - "0"); - test(new BigDecimal("-123456789012345678901234567890"), - "-123,456,789,012,345,678,901,234,567,890"); - test(new BigDecimal("-0.000000000123456789012345678901234567890"), - "-0"); - - test(new BigDecimal("0"), "0"); - test(new BigDecimal("-0"), "0"); - - /* Specify a pattern and the minus sign. */ - prepareFormatter("

      #.####################E00;

      -#.####################E00", 'm'); - test(new BigDecimal("123456789012345678901234567890"), - "

      1.23456789012345678901E29"); - test(new BigDecimal("0.000000000123456789012345678901234567890"), - "

      1.23456789012345678901Em10"); - test(new BigDecimal("-123456789012345678901234567890"), - "

      m1.23456789012345678901E29"); - test(new BigDecimal("-0.000000000123456789012345678901234567890"), - "

      m1.23456789012345678901Em10"); - - test(new BigDecimal("0"), "

      0E00"); - test(new BigDecimal("-0"), "

      0E00"); + // Check that negative exponent number recognized for doubles + @ParameterizedTest + @MethodSource("doubles") + public void doubleTest(Number num, String str, DecimalFormat df) { + test(num, str, df); } - static void test_long() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); - - /* Test with default pattern */ - test(123456789L, "123,456,789"); - test(-123456789L, "-123,456,789"); - - test(0L, "0"); - test(-0L, "0"); - - /* Specify a pattern and the minus sign. */ - prepareFormatter("

      #,###;

      -#,###", 'm'); - test(123456789L, "

      123,456,789"); - test(-123456789L, "

      m123,456,789"); - - test(0L, "

      0"); - test(-0L, "

      0"); + // Provides a double to be formatted, which is compared to the expected String. + // Additionally, provides a DecimalFormat to do the formatting (can have a custom + // pattern and minus sign). Given in the form (double, String, DecimalFormat). + private static Stream doubles() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf1 = getDecimalFormat("

      #.###E00", 'm'); + DecimalFormat customDf2 = getDecimalFormat("

      #.###E00;#.###E00", 'm'); + DecimalFormat customDf3 = getDecimalFormat("#.###E00;

      #.###E00", 'm'); + DecimalFormat customDf4 = getDecimalFormat("

      #.###E00;

      -#.###E00", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(1234D, "1,234", defaultDf), + Arguments.of(0.1234, "0.123", defaultDf), // rounded + Arguments.of(-1234D, "-1,234", defaultDf), + Arguments.of(-0.1234, "-0.123", defaultDf), // rounded + Arguments.of(Double.POSITIVE_INFINITY, "\u221e", defaultDf), + Arguments.of(Double.NEGATIVE_INFINITY, "-\u221e", defaultDf), + Arguments.of(Double.NaN, "\ufffd", defaultDf), // without prefix and suffix + Arguments.of(0.0, "0", defaultDf), + Arguments.of(-0.0, "-0", defaultDf), // with the minus sign + // Test with a pattern and the minus sign + Arguments.of(1234D, "

      1.234E03", customDf1), + Arguments.of(0.1234, "

      1.234Em01", customDf1), + Arguments.of(-1234D, "m

      1.234E03", customDf1), + Arguments.of(-0.1234, "m

      1.234Em01", customDf1), + Arguments.of(1234D, "

      1.234E03", customDf2), + Arguments.of(0.1234, "

      1.234Em01", customDf2), + Arguments.of(-1234D, "1.234E03", customDf2), + Arguments.of(-0.1234, "1.234Em01", customDf2), + Arguments.of(1234D, "1.234E03", customDf3), + Arguments.of(0.1234, "1.234Em01", customDf3), + Arguments.of(-1234D, "

      1.234E03", customDf3), + Arguments.of(-0.1234, "

      1.234Em01", customDf3), + Arguments.of(1234D, "

      1.234E03", customDf4), + Arguments.of(0.1234, "

      1.234Em01", customDf4), + Arguments.of(-1234D, "

      m1.234E03", customDf4), + Arguments.of(-0.1234, "

      m1.234Em01", customDf4), + Arguments.of(Double.POSITIVE_INFINITY, "

      \u221e", customDf4), + Arguments.of(Double.NEGATIVE_INFINITY, "

      m\u221e", customDf4), + Arguments.of(Double.NaN, "\ufffd", customDf4), // without prefix and suffix + Arguments.of(0.0, "

      0E00", customDf4), + Arguments.of(-0.0, "

      m0E00", customDf4) // with the minus sign + ); } - static void test_BigInteger() { - df = new DecimalFormat(); - dfs = df.getDecimalFormatSymbols(); + // Check that negative exponent number recognized for longs + @ParameterizedTest + @MethodSource("longs") + public void longTest(Number num, String str, DecimalFormat df) { + test(num, str, df); + } - /* Test with default pattern */ - test(new BigInteger("123456789012345678901234567890"), - "123,456,789,012,345,678,901,234,567,890"); - test(new BigInteger("-123456789012345678901234567890"), - "-123,456,789,012,345,678,901,234,567,890"); + // Same as doubles() data provider, but with long values + // Given in the form (long, String, DecimalFormat). + private static Stream longs() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf = getDecimalFormat( + "

      #,###;

      -#,###", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(123456789L, "123,456,789", defaultDf), + Arguments.of(-123456789L, "-123,456,789", defaultDf), + Arguments.of(0L, "0", defaultDf), + Arguments.of(-0L, "0", defaultDf), + // Test with a pattern and the minus sign + Arguments.of(123456789L, "

      123,456,789", customDf), + Arguments.of(-123456789L, "

      m123,456,789", customDf), + Arguments.of(0L, "

      0", customDf), + Arguments.of(-0L, "

      0", customDf) + ); + } - test(new BigInteger("0"), "0"); - test(new BigInteger("-0"), "0"); + // Check that negative exponent number recognized for bigDecimals + @ParameterizedTest + @MethodSource("bigDecimals") + public void bigDecimalTest(Number num, String str, DecimalFormat df) { + test(num, str, df); + } - /* Specify a pattern and the minus sign. */ - prepareFormatter("

      #,###;

      -#,###", 'm'); - test(new BigInteger("123456789012345678901234567890"), - "

      123,456,789,012,345,678,901,234,567,890"); - test(new BigInteger("-123456789012345678901234567890"), - "

      m123,456,789,012,345,678,901,234,567,890"); + // Same as doubles() data provider, but with BigDecimal values + // Given in the form (BigDecimal, String, DecimalFormat). + private static Stream bigDecimals() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf = getDecimalFormat( + "

      #.####################E00;

      -#.####################E00", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(new BigDecimal("123456789012345678901234567890"), + "123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigDecimal("0.000000000123456789012345678901234567890"), + "0", defaultDf), + Arguments.of(new BigDecimal("-123456789012345678901234567890"), + "-123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigDecimal("-0.000000000123456789012345678901234567890"), + "-0", defaultDf), + Arguments.of(new BigDecimal("0"), "0", defaultDf), + Arguments.of(new BigDecimal("-0"), "0", defaultDf), + // Test with a pattern and the minus sign + Arguments.of(new BigDecimal("123456789012345678901234567890"), + "

      1.23456789012345678901E29", customDf), + Arguments.of(new BigDecimal("0.000000000123456789012345678901234567890"), + "

      1.23456789012345678901Em10", customDf), + Arguments.of(new BigDecimal("-123456789012345678901234567890"), + "

      m1.23456789012345678901E29", customDf), + Arguments.of(new BigDecimal("-0.000000000123456789012345678901234567890"), + "

      m1.23456789012345678901Em10", customDf), + Arguments.of(new BigDecimal("0"), "

      0E00", customDf), + Arguments.of(new BigDecimal("-0"), "

      0E00", customDf) + ); + } - test(new BigInteger("0"), "

      0"); - test(new BigInteger("-0"), "

      0"); + // Check that negative exponent number recognized for bigIntegers + @ParameterizedTest + @MethodSource("bigIntegers") + public void bigIntegerTest(Number num, String str, DecimalFormat df) { + test(num, str, df); } - static void prepareFormatter(String pattern, char minusSign) { - dfs = df.getDecimalFormatSymbols(); - df.applyPattern(pattern); - dfs.setMinusSign(minusSign); - df.setDecimalFormatSymbols(dfs); + // Same as doubles() data provider, but with BigInteger values + // Given in the form (BigInteger, String, DecimalFormat). + private static Stream bigIntegers() { + DecimalFormat defaultDf = new DecimalFormat(); + DecimalFormat customDf = getDecimalFormat( + "

      #,###;

      -#,###", 'm'); + return Stream.of( + // Test with default pattern + Arguments.of(new BigInteger("123456789012345678901234567890"), + "123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigInteger("-123456789012345678901234567890"), + "-123,456,789,012,345,678,901,234,567,890", defaultDf), + Arguments.of(new BigInteger("0"), "0", defaultDf), + Arguments.of(new BigInteger("-0"), "0", defaultDf), + // Test with a pattern and the minus sign + Arguments.of(new BigInteger("123456789012345678901234567890"), + "

      123,456,789,012,345,678,901,234,567,890", customDf), + Arguments.of(new BigInteger("-123456789012345678901234567890"), + "

      m123,456,789,012,345,678,901,234,567,890", customDf), + Arguments.of(new BigInteger("0"), "

      0", customDf), + Arguments.of(new BigInteger("-0"), "

      0", customDf) + ); } - static void test(Number num, String str) { + // Check that the formatted value is correct and also check that + // it can be round-tripped via parse() and format() + private static void test(Number num, String str, DecimalFormat df) { String formatted = df.format(num); - if (!formatted.equals(str)) { - err = true; - System.err.println(" DecimalFormat format(" + - num.getClass().getName() + - ") error: \n\tnumber: " + num + - "\n\tminus sign: " + dfs.getMinusSign() + - "\n\tgot: " + formatted + - "\n\texpected: " + str); - return; - } + assertEquals(str, formatted, String.format("DecimalFormat format(%s) " + + "Error: number: %s, minus sign: %s", num.getClass().getName(), num, df.getDecimalFormatSymbols().getMinusSign())); if (num instanceof BigDecimal || num instanceof BigInteger) { df.setParseBigDecimal(true); } + testRoundTrip(formatted, str, num, df); + } + + // Test that a parsed value can be round-tripped via format() and parse() + private static void testRoundTrip(String formatted, String str, + Number num, DecimalFormat df) { Number parsed1 = null, parsed2 = null; try { parsed1 = df.parse(formatted); formatted = df.format(parsed1); parsed2 = df.parse(formatted); - if (!parsed1.equals(parsed2)) { - err = true; - System.err.println(" DecimalFormat roundtrip parse(" + - num.getClass().getName() + - ") error: \n\toriginal number: " + str + - "\n\tparsed number: " + parsed1 + - " (" + parsed1.getClass().getName() + ")" + - "\n\tformatted number: " + formatted + - "\n\tre-parsed number: " + parsed2 + - " (" + parsed2.getClass().getName() + ")" + - "\n\tminus sign: " + dfs.getMinusSign()); - } + assertEquals(parsed2, parsed1, """ + DecimalFormat round trip parse(%s) error: + original number: %s + parsed number: %s + (%s) + formatted number: %s + re-parsed number: %s + (%s) + minus sign: %s + """.formatted(num.getClass().getName(), str, parsed1, parsed1.getClass().getName(), + formatted, parsed2, parsed2.getClass().getName(), df.getDecimalFormatSymbols().getMinusSign())); } catch (Exception e) { - err = true; - System.err.println(" DecimalFormat parse(" + - num.getClass().getName() + - ") threw an Exception: " + e.getMessage() + - "\n\toriginal number: " + str + - "\n\tparsed number : " + parsed1 + - " (" + parsed1.getClass().getName() + ")" + - "\n\tformatted number: " + formatted + - "\n\tre-parsed number: " + parsed2 + - " (" + parsed2.getClass().getName() + ")" + - "\n\tminus sign: " + dfs.getMinusSign()); + fail(""" + DecimalFormat parse(%s) threw an Exception: %s + original number: %s + parsed number: %s + (%s) + formatted number: %s + re-parsed number: %s + (%s) + minus sign: %s + """.formatted(num.getClass().getName(), e.getMessage(), str, parsed1, parsed1.getClass().getName(), + formatted, parsed2, parsed2.getClass().getName(), df.getDecimalFormatSymbols().getMinusSign())); } } + + // Set up custom DecimalFormat with DecimalFormatSymbols + private static DecimalFormat getDecimalFormat(String pattern, char minusSign) { + DecimalFormat df = new DecimalFormat(); + DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); + df.applyPattern(pattern); + dfs.setMinusSign(minusSign); + df.setDecimalFormatSymbols(dfs); + return df; + } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4944439.java b/test/jdk/java/text/Format/NumberFormat/Bug4944439.java index 0e85a98119d..561052e9a95 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4944439.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4944439.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, 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,87 +25,97 @@ * @test * @bug 4944439 * @summary Confirm that numbers where all digits after the decimal separator are 0 - * and which are between Long.MIN_VALUE and Long.MAX_VALUE are returned as Long(not double). + * and which are between Long.MIN_VALUE and Long.MAX_VALUE are returned + * as Long(not double). + * @run junit Bug4944439 */ -import java.math.BigDecimal; -import java.math.BigInteger; import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.Locale; +import java.util.stream.Stream; -public class Bug4944439 { - - static boolean err = false; - static DecimalFormat df; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; - public static void main(String[] args) throws Exception { - - Locale defaultLoc = Locale.getDefault(); - Locale.setDefault(Locale.US); +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; - df = new DecimalFormat(); - String s = "-9223372036854775809"; // Long.MIN_VALUE-1 - check_Double(s); +public class Bug4944439 { - test(Long.MIN_VALUE, Long.MIN_VALUE+10); - test(-10, 10); - test(Long.MAX_VALUE-10, Long.MAX_VALUE-1); + // Save JVM default locale + private static final Locale savedLocale = Locale.getDefault(); + private static final DecimalFormat df = new DecimalFormat(); - s = "9223372036854775807.00"; // Long.MAX_VALUE - check_Long(s); - s = "9223372036854775808"; // Long.MAX_VALUE+1 - check_Double(s); + // Set JVM default locale to US for testing + @BeforeAll + static void initAll() { + Locale.setDefault(Locale.US); + } - s = "-0.0"; - check_Double(s); - s = "0.0"; - check_Long(s); + // Restore JVM default locale + @AfterAll + static void tearDownAll() { + Locale.setDefault(savedLocale); + } - Locale.setDefault(defaultLoc); + // Check return type and value returned by DecimalFormat.parse() for longs + @ParameterizedTest + @MethodSource("longs") + public void parseLongTest(String s) { + // This was originally intended to ensure a ParseException is not thrown + Number parsedNumber = assertDoesNotThrow(() -> df.parse(s), + "DecimalFormat.parse(\"%s\") should not throw an Exception"); + assertInstanceOf(Long.class, parsedNumber, + "DecimalFormat.parse(\"%s\") did not return Long"); + // Grab integer portion of value + Long expectedVal = Long.valueOf(s.substring(0, s.indexOf('.'))); + assertEquals(parsedNumber, expectedVal, + "DecimalFormat.parse(\"%s\") returned numerically incorrect value"); + } - if (err) { - throw new RuntimeException("Wrong parsing with DecimalFormat"); - } + // Test some values between Long.MIN_VALUE and Long.MAX_VALUE + private static Stream longs() { + ArrayList longs = new ArrayList<>(); + addLongData(Long.MIN_VALUE, Long.MIN_VALUE+10, longs); + addLongData(-10, 10, longs); + addLongData(Long.MAX_VALUE-10, Long.MAX_VALUE-1, longs); + longs.add("9223372036854775807.00"); + longs.add("0.0"); + return longs.stream(); } - private static void test(long from, long to) throws Exception { + // Utility to add values between parameters(long, to) to testLongs ArrayList + private static void addLongData(long from, long to, ArrayList testLongs){ for (long l = from; l <= to; l++) { - check_Long(Long.toString(l) + ".00"); + testLongs.add(l + ".00"); } } - private static void check_Long(String s) throws Exception { - Number number = df.parse(s); - if (!(number instanceof Long)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(\"" + s + - "\") should return a Long, but returned a " + - number.getClass().getName()); - } - - int index = s.indexOf('.'); - Long l = Long.valueOf(s.substring(0, index)); - if (!l.equals(number)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(" + s + - ") should return a Long(" + l + "), but returned " + number); - } + // Check return type and value returned by DecimalFormat.parse() for doubles + @ParameterizedTest + @MethodSource("doubles") + public void parseDoubleTest(String s) { + // This was originally intended to ensure a ParseException is not thrown + Number parsedNumber = assertDoesNotThrow(() -> df.parse(s), + "DecimalFormat.parse(\"%s\") should not throw an Exception"); + assertInstanceOf(Double.class, parsedNumber, + "DecimalFormat.parse(\"%s\") did not return Double"); + Double expectedVal = Double.valueOf(s); + assertEquals(parsedNumber, expectedVal, + "DecimalFormat.parse(\"%s\") returned numerically incorrect value"); } - private static void check_Double(String s) throws Exception { - Number number = df.parse(s); - if (!(number instanceof Double)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(\"" + s + - "\") should return a Double, but returned a " + - number.getClass().getName()); - } - - Double d = Double.valueOf(s); - if (!d.equals(number)) { - err = true; - System.err.println("Failed: DecimalFormat.parse(" + s + - ") should return a Double(" + d + "), but returned " + number); - } + // Check values not between Long.MIN_VALUE and Long.MAX_VALUE + private static Stream doubles() { + return Stream.of( + "-9223372036854775809", // Long.MIN_VALUE-1 + "9223372036854775808", // Long.MAX_VALUE+1 + "-0.0" + ); } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug4990596.java b/test/jdk/java/text/Format/NumberFormat/Bug4990596.java index 2b311fb1b5f..36c0259ae56 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug4990596.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug4990596.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2016, 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 @@ -21,20 +21,32 @@ * questions. */ -/** +/* * @test * @bug 4990596 - * @summary Make sure that any subclass of Number can be formatted using DecimalFormat.format(). + * @summary Make sure that any subclass of Number can be formatted using + * DecimalFormat.format() without throwing an exception. + * @run junit Bug4990596 */ import java.text.DecimalFormat; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + public class Bug4990596 { - public static void main(String[] args) { - new DecimalFormat().format(new MutableInteger(0)); + // Test that a custom subclass of Number can be formatted by + // DecimalFormat without throwing an IllegalArgumentException + @Test + public void formatSubclassedNumberTest() { + assertDoesNotThrow(() -> new DecimalFormat().format(new MutableInteger(0)), + "DecimalFormat.format() should support subclasses of Number"); } + // A custom subclass of Number. Prior to this fix, if an instance of this + // class was formatted by DecimalFormat, an exception would be thrown. @SuppressWarnings("serial") public static class MutableInteger extends Number { public int value; diff --git a/test/jdk/java/text/Format/NumberFormat/Bug6278616.java b/test/jdk/java/text/Format/NumberFormat/Bug6278616.java index b1684b4d177..8f9fc7ca52d 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug6278616.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug6278616.java @@ -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 @@ -24,46 +24,55 @@ /* * @test * @summary Confirm that AtomicInteger and AtomicLong are formatted correctly. + * That is, make sure they are not treated as a double when formatted + * anymore (which can result in the loss of precision). * @bug 6278616 + * @run junit Bug6278616 */ import java.text.NumberFormat; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import java.util.Locale; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class Bug6278616 { - static final int[] ints = { - Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE - }; + private static final NumberFormat nf = NumberFormat.getInstance(); - static final long[] longs = { - Long.MIN_VALUE, -1, 0, 1, Long.MAX_VALUE - }; + // Test that NumberFormat formats numerically equivalent int + // and AtomicInteger values the same + @ParameterizedTest + @MethodSource("ints") + public void formattedAtomicIntTest(int testInt) { + String formattedInt = nf.format(testInt); + String formattedAtomicInt = nf.format(new AtomicInteger(testInt)); + assertEquals(formattedAtomicInt, formattedInt, "Formatting numerically" + + " equivalent AtomicInteger and int should produce the same String value"); + } - public static void main(String[] args) { - NumberFormat nf = NumberFormat.getInstance(); + // Various int values + private static int[] ints() { + return new int[] { Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE}; + } - for (int j = 0; j < ints.length; j++) { - String s_i = nf.format(ints[j]); - String s_ai = nf.format(new AtomicInteger(ints[j])); - if (!s_i.equals(s_ai)) { - throw new RuntimeException("format(AtomicInteger " + s_ai + - ") doesn't equal format(Integer " + - s_i + ")"); - } - } + // Test that NumberFormat formats numerically equivalent long + // and AtomicLong values the same + @ParameterizedTest + @MethodSource("longs") + public void formattedAtomicLongTest(long testLong) { + String formattedLong = nf.format(testLong); + String formattedAtomicLong = nf.format(new AtomicLong(testLong)); + assertEquals(formattedAtomicLong, formattedLong, "Formatting numerically" + + " equivalent AtomicLong and long should produce the same String value"); + } - for (int j = 0; j < longs.length; j++) { - String s_l = nf.format(longs[j]); - String s_al = nf.format(new AtomicLong(longs[j])); - if (!s_l.equals(s_al)) { - throw new RuntimeException("format(AtomicLong " + s_al + - ") doesn't equal format(Long " + - s_l + ")"); - } - } + // Various long values + private static long[] longs() { + return new long[] { Long.MIN_VALUE, -1, 0, 1, Long.MAX_VALUE}; } } diff --git a/test/jdk/java/text/Format/NumberFormat/Bug8132125.java b/test/jdk/java/text/Format/NumberFormat/Bug8132125.java index 9c02ad44f78..edd1d3c5bcd 100644 --- a/test/jdk/java/text/Format/NumberFormat/Bug8132125.java +++ b/test/jdk/java/text/Format/NumberFormat/Bug8132125.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, 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 @@ -26,20 +26,27 @@ * @bug 8132125 8202537 * @summary Checks Swiss' number elements * @modules jdk.localedata + * @run junit Bug8132125 */ -import java.text.*; -import java.util.*; +import java.text.NumberFormat; +import java.util.Locale; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class Bug8132125 { - public static void main(String[] args) { + + // Ensure the CLDRConverter does not omit the Swiss number elements + @Test + public void swissNumElementsTest() { Locale deCH = new Locale("de", "CH"); NumberFormat nf = NumberFormat.getInstance(deCH); - String expected = "54\u2019839\u2019483.142"; // i.e. "\u2019" as decimal separator, "\u2019" as grouping separator + // "\u002E" as decimal separator, "\u2019" as grouping separator + String expected = "54\u2019839\u2019483.142"; String actual = nf.format(54839483.1415); - if (!actual.equals(expected)) { - throw new RuntimeException("incorrect for de_CH: " + expected + " vs. actual " + actual); - } + assertEquals(expected, actual, "incorrect number elements for de_CH"); } } diff --git a/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java b/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java index c25a6d88b26..2eb01c3e403 100644 --- a/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java +++ b/test/jdk/java/text/Format/NumberFormat/CurrencyFormat.java @@ -25,13 +25,17 @@ * @test * @bug 4290801 4942982 5102005 8008577 8021121 8210153 8227313 * @summary Basic tests for currency formatting. + * Tests both COMPAT and CLDR data. * @modules jdk.localedata - * @run main/othervm -Djava.locale.providers=COMPAT CurrencyFormat COMPAT - * @run main/othervm -Djava.locale.providers=CLDR CurrencyFormat CLDR + * @run junit/othervm -Djava.locale.providers=COMPAT CurrencyFormat + * @run junit/othervm -Djava.locale.providers=CLDR CurrencyFormat */ import java.io.File; import java.io.FileInputStream; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; import java.util.Currency; import java.util.Locale; import java.util.Properties; @@ -40,126 +44,139 @@ import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.SimpleDateFormat; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class CurrencyFormat { - private static boolean isCompat; + // Expected data is switched depending on COMPAT or CLDR + // currencySymbolsTest() is only ran for COMPAT + private static final boolean isCompat = + "COMPAT".equals(System.getProperty("java.locale.providers")); - public static void main(String[] args) throws Exception { - isCompat = "COMPAT".equals(args[0]); - testFormatting(); - testSymbols(); + // Tests the formatting of data for COMPAT + CLDR under various currencies + // Using a NumberFormat generated by getCurrencyInstance() + @ParameterizedTest + @MethodSource("currencyFormatDataProvider") + public void currencyFormatTest(String expected, Currency currency, + NumberFormat format, Locale locale) { + if (currency != null) { + format.setCurrency(currency); + int digits = currency.getDefaultFractionDigits(); + format.setMinimumFractionDigits(digits); + format.setMaximumFractionDigits(digits); + } + String result = format.format(1234.56); + assertEquals(expected, result, String.format("Failed with locale: %s%s", + locale, (currency == null ? ", default currency" : (", currency: " + currency)))); } - static void testFormatting() { - boolean failed = false; + // Generate a combination of expected data for 1234.56 formatted + // under various currencies/locale provider/locale + private static Stream currencyFormatDataProvider() { + ArrayList data = new ArrayList(); Locale[] locales = { - Locale.US, - Locale.JAPAN, - Locale.GERMANY, - Locale.ITALY, - new Locale("it", "IT", "EURO"), - Locale.forLanguageTag("de-AT"), - Locale.forLanguageTag("fr-CH"), + Locale.US, + Locale.JAPAN, + Locale.GERMANY, + Locale.ITALY, + new Locale("it", "IT", "EURO"), + Locale.forLanguageTag("de-AT"), + Locale.forLanguageTag("fr-CH"), }; Currency[] currencies = { - null, - Currency.getInstance("USD"), - Currency.getInstance("JPY"), - Currency.getInstance("DEM"), - Currency.getInstance("EUR"), + null, + Currency.getInstance("USD"), + Currency.getInstance("JPY"), + Currency.getInstance("DEM"), + Currency.getInstance("EUR"), }; - String[][] expecteds = { - {"$1,234.56", "$1,234.56", "JPY1,235", "DEM1,234.56", "EUR1,234.56"}, - {"\uFFE51,235", "USD1,234.56", "\uFFE51,235", "DEM1,234.56", "EUR1,234.56"}, - {"1.234,56 \u20AC", "1.234,56 USD", "1.235 JPY", "1.234,56 DM", "1.234,56 \u20AC"}, - {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, - {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, - {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, - {"SFr. 1'234.56", "USD 1'234.56", "JPY 1'235", "DEM 1'234.56", "EUR 1'234.56"}, + String[][] expectedCOMPATData = { + {"$1,234.56", "$1,234.56", "JPY1,235", "DEM1,234.56", "EUR1,234.56"}, + {"\uFFE51,235", "USD1,234.56", "\uFFE51,235", "DEM1,234.56", "EUR1,234.56"}, + {"1.234,56 \u20AC", "1.234,56 USD", "1.235 JPY", "1.234,56 DM", "1.234,56 \u20AC"}, + {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, + {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, + {"\u20AC 1.234,56", "USD 1.234,56", "JPY 1.235", "DEM 1.234,56", "\u20AC 1.234,56"}, + {"SFr. 1'234.56", "USD 1'234.56", "JPY 1'235", "DEM 1'234.56", "EUR 1'234.56"}, }; - String[][] expecteds_cldr = { - {"$1,234.56", "$1,234.56", "\u00a51,235", "DEM1,234.56", "\u20ac1,234.56"}, - {"\uFFE51,235", "$1,234.56", "\uFFE51,235", "DEM1,234.56", "\u20ac1,234.56"}, - {"1.234,56\u00a0\u20ac", "1.234,56\u00a0$", "1.235\u00a0\u00a5", "1.234,56\u00a0DM", "1.234,56\u00a0\u20ac"}, - {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, - {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, - {"\u20ac\u00a01.234,56", "$\u00a01.234,56", "\u00a5\u00a01.235", "DM\u00a01.234,56", "\u20ac\u00a01.234,56"}, - {"1\u202f234.56\u00a0CHF", "1\u202f234.56\u00a0$US", "1\u202f235\u00a0JPY", "1\u202f234.56\u00a0DEM", "1\u202f234.56\u00a0\u20ac"}, + String[][] expectedCLDRData = { + {"$1,234.56", "$1,234.56", "\u00a51,235", "DEM1,234.56", "\u20ac1,234.56"}, + {"\uFFE51,235", "$1,234.56", "\uFFE51,235", "DEM1,234.56", "\u20ac1,234.56"}, + {"1.234,56\u00a0\u20ac", "1.234,56\u00a0$", "1.235\u00a0\u00a5", "1.234,56\u00a0DM", "1.234,56\u00a0\u20ac"}, + {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, + {"1.234,56\u00a0\u20ac", "1.234,56\u00a0USD", "1.235\u00a0JPY", "1.234,56\u00a0DEM", "1.234,56\u00a0\u20ac"}, + {"\u20ac\u00a01.234,56", "$\u00a01.234,56", "\u00a5\u00a01.235", "DM\u00a01.234,56", "\u20ac\u00a01.234,56"}, + {"1\u202f234.56\u00a0CHF", "1\u202f234.56\u00a0$US", "1\u202f235\u00a0JPY", "1\u202f234.56\u00a0DEM", "1\u202f234.56\u00a0\u20ac"}, }; - for (int i = 0; i < locales.length; i++) { Locale locale = locales[i]; NumberFormat format = NumberFormat.getCurrencyInstance(locale); for (int j = 0; j < currencies.length; j++) { Currency currency = currencies[j]; - String expected = isCompat ? expecteds[i][j] : expecteds_cldr[i][j]; - if (currency != null) { - format.setCurrency(currency); - int digits = currency.getDefaultFractionDigits(); - format.setMinimumFractionDigits(digits); - format.setMaximumFractionDigits(digits); - } - String result = format.format(1234.56); - if (!result.equals(expected)) { - failed = true; - System.out.println("FAIL: Locale " + locale - + (currency == null ? ", default currency" : (", currency: " + currency)) - + ", expected: " + expected - + ", actual: " + result); - } + String expected = isCompat ? expectedCOMPATData[i][j] : expectedCLDRData[i][j]; + data.add(Arguments.of(expected, currency, format, locale)); } } - - if (failed) { - throw new RuntimeException(); - } + return data.stream(); } - static void testSymbols() throws Exception { + // Compares the expected currency symbol of a locale to the value returned by + // DecimalFormatSymbols.getCurrencySymbol(). + @ParameterizedTest + @MethodSource("currencySymbolsDataProvider") + public void currencySymbolsTest(String expected, Locale locale) throws ParseException { if (!isCompat) { - // For COMPAT only. - return; + return; // For COMPAT only. } + if (expected == null) { + System.out.println("Warning: No expected currency symbol defined for locale " + locale); + } else { + // Reserved for when a currency will change its symbol at a given time in the future + if (expected.contains(";")) { + expected = getFutureSymbol(expected); + } + DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); + String result = symbols.getCurrencySymbol(); + assertEquals(expected, result, "Wrong currency symbol for locale " + + locale + ", expected: " + expected + ", got: " + result); + } + } - FileInputStream stream = new FileInputStream(new File(System.getProperty("test.src", "."), "CurrencySymbols.properties")); + // Grabs the custom CurrencySymbols.properties and loads the file into a Properties + // instance. Building the data set, which consists of the currency symbol for the locale. + private static Stream currencySymbolsDataProvider() throws IOException { + ArrayList data = new ArrayList(); + FileInputStream stream = new FileInputStream(new File( + System.getProperty("test.src", "."), "CurrencySymbols.properties")); Properties props = new Properties(); props.load(stream); - SimpleDateFormat format = null; - Locale[] locales = NumberFormat.getAvailableLocales(); - for (int i = 0; i < locales.length; i++) { - Locale locale = locales[i]; - DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); - String result = symbols.getCurrencySymbol(); + for (Locale locale : locales) { String expected = (String) props.get(locale.toString()); + data.add(Arguments.of(expected, locale)); + } + return data.stream(); + } - if (expected == null) { - System.out.println("Warning: No expected currency symbol defined for locale " + locale); - } else { - if (expected.contains(";")) { - StringTokenizer tokens = new StringTokenizer(expected, ";"); - int tokensCount = tokens.countTokens(); - - if (tokensCount == 3) { - expected = tokens.nextToken(); - if (format == null) { - format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); - format.setTimeZone(TimeZone.getTimeZone("GMT")); - format.setLenient(false); - } - - if (format.parse(tokens.nextToken()).getTime() < System.currentTimeMillis()) { - expected = tokens.nextToken(); - } - } - } - - if (!expected.equals(result)) { - throw new RuntimeException("Wrong currency symbol for locale " + - locale + ", expected: " + expected + ", got: " + result); - } + // Utility to grab the future symbol if in the right format and date cut-over allows + private static String getFutureSymbol(String expected) throws ParseException { + StringTokenizer tokens = new StringTokenizer(expected, ";"); + int tokensCount = tokens.countTokens(); + if (tokensCount == 3) { + expected = tokens.nextToken(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + format.setLenient(false); + if (format.parse(tokens.nextToken()).getTime() < System.currentTimeMillis()) { + expected = tokens.nextToken(); } } + return expected; } } diff --git a/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java b/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java index 246208b7aaf..0bfb13888e2 100644 --- a/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java +++ b/test/jdk/java/text/Format/NumberFormat/TestPeruCurrencyFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 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 @@ -21,27 +21,31 @@ * questions. */ -/** +/* * @test * @bug 8206879 - * @summary Currency decimal marker incorrect for Peru. * @modules jdk.localedata - * @run main/othervm -Djava.locale.providers=JRE TestPeruCurrencyFormat + * @summary Currency decimal marker incorrect for Peru (COMPAT). + * @run junit/othervm -Djava.locale.providers=COMPAT TestPeruCurrencyFormat */ import java.text.NumberFormat; import java.util.Locale; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + public class TestPeruCurrencyFormat { - public static void main(String[] args) { + // Confirm correct decimal marker for Peru locale on COMPAT + @Test + public void peruDecimalMarketCOMPAT() { final String expected = "S/.1,234.56"; NumberFormat currencyFmt = NumberFormat.getCurrencyInstance(new Locale("es", "PE")); String s = currencyFmt.format(1234.56); - - if (!s.equals(expected)) { - throw new RuntimeException("Currency format for Peru failed, expected " + expected + ", got " + s); - } + assertEquals(expected, s, + "Currency format for Peru failed, expected " + expected + ", got " + s); } } diff --git a/test/jdk/java/util/Currency/CheckDataVersion.java b/test/jdk/java/util/Currency/CheckDataVersion.java index ba18677dbbe..303603c5b85 100644 --- a/test/jdk/java/util/Currency/CheckDataVersion.java +++ b/test/jdk/java/util/Currency/CheckDataVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, 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 @@ -38,7 +38,7 @@ import java.util.Currency; class CheckDataVersion { - static final String datafile = "tablea1.txt"; + static final String datafile = "ISO4217-list-one.txt"; static final String FILEVERSIONKEY = "FILEVERSION="; static final String DATAVERSIONKEY = "DATAVERSION="; static String fileVersion; diff --git a/test/jdk/java/util/Currency/CurrencyTest.java b/test/jdk/java/util/Currency/CurrencyTest.java index 256e6ee8650..deb1854ffd8 100644 --- a/test/jdk/java/util/Currency/CurrencyTest.java +++ b/test/jdk/java/util/Currency/CurrencyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, 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 @@ -25,7 +25,7 @@ * @test * @bug 4290801 4692419 4693631 5101540 5104960 6296410 6336600 6371531 * 6488442 7036905 8008577 8039317 8074350 8074351 8150324 8167143 - * 8264792 + * 8264792 8334653 * @summary Basic tests for Currency class. * @modules java.base/java.util:open * jdk.localedata @@ -59,7 +59,7 @@ public class CurrencyTest { - // 'tablea1.txt' should be up-to-date before testing + // 'ISO4217-list-one.txt' should be up-to-date before testing @Test public void dataVersionTest() { CheckDataVersion.check(); diff --git a/test/jdk/java/util/Currency/tablea1.txt b/test/jdk/java/util/Currency/ISO4217-list-one.txt similarity index 97% rename from test/jdk/java/util/Currency/tablea1.txt rename to test/jdk/java/util/Currency/ISO4217-list-one.txt index 6e85de5e6d2..1912b5cc7db 100644 --- a/test/jdk/java/util/Currency/tablea1.txt +++ b/test/jdk/java/util/Currency/ISO4217-list-one.txt @@ -1,12 +1,12 @@ # # -# Amendments up until ISO 4217 AMENDMENT NUMBER 176 -# (As of 06 December 2023) +# Amendments up until ISO 4217 AMENDMENT NUMBER 177 +# (As of 20 June 2024) # # Version FILEVERSION=3 -DATAVERSION=176 +DATAVERSION=177 # ISO 4217 currency data AF AFN 971 2 @@ -276,7 +276,7 @@ WF XPF 953 0 EH MAD 504 2 YE YER 886 2 ZM ZMW 967 2 -ZW ZWL 932 2 +ZW ZWG 924 2 #XAU XAU 959 #XBA XBA 955 #XBB XBB 956 diff --git a/test/jdk/java/util/Currency/ValidateISO4217.java b/test/jdk/java/util/Currency/ValidateISO4217.java index 3d2bae0e5e7..01c225e531c 100644 --- a/test/jdk/java/util/Currency/ValidateISO4217.java +++ b/test/jdk/java/util/Currency/ValidateISO4217.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2023, 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 @@ -25,7 +25,7 @@ * @test * @bug 4691089 4819436 4942982 5104960 6544471 6627549 7066203 7195759 * 8039317 8074350 8074351 8145952 8187946 8193552 8202026 8204269 - * 8208746 8209775 8264792 8274658 8283277 8296239 8321480 + * 8208746 8209775 8264792 8274658 8283277 8296239 8321480 8334653 * @summary Validate ISO 4217 data for Currency class. * @modules java.base/java.util:open * jdk.localedata @@ -60,7 +60,8 @@ /** * This class tests the latest ISO 4217 data and Java's currency data which is - * based on ISO 4217. The golden-data file (ISO 4217 data) 'tablea1.txt' has the following + * based on ISO 4217. The golden-data file, 'ISO4217-list-one.txt', based on the + * "List one: Currency, fund and precious metal codes" has the following * format: \t\t\t[\t\t\t\t] * The Cutover Date is given in SimpleDateFormat's 'yyyy-MM-dd-HH-mm-ss' format in the GMT time zone. */ @@ -68,7 +69,7 @@ public class ValidateISO4217 { // Input golden-data file private static final File dataFile = new File(System.getProperty( - "test.src", "."), "tablea1.txt"); + "test.src", "."), "ISO4217-list-one.txt"); // Code statuses private static final byte UNDEFINED = 0; private static final byte DEFINED = 1; @@ -89,7 +90,7 @@ public class ValidateISO4217 { + "DEM-EEK-ESP-FIM-FRF-GHC-GRD-GWP-HRK-IEP-ITL-LTL-LUF-LVL-MGF-MRO-MTL-MXV-MZM-NLG-" + "PTE-ROL-RUR-SDD-SIT-SLL-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-VED-" + "XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" - + "YUM-ZMK-ZWD-ZWN-ZWR"; + + "YUM-ZMK-ZWD-ZWL-ZWN-ZWR"; private static final String[][] extraCodes = { /* Defined in ISO 4217 list, but don't have code and minor unit info. */ {"AQ", "", "", "0"}, // Antarctica @@ -319,4 +320,4 @@ private static String getSetDiffs(Set jreCurrencies, Set tes bldr.append("\n"); return bldr.toString(); } -} \ No newline at end of file +} diff --git a/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java b/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java index 04a6e89db7b..cb3d4dde914 100644 --- a/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java +++ b/test/jdk/java/util/Locale/LanguageSubtagRegistryTest.java @@ -25,9 +25,9 @@ * @test * @bug 8025703 8040211 8191404 8203872 8222980 8225435 8241082 8242010 8247432 * 8258795 8267038 8287180 8302512 8304761 8306031 8308021 8313702 8318322 - * 8327631 + * 8327631 8332424 8334418 * @summary Checks the IANA language subtag registry data update - * (LSR Revision: 2024-03-07) with Locale and Locale.LanguageRange + * (LSR Revision: 2024-06-14) with Locale and Locale.LanguageRange * class methods. * @run main LanguageSubtagRegistryTest */ diff --git a/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java b/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java index 0ed86582846..6fd79ebf88a 100644 --- a/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java +++ b/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 4354216 8213127 + * @bug 4354216 8213127 8334333 * @summary Test for the cause support when throwing a * MissingResourceBundle. (This test exists under * ResourceBundle/Control because bad resource bundle data can be @@ -32,6 +32,7 @@ * @build jdk.test.lib.JDKToolLauncher * jdk.test.lib.Utils * jdk.test.lib.process.ProcessTools + * jdk.test.lib.Platform * MissingResourceCauseTest * NonResourceBundle * PrivateConstructorRB @@ -50,9 +51,14 @@ import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Platform; +import jtreg.SkippedException; public class MissingResourceCauseTestRun { public static void main(String[] args) throws Throwable { + if (Platform.isRoot() && !Platform.isWindows()) { + throw new SkippedException("Unable to create an unreadable properties file."); + } Path path = Paths.get("UnreadableRB.properties"); Files.deleteIfExists(path); try { @@ -100,7 +106,7 @@ private static void runCmd() throws Throwable { } private static void deleteFile(Path path) throws Throwable { - if(path.toFile().exists()) { + if (path.toFile().exists()) { ProcessTools.executeCommand("chmod", "666", path.toString()) .outputTo(System.out) .errorTo(System.out) diff --git a/test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java b/test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java index 0f154f307f8..340efc67f7f 100644 --- a/test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java +++ b/test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2013, 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 @@ -31,6 +31,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -45,57 +47,62 @@ public static void main(String[] args) throws Throwable { } static void executeTest() throws Throwable { - final ConcurrentHashMap m = new ConcurrentHashMap<>(); - final ThreadLocalRandom rnd = ThreadLocalRandom.current(); - final int nCPU = Runtime.getRuntime().availableProcessors(); - final int minWorkers = 2; - final int maxWorkers = Math.max(minWorkers, Math.min(32, nCPU)); - final int nWorkers = rnd.nextInt(minWorkers, maxWorkers + 1); - final int sizePerWorker = 1024; - final int maxSize = nWorkers * sizePerWorker; + ExecutorService executor = Executors.newCachedThreadPool(); + try { + final ConcurrentHashMap m = new ConcurrentHashMap<>(); + final ThreadLocalRandom rnd = ThreadLocalRandom.current(); + final int nCPU = Runtime.getRuntime().availableProcessors(); + final int minWorkers = 2; + final int maxWorkers = Math.max(minWorkers, Math.min(32, nCPU)); + final int nWorkers = rnd.nextInt(minWorkers, maxWorkers + 1); + final int sizePerWorker = 1024; + final int maxSize = nWorkers * sizePerWorker; - // The foreman busy-checks that the size of the arrays obtained - // from the keys and values views grows monotonically until it - // reaches the maximum size. + // The foreman busy-checks that the size of the arrays obtained + // from the keys and values views grows monotonically until it + // reaches the maximum size. - // NOTE: these size constraints are not specific to toArray and are - // applicable to any form of traversal of the collection views - CompletableFuture foreman = CompletableFuture.runAsync(new Runnable() { - private int prevSize = 0; + // NOTE: these size constraints are not specific to toArray and are + // applicable to any form of traversal of the collection views + CompletableFuture foreman = CompletableFuture.runAsync(new Runnable() { + private int prevSize = 0; - private boolean checkProgress(Object[] a) { - int size = a.length; - if (size < prevSize || size > maxSize) - throw new AssertionError( - String.format("prevSize=%d size=%d maxSize=%d", - prevSize, size, maxSize)); - prevSize = size; - return size == maxSize; - } + private boolean checkProgress(Object[] a) { + int size = a.length; + if (size < prevSize || size > maxSize) + throw new AssertionError( + String.format("prevSize=%d size=%d maxSize=%d", + prevSize, size, maxSize)); + prevSize = size; + return size == maxSize; + } - public void run() { - Integer[] empty = new Integer[0]; - for (;;) - if (checkProgress(m.values().toArray()) - & checkProgress(m.keySet().toArray()) - & checkProgress(m.values().toArray(empty)) - & checkProgress(m.keySet().toArray(empty))) - return; - } - }); + public void run() { + Integer[] empty = new Integer[0]; + for (; ; ) + if (checkProgress(m.values().toArray()) + & checkProgress(m.keySet().toArray()) + & checkProgress(m.values().toArray(empty)) + & checkProgress(m.keySet().toArray(empty))) + return; + } + }, executor); - // Each worker puts globally unique keys into the map - List> workers = - IntStream.range(0, nWorkers) - .mapToObj(w -> (Runnable) () -> { - for (int i = 0, o = w * sizePerWorker; i < sizePerWorker; i++) - m.put(o + i, i); - }) - .map(CompletableFuture::runAsync) - .collect(Collectors.toList()); + // Each worker puts globally unique keys into the map + List> workers = + IntStream.range(0, nWorkers) + .mapToObj(w -> (Runnable) () -> { + for (int i = 0, o = w * sizePerWorker; i < sizePerWorker; i++) + m.put(o + i, i); + }) + .map(r -> CompletableFuture.runAsync(r, executor)) + .collect(Collectors.toList()); - // Wait for workers and foreman to complete - workers.forEach(CompletableFuture::join); - foreman.join(); + // Wait for workers and foreman to complete + workers.forEach(CompletableFuture::join); + foreman.join(); + } finally { + executor.shutdown(); + } } } diff --git a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java index 114816d4787..e8abec354ed 100644 --- a/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.java +++ b/test/jdk/java/util/jar/JarFile/mrjar/MultiReleaseJarAPI.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 @@ -101,15 +101,12 @@ public void isMultiReleaseJar() throws Exception { testCustomMultiReleaseValue("true", true); testCustomMultiReleaseValue("true\r\nOther: value", true); testCustomMultiReleaseValue("true\nOther: value", true); - // JDK-8200530: '\r' support in Manifest/Attributes will be addressed separately - // testCustomMultiReleaseValue("true\rOther: value", true); + testCustomMultiReleaseValue("true\rOther: value", true); testCustomMultiReleaseValue("false", false); testCustomMultiReleaseValue(" true", false); testCustomMultiReleaseValue("true ", false); testCustomMultiReleaseValue("true\n true", false); - - // JDK-8200530: '\r' support in Manifest/Attributes will be addressed separately testCustomMultiReleaseValue("true\r true", false); testCustomMultiReleaseValue("true\r\n true", false); diff --git a/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexResetUpdate.java b/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexResetUpdate.java index f0796bed901..032457aedb3 100644 --- a/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexResetUpdate.java +++ b/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexResetUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, 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 @@ -86,7 +86,7 @@ public void run(List properties) throws Exception { TIMEOUT_FACTOR = Double.parseDouble(toFactor); } static int adjustCount(int count) { - return (int) Math.ceil(TIMEOUT_FACTOR * count); + return Math.min(count, (int) Math.ceil(TIMEOUT_FACTOR * count)); } private static final String PREFIX = @@ -211,21 +211,20 @@ static void test(String name, List properties) ReferenceQueue queue = new ReferenceQueue(); WeakReference fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue); - if (fooRef.get() != fooChild.getParent()) { + if (!fooRef.refersTo(fooChild.getParent())) { throw new RuntimeException("Unexpected parent logger: " + fooChild.getParent() +"\n\texpected: " + fooRef.get()); } WeakReference barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue); - if (barRef.get() != barChild.getParent()) { + if (!barRef.refersTo(barChild.getParent())) { throw new RuntimeException("Unexpected parent logger: " + barChild.getParent() +"\n\texpected: " + barRef.get()); } Reference ref2; - int max = adjustCount(3); + int max = adjustCount(6); barChild = null; - while ((ref2 = queue.poll()) == null) { + while ((ref2 = queue.remove(500)) == null) { System.gc(); - Thread.sleep(1000); if (--max == 0) break; } @@ -347,7 +346,7 @@ static void test(String name, List properties) throw new RuntimeException("Unexpected reference: " + ref2 +"\n\texpected: " + fooRef); } - if (ref2.get() != null) { + if (!ref2.refersTo(null)) { throw new RuntimeException("Referent not cleared: " + ref2.get()); } System.out.println("Got fooRef after reset(), fooChild is " + fooChild); diff --git a/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexUpdate.java b/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexUpdate.java index a8d3240b976..2f9f360d7d4 100644 --- a/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexUpdate.java +++ b/test/jdk/java/util/logging/LogManager/Configuration/updateConfiguration/HandlersOnComplexUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, 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 @@ -86,7 +86,7 @@ public void run(List properties) throws Exception { TIMEOUT_FACTOR = Double.parseDouble(toFactor); } static int adjustCount(int count) { - return (int) Math.ceil(TIMEOUT_FACTOR * count); + return Math.min(count, (int) Math.ceil(TIMEOUT_FACTOR * count)); } private static final String PREFIX = @@ -211,21 +211,20 @@ static void test(String name, List properties) ReferenceQueue queue = new ReferenceQueue(); WeakReference fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue); - if (fooRef.get() != fooChild.getParent()) { + if (!fooRef.refersTo(fooChild.getParent())) { throw new RuntimeException("Unexpected parent logger: " + fooChild.getParent() +"\n\texpected: " + fooRef.get()); } WeakReference barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue); - if (barRef.get() != barChild.getParent()) { + if (!barRef.refersTo(barChild.getParent())) { throw new RuntimeException("Unexpected parent logger: " + barChild.getParent() +"\n\texpected: " + barRef.get()); } Reference ref2; - int max = adjustCount(3); + int max = adjustCount(6); barChild = null; - while ((ref2 = queue.poll()) == null) { + while ((ref2 = queue.remove(500)) == null) { System.gc(); - Thread.sleep(1000); if (--max == 0) break; } @@ -335,7 +334,7 @@ static void test(String name, List properties) throw new RuntimeException("Unexpected reference: " + ref2 +"\n\texpected: " + fooRef); } - if (ref2.get() != null) { + if (!ref2.refersTo(null)) { throw new RuntimeException("Referent not cleared: " + ref2.get()); } System.out.println("Got fooRef after reset(), fooChild is " + fooChild); diff --git a/test/jdk/java/util/regex/GraphemeTest.java b/test/jdk/java/util/regex/GraphemeTest.java deleted file mode 100644 index e0370220f43..00000000000 --- a/test/jdk/java/util/regex/GraphemeTest.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * 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 - * 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 7071819 8221431 8239383 - * @summary tests Unicode Extended Grapheme support - * @library /lib/testlibrary/java/lang - * @run testng GraphemeTest - */ - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.testng.annotations.Test; -import static org.testng.Assert.fail; - -public class GraphemeTest { - - @Test - public static void testGraphemeBreakProperty() throws Throwable { - testProps(UCDFiles.GRAPHEME_BREAK_PROPERTY); - } - - @Test - public static void testEmojiData() throws Throwable { - testProps(UCDFiles.EMOJI_DATA); - } - - private static void testProps(Path path) throws IOException { - Files.lines(path) - .map( ln -> ln.replaceFirst("#.*", "") ) - .filter( ln -> ln.length() != 0 ) - .forEach(ln -> { - String[] strs = ln.split("\\s+"); - int off = strs[0].indexOf(".."); - int cp0, cp1; - String expected = strs[2]; - if (off != -1) { - cp0 = Integer.parseInt(strs[0], 0, off, 16); - cp1 = Integer.parseInt(strs[0], off + 2, strs[0].length(), 16); - } else { - cp0 = cp1 = Integer.parseInt(strs[0], 16); - } - for (int cp = cp0; cp <= cp1; cp++) { - // Ignore Emoji* for now (only interested in Extended_Pictographic) - if (expected.startsWith("Emoji")) { - continue; - } - - // NOTE: - // #tr29 "plus a few General_Category = Spacing_Mark needed for - // canonical equivalence." - // For "extended grapheme clusters" support, there is no - // need actually to diff "extend" and "spackmark" given GB9, GB9a. - if (!expected.equals(types[getType(cp)])) { - if ("Extend".equals(expected) && - "SpacingMark".equals(types[getType(cp)])) - System.out.printf("[%x] [%s][%d] -> [%s]%n", - cp, expected, Character.getType(cp), types[getType(cp)]); - else - fail(String.format( - "cp=[%x], expeced:[%s] result:[%s]%n", - cp, expected, types[getType(cp)])); - } - } - }); - } - - private static final String[] types = { - "Other", "CR", "LF", "Control", "Extend", "ZWJ", "Regional_Indicator", - "Prepend", "SpacingMark", - "L", "V", "T", "LV", "LVT", - "Extended_Pictographic" }; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - // from java.util.regex.Grapheme.java - // types - private static final int OTHER = 0; - private static final int CR = 1; - private static final int LF = 2; - private static final int CONTROL = 3; - private static final int EXTEND = 4; - private static final int ZWJ = 5; - private static final int RI = 6; - private static final int PREPEND = 7; - private static final int SPACINGMARK = 8; - private static final int L = 9; - private static final int V = 10; - private static final int T = 11; - private static final int LV = 12; - private static final int LVT = 13; - private static final int EXTENDED_PICTOGRAPHIC = 14; - - private static final int FIRST_TYPE = 0; - private static final int LAST_TYPE = 14; - - private static boolean[][] rules; - static { - rules = new boolean[LAST_TYPE + 1][LAST_TYPE + 1]; - // GB 999 Any + Any -> default - for (int i = FIRST_TYPE; i <= LAST_TYPE; i++) - for (int j = FIRST_TYPE; j <= LAST_TYPE; j++) - rules[i][j] = true; - // GB 6 L x (L | V | LV | VT) - rules[L][L] = false; - rules[L][V] = false; - rules[L][LV] = false; - rules[L][LVT] = false; - // GB 7 (LV | V) x (V | T) - rules[LV][V] = false; - rules[LV][T] = false; - rules[V][V] = false; - rules[V][T] = false; - // GB 8 (LVT | T) x T - rules[LVT][T] = false; - rules[T][T] = false; - // GB 9 x (Extend|ZWJ) - // GB 9a x Spacing Mark - // GB 9b Prepend x - for (int i = FIRST_TYPE; i <= LAST_TYPE; i++) { - rules[i][EXTEND] = false; - rules[i][ZWJ] = false; - rules[i][SPACINGMARK] = false; - rules[PREPEND][i] = false; - } - // GB 4 (Control | CR | LF) + - // GB 5 + (Control | CR | LF) - for (int i = FIRST_TYPE; i <= LAST_TYPE; i++) - for (int j = CR; j <= CONTROL; j++) { - rules[i][j] = true; - rules[j][i] = true; - } - // GB 3 CR x LF - rules[CR][LF] = false; - // GB 11 Exended_Pictographic x (Extend|ZWJ) - rules[EXTENDED_PICTOGRAPHIC][EXTEND] = false; - rules[EXTENDED_PICTOGRAPHIC][ZWJ] = false; - } - - // Hangul syllables - private static final int SYLLABLE_BASE = 0xAC00; - private static final int LCOUNT = 19; - private static final int VCOUNT = 21; - private static final int TCOUNT = 28; - private static final int NCOUNT = VCOUNT * TCOUNT; // 588 - private static final int SCOUNT = LCOUNT * NCOUNT; // 11172 - - // #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) { - return cp == 0x102B || cp == 0x102C || cp == 0x1038 || - cp >= 0x1062 && cp <= 0x1064 || - cp >= 0x1062 && cp <= 0x106D || - cp == 0x1083 || - cp >= 0x1087 && cp <= 0x108C || - cp == 0x108F || - cp >= 0x109A && cp <= 0x109C || - cp == 0x1A61 || cp == 0x1A63 || cp == 0x1A64 || - cp == 0xAA7B || cp == 0xAA7D; - } - - @SuppressWarnings("fallthrough") - private static int getType(int cp) { - if (isExtendedPictographic(cp)) { - return EXTENDED_PICTOGRAPHIC; - } - - int type = Character.getType(cp); - switch(type) { - case Character.CONTROL: - if (cp == 0x000D) - return CR; - if (cp == 0x000A) - return LF; - return CONTROL; - case Character.UNASSIGNED: - // NOTE: #tr29 lists "Unassigned and Default_Ignorable_Code_Point" as Control - // but GraphemeBreakTest.txt lists u+0378/reserved-0378 as "Other" - // so type it as "Other" to make the test happy - if (cp == 0x0378) - return OTHER; - - case Character.LINE_SEPARATOR: - case Character.PARAGRAPH_SEPARATOR: - case Character.SURROGATE: - return CONTROL; - case Character.FORMAT: - if (cp == 0x200C || - cp >= 0xE0020 && cp <= 0xE007F) - return EXTEND; - if (cp == 0x200D) - return ZWJ; - if (cp >= 0x0600 && cp <= 0x0605 || - cp == 0x06DD || cp == 0x070F || cp == 0x08E2 || - cp == 0x110BD || cp == 0x110CD) - return PREPEND; - return CONTROL; - case Character.NON_SPACING_MARK: - case Character.ENCLOSING_MARK: - // NOTE: - // #tr29 "plus a few General_Category = Spacing_Mark needed for - // canonical equivalence." - // but for "extended grapheme clusters" support, there is no - // need actually to diff "extend" and "spackmark" given GB9, GB9a - return EXTEND; - case Character.COMBINING_SPACING_MARK: - if (isExcludedSpacingMark(cp)) - return OTHER; - // NOTE: - // 0x11720 and 0x11721 are mentioned in #tr29 as - // OTHER_LETTER but it appears their category has been updated to - // COMBING_SPACING_MARK already (verified in ver.8) - return SPACINGMARK; - case Character.OTHER_SYMBOL: - if (cp >= 0x1F1E6 && cp <= 0x1F1FF) - return RI; - return OTHER; - case Character.MODIFIER_LETTER: - case Character.MODIFIER_SYMBOL: - // WARNING: - // not mentioned in #tr29 but listed in GraphemeBreakProperty.txt - if (cp == 0xFF9E || cp == 0xFF9F || - cp >= 0x1F3FB && cp <= 0x1F3FF) - return EXTEND; - return OTHER; - case Character.OTHER_LETTER: - if (cp == 0x0E33 || cp == 0x0EB3) - return SPACINGMARK; - // hangul jamo - if (cp >= 0x1100 && cp <= 0x11FF) { - if (cp <= 0x115F) - return L; - if (cp <= 0x11A7) - return V; - return T; - } - // hangul syllables - int sindex = cp - SYLLABLE_BASE; - if (sindex >= 0 && sindex < SCOUNT) { - - if (sindex % TCOUNT == 0) - return LV; - return LVT; - } - // hangul jamo_extended A - if (cp >= 0xA960 && cp <= 0xA97C) - return L; - // hangul jamo_extended B - if (cp >= 0xD7B0 && cp <= 0xD7C6) - return V; - if (cp >= 0xD7CB && cp <= 0xD7FB) - return T; - - // Prepend - switch (cp) { - case 0x0D4E: - case 0x111C2: - case 0x111C3: - case 0x1193F: - case 0x11941: - case 0x11A3A: - case 0x11A84: - case 0x11A85: - case 0x11A86: - case 0x11A87: - case 0x11A88: - case 0x11A89: - case 0x11D46: - return PREPEND; - } - } - return OTHER; - } - - // from generated java.util.regex.EmojiData.java - static boolean isExtendedPictographic(int cp) { - return - cp == 0x00A9 || - cp == 0x00AE || - cp == 0x203C || - cp == 0x2049 || - cp == 0x2122 || - cp == 0x2139 || - (cp >= 0x2194 && cp <= 0x2199) || - cp == 0x21A9 || - cp == 0x21AA || - cp == 0x231A || - cp == 0x231B || - cp == 0x2328 || - cp == 0x2388 || - cp == 0x23CF || - (cp >= 0x23E9 && cp <= 0x23F3) || - (cp >= 0x23F8 && cp <= 0x23FA) || - cp == 0x24C2 || - cp == 0x25AA || - cp == 0x25AB || - cp == 0x25B6 || - cp == 0x25C0 || - (cp >= 0x25FB && cp <= 0x25FE) || - (cp >= 0x2600 && cp <= 0x2605) || - (cp >= 0x2607 && cp <= 0x2612) || - (cp >= 0x2614 && cp <= 0x2685) || - (cp >= 0x2690 && cp <= 0x2705) || - (cp >= 0x2708 && cp <= 0x2712) || - cp == 0x2714 || - cp == 0x2716 || - cp == 0x271D || - cp == 0x2721 || - cp == 0x2728 || - cp == 0x2733 || - cp == 0x2734 || - cp == 0x2744 || - cp == 0x2747 || - cp == 0x274C || - cp == 0x274E || - (cp >= 0x2753 && cp <= 0x2755) || - cp == 0x2757 || - (cp >= 0x2763 && cp <= 0x2767) || - (cp >= 0x2795 && cp <= 0x2797) || - cp == 0x27A1 || - cp == 0x27B0 || - cp == 0x27BF || - cp == 0x2934 || - cp == 0x2935 || - (cp >= 0x2B05 && cp <= 0x2B07) || - cp == 0x2B1B || - cp == 0x2B1C || - cp == 0x2B50 || - cp == 0x2B55 || - cp == 0x3030 || - cp == 0x303D || - cp == 0x3297 || - cp == 0x3299 || - (cp >= 0x1F000 && cp <= 0x1F0FF) || - (cp >= 0x1F10D && cp <= 0x1F10F) || - cp == 0x1F12F || - (cp >= 0x1F16C && cp <= 0x1F171) || - cp == 0x1F17E || - cp == 0x1F17F || - cp == 0x1F18E || - (cp >= 0x1F191 && cp <= 0x1F19A) || - (cp >= 0x1F1AD && cp <= 0x1F1E5) || - (cp >= 0x1F201 && cp <= 0x1F20F) || - cp == 0x1F21A || - cp == 0x1F22F || - (cp >= 0x1F232 && cp <= 0x1F23A) || - (cp >= 0x1F23C && cp <= 0x1F23F) || - (cp >= 0x1F249 && cp <= 0x1F3FA) || - (cp >= 0x1F400 && cp <= 0x1F53D) || - (cp >= 0x1F546 && cp <= 0x1F64F) || - (cp >= 0x1F680 && cp <= 0x1F6FF) || - (cp >= 0x1F774 && cp <= 0x1F77F) || - (cp >= 0x1F7D5 && cp <= 0x1F7FF) || - (cp >= 0x1F80C && cp <= 0x1F80F) || - (cp >= 0x1F848 && cp <= 0x1F84F) || - (cp >= 0x1F85A && cp <= 0x1F85F) || - (cp >= 0x1F888 && cp <= 0x1F88F) || - (cp >= 0x1F8AE && cp <= 0x1F8FF) || - (cp >= 0x1F90C && cp <= 0x1F93A) || - (cp >= 0x1F93C && cp <= 0x1F945) || - (cp >= 0x1F947 && cp <= 0x1FAFF) || - (cp >= 0x1FC00 && cp <= 0x1FFFD); - } -} diff --git a/test/jdk/java/util/regex/whitebox/GraphemeTest.java b/test/jdk/java/util/regex/whitebox/GraphemeTest.java new file mode 100644 index 00000000000..d3c85276dbb --- /dev/null +++ b/test/jdk/java/util/regex/whitebox/GraphemeTest.java @@ -0,0 +1,106 @@ +/* + * 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 + * 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 7071819 8221431 8239383 8273430 + * @summary tests Unicode Extended Grapheme support + * @library /lib/testlibrary/java/lang + * @build java.base/java.util.regex.GraphemeTestAccessor + * @run testng GraphemeTest + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.testng.annotations.Test; +import static org.testng.Assert.fail; +import static org.testng.Assert.assertFalse; +import java.util.regex.GraphemeTestAccessor; + +public class GraphemeTest { + + @Test + public static void testGraphemeBreakProperty() throws Throwable { + testProps(UCDFiles.GRAPHEME_BREAK_PROPERTY); + } + + @Test + public static void testEmojiData() throws Throwable { + testProps(UCDFiles.EMOJI_DATA); + } + + @Test + public static void testExcludedSpacingMarks() { + assertFalse(GraphemeTestAccessor.isExcludedSpacingMark(0x1065)); + assertFalse(GraphemeTestAccessor.isExcludedSpacingMark(0x1066)); + } + + private static void testProps(Path path) throws IOException { + Files.lines(path) + .map(ln -> ln.replaceFirst("#.*", "")) + .filter(ln -> ln.length() != 0) + .forEach(ln -> { + String[] strs = ln.split("\\s+"); + int off = strs[0].indexOf(".."); + int cp0, cp1; + String expected = strs[2]; + if (off != -1) { + cp0 = Integer.parseInt(strs[0], 0, off, 16); + cp1 = Integer.parseInt(strs[0], off + 2, strs[0].length(), 16); + } else { + cp0 = cp1 = Integer.parseInt(strs[0], 16); + } + for (int cp = cp0; cp <= cp1; cp++) { + // Ignore Emoji* for now (only interested in Extended_Pictographic) + if (expected.startsWith("Emoji")) { + continue; + } + + // NOTE: + // #tr29 "plus a few General_Category = Spacing_Mark needed for + // canonical equivalence." + // For "extended grapheme clusters" support, there is no + // need actually to diff "extend" and "spackmark" given GB9, GB9a. + if (!expected.equals(types[GraphemeTestAccessor.getType(cp)])) { + if ("Extend".equals(expected) && + "SpacingMark".equals(types[GraphemeTestAccessor.getType(cp)])) + System.out.printf("[%x] [%s][%d] -> [%s]%n", + cp, expected, Character.getType(cp), types[GraphemeTestAccessor.getType(cp)]); + else + fail(String.format( + "cp=[%x], expeced:[%s] result:[%s]%n", + cp, expected, types[GraphemeTestAccessor.getType(cp)])); + } + } + }); + } + + private static final String[] types = { + "Other", "CR", "LF", "Control", "Extend", "ZWJ", "Regional_Indicator", + "Prepend", "SpacingMark", + "L", "V", "T", "LV", "LVT", + "Extended_Pictographic"}; +} + diff --git a/test/jdk/java/util/regex/whitebox/java.base/java/util/regex/GraphemeTestAccessor.java b/test/jdk/java/util/regex/whitebox/java.base/java/util/regex/GraphemeTestAccessor.java new file mode 100644 index 00000000000..feb04592d91 --- /dev/null +++ b/test/jdk/java/util/regex/whitebox/java.base/java/util/regex/GraphemeTestAccessor.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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 + * 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 java.util.regex; + +public class GraphemeTestAccessor { + + public static boolean isExcludedSpacingMark(int cp) { + return Grapheme.isExcludedSpacingMark(cp); + } + + public static int getType(int cp) { + return Grapheme.getType(cp); + } +} + + + + diff --git a/test/jdk/java/util/zip/DataDescriptorSignatureMissing.java b/test/jdk/java/util/zip/DataDescriptorSignatureMissing.java index f7a2ae2f8eb..db641ccba4c 100644 --- a/test/jdk/java/util/zip/DataDescriptorSignatureMissing.java +++ b/test/jdk/java/util/zip/DataDescriptorSignatureMissing.java @@ -1,5 +1,6 @@ /* * Copyright 2012 Google, Inc. All Rights Reserved. + * 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 @@ -24,124 +25,137 @@ /** * @test * @bug 8056934 - * @summary Check ability to read zip files created by python zipfile - * implementation, which fails to write optional (but recommended) data - * descriptor signatures. Repro scenario is a Java -> Python -> Java round trip: - * - ZipOutputStream creates zip file with DEFLATED entries and data - * descriptors with optional signature "PK0x0708". - * - Python reads those entries, preserving the 0x08 flag byte - * - Python outputs those entries with data descriptors lacking the - * optional signature. - * - ZipInputStream cannot handle the missing signature - * + * @summary Verify the ability to read zip files whose local header + * data descriptor is missing the optional signature + *

      * No way to adapt the technique in this test to get a ZIP64 zip file * without data descriptors was found. - * - * @ignore This test has brittle dependencies on an external working python. + * @run junit DataDescriptorSignatureMissing */ + +import org.junit.jupiter.api.Test; + import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.util.zip.*; -public class DataDescriptorSignatureMissing { - void printStream(InputStream is) throws IOException { - Reader r = new InputStreamReader(is); - StringBuilder sb = new StringBuilder(); - char[] buf = new char[1024]; - int n; - while ((n = r.read(buf)) > 0) { - sb.append(buf, 0, n); - } - System.out.print(sb); - } +import static org.junit.jupiter.api.Assertions.*; + +public class DataDescriptorSignatureMissing { + + /** + * Verify that ZipInputStream correctly parses a ZIP with a Data Descriptor without + * the recommended but optional signature. + */ + @Test + public void shouldParseSignaturelessDescriptor() throws IOException { + // The ZIP with a signature-less descriptor + byte[] zip = makeZipWithSignaturelessDescriptor(); - int entryCount(File zipFile) throws IOException { - try (FileInputStream fis = new FileInputStream(zipFile); - ZipInputStream zis = new ZipInputStream(fis)) { - for (int count = 0;; count++) - if (zis.getNextEntry() == null) - return count; + // ZipInputStream should read the signature-less data descriptor + try (ZipInputStream in = new ZipInputStream( + new ByteArrayInputStream(zip))) { + ZipEntry first = in.getNextEntry(); + assertNotNull(first, "Zip file is unexpectedly missing first entry"); + assertEquals("first", first.getName()); + assertArrayEquals("first".getBytes(StandardCharsets.UTF_8), in.readAllBytes()); + + ZipEntry second = in.getNextEntry(); + assertNotNull(second, "Zip file is unexpectedly missing second entry"); + assertEquals("second", second.getName()); + assertArrayEquals("second".getBytes(StandardCharsets.UTF_8), in.readAllBytes()); } + } - void test(String[] args) throws Throwable { - if (! new File("/usr/bin/python").canExecute()) - return; - - // Create a java zip file with DEFLATED entries and data - // descriptors with signatures. - final File in = new File("in.zip"); - final File out = new File("out.zip"); - final int count = 3; - try (FileOutputStream fos = new FileOutputStream(in); - ZipOutputStream zos = new ZipOutputStream(fos)) { - for (int i = 0; i < count; i++) { - ZipEntry ze = new ZipEntry("hello.python" + i); - ze.setMethod(ZipEntry.DEFLATED); - zos.putNextEntry(ze); - zos.write(new byte[10]); - zos.closeEntry(); - } + /** + * The 'Data descriptor' record is used to facilitate ZIP streaming. If the size of an + * entry is unknown at the time the LOC header is written, bit 3 of the General Purpose Bit Flag + * is set, and the File data is immediately followed by the 'Data descriptor' record. This record + * then contains the compressed and uncompressed sizes of the entry and also the CRC value. + * + * The 'Data descriptor' record is usually preceded by the recommended, but optional + * signature value 0x08074b50. + * + * A ZIP entry in streaming mode has the following structure: + * + * ------ Local File Header ------ + * 000000 signature 0x04034b50 + * 000004 version 20 + * 000006 flags 0x0808 # Notice bit 3 is set + * [..] Omitted for brevity + * + * ------ File Data ------ + * 000035 data 7 bytes + * + * ------ Data Descriptor ------ + * 000042 signature 0x08074b50 + * 000046 crc 0x3610a686 + * 000050 csize 7 + * 000054 size 5 + * + * A signature-less data descriptor will look like the following: + * + * ------ Data Descriptor ------ + * 000042 crc 0x3610a686 + * 000046 csize 7 + * 000050 size 5 + * + * This method produces a ZIP with two entries, where the first entry + * is made signature-less. + */ + private static byte[] makeZipWithSignaturelessDescriptor() throws IOException { + // Offset of the signed data descriptor + int sigOffset; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (ZipOutputStream zo = new ZipOutputStream(out)) { + // Write a first entry + zo.putNextEntry(new ZipEntry("first")); + zo.write("first".getBytes(StandardCharsets.UTF_8)); + // Force the data descriptor to be written out + zo.closeEntry(); + // Signed data descriptor starts 16 bytes before current offset + sigOffset = out.size() - 4 * Integer.BYTES; + // Add a second entry + zo.putNextEntry(new ZipEntry("second")); + zo.write("second".getBytes(StandardCharsets.UTF_8)); } - // Copy the zip file using python's zipfile module - String[] python_program_lines = { - "import os", - "import zipfile", - "input_zip = zipfile.ZipFile('in.zip', mode='r')", - "output_zip = zipfile.ZipFile('out.zip', mode='w')", - "count08 = 0", - "for input_info in input_zip.infolist():", - " output_info = input_info", - " if output_info.flag_bits & 0x08 == 0x08:", - " count08 += 1", - " output_zip.writestr(output_info, input_zip.read(input_info))", - "output_zip.close()", - "if count08 == 0:", - " raise ValueError('Expected to see entries with 0x08 flag_bits set')", - }; - StringBuilder python_program_builder = new StringBuilder(); - for (String line : python_program_lines) - python_program_builder.append(line).append('\n'); - String python_program = python_program_builder.toString(); - String[] cmdline = { "/usr/bin/python", "-c", python_program }; - ProcessBuilder pb = new ProcessBuilder(cmdline); - pb.redirectErrorStream(true); - Process p = pb.start(); - printStream(p.getInputStream()); - p.waitFor(); - equal(p.exitValue(), 0); - - File pythonZipFile = new File("out.zip"); - check(pythonZipFile.exists()); - - equal(entryCount(in), - entryCount(out)); - - // We expect out to be identical to in, except for the removal of - // the optional data descriptor signatures. - final int SIG_LENGTH = 4; // length of a zip signature - PKxx - equal(in.length(), - out.length() + SIG_LENGTH * count); - - in.delete(); - out.delete(); - } + // The generated ZIP file with a signed data descriptor + byte[] sigZip = out.toByteArray(); + + // The offset of the CRC immediately following the 4-byte signature + int crcOffset = sigOffset + Integer.BYTES; + + // Create a ZIP file with a signature-less data descriptor for the first entry + ByteArrayOutputStream sigLess = new ByteArrayOutputStream(); + sigLess.write(sigZip, 0, sigOffset); + // Skip the signature + sigLess.write(sigZip, crcOffset, sigZip.length - crcOffset); + + byte[] siglessZip = sigLess.toByteArray(); - //--------------------- Infrastructure --------------------------- - volatile int passed = 0, failed = 0; - void pass() {passed++;} - void fail() {failed++; Thread.dumpStack();} - void fail(String msg) {System.err.println(msg); fail();} - void unexpected(Throwable t) {failed++; t.printStackTrace();} - void check(boolean cond) {if (cond) pass(); else fail();} - void equal(Object x, Object y) { - if (x == null ? y == null : x.equals(y)) pass(); - else fail(x + " not equal to " + y);} - public static void main(String[] args) throws Throwable { - new DataDescriptorSignatureMissing().instanceMain(args);} - public void instanceMain(String[] args) throws Throwable { - try {test(args);} catch (Throwable t) {unexpected(t);} - System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); - if (failed > 0) throw new AssertionError("Some tests failed");} + // Adjust the CEN offset in the END header + ByteBuffer buffer = ByteBuffer.wrap(siglessZip).order(ByteOrder.LITTLE_ENDIAN); + // Reduce cenOffset by 4 bytes + int cenOff = siglessZip.length - ZipFile.ENDHDR + ZipFile.ENDOFF; + int realCenOff = buffer.getInt(cenOff) - Integer.BYTES; + buffer.putInt(cenOff, realCenOff); + + // Adjust the LOC offset in the second CEN header + int cen = realCenOff; + // Skip past the first CEN header + int nlen = buffer.getShort(cen + ZipFile.CENNAM); + cen += ZipFile.CENHDR + nlen; + + // Reduce LOC offset by 4 bytes + int locOff = cen + ZipFile.CENOFF; + buffer.putInt(locOff, buffer.getInt(locOff) - Integer.BYTES); + + return siglessZip; + } } diff --git a/test/jdk/java/util/zip/ZipFile/ReadLongZipFileName.java b/test/jdk/java/util/zip/ZipFile/ReadLongZipFileName.java index c4e47193e14..d6fa7269ff0 100644 --- a/test/jdk/java/util/zip/ZipFile/ReadLongZipFileName.java +++ b/test/jdk/java/util/zip/ZipFile/ReadLongZipFileName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -21,114 +21,80 @@ * questions. */ -/** +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + + +/* * @test * @bug 6374379 - * @library /test/lib - * @build jdk.test.lib.Platform - * jdk.test.lib.util.FileUtils - * @run main ReadLongZipFileName * @summary Verify that we can read zip file names > 255 chars long + * @run junit ReadLongZipFileName */ - -import java.io.*; -import java.util.jar.*; -import java.util.Stack; -import jdk.test.lib.util.FileUtils; - public class ReadLongZipFileName { - private static String entryName = "testFile.txt";; + private static final String ENTRY_NAME = "testFile.txt"; + private static final String LONG_DIR_NAME = "abcdefghijklmnopqrstuvwx"; // 24 chars + private static final String JAR_FILE_NAME = "areallylargejarfilename.jar"; // 27 chars - public static void realMain(String args[]) { - String longDirName = "abcdefghijklmnopqrstuvwx"; // 24 chars. - String jarFileName = "areallylargejarfilename.jar"; // 27 chars. - File file = null; - File myJarFile = null; - int currentFileLength = 0; - int minRequiredLength = 600; // long enough to definitely fail. - Stack directories = new Stack(); + /* + * Creates a jar file at a path whose path name length and jar file name length + * combined is large. Then use the java.util.jar.JarFile APIs to open and read the jar file + * to verify the APIs work against those jar/zip files. + */ + @Test + public void testOpenAndReadJarFile() throws Exception { + int minRequiredPathLength = 600; // long enough to definitely fail. + Path tmpDir = Files.createTempDirectory(Path.of("."), "ReadLongZipFileName-test") + .normalize().toAbsolutePath(); + // Create a directory structure long enough that the filename will + // put us over the minRequiredLength. + int currentPathLength = 0; + Path jarFileDir = tmpDir; + do { + jarFileDir = jarFileDir.resolve(LONG_DIR_NAME); + Files.createDirectories(jarFileDir); + currentPathLength = jarFileDir.toFile().getCanonicalPath().length(); + } while (currentPathLength < (minRequiredPathLength - JAR_FILE_NAME.length())); - String filename = "." + File.separator; - try { - // Create a directory structure long enough that the filename will - // put us over the minRequiredLength. - do { - filename = filename + longDirName + File.separator; - file = new File(filename); - file.mkdir(); - currentFileLength = file.getCanonicalPath().length(); - directories.push(file); - } while (currentFileLength < (minRequiredLength - jarFileName.length())); - - // Create a new Jar file: use jar instead of zip to make sure long - // names work for both zip and jar subclass. - filename = filename + jarFileName; - JarOutputStream out = new JarOutputStream( - new BufferedOutputStream( - new FileOutputStream(filename.toString()))); - out.putNextEntry(new JarEntry(entryName)); + // Create a new Jar file: use jar instead of zip to make sure long + // names work for both zip and jar subclass. + Path jarFilePath = jarFileDir.resolve(JAR_FILE_NAME); + System.out.println("creating a jar file at " + jarFilePath); + try (JarOutputStream out = new JarOutputStream( + new BufferedOutputStream(Files.newOutputStream(jarFilePath)))) { + out.putNextEntry(new JarEntry(ENTRY_NAME)); out.write(1); - out.close(); - myJarFile = new File(filename.toString()); - currentFileLength = myJarFile.getCanonicalPath().length(); - if (!myJarFile.exists()) { - fail("Jar file does not exist."); - } - } catch (IOException e) { - unexpected(e, "Problem creating the Jar file."); } + assertTrue(Files.isRegularFile(jarFilePath), + "jar file " + jarFilePath + " does not exist or is not a file"); - try { - JarFile readJarFile = new JarFile(myJarFile); - JarEntry je = readJarFile.getJarEntry(entryName); - check(je != null); - DataInputStream dis = new DataInputStream( - readJarFile.getInputStream(je)); + try (JarFile readJarFile = new JarFile(jarFilePath.toFile())) { + JarEntry je = readJarFile.getJarEntry(ENTRY_NAME); + assertNotNull(je, "missing jar entry: " + ENTRY_NAME + " in jar file " + jarFilePath); + DataInputStream dis = new DataInputStream(readJarFile.getInputStream(je)); byte val = dis.readByte(); - check(val == 1); + assertEquals(1, val, "unexpected byte " + val + " read from entry " + ENTRY_NAME); try { dis.readByte(); - fail("Read past expected EOF"); + Assertions.fail("Read past expected EOF"); } catch (IOException e) { - pass(); - } - readJarFile.close(); - pass("Opened Jar file for reading with a name " + currentFileLength - + " characters long"); - } catch (IOException e) { - unexpected(e, "Test failed - problem reading the Jar file back in."); - } - - if (myJarFile != null) { - check(myJarFile.delete()); - } - - while (! directories.empty()) { - File f = directories.pop(); - try { - FileUtils.deleteFileWithRetry(f.toPath()); - } catch (IOException e) { - unexpected(e, "Fail to clean up directory, " + f); - break; + // expected + System.out.println("received the expected exception: " + e); } } + System.out.println("Successfully opened and read contents from a jar file with a name " + + jarFilePath.toFile().getCanonicalPath().length() + " characters long"); } - - //--------------------- Infrastructure --------------------------- - static volatile int passed = 0, failed = 0; - static void pass() {passed++;} - static void pass(String msg) {System.out.println(msg); passed++;} - static void fail() {failed++; Thread.dumpStack();} - static void fail(String msg) {System.out.println(msg); fail();} - static void unexpected(Throwable t) {failed++; t.printStackTrace();} - static void unexpected(Throwable t, String msg) { - System.out.println(msg); failed++; t.printStackTrace();} - static void check(boolean cond) {if (cond) pass(); else fail();} - static void equal(Object x, Object y) { - if (x == null ? y == null : x.equals(y)) pass(); - else fail(x + " not equal to " + y);} - public static void main(String[] args) throws Throwable { - try {realMain(args);} catch (Throwable t) {unexpected(t);} - System.out.println("\nPassed = " + passed + " failed = " + failed); - if (failed > 0) throw new AssertionError("Some tests failed");} } diff --git a/test/jdk/java/util/zip/ZipFile/Zip64SizeTest.java b/test/jdk/java/util/zip/ZipFile/Zip64SizeTest.java index a7afa2681a0..ca472aa2aa7 100644 --- a/test/jdk/java/util/zip/ZipFile/Zip64SizeTest.java +++ b/test/jdk/java/util/zip/ZipFile/Zip64SizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, 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 @@ -20,117 +20,174 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; -import java.io.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * @test - * @bug 8226530 - * @summary ZIP File System tests that leverage DirectoryStream + * @bug 8226530 8303891 + * @summary Verify that ZipFile reads size fields using the Zip64 extra + * field when only the 'uncompressed size' field has the ZIP64 "magic value" 0xFFFFFFFF * @compile Zip64SizeTest.java - * @run testng Zip64SizeTest + * @run junit Zip64SizeTest */ public class Zip64SizeTest { - - private static final int BUFFER_SIZE = 2048; // ZIP file to create - private static final String ZIP_FILE_NAME = "Zip64SizeTest.zip"; - // File that will be created with a size greater than 0xFFFFFFFF - private static final String LARGE_FILE_NAME = "LargeZipEntry.txt"; - // File that will be created with a size less than 0xFFFFFFFF - private static final String SMALL_FILE_NAME = "SmallZipEntry.txt"; - // List of files to be added to the ZIP file - private static final List ZIP_ENTRIES = List.of(LARGE_FILE_NAME, - SMALL_FILE_NAME); - private static final long LARGE_FILE_SIZE = 5L * 1024L * 1024L * 1024L; // 5GB - private static final long SMALL_FILE_SIZE = 0x100000L; // 1024L x 1024L; + private static final Path ZIP_FILE = Path.of("Zip64SizeTest.zip"); + // Contents to write to ZIP entries + private static final byte[] CONTENT = "Hello".getBytes(StandardCharsets.UTF_8); + // This opaque tag will be ignored by ZipEntry.setExtra0 + private static final int UNKNOWN_TAG = 0x9902; + // Tag used when converting the extra field to a real ZIP64 extra field + private static final short ZIP64_TAG = 0x1; + // Marker value to indicate that the actual value is stored in the ZIP64 extra field + private static final int ZIP64_MAGIC_VALUE = 0xFFFFFFFF; /** - * Validate that if the size of a ZIP entry exceeds 0xFFFFFFFF, that the - * correct size is returned from the ZIP64 Extended information. - * @throws IOException + * Validate that if the 'uncompressed size' of a ZIP CEN header is 0xFFFFFFFF, then the + * actual size is retrieved from the corresponding ZIP64 Extended information field. + * + * @throws IOException if an unexpected IOException occurs */ @Test - private static void validateZipEntrySizes() throws IOException { - createFiles(); + public void validateZipEntrySizes() throws IOException { createZipFile(); System.out.println("Validating Zip Entry Sizes"); - try (ZipFile zip = new ZipFile(ZIP_FILE_NAME)) { - ZipEntry ze = zip.getEntry(LARGE_FILE_NAME); + try (ZipFile zip = new ZipFile(ZIP_FILE.toFile())) { + ZipEntry ze = zip.getEntry("first"); System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize()); - assertTrue(ze.getSize() == LARGE_FILE_SIZE); - ze = zip.getEntry(SMALL_FILE_NAME); + assertEquals(CONTENT.length, ze.getSize()); + ze = zip.getEntry("second"); System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize()); - assertTrue(ze.getSize() == SMALL_FILE_SIZE); - + assertEquals(CONTENT.length, ze.getSize()); } } /** - * Delete the files created for use by the test - * @throws IOException if an error occurs deleting the files + * Create a ZIP file with a CEN entry where the 'uncompressed size' is stored in + * the ZIP64 field, but the 'compressed size' is in the CEN field. This makes the + * ZIP64 data block 8 bytes long, which triggers the regression described in 8226530. + * + * The CEN entry for the "first" entry will have the following structure: + * (Note the CEN 'Uncompressed Length' being 0xFFFFFFFF and the ZIP64 + * 'Uncompressed Size' being 5) + * + * 0081 CENTRAL HEADER #1 02014B50 + * 0085 Created Zip Spec 14 '2.0' + * 0086 Created OS 00 'MS-DOS' + * [...] Omitted for brevity + * 0091 CRC F7D18982 + * 0095 Compressed Length 00000007 + * 0099 Uncompressed Length FFFFFFFF + * [...] Omitted for brevity + * 00AF Filename 'first' + * 00B4 Extra ID #0001 0001 'ZIP64' + * 00B6 Length 0008 + * 00B8 Uncompressed Size 0000000000000005 + * + * @throws IOException if an error occurs creating the ZIP File */ - private static void deleteFiles() throws IOException { - Files.deleteIfExists(Path.of(ZIP_FILE_NAME)); - Files.deleteIfExists(Path.of(LARGE_FILE_NAME)); - Files.deleteIfExists(Path.of(SMALL_FILE_NAME)); + private static void createZipFile() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + + // The 'first' entry will store 'uncompressed size' in the Zip64 format + ZipEntry e1 = new ZipEntry("first"); + + // Make an extra field with the correct size for an 8-byte 'uncompressed size' + // Zip64 field. Temporarily use the 'unknown' tag 0x9902 to make + // ZipEntry.setExtra0 skip parsing this as a Zip64. + // See APPNOTE.TXT, 4.6.1 Third Party Mappings + byte[] opaqueExtra = createBlankExtra((short) UNKNOWN_TAG, (short) Long.BYTES); + e1.setExtra(opaqueExtra); + + zos.putNextEntry(e1); + zos.write(CONTENT); + + // A second entry, not in Zip64 format + ZipEntry e2 = new ZipEntry("second"); + zos.putNextEntry(e2); + zos.write(CONTENT); + } + + byte[] zip = baos.toByteArray(); + + // Update the CEN of 'first' to use the Zip64 format + updateCENHeaderToZip64(zip); + Files.write(ZIP_FILE, zip); } /** - * Create the ZIP file adding an entry whose size exceeds 0xFFFFFFFF - * @throws IOException if an error occurs creating the ZIP File + * Update the CEN entry of the "first" entry to use ZIP64 format for the + * 'uncompressed size' field. The updated extra field will have the following + * structure: + * + * 00B4 Extra ID #0001 0001 'ZIP64' + * 00B6 Length 0008 + * 00B8 Uncompressed Size 0000000000000005 + * + * @param zip the ZIP file to update to ZIP64 */ - private static void createZipFile() throws IOException { - try (FileOutputStream fos = new FileOutputStream(ZIP_FILE_NAME); - ZipOutputStream zos = new ZipOutputStream(fos)) { - System.out.printf("Creating Zip file: %s%n", ZIP_FILE_NAME); - for (String srcFile : ZIP_ENTRIES) { - System.out.printf("...Adding Entry: %s%n", srcFile); - File fileToZip = new File(srcFile); - try (FileInputStream fis = new FileInputStream(fileToZip)) { - ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); - zipEntry.setSize(fileToZip.length()); - zos.putNextEntry(zipEntry); - byte[] bytes = new byte[BUFFER_SIZE]; - int length; - while ((length = fis.read(bytes)) >= 0) { - zos.write(bytes, 0, length); - } - } - } - } + private static void updateCENHeaderToZip64(byte[] zip) { + ByteBuffer buffer = ByteBuffer.wrap(zip).order(ByteOrder.LITTLE_ENDIAN); + // Find the offset of the first CEN header + int cenOffset = buffer.getInt(zip.length- ZipFile.ENDHDR + ZipFile.ENDOFF); + // Find the offset of the extra field + int nlen = buffer.getShort(cenOffset + ZipFile.CENNAM); + int extraOffset = cenOffset + ZipFile.CENHDR + nlen; + + // Change the header ID from 'unknown' to ZIP64 + buffer.putShort(extraOffset, ZIP64_TAG); + // Update the 'uncompressed size' ZIP64 value to the actual uncompressed length + int fieldOffset = extraOffset + + Short.BYTES // TAG + + Short.BYTES; // data size + buffer.putLong(fieldOffset, CONTENT.length); + + // Set the 'uncompressed size' field of the CEN to 0xFFFFFFFF + buffer.putInt(cenOffset + ZipFile.CENLEN, ZIP64_MAGIC_VALUE); } /** - * Create the files that will be added to the ZIP file - * @throws IOException if there is a problem creating the files + * Create an extra field with the given tag and data block size, and a + * blank data block. + * @return an extra field with the specified tag and size + * @param tag the header id of the extra field + * @param blockSize the size of the extra field's data block */ - private static void createFiles() throws IOException { - try (RandomAccessFile largeFile = new RandomAccessFile(LARGE_FILE_NAME, "rw"); - RandomAccessFile smallFile = new RandomAccessFile(SMALL_FILE_NAME, "rw")) { - System.out.printf("Creating %s%n", LARGE_FILE_NAME); - largeFile.setLength(LARGE_FILE_SIZE); - System.out.printf("Creating %s%n", SMALL_FILE_NAME); - smallFile.setLength(SMALL_FILE_SIZE); - } + private static byte[] createBlankExtra(short tag, short blockSize) { + int size = Short.BYTES // tag + + Short.BYTES // data block size + + blockSize; // data block; + + byte[] extra = new byte[size]; + ByteBuffer.wrap(extra).order(ByteOrder.LITTLE_ENDIAN) + .putShort(0, tag) + .putShort(Short.BYTES, blockSize); + return extra; } /** * Make sure the needed test files do not exist prior to executing the test * @throws IOException */ - @BeforeMethod + @BeforeEach public void setUp() throws IOException { deleteFiles(); } @@ -139,8 +196,16 @@ public void setUp() throws IOException { * Remove the files created for the test * @throws IOException */ - @AfterMethod + @AfterEach public void tearDown() throws IOException { deleteFiles(); } + + /** + * Delete the files created for use by the test + * @throws IOException if an error occurs deleting the files + */ + private static void deleteFiles() throws IOException { + Files.deleteIfExists(ZIP_FILE); + } } diff --git a/test/jdk/javax/accessibility/manual/ButtonDemo.html b/test/jdk/javax/accessibility/manual/ButtonDemo.html new file mode 100644 index 00000000000..c815fad3a9e --- /dev/null +++ b/test/jdk/javax/accessibility/manual/ButtonDemo.html @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      S.NoTestScenario
      1Button Demo +
        +
      1. Tab until the Button Demo icon has focus. Press 'space' to + choose.
        +
      2. Tab until the "Button Demo" tab has focus. Press 'space'. Press 'tab'.
        +
      3. Use the arrow keys to navigate between "Buttons", "Radio Buttons", and "Check Boxes".
        +
      4. Tab to enter the demo pane. Tab & Shift-Tab to move between each button.
        +
      5. Press 'space' to trigger (i.e. "press") a button.
        +
      6. Repeat steps 1 through 5 for the Radio Button & Check Boxes tabs.
        +
      +
      Expected Result
      +
        +
      1. Verify that as you navigate, the focus is shown, e.g.
      2. + +
      3. As you press 'space' to trigger each button, verify that you see each button visually depress. + e.g.: +
      4. + +
      +
      Note: actual component appearence may vary depending on look and + feel.
      + + + diff --git a/test/jdk/javax/accessibility/manual/ButtonDemo.java b/test/jdk/javax/accessibility/manual/ButtonDemo.java new file mode 100644 index 00000000000..0c352ebf906 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/ButtonDemo.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +/* +@test +@key headful +@summary manual test for accessibility button demo +@run main/manual SwingSetTest ButtonDemo +*/ diff --git a/test/jdk/javax/accessibility/manual/ComboBoxDemo.html b/test/jdk/javax/accessibility/manual/ComboBoxDemo.html new file mode 100644 index 00000000000..aeb6e8f681a --- /dev/null +++ b/test/jdk/javax/accessibility/manual/ComboBoxDemo.html @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      S.NoTestScenario
      1ComboBox Demo +
        +
      1. Tab until ComboBox icon has focus. Press 'Space' to choose.
      2. +
      3. Tab until the "ComboBox Demo" tab has focus. Press 'space'. Press 'tab'.
      4. +
      5. Use Tab and Shift-Tab to move between the four ComboBox widgets. +
      6. Use the space and down arrow keys to bring up the drop-down list.
      7. +
      8. Use the up and down arrows to navigate up and down the list.
      9. +
      10. Use the 'space' key to make a the selection.
      11. +
      12. Repeat 4,5 but hit Esc key to cancel the drop-down.
      13. +
      +
      Expected Result
      +
        +
      1. Verify that space and down arrow bring up the drop-down list.
      2. + +
      3. Verify that up and down arrows move up and down the list.
      4. +
      5. Verify that 'space' makes the selection (the drop-down list should collapse).
      6. +
      +
      + + + + \ No newline at end of file diff --git a/test/jdk/javax/accessibility/manual/ComboBoxDemo.java b/test/jdk/javax/accessibility/manual/ComboBoxDemo.java new file mode 100644 index 00000000000..0522d3caa77 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/ComboBoxDemo.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +/* +@test +@key headful +@summary manual test for accessibility button demo +@run main/manual SwingSetTest ComboBoxDemo +*/ diff --git a/test/jdk/javax/accessibility/manual/DemoSelection.html b/test/jdk/javax/accessibility/manual/DemoSelection.html new file mode 100644 index 00000000000..7f9a28b05a9 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/DemoSelection.html @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      S.NoTestScenario
      1Demo SelectionMove between demos with 'tab' and 'shift-tab', and left and right arrows. Type 'space' to activate a demo. + +
      Expected Result
      + Verify that there is visible focus as you tab (or arrow) between demo icons. Typing 'space' should change + the selected demo. +
      Note: actual component appearence may vary depending on look and + feel.
      + + + diff --git a/test/jdk/javax/accessibility/manual/DemoSelection.java b/test/jdk/javax/accessibility/manual/DemoSelection.java new file mode 100644 index 00000000000..f3e31aa88ec --- /dev/null +++ b/test/jdk/javax/accessibility/manual/DemoSelection.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +/* +@test +@key headful +@summary manual test for accessibility button demo +@run main/manual SwingSetTest DemoSelection +*/ diff --git a/test/jdk/javax/accessibility/manual/OptionPaneDemo.html b/test/jdk/javax/accessibility/manual/OptionPaneDemo.html new file mode 100644 index 00000000000..23693043b5d --- /dev/null +++ b/test/jdk/javax/accessibility/manual/OptionPaneDemo.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
      S.NoTestScenario
      1OptionPane Demo +
        +
      1. Tab until the OptionPane icon has focus. Press 'space' to choose. +
      2. Tab until the "OptionPane Demo" tab has focus. Press 'space'. Press 'tab'. The 'Show Input Dialog' + button should have focus. +
      3. Press 'space'. An Input dialog should pop up. Type some text, and hit return. The dialog should + change to a Message dialog with text saying "That was a pretty good movie!" Press return. +
      4. Bring up the dialog again (space). Press 'esc' and confirm that the dialog goes away without the + Message from above. +
      5. Press Tab to move down through the buttons, and select "Component Dialog Example" (press space). A + dialog should appear. Tab to ensure that you can navigate through all the components:
        + a. Textfield
        + b. ComboBox
        + c. All 4 buttons: "Cancel", "Probably", "Maybe", "No", "Yes".
        + d. Press 'esc' to cancel the dialog.
        +
      +
      + Expected Result +
      + When a popup window is created, focus must be on the popup window; when the window is closed, focus must + return to the previous focus point. +
      + + + + \ No newline at end of file diff --git a/src/hotspot/share/metaprogramming/isVolatile.hpp b/test/jdk/javax/accessibility/manual/OptionPaneDemo.java similarity index 70% rename from src/hotspot/share/metaprogramming/isVolatile.hpp rename to test/jdk/javax/accessibility/manual/OptionPaneDemo.java index cd51e3b53e5..42d3bade529 100644 --- a/src/hotspot/share/metaprogramming/isVolatile.hpp +++ b/test/jdk/javax/accessibility/manual/OptionPaneDemo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -19,15 +19,11 @@ * 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 +@key headful +@summary manual test for accessibility button demo +@run main/manual SwingSetTest OptionPaneDemo +*/ -#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/test/jdk/javax/accessibility/manual/README.md b/test/jdk/javax/accessibility/manual/README.md new file mode 100644 index 00000000000..3b96515c632 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/README.md @@ -0,0 +1,79 @@ + + +# Manual javax.accessibility test suite + +## Configure environment + +### Swing Set 2 + +Prepare an appropriate version of Swing Set 2 from JDK demos. + +### Acessibility frameworks + +Testing can be performed without an accessibility framework or with one of these frameworks: + +1. Windows + 1. JAWS + 2. NVDA +2. Mac OS X + 1. Voice over + +## Executing a test run + +* Start the required accessibility framework, if necessary +* Swing Set 2 jar default location is +<tested jdk>/demo/jfc/SwingSet2/SwingSet2.jar +* To override Swing Set 2 jar use SWINGSET2_JAR environment variable: + + + jtreg ... -e:SWINGSET2_JAR= -m .../javax/accessibility/manual/... + + +## Performing tests + +When a test a started, a UI appears consisting of two frames: test framework frame and Swing Set 2 frame. Test framework +frame will contain a name of the test in the title and detailed instructions. + +1. Follow the test instructions +2. If everything goes as expected + 1. Push "Pass" + 2. UI for this test closes +3. If something goes not accordding to the instructions: + 1. Push "Fail" + 2. A screenshot is taken automatically + 3. Describe the problem + 4. Retake the screenshot, if necessary + 1. Interract with the Swing Set 2 UI to make it showing the failure. Hit "Retake screenshot" + 2. If to demonstrate the failure the UI need to be in a state which prevents using test framework UI, such as model dialogs need to be opened or menu expanded + 1. Enter delay (in seconds) + 2. Push "Retake screenshot" + 3. Prepare the UI + 4. Wait for the screenshot to be retaken + 5. Push "Fail" button again + 6. Screenshot and the description are saved for further analysis + 7. Test UI closes + +**Wasning: Do not close any window directly, all windows will be closed once the test is finished as passed or failed.** + +**Note: Keyboard navigation is supported throughout the test framework UI.** diff --git a/test/jdk/javax/accessibility/manual/SwingSetTest.java b/test/jdk/javax/accessibility/manual/SwingSetTest.java new file mode 100644 index 00000000000..8700f9a07d2 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/SwingSetTest.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +import lib.ManualTestFrame; +import lib.TestResult; + +import java.util.function.Consumer; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.function.Supplier; +import javax.swing.JEditorPane; + +import static java.io.File.separator; + +public class SwingSetTest { + + public static void main(String[] args) throws IOException, InterruptedException, + ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + System.out.println("test image " + System.getenv("TEST_IMAGE_DIR")); + + Consumer testInstructionProvider = e -> { + try { + e.setContentType("text/html"); + e.setPage(SwingSetTest.class.getResource(args[0] + ".html")); + } catch (IOException exception) { + exception.printStackTrace(); + } + }; + + Supplier resultSupplier = ManualTestFrame.showUI(args[0], + "Wait for SwingSet2 to load, follow the instructions, select pass or fail. " + + "Do not close windows manually.", + testInstructionProvider); + + String swingSetJar = System.getenv("SWINGSET2_JAR"); + if (swingSetJar == null) { + swingSetJar = "file://" + System.getProperty("java.home") + + separator + "demo" + + separator + "jfc" + + separator + "SwingSet2" + + separator + "SwingSet2.jar"; + } + System.out.println("Loading SwingSet2 from " + swingSetJar); + ClassLoader ss = new URLClassLoader(new URL[]{new URL(swingSetJar)}); + ss.loadClass("SwingSet2").getMethod("main", String[].class).invoke(null, (Object)new String[0]); + + //this will block until user decision to pass or fail the test + TestResult result = resultSupplier.get(); + ManualTestFrame.handleResult(result, args[0]); + } +} + diff --git a/test/jdk/javax/accessibility/manual/TableDemo.html b/test/jdk/javax/accessibility/manual/TableDemo.html new file mode 100644 index 00000000000..1f0b821b410 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/TableDemo.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      S.NoTestScenario
      1Table Demo (tests table navigation, as well as textfield input) +
        +
      1. Tab until the Table icon has focus. Press 'space' to choose. +
      2. Tab until the "Table Demo" tab has focus. Press 'space'. Press 'tab'. "Reordering allowed" should + have focus. +
      3. Tab to the Printing/Header textfield. Verify that you can type in some text. +
      4. Continue tabbing until focus moves to the table. The table should show focus. +
      5. Press the down arrow. "Mike Albers" should have focus. +
      6. Use the right and left arrow keys to navigate between cells. +
      7. Set focus to a text cell (e.g. someone's first name). Press space to edit. Type some text. Hit + 'enter' and verify the text has been changed. After editing a text cell and hitting 'enter', the + focus could remain on the current cell or go to the next line. +
      8. Press the 'Page Up' and 'Page Down' keys (if available on your keyboard); verify that the Table + scrolls up and down, page by page. +

          + +
      + Expected Result +
      + See above test description. +
      Note: actual component appearence may vary depending on look and + feel.
      + + + + \ No newline at end of file diff --git a/test/jdk/javax/accessibility/manual/TableDemo.java b/test/jdk/javax/accessibility/manual/TableDemo.java new file mode 100644 index 00000000000..ff56bcec4e8 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/TableDemo.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +/* +@test +@key headful +@summary manual test for accessibility button demo +@run main/manual SwingSetTest TableDemo +*/ diff --git a/test/jdk/javax/accessibility/manual/TabsDemo.html b/test/jdk/javax/accessibility/manual/TabsDemo.html new file mode 100644 index 00000000000..06a055d0229 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/TabsDemo.html @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      S.NoTestScenario
      1Tabs within demos (tests table navigation, as well as textfield input) + Continue tabbing to enter a demo's pane. When the top demo tabs have focus, the left and right arrow keys + move between tabs.
      + +
      + Expected Result +
      + Verify that the selected tab changes, e.g. between 'Internal Frame Demo' and "Source Code'. +
      Note: actual component appearence may vary depending on look and + feel.
      + + \ No newline at end of file diff --git a/test/jdk/javax/accessibility/manual/TabsDemo.java b/test/jdk/javax/accessibility/manual/TabsDemo.java new file mode 100644 index 00000000000..35fad6abf82 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/TabsDemo.java @@ -0,0 +1,29 @@ +/* + * 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. + */ +/* +@test +@key headful +@summary manual test for accessibility button demo +@run main/manual SwingSetTest TabsDemo +*/ + diff --git a/test/jdk/javax/accessibility/manual/TestJProgressBarAccessibility.java b/test/jdk/javax/accessibility/manual/TestJProgressBarAccessibility.java new file mode 100644 index 00000000000..0740e8d2971 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/TestJProgressBarAccessibility.java @@ -0,0 +1,117 @@ +/* + * 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. + */ + + +/* +@test +@key headful +@summary manual test for accessibility JProgressBar +@run main/manual TestJProgressBarAccessibility +*/ + +import java.awt.BorderLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.accessibility.AccessibleContext; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; + +import lib.ManualTestFrame; +import lib.TestResult; + +public class TestJProgressBarAccessibility { + + private static JFrame frame; + private static volatile int value = 10; + private static final String instruction = """ + Aim : Check whether JProgressBar value is read in case of VoiceOver or + Screen magnifier shows the magnified value in case of Screen magnifier is enabled + 1) Move the mouse pointer over the JProgressBar and if you + hear the JProgressBar value in case of VoiceOver then the test pass else fail. + 2) Move the mouse pointer over the JProgressBar and if you see the magnified value + when Screen magnifier is enabled then the test pass else fail. + """; + + private static void createTestUI() throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Test JProgressBar accessibility"); + JProgressBar progressBar = new JProgressBar(); + progressBar.setValue(value); + progressBar.setStringPainted(true); + + progressBar.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + if ( value == 100) { + value = 0; + } else { + value += 5; + } + progressBar.setValue(value); + } + }); + + AccessibleContext accessibleContext = + progressBar.getAccessibleContext(); + accessibleContext.setAccessibleName("JProgressBar accessibility name"); + accessibleContext.setAccessibleDescription("Jprogress accessibility " + + "description"); + + frame.getContentPane().add(progressBar, BorderLayout.CENTER); + + frame.setSize(200,200); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, IOException { + + Consumer testInstProvider = e -> { + e.setContentType("text/plain"); + e.setText(instruction); + }; + + Supplier resultSupplier = ManualTestFrame.showUI( + "JProgressBar " + + "Accessibility Test", "Wait until the Test UI is " + + "seen", testInstProvider); + + // Create and show TestUI + createTestUI(); + + //this will block until user decision to pass or fail the test + TestResult testResult = resultSupplier.get(); + ManualTestFrame.handleResult(testResult,"TestJProgressBarAccessibility"); + } +} + diff --git a/test/jdk/javax/accessibility/manual/TreeDemo.html b/test/jdk/javax/accessibility/manual/TreeDemo.html new file mode 100644 index 00000000000..c418b3e5404 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/TreeDemo.html @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
      S.NoTestScenario
      1Tree Demo + Tab until the Tree icon has focus. Press 'space' to choose. + Tab until the "Tree Demo" tab has focus. Press 'space'. Press 'tab'. Press the down arrow. "Music" should + have focus. + Navigate up and down the tree using the up and down arrow keys. + Expand and collapse folders using the right and left arrow keys. +
      + Expected Result +
      + See above test description. +
      + + \ No newline at end of file diff --git a/test/jdk/javax/accessibility/manual/TreeDemo.java b/test/jdk/javax/accessibility/manual/TreeDemo.java new file mode 100644 index 00000000000..1f38f9e9f73 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/TreeDemo.java @@ -0,0 +1,28 @@ +/* + * 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. + */ +/* +@test +@key headful +@summary manual test for accessibility button demo +@run main/manual SwingSetTest TreeDemo +*/ diff --git a/test/jdk/sun/net/www/httptest/HttpCallback.java b/test/jdk/javax/accessibility/manual/lib/DescriptionPane.java similarity index 53% rename from test/jdk/sun/net/www/httptest/HttpCallback.java rename to test/jdk/javax/accessibility/manual/lib/DescriptionPane.java index 27af7b7aaf4..59580d53ace 100644 --- a/test/jdk/sun/net/www/httptest/HttpCallback.java +++ b/test/jdk/javax/accessibility/manual/lib/DescriptionPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -21,19 +21,34 @@ * questions. */ +package lib; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.io.IOException; +import java.util.function.Consumer; +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + /** - * This interface is implemented by classes that wish to handle incoming HTTP - * requests and generate responses. This could be a general purpose HTTP server - * or a test case that expects specific requests from a client. - *

      - * The incoming request fields can be examined via the {@link HttpTransaction} - * object, and a response can also be generated and sent via the request object. + * Displays instructions provided through a URL. */ -public interface HttpCallback { - /** - * handle the given request and generate an appropriate response. - * @param msg the transaction containing the request from the - * client and used to send the response - */ - void request (HttpTransaction msg); +class DescriptionPane extends JPanel { + + DescriptionPane(Consumer instructions) { + JEditorPane editorPane = new JEditorPane(); + editorPane.setFocusable(false); + instructions.accept(editorPane); + editorPane.setEditable(false); + + JScrollPane esp = new JScrollPane(editorPane); + esp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + esp.setPreferredSize(new Dimension(250, 350)); + + setLayout(new BorderLayout()); + + add(esp); + } } + diff --git a/test/jdk/javax/accessibility/manual/lib/FailureReasonPane.java b/test/jdk/javax/accessibility/manual/lib/FailureReasonPane.java new file mode 100644 index 00000000000..b1cfc0d32cb --- /dev/null +++ b/test/jdk/javax/accessibility/manual/lib/FailureReasonPane.java @@ -0,0 +1,73 @@ +/* + * 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 lib; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.BorderLayout; +import java.util.function.Consumer; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; + +/** + * Allows to enter reason for the test failure. + */ +class FailureReasonPane extends JPanel { + + private final JTextArea text; + + FailureReasonPane(Consumer listener) { + setLayout(new BorderLayout(10, 10)); + add(new JLabel("Failure reason:"), NORTH); + text = new JTextArea(3, 10); + text.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + listener.accept(text.getText()); + } + + @Override + public void removeUpdate(DocumentEvent e) { + listener.accept(text.getText()); + } + + @Override + public void changedUpdate(DocumentEvent e) { + listener.accept(text.getText()); + } + }); + add(text, CENTER); + } + + public String getReason() { + return text.getText(); + } + + public void requestFocus() { + text.requestFocus(); + } +} diff --git a/test/jdk/javax/accessibility/manual/lib/ManualTestFrame.java b/test/jdk/javax/accessibility/manual/lib/ManualTestFrame.java new file mode 100644 index 00000000000..6aad4164dee --- /dev/null +++ b/test/jdk/javax/accessibility/manual/lib/ManualTestFrame.java @@ -0,0 +1,204 @@ +/* + * 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 + * 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 lib; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.imageio.ImageIO; +import javax.swing.BorderFactory; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.border.BevelBorder; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.io.File.separator; +import static javax.swing.SwingUtilities.invokeAndWait; + +/** + * A frame which can be used to display manual test descriptions as well as, in case of a failure, + * enter failure reason and capture the screen. + */ +public class ManualTestFrame extends JFrame { + + private boolean alreadyFailed = false; + + private ManualTestFrame(String testName, String headerText, + Consumer instructions, + Consumer listener) throws IOException { + + super(testName); + + JLabel statusLabel = new JLabel("Follow test description, select \"Pass\" or \"Fail\""); + statusLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + PassFailPane[] passFail = new PassFailPane[1]; + FailureReasonPane failureReason = new FailureReasonPane(reason -> { + passFail[0].setFailEnabled(!reason.isEmpty()); + }); + ScreenImagePane image = new ScreenImagePane(e -> { + listener.accept(new TestResult(e)); + dispose(); + }); + + JPanel failureInfoPane = new JPanel(); + failureInfoPane.setLayout(new GridLayout(1, 2, 10, 10)); + failureInfoPane.add(failureReason); + failureInfoPane.add(image); + failureInfoPane.setVisible(false); + + JPanel main = new JPanel(); + main.setLayout(new BorderLayout(10, 10)); + DescriptionPane description = new DescriptionPane(instructions); + main.add(description, CENTER); + passFail[0] = new PassFailPane((status) -> { + if (status) { + listener.accept(new TestResult()); + dispose(); + } else { + if (!alreadyFailed) { + alreadyFailed = true; + split.setDividerLocation(.5); + failureInfoPane.setVisible(true); + pack(); + image.capture(); + failureReason.requestFocus(); + statusLabel.setText("Enter failure reason, re-take screenshot, push \"Fail\""); + } else { + listener.accept(new TestResult(failureReason.getReason(), image.getImage())); + dispose(); + } + } + }); + main.add(passFail[0], SOUTH); + + split.setLeftComponent(main); + split.setRightComponent(failureInfoPane); + split.setDividerLocation(1.); + + getContentPane().setLayout(new BorderLayout()); + + if (headerText != null) { + JTextArea warningLabel = new JTextArea(headerText); + warningLabel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); + warningLabel.setEditable(false); + warningLabel.setFocusable(false); + getContentPane().add(warningLabel, NORTH); + } + + getContentPane().add(statusLabel, SOUTH); + getContentPane().add(split, CENTER); + + setPreferredSize(new Dimension(800, 600)); + pack(); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + setVisible(true); + } + + /** + * Show a test control frame which allows a user to either pass or fail the test. + * + * @param testName name of the testcase + * @param headerText information to the user to wait for the test frame. + * @param instructions test instruction for the user + * @return Returning supplier blocks till the test is passed or failed by the user. + * @throws InterruptedException exception + * @throws InvocationTargetException exception + */ + public static Supplier showUI(String testName, + String headerText, + Consumer instructions) + throws InterruptedException, InvocationTargetException { + AtomicReference resultContainer = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + invokeAndWait(() -> { + try { + new ManualTestFrame(testName, headerText, instructions, (status) -> { + resultContainer.set(status); + latch.countDown(); + }); + } catch (IOException e) { + resultContainer.set(new TestResult(e)); + e.printStackTrace(); + } + }); + return () -> { + try { + int timeout = Integer.getInteger("timeout", 10); + System.out.println("timeout value : " + timeout); + if (!latch.await(timeout, TimeUnit.MINUTES)) { + throw new RuntimeException("Timeout : User failed to " + + "take decision on the test result."); + } + } catch (InterruptedException e) { + return new TestResult(e); + } + return resultContainer.get(); + }; + } + + /** + * Checks the TestResult after user interacted with the manual TestFrame + * and the test UI. + * + * @param result Instance of the TestResult + * @param testName name of the testcase + * @throws IOException exception + */ + public static void handleResult(TestResult result, String testName) throws IOException { + if (result != null) { + System.err.println("Failure reason: \n" + result.getFailureDescription()); + if (result.getScreenCapture() != null) { + File screenDump = new File(System.getProperty("test.classes") + separator + testName + ".png"); + System.err.println("Saving screen image to " + screenDump.getAbsolutePath()); + ImageIO.write(result.getScreenCapture(), "png", screenDump); + } + Throwable e = result.getException(); + if (e != null) { + throw new RuntimeException(e); + } else { + if (!result.getStatus()) + throw new RuntimeException("Test failed!"); + } + } else { + throw new RuntimeException("No result returned!"); + } + } + +} + diff --git a/test/jdk/javax/accessibility/manual/lib/PassFailPane.java b/test/jdk/javax/accessibility/manual/lib/PassFailPane.java new file mode 100644 index 00000000000..0cc99353ef1 --- /dev/null +++ b/test/jdk/javax/accessibility/manual/lib/PassFailPane.java @@ -0,0 +1,74 @@ +/* + * 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 lib; + +import javax.swing.JButton; +import javax.swing.JPanel; +import java.awt.HeadlessException; +import java.io.IOException; +import java.util.function.Consumer; + +/** + * Allows to chose if a test fails or passes. It is a multi-use component. A chosen answer can be confirmed later + * upon providing additional information. + */ +class PassFailPane extends JPanel { + private final Consumer listener; + + private final JButton btnPass = new JButton("Pass"); + private final JButton btnFail = new JButton("Fail"); + + /** + * @param listener gets called with true (pass) or false (fail). + * @throws HeadlessException + */ + PassFailPane(Consumer listener) + throws HeadlessException, IOException { + this.listener = listener; + + add(btnPass); + add(btnFail); + + btnPass.requestFocus(); + + btnPass.addActionListener((e) -> { + disableButtons(); + listener.accept(true); + }); + + btnFail.addActionListener((e) -> { + disableButtons(); + listener.accept(false); + }); + } + + private void disableButtons() { + btnFail.setEnabled(false); + btnPass.setEnabled(false); + } + + public void setFailEnabled(boolean enabled) { + btnFail.setEnabled(enabled); + } + +} diff --git a/test/jdk/javax/accessibility/manual/lib/ScreenImagePane.java b/test/jdk/javax/accessibility/manual/lib/ScreenImagePane.java new file mode 100644 index 00000000000..1d35b740dad --- /dev/null +++ b/test/jdk/javax/accessibility/manual/lib/ScreenImagePane.java @@ -0,0 +1,124 @@ +/* + * 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 lib; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.text.NumberFormat; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.lang.String.format; +import static javax.swing.SwingUtilities.invokeAndWait; +import static javax.swing.SwingUtilities.invokeLater; + +/** + * Allows ti take screenshot, possible with a delay to preapare the UI. + */ +class ScreenImagePane extends JPanel { + private final JPanel imagePanel; + private final JLabel imageLabel; + private final AtomicReference image = new AtomicReference<>(); + private final Rectangle screenRect; + private final JFormattedTextField delayField; + private final Consumer exceptionHandler; + + /** + * + * @param handler should an exception appear on other threads + */ + ScreenImagePane(Consumer handler) { + exceptionHandler = handler; + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + screenRect = new Rectangle(0, 0, screenSize.width, screenSize.height); + JPanel controls = new JPanel(); + delayField = new JFormattedTextField(NumberFormat.getNumberInstance()); + delayField.setText("0"); + delayField.setColumns(3); + JButton capture = new JButton("Retake screenshot"); + controls.add(new JLabel("in ")); + controls.add(delayField); + controls.add(new JLabel(" seconds ")); + controls.add(capture); + capture.addActionListener((e) -> capture()); + imagePanel = new JPanel(); + imageLabel = new JLabel(); + imagePanel.add(imageLabel); + + setLayout(new BorderLayout()); + add(controls, NORTH); + add(imagePanel, CENTER); + } + + public void capture() { + new Thread(() -> { + try { + int delay = Integer.parseInt(delayField.getText()); + invokeAndWait(() -> imageLabel.setIcon(null)); + while (delay > 0) { + String message = format("Retaking screenshot in %d seconds", delay); + invokeLater(() -> imageLabel.setText(message)); + delay--; + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + } + BufferedImage image = new Robot().createScreenCapture(screenRect); + ScreenImagePane.this.image.set(image); + int newWidth = imagePanel.getWidth(); + int newHeight = imagePanel.getHeight(); + float xratio = (float) newWidth / (float) image.getWidth(); + float yratio = (float) newHeight / (float) image.getHeight(); + if (xratio < yratio) { + newHeight = (int) (image.getHeight() * xratio); + } else { + newWidth = (int) (image.getWidth() * yratio); + } + Image scaled = image.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST); + invokeAndWait(() -> { + imageLabel.setText(null); + imageLabel.setIcon(new ImageIcon(scaled)); + }); + } catch (Throwable e) { + exceptionHandler.accept(e); + } + }).start(); + } + + public BufferedImage getImage() { + return image.get(); + } +} diff --git a/test/jdk/javax/accessibility/manual/lib/TestResult.java b/test/jdk/javax/accessibility/manual/lib/TestResult.java new file mode 100644 index 00000000000..42e5c72a49e --- /dev/null +++ b/test/jdk/javax/accessibility/manual/lib/TestResult.java @@ -0,0 +1,79 @@ +/* + * 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 lib; + +import java.awt.image.BufferedImage; + +public final class TestResult { + private final boolean status; + private final String failureDescription; + private final BufferedImage screenCapture; + private final Throwable exception; + + /** + * Failed due to an exception. + */ + public TestResult(Throwable exception) { + status = false; + failureDescription = exception.getMessage(); + screenCapture = null; + this.exception = exception; + } + + /** + * Failed by used decision. + */ + public TestResult(String description, BufferedImage capture) { + status = false; + failureDescription = description; + screenCapture = capture; + exception = null; + } + + /** + * Passed. + */ + public TestResult() { + this.status = true; + failureDescription = null; + screenCapture = null; + exception = null; + } + + /** + * true - pass, false - no pass. + */ + public boolean getStatus() { + return status; + } + + public String getFailureDescription() { + return failureDescription; + } + + public BufferedImage getScreenCapture() { + return screenCapture; + } + + public Throwable getException() {return exception;} +} diff --git a/test/jdk/javax/accessibility/manual/resource/btn.png b/test/jdk/javax/accessibility/manual/resource/btn.png new file mode 100644 index 00000000000..0523db95125 Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/btn.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/cmb.png b/test/jdk/javax/accessibility/manual/resource/cmb.png new file mode 100644 index 00000000000..02d989660c0 Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/cmb.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/dep.png b/test/jdk/javax/accessibility/manual/resource/dep.png new file mode 100644 index 00000000000..89d15e2b5db Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/dep.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/dms.png b/test/jdk/javax/accessibility/manual/resource/dms.png new file mode 100644 index 00000000000..63809383c5e Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/dms.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/hc.jpg b/test/jdk/javax/accessibility/manual/resource/hc.jpg new file mode 100644 index 00000000000..773ca040dcb Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/hc.jpg differ diff --git a/test/jdk/javax/accessibility/manual/resource/if.png b/test/jdk/javax/accessibility/manual/resource/if.png new file mode 100644 index 00000000000..3c978d685eb Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/if.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/ifm.png b/test/jdk/javax/accessibility/manual/resource/ifm.png new file mode 100644 index 00000000000..483ae7c76e3 Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/ifm.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/list.png b/test/jdk/javax/accessibility/manual/resource/list.png new file mode 100644 index 00000000000..1e026e83b2b Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/list.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/op.png b/test/jdk/javax/accessibility/manual/resource/op.png new file mode 100644 index 00000000000..82ff0253d6e Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/op.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/rbtn.png b/test/jdk/javax/accessibility/manual/resource/rbtn.png new file mode 100644 index 00000000000..807f29b58c1 Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/rbtn.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/tbl.png b/test/jdk/javax/accessibility/manual/resource/tbl.png new file mode 100644 index 00000000000..f2eef6a2fb4 Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/tbl.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/tbld.png b/test/jdk/javax/accessibility/manual/resource/tbld.png new file mode 100644 index 00000000000..8e2a3b77d1a Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/tbld.png differ diff --git a/test/jdk/javax/accessibility/manual/resource/tree.png b/test/jdk/javax/accessibility/manual/resource/tree.png new file mode 100644 index 00000000000..9e7f1daa064 Binary files /dev/null and b/test/jdk/javax/accessibility/manual/resource/tree.png differ diff --git a/test/jdk/javax/crypto/KEM/RSA_KEM.java b/test/jdk/javax/crypto/KEM/RSA_KEM.java new file mode 100644 index 00000000000..61592b8272b --- /dev/null +++ b/test/jdk/javax/crypto/KEM/RSA_KEM.java @@ -0,0 +1,533 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8297878 + * @summary RSA_KEM example + * @modules java.base/sun.security.jca + * java.base/sun.security.rsa + * java.base/sun.security.util + * java.base/javax.crypto:+open + */ +import sun.security.jca.JCAUtil; +import sun.security.rsa.RSACore; +import sun.security.util.*; + +import javax.crypto.*; +import javax.crypto.spec.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +// This test implements RSA-KEM as described in RFC 5990. In this KEM, the +// sender configures the encapsulator with an RSAKEMParameterSpec object. +// This object is encoded as a byte array and included in the Encapsulated +// output. The receiver is then able to recover the same RSAKEMParameterSpec +// object from the encoding using an AlgorithmParameters implementation +// and use the object to configure the decapsulator. +public class RSA_KEM { + public static void main(String[] args) throws Exception { + Provider p = new ProviderImpl(); + RSAKEMParameterSpec[] kspecs = new RSAKEMParameterSpec[] { + RSAKEMParameterSpec.kdf1("SHA-256", "AES_128/KW/NoPadding"), + RSAKEMParameterSpec.kdf1("SHA-512", "AES_256/KW/NoPadding"), + RSAKEMParameterSpec.kdf2("SHA-256", "AES_128/KW/NoPadding"), + RSAKEMParameterSpec.kdf2("SHA-512", "AES_256/KW/NoPadding"), + RSAKEMParameterSpec.kdf3("SHA-256", new byte[10], "AES_128/KW/NoPadding"), + RSAKEMParameterSpec.kdf3("SHA-256", new byte[0], "AES_128/KW/NoPadding"), + RSAKEMParameterSpec.kdf3("SHA-512", new byte[0], "AES_128/KW/NoPadding"), + }; + for (RSAKEMParameterSpec kspec : kspecs) { + System.err.println("---------"); + System.err.println(kspec); + AlgorithmParameters d = AlgorithmParameters.getInstance("RSA-KEM", p); + d.init(kspec); + AlgorithmParameters s = AlgorithmParameters.getInstance("RSA-KEM", p); + s.init(d.getEncoded()); + AlgorithmParameterSpec spec = s.getParameterSpec(AlgorithmParameterSpec.class); + if (!spec.toString().equals(kspec.toString())) { + throw new RuntimeException(spec.toString()); + } + } + byte[] msg = "hello".getBytes(StandardCharsets.UTF_8); + byte[] iv = new byte[16]; + for (int size : List.of(1024, 2048)) { + KeyPairGenerator g = KeyPairGenerator.getInstance("RSA"); + g.initialize(size); + KeyPair kp = g.generateKeyPair(); + for (RSAKEMParameterSpec kspec : kspecs) { + SecretKey cek = KeyGenerator.getInstance("AES").generateKey(); + KEM kem1 = getKemImpl(p); + Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); + c.init(Cipher.ENCRYPT_MODE, cek, new IvParameterSpec(iv)); + byte[] ciphertext = c.doFinal(msg); + + KEM.Encapsulator e = kem1.newEncapsulator(kp.getPublic(), kspec, null); + KEM.Encapsulated enc = e.encapsulate(0, e.secretSize(), "AES"); + Cipher c2 = Cipher.getInstance(kspec.encAlg); + c2.init(Cipher.WRAP_MODE, enc.key()); + byte[] ek = c2.wrap(cek); + + AlgorithmParameters a = AlgorithmParameters.getInstance("RSA-KEM", p); + a.init(enc.params()); + KEM kem2 = getKemImpl(p); + KEM.Decapsulator d = kem2.newDecapsulator(kp.getPrivate(), a.getParameterSpec(AlgorithmParameterSpec.class)); + SecretKey k = d.decapsulate(enc.encapsulation(), 0, d.secretSize(), "AES"); + Cipher c3 = Cipher.getInstance(kspec.encAlg); + c3.init(Cipher.UNWRAP_MODE, k); + cek = (SecretKey) c3.unwrap(ek, "AES", Cipher.SECRET_KEY); + Cipher c4 = Cipher.getInstance("AES/CBC/PKCS5Padding"); + c4.init(Cipher.DECRYPT_MODE, cek, new IvParameterSpec(iv)); + byte[] cleartext = c4.doFinal(ciphertext); + + if (!Arrays.equals(cleartext, msg)) { + throw new RuntimeException(); + } + System.out.printf("%4d %20s - %11d %11d %11d %11d %s\n", + size, kspec, + e.secretSize(), e.encapsulationSize(), + d.secretSize(), d.encapsulationSize(), k.getAlgorithm()); + } + } + } + + // To bypass the JCE security provider signature check + private static KEM getKemImpl(Provider p) throws Exception { + var ctor = KEM.class.getDeclaredConstructor( + String.class, KEMSpi.class, Provider.class); + ctor.setAccessible(true); + return ctor.newInstance("RSA-KEM", new KEMImpl(), p); + } + + static final String RSA_KEM = "1.2.840.113549.1.9.16.3.14"; + static final String KEM_RSA = "1.0.18033.2.2.4"; + + public static class ProviderImpl extends Provider { + public ProviderImpl() { + super("MYKEM", "1", "RSA-KEM"); + List alias = List.of(RSA_KEM, "OID." + RSA_KEM); + Map attrs = Map.of( + "SupportedKeyClasses", "java.security.interfaces.RSAKey"); + putService(new Service(this, "KEM", "RSA-KEM", + "RSA_KEM$KEMImpl", alias, attrs)); + putService(new Service(this, "AlgorithmParameters", "RSA-KEM", + "RSA_KEM$AlgorithmParametersImpl", alias, attrs)); + } + } + + public static class AlgorithmParametersImpl extends AlgorithmParametersSpi { + RSAKEMParameterSpec spec; + @Override + protected void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException { + if (paramSpec instanceof RSAKEMParameterSpec rspec) { + spec = rspec; + } else { + throw new InvalidParameterSpecException(); + } + } + + @Override + protected void engineInit(byte[] params) throws IOException { + spec = decode(params); + } + + @Override + protected void engineInit(byte[] params, String format) throws IOException { + spec = decode(params); + } + + @Override + protected T engineGetParameterSpec( + Class paramSpec) throws InvalidParameterSpecException { + if (paramSpec.isAssignableFrom(RSAKEMParameterSpec.class)) { + return paramSpec.cast(spec); + } else { + throw new InvalidParameterSpecException(); + } + } + + @Override + protected byte[] engineGetEncoded() throws IOException { + return encode(spec); + } + + @Override + protected byte[] engineGetEncoded(String format) throws IOException { + return encode(spec); + } + + @Override + protected String engineToString() { + return spec == null ? "" : spec.toString(); + } + + static final ObjectIdentifier id_rsa_kem; + static final ObjectIdentifier id_kem_rsa; + static final ObjectIdentifier id_kdf1; + static final ObjectIdentifier id_kdf2; + static final ObjectIdentifier id_kdf3; + + static { + try { + id_rsa_kem = ObjectIdentifier.of("1.2.840.113549.1.9.16.3.14"); + id_kem_rsa = ObjectIdentifier.of("1.0.18033.2.2.4"); + id_kdf1 = ObjectIdentifier.of("1.3.133.16.840.9.44.1.0"); // fake + id_kdf2 = ObjectIdentifier.of("1.3.133.16.840.9.44.1.1"); + id_kdf3 = ObjectIdentifier.of("1.3.133.16.840.9.44.1.2"); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + static byte[] encode(RSAKEMParameterSpec spec) throws IOException { + var d1 = new DerOutputStream(); + d1.putOID(oid4(spec.hashAlg)); + var d2 = new DerOutputStream(); + d2.putOID(oid4(spec.kdfAlg)); + d2.write(DerValue.tag_Sequence, d1); + DerOutputStream kdf = new DerOutputStream(); + kdf.write(DerValue.tag_Sequence, d2); + kdf.putInteger(spec.kdfLen()); + // The next line is not in RFC 5990 + if (spec.fixedInfo != null) { + kdf.putOctetString(spec.fixedInfo); + } + var d3 = new DerOutputStream(); + d3.putOID(oid4(spec.encAlg)); + var d4 = new DerOutputStream(); + d4.putOID(id_kem_rsa); + d4.write(DerValue.tag_Sequence, kdf); + var d5 = new DerOutputStream(); + d5.write(DerValue.tag_Sequence, d4); + d5.write(DerValue.tag_Sequence, d3); + var d6 = new DerOutputStream(); + d6.write(DerValue.tag_Sequence, d5); + return d6.toByteArray(); + } + + static RSAKEMParameterSpec decode(byte[] der) throws IOException { + System.out.println(new HexDumpEncoder().encodeBuffer(der)); + String kdfAlg, encAlg, hashAlg; + int kdfLen; + byte[] fixedInfo; + DerInputStream d2 = new DerValue(der).toDerInputStream(); + DerInputStream d3 = d2.getDerValue().toDerInputStream(); + if (!d3.getOID().equals(id_kem_rsa)) { + throw new IOException("not id_kem_rsa"); + } + DerInputStream d4 = d3.getDerValue().toDerInputStream(); + DerInputStream d5 = d4.getDerValue().toDerInputStream(); + kdfLen = d4.getInteger(); + fixedInfo = d4.available() > 0 ? d4.getOctetString() : null; + if (d4.available() != 0) { + throw new IOException("Extra unused bytes"); + } + ObjectIdentifier kdfOid = d5.getOID(); + if (kdfOid.equals(id_kdf1)) { + kdfAlg = "kdf1"; + } else if (kdfOid.equals(id_kdf2)) { + kdfAlg = "kdf2"; + } else if (kdfOid.equals(id_kdf3)) { + kdfAlg = "kdf3"; + } else { + throw new IOException("unknown kdf"); + } + DerInputStream d6 = d5.getDerValue().toDerInputStream(); + String hashOID = d6.getOID().toString(); + KnownOIDs k = KnownOIDs.findMatch(hashOID); + hashAlg = k == null ? hashOID : k.stdName(); + if (d6.available() != 0) { + throw new IOException("Extra unused bytes"); + } + if (d5.available() != 0) { + throw new IOException("Extra unused bytes"); + } + + if (d3.available() != 0) { + throw new IOException("Extra unused bytes"); + } + DerInputStream d7 = d2.getDerValue().toDerInputStream(); + String encOID = d7.getOID().toString(); + KnownOIDs e = KnownOIDs.findMatch(encOID); + encAlg = e == null ? encOID : e.stdName(); + if (d7.available() != 0) { + throw new IOException("Extra unused bytes"); + } + if (d2.available() != 0) { + throw new IOException("Extra unused bytes"); + } + if (kdfLen != RSAKEMParameterSpec.kdfLen(encAlg)) { + throw new IOException("kdfLen does not match encAlg"); + } + return new RSAKEMParameterSpec(kdfAlg, hashAlg, fixedInfo, encAlg); + } + + static ObjectIdentifier oid4(String s) { + return switch (s) { + case "kdf1" -> id_kdf1; + case "kdf2" -> id_kdf2; + case "kdf3" -> id_kdf3; + default -> { + KnownOIDs k = KnownOIDs.findMatch(s); + if (k == null) throw new UnsupportedOperationException(); + yield ObjectIdentifier.of(k); + } + }; + } + } + + public static class RSAKEMParameterSpec implements AlgorithmParameterSpec { + private final String kdfAlg; + private final String hashAlg; + private final byte[] fixedInfo; + private final String encAlg; + + private RSAKEMParameterSpec(String kdfAlg, String hashAlg, byte[] fixedInfo, String encAlg) { + this.hashAlg = hashAlg; + this.kdfAlg = kdfAlg; + this.fixedInfo = fixedInfo == null ? null : fixedInfo.clone(); + this.encAlg = encAlg; + } + + public static RSAKEMParameterSpec kdf1(String hashAlg, String encAlg) { + return new RSAKEMParameterSpec("kdf1", hashAlg, null, encAlg); + } + public static RSAKEMParameterSpec kdf2(String hashAlg, String encAlg) { + return new RSAKEMParameterSpec("kdf2", hashAlg, null, encAlg); + } + public static RSAKEMParameterSpec kdf3(String hashAlg, byte[] fixedInfo, String encAlg) { + return new RSAKEMParameterSpec("kdf3", hashAlg, fixedInfo, encAlg); + } + + public int kdfLen() { + return RSAKEMParameterSpec.kdfLen(encAlg); + } + + public static int kdfLen(String encAlg) { + return Integer.parseInt(encAlg, 4, 7, 10) / 8; + } + + public String hashAlgorithm() { + return hashAlg; + } + public String kdfAlgorithm() { + return kdfAlg; + } + public byte[] fixedInfo() { + return fixedInfo == null ? null : fixedInfo.clone(); + } + + public String getEncAlg() { + return encAlg; + } + + @Override + public String toString() { + return String.format("[%s,%s,%s]", kdfAlg, hashAlg, encAlg); + } + } + + public static class KEMImpl implements KEMSpi { + + @Override + public KEMSpi.EncapsulatorSpi engineNewEncapsulator( + PublicKey pk, AlgorithmParameterSpec spec, SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (!(pk instanceof RSAPublicKey rpk)) { + throw new InvalidKeyException("Not an RSA key"); + } + return Handler.newEncapsulator(spec, rpk, secureRandom); + } + + @Override + public KEMSpi.DecapsulatorSpi engineNewDecapsulator( + PrivateKey sk, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (!(sk instanceof RSAPrivateCrtKey rsk)) { + throw new InvalidKeyException("Not an RSA key"); + } + return Handler.newDecapsulator(spec, rsk); + } + + static class Handler implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi { + + private final RSAPublicKey rpk; // not null for encapsulator + private final RSAPrivateKey rsk; // not null for decapsulator + private final RSAKEMParameterSpec kspec; // not null + private final SecureRandom sr; // not null for encapsulator + + Handler(AlgorithmParameterSpec spec, RSAPublicKey rpk, RSAPrivateCrtKey rsk, SecureRandom sr) + throws InvalidAlgorithmParameterException { + this.rpk = rpk; + this.rsk = rsk; + this.sr = sr; + if (spec != null) { + if (spec instanceof RSAKEMParameterSpec rs) { + this.kspec = rs; + } else { + throw new InvalidAlgorithmParameterException(); + } + } else { + this.kspec = RSAKEMParameterSpec + .kdf2("SHA-256", "AES_256/KW/NoPadding"); + } + } + + static Handler newEncapsulator(AlgorithmParameterSpec spec, RSAPublicKey rpk, SecureRandom sr) + throws InvalidAlgorithmParameterException { + if (sr == null) { + sr = JCAUtil.getDefSecureRandom(); + } + return new Handler(spec, rpk, null, sr); + } + + static Handler newDecapsulator(AlgorithmParameterSpec spec, RSAPrivateCrtKey rsk) + throws InvalidAlgorithmParameterException { + return new Handler(spec, null, rsk, null); + } + + @Override + public SecretKey engineDecapsulate(byte[] encapsulation, + int from, int to, String algorithm) + throws DecapsulateException { + Objects.checkFromToIndex(from, to, kspec.kdfLen()); + Objects.requireNonNull(algorithm, "null algorithm"); + Objects.requireNonNull(encapsulation, "null encapsulation"); + if (encapsulation.length != KeyUtil.getKeySize(rsk) / 8) { + throw new DecapsulateException("incorrect encapsulation size"); + } + try { + byte[] Z = RSACore.rsa(encapsulation, rsk, false); + return new SecretKeySpec(kdf(Z), from, to - from, algorithm); + } catch (BadPaddingException e) { + throw new DecapsulateException("cannot decrypt", e); + } + } + + @Override + public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { + Objects.checkFromToIndex(from, to, kspec.kdfLen()); + Objects.requireNonNull(algorithm, "null algorithm"); + int nLen = rpk.getModulus().bitLength(); + int nSize = (nLen + 7) / 8; + BigInteger z; + int tried = 0; + while (true) { + z = new BigInteger(nLen, sr); + if (z.compareTo(rpk.getModulus()) < 0) { + break; + } + if (tried++ > 20) { + throw new ProviderException("Cannot get good random number"); + } + } + byte[] Z = z.toByteArray(); + if (Z.length > nSize) { + Z = Arrays.copyOfRange(Z, Z.length - nSize, Z.length); + } else if (Z.length < nSize) { + byte[] tmp = new byte[nSize]; + System.arraycopy(Z, 0, tmp, nSize - Z.length, Z.length); + Z = tmp; + } + byte[] c; + try { + c = RSACore.rsa(Z, rpk); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + try { + return new KEM.Encapsulated( + new SecretKeySpec(kdf(Z), from, to - from, algorithm), + c, AlgorithmParametersImpl.encode(kspec)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + byte[] kdf(byte[] input) { + String hashAlg = kspec.hashAlgorithm(); + MessageDigest md; + try { + md = MessageDigest.getInstance(hashAlg); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + String kdfAlg = kspec.kdfAlgorithm(); + byte[] fixedInput = kspec.fixedInfo(); + int length = kspec.kdfLen(); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + int n = kdfAlg.equals("kdf1") ? 0 : 1; + while (true) { + switch (kdfAlg) { + case "kdf1", "kdf2" -> { + md.update(input); + md.update(u32str(n)); + } + case "kdf3" -> { + md.update(u32str(n)); + md.update(input); + md.update(fixedInput); + } + default -> throw new ProviderException(); + } + bout.writeBytes(md.digest()); + if (bout.size() > length) break; + n++; + } + byte[] result = bout.toByteArray(); + return result.length == length + ? result + : Arrays.copyOf(result, length); + } + + @Override + public int engineSecretSize() { + return kspec.kdfLen(); + } + + @Override + public int engineEncapsulationSize() { + return KeyUtil.getKeySize(rsk == null ? rpk : rsk) / 8; + } + } + } + + static byte[] u32str(int i) { + return new byte[] { + (byte)(i >> 24), (byte)(i >> 16), (byte)(i >> 8), (byte)i }; + } +} diff --git a/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile b/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile index 918c31f0947..800e1817b42 100644 --- a/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile +++ b/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile @@ -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 @@ -26,7 +26,7 @@ JAVABIN=$(JAVA_BASE)/bin JAVAC=$(JAVABIN)/javac JAVA=$(JAVABIN)/java -JAR=$(JAVABIN)/jar +JAR=$(JAVABIN)/jar JARSIGNER=$(JAVABIN)/jarsigner # Compile-time flags and paths diff --git a/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java b/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java index 41028039b78..08ea48807c4 100644 --- a/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java +++ b/test/jdk/javax/management/remote/mandatory/connection/DefaultAgentFilterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, 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 @@ -112,7 +112,7 @@ public synchronized void start() throws Exception { AtomicBoolean error = new AtomicBoolean(false); AtomicBoolean bindError = new AtomicBoolean(false); // The predicate below tries to recognise failures. On a port clash, it sees e.g. - // Error: Exception thrown by the agent : java.rmi.server.ExportException: Port already in use: 46481; nested exception is: + // Error: Exception thrown by the agent: java.rmi.server.ExportException: Port already in use: 46481; nested exception is: // ...and will never see "main enter" from TestApp. p = ProcessTools.startProcess( TEST_APP_NAME + "{" + name + "}", diff --git a/test/jdk/javax/naming/module/RunBasic.java b/test/jdk/javax/naming/module/RunBasic.java index 0fd9d2b28aa..512062de409 100644 --- a/test/jdk/javax/naming/module/RunBasic.java +++ b/test/jdk/javax/naming/module/RunBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -31,6 +31,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -92,10 +93,14 @@ public static void main(String[] args) throws Throwable { System.out.println("Hostname: [" + HOST_NAME + "]"); // run tests - runTest("java.desktop", "test.StoreObject"); - runTest("person", "test.StorePerson"); - runTest("fruit", "test.StoreFruit"); - runTest("hello", "test.StoreRemote"); + runTest("java.desktop", "test.StoreObject", + "-Dcom.sun.jndi.ldap.object.trustSerialData=true"); + runTest("person", "test.StorePerson", + "-Dcom.sun.jndi.ldap.object.trustSerialData=true"); + runTest("fruit", "test.StoreFruit", + "-Dcom.sun.jndi.ldap.object.trustSerialData=true"); + runTest("hello", "test.StoreRemote", + "-Dcom.sun.jndi.ldap.object.trustSerialData=true"); runTest("foo", "test.ConnectWithFoo"); runTest("authz", "test.ConnectWithAuthzId"); runTest("ldapv4", "test.ReadByUrl"); @@ -117,10 +122,19 @@ private static void makeDir(String first, String... more) Files.createDirectories(Path.of(first, more)); } - private static void runTest(String desc, String clsName) throws Throwable { + private static void runTest(String desc, String clsName, String... additionalVmOpts) throws Throwable { + List opts = new ArrayList<>(); + opts.add("-Dtest.src=" + TEST_SRC); + for (String opt : additionalVmOpts) { + opts.add(opt); + } + opts.add("-p"); + opts.add("mods"); + opts.add("-m"); + opts.add("test/" + clsName); + opts.add("ldap://" + HOST_NAME + "/dc=ie,dc=oracle,dc=com"); System.out.println("Running with the '" + desc + "' module..."); - runJava("-Dtest.src=" + TEST_SRC, "-p", "mods", "-m", "test/" + clsName, - "ldap://" + HOST_NAME + "/dc=ie,dc=oracle,dc=com"); + runJava(opts.toArray(String[]::new)); } private static void runJava(String... opts) throws Throwable { diff --git a/test/jdk/javax/naming/module/src/test/test/ConnectWithAuthzId.java b/test/jdk/javax/naming/module/src/test/test/ConnectWithAuthzId.java index 5f707e44d69..3a50981f14e 100644 --- a/test/jdk/javax/naming/module/src/test/test/ConnectWithAuthzId.java +++ b/test/jdk/javax/naming/module/src/test/test/ConnectWithAuthzId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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,7 +33,6 @@ import java.net.*; import java.util.*; import javax.naming.*; -import javax.naming.directory.*; import javax.naming.ldap.*; import org.example.authz.AuthzIdRequestControl; @@ -68,7 +67,7 @@ public static void main(String[] args) throws Exception { System.err.println(" is the LDAP URL of the parent entry\n"); System.err.println("example:"); System.err.println(" java ConnectWithAuthzId ldap://oasis/o=airius.com"); - return; + throw new IllegalArgumentException(); } /* @@ -134,6 +133,7 @@ public void run() { } } catch (NamingException e) { System.err.println("ConnectWithAuthzId: error connecting " + e); + throw e; } finally { if (ctx != null) { ctx.close(); diff --git a/test/jdk/javax/naming/module/src/test/test/ConnectWithFoo.java b/test/jdk/javax/naming/module/src/test/test/ConnectWithFoo.java index 6df3b97cc61..41108e2c6f6 100644 --- a/test/jdk/javax/naming/module/src/test/test/ConnectWithFoo.java +++ b/test/jdk/javax/naming/module/src/test/test/ConnectWithFoo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -65,7 +65,7 @@ public static void main(String[] args) throws Exception { System.err.println(" is the LDAP URL of the parent entry\n"); System.err.println("example:"); System.err.println(" java ConnectWithFoo ldap://oasis/o=airius.com"); - return; + throw new IllegalArgumentException(); } /* @@ -112,6 +112,7 @@ public void run() { System.out.println("ConnectWithFoo: connected"); } catch (NamingException e) { System.err.println("ConnectWithFoo: error connecting " + e); + throw e; } finally { if (ctx != null) { ctx.close(); diff --git a/test/jdk/javax/naming/module/src/test/test/ReadByUrl.java b/test/jdk/javax/naming/module/src/test/test/ReadByUrl.java index cc2861b433b..df610dce3ca 100644 --- a/test/jdk/javax/naming/module/src/test/test/ReadByUrl.java +++ b/test/jdk/javax/naming/module/src/test/test/ReadByUrl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -63,7 +63,7 @@ public static void main(String[] args) throws Exception { System.err.println(" is the LDAP URL of the parent entry\n"); System.err.println("example:"); System.err.println(" java ReadByUrl ldap://oasis/o=airius.com"); - return; + throw new IllegalArgumentException(); } /* @@ -112,6 +112,7 @@ public void run() { entry.close(); } catch (NamingException e) { System.err.println("ReadByUrl: error connecting " + e); + throw e; } finally { if (ctx != null) { ctx.close(); diff --git a/test/jdk/javax/naming/module/src/test/test/StoreFruit.java b/test/jdk/javax/naming/module/src/test/test/StoreFruit.java index 8121c6ac906..6eaee6b6adc 100644 --- a/test/jdk/javax/naming/module/src/test/test/StoreFruit.java +++ b/test/jdk/javax/naming/module/src/test/test/StoreFruit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -65,7 +65,7 @@ public static void main(String[] args) throws Exception { System.err.println(" is the LDAP URL of the parent entry\n"); System.err.println("example:"); System.err.println(" java StoreFruit ldap://oasis/o=airius.com"); - return; + throw new IllegalArgumentException(); } /* @@ -116,7 +116,7 @@ public void run() { System.err.println("StoreFruit: entry '" + dn + "' already exists"); cleanup(ctx, (String)null); - return; + throw e; } try { @@ -126,7 +126,7 @@ public void run() { System.err.println("StoreFruit: entry '" + dn2 + "' already exists"); cleanup(ctx, dn); - return; + throw e; } /* @@ -141,7 +141,7 @@ public void run() { dn + "' " + e); e.printStackTrace(); cleanup(ctx, dn, dn2); - return; + throw e; } try { @@ -152,7 +152,7 @@ public void run() { dn2 + "' " + e); e.printStackTrace(); cleanup(ctx, dn, dn2); - return; + throw e; } cleanup(ctx, dn, dn2); diff --git a/test/jdk/javax/naming/module/src/test/test/StoreObject.java b/test/jdk/javax/naming/module/src/test/test/StoreObject.java index ce1625ee8d9..8e4e82d5646 100644 --- a/test/jdk/javax/naming/module/src/test/test/StoreObject.java +++ b/test/jdk/javax/naming/module/src/test/test/StoreObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -64,7 +64,7 @@ public static void main(String[] args) throws Exception { System.err.println(" is the LDAP URL of the parent entry\n"); System.err.println("example:"); System.err.println(" java StoreObject ldap://oasis/o=airius.com"); - return; + throw new IllegalArgumentException(); } /* @@ -114,7 +114,7 @@ public void run() { System.err.println("StoreObject: entry '" + dn + "' already exists"); cleanup(ctx, (String)null); - return; + throw e; } try { @@ -124,7 +124,7 @@ public void run() { System.err.println("StoreObject: entry '" + dn2 + "' already exists"); cleanup(ctx, dn); - return; + throw e; } /* @@ -139,7 +139,7 @@ public void run() { dn + "' " + e); e.printStackTrace(); cleanup(ctx, dn, dn2); - return; + throw e; } try { @@ -150,7 +150,7 @@ public void run() { dn2 + "' " + e); e.printStackTrace(); cleanup(ctx, dn, dn2); - return; + throw e; } cleanup(ctx, dn, dn2); diff --git a/test/jdk/javax/naming/module/src/test/test/StorePerson.java b/test/jdk/javax/naming/module/src/test/test/StorePerson.java index 6a5baebe5a9..722d415f3c2 100644 --- a/test/jdk/javax/naming/module/src/test/test/StorePerson.java +++ b/test/jdk/javax/naming/module/src/test/test/StorePerson.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 static void main(String[] args) throws Exception { System.err.println(" is the LDAP URL of the parent entry\n"); System.err.println("example:"); System.err.println(" java StorePerson ldap://oasis/o=airius.com"); - return; + throw new IllegalArgumentException(); } /* @@ -124,7 +124,7 @@ public void run() { System.err.println("StorePerson: entry '" + dn + "' already exists"); cleanup(ctx, (String)null); - return; + throw e; } name = "Jill Smyth"; @@ -139,7 +139,7 @@ public void run() { System.err.println("StorePerson: entry '" + dn2 + "' already exists"); cleanup(ctx, dn); - return; + throw e; } /* @@ -161,7 +161,7 @@ public void run() { dn + "' " + e); e.printStackTrace(); cleanup(ctx, dn, dn2); - return; + throw e; } try { @@ -179,7 +179,7 @@ public void run() { dn2 + "' " + e); e.printStackTrace(); cleanup(ctx, dn, dn2); - return; + throw e; } cleanup(ctx, dn, dn2); diff --git a/test/jdk/javax/naming/module/src/test/test/StoreRemote.java b/test/jdk/javax/naming/module/src/test/test/StoreRemote.java index a7c14138aae..e8ee241d580 100644 --- a/test/jdk/javax/naming/module/src/test/test/StoreRemote.java +++ b/test/jdk/javax/naming/module/src/test/test/StoreRemote.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -53,7 +53,6 @@ public class StoreRemote { System.getProperty("test.src") + "/src/test/test/StoreRemote.ldap"; public static void main(String[] args) throws Exception { - /* * Process arguments */ @@ -66,7 +65,7 @@ public static void main(String[] args) throws Exception { System.err.println(" is the LDAP URL of the parent entry\n"); System.err.println("example:"); System.err.println(" java StoreRemote ldap://oasis/o=airius.com"); - return; + throw new IllegalArgumentException(); } /* @@ -120,7 +119,7 @@ public void run() { System.err.println("StoreRemote: entry '" + dn + "' already exists"); cleanup(ctx, (String)null); - return; + throw e; } /* @@ -141,7 +140,7 @@ public void run() { dn + "' " + e); e.printStackTrace(); cleanup(ctx, dn); - return; + throw e; } cleanup(ctx, dn); diff --git a/test/jdk/javax/net/ssl/DTLS/CipherSuite.java b/test/jdk/javax/net/ssl/DTLS/CipherSuite.java index 773fb08d317..0b277792766 100644 --- a/test/jdk/javax/net/ssl/DTLS/CipherSuite.java +++ b/test/jdk/javax/net/ssl/DTLS/CipherSuite.java @@ -43,10 +43,10 @@ * @run main/othervm CipherSuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 * @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 * @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256 - * @run main/othervm CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 re-enable * @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 * @run main/othervm CipherSuite TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 - * @run main/othervm CipherSuite TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * @run main/othervm CipherSuite TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 re-enable */ import javax.net.ssl.SSLEngine; diff --git a/test/jdk/javax/net/ssl/DTLS/DTLSWontNegotiateV10.java b/test/jdk/javax/net/ssl/DTLS/DTLSWontNegotiateV10.java index 2532f524371..f67a02b3052 100644 --- a/test/jdk/javax/net/ssl/DTLS/DTLSWontNegotiateV10.java +++ b/test/jdk/javax/net/ssl/DTLS/DTLSWontNegotiateV10.java @@ -48,6 +48,8 @@ public class DTLSWontNegotiateV10 { private static final String DTLS = "DTLS"; private static final String DTLSV_1_2 = "DTLSv1.2"; + private static final int READ_TIMEOUT_SECS = Integer.getInteger("readtimeout", 30); + public static void main(String[] args) throws Exception { if (args[0].equals(DTLSV_1_0)) { SecurityUtils.removeFromDisabledTlsAlgs(DTLSV_1_0); @@ -63,20 +65,43 @@ public static void main(String[] args) throws Exception { } else { // server process // args: protocol - try (DTLSServer server = new DTLSServer(args[0])) { - List command = List.of( - Path.of(System.getProperty("java.home"), "bin", "java").toString(), - "DTLSWontNegotiateV10", - // if server is "DTLS" then the client should be v1.0 and vice versa - args[0].equals(DTLS) ? DTLSV_1_0 : DTLS, - Integer.toString(server.getListeningPortNumber()) - ); - - ProcessBuilder builder = new ProcessBuilder(command); - Process p = builder.inheritIO().start(); - server.run(); - p.destroy(); - System.out.println("Success: DTLSv1.0 connection was not established."); + final int totalAttempts = 5; + int tries; + for (tries = 0 ; tries < totalAttempts ; ++tries) { + try { + System.out.printf("Starting server %d/%d attempts%n", tries+1, totalAttempts); + runServer(args[0]); + break; + } catch (SocketTimeoutException exc) { + System.out.println("The server timed-out waiting for packets from the client."); + } + } + if (tries == totalAttempts) { + throw new RuntimeException("The server/client communications timed-out after " + totalAttempts + " tries."); + } + } + } + + private static void runServer(String protocol) throws Exception { + // args: protocol + Process clientProcess = null; + try (DTLSServer server = new DTLSServer(protocol)) { + List command = List.of( + Path.of(System.getProperty("java.home"), "bin", "java").toString(), + "DTLSWontNegotiateV10", + // if server is "DTLS" then the client should be v1.0 and vice versa + protocol.equals(DTLS) ? DTLSV_1_0 : DTLS, + Integer.toString(server.getListeningPortNumber()) + ); + + ProcessBuilder builder = new ProcessBuilder(command); + clientProcess = builder.inheritIO().start(); + server.run(); + System.out.println("Success: DTLSv1.0 connection was not established."); + + } finally { + if (clientProcess != null) { + clientProcess.destroy(); } } } @@ -89,6 +114,9 @@ private static class DTLSClient extends DTLSEndpoint { public DTLSClient(String protocol, int portNumber) throws Exception { super(true, protocol); remotePort = portNumber; + socket.setSoTimeout(READ_TIMEOUT_SECS * 1000); + log("Client listening on port " + socket.getLocalPort() + + ". Sending data to server port " + remotePort); log("Enabled protocols: " + String.join(" ", engine.getEnabledProtocols())); } @@ -287,6 +315,8 @@ private static class DTLSServer extends DTLSEndpoint implements AutoCloseable { public DTLSServer(String protocol) throws Exception { super(false, protocol); + socket.setSoTimeout(READ_TIMEOUT_SECS * 1000); + log("Server listening on port: " + socket.getLocalPort()); log("Enabled protocols: " + String.join(" ", engine.getEnabledProtocols())); } diff --git a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java index 304cb0695d6..120e6b258e6 100644 --- a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java +++ b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, 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 @@ -26,7 +26,7 @@ /* * @test - * @bug 8043758 + * @bug 8043758 8307383 * @summary Datagram Transport Layer Security (DTLS) * @modules java.base/sun.security.util * @library /test/lib @@ -36,6 +36,7 @@ import java.net.DatagramPacket; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -73,11 +74,34 @@ DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) { // ClientHello with cookie needInvalidRecords.set(false); System.out.println("invalidate ClientHello message"); - if (ba[ba.length - 1] == (byte)0xFF) { - ba[ba.length - 1] = (byte)0xFE; + // We will alter the compression method field in order to make the cookie + // check fail. + ByteBuffer chRec = ByteBuffer.wrap(ba); + // Skip 59 bytes past the record header (13), the handshake header (12), + // the protocol version (2), and client random (32) + chRec.position(59); + // Jump past the session ID + int len = Byte.toUnsignedInt(chRec.get()); + chRec.position(chRec.position() + len); + // Skip the cookie + len = Byte.toUnsignedInt(chRec.get()); + chRec.position(chRec.position() + len); + // Skip past cipher suites + len = Short.toUnsignedInt(chRec.getShort()); + chRec.position(chRec.position() + len); + // Read the data on the compression methods, should be at least 1 + len = Byte.toUnsignedInt(chRec.get()); + if (len >= 1) { + System.out.println("Detected compression methods (count = " + len + ")"); } else { ba[ba.length - 1] = (byte)0xFF; + throw new RuntimeException("Got zero length comp methods"); } + // alter the first comp method. + int compMethodVal = Byte.toUnsignedInt(chRec.get(chRec.position())); + System.out.println("Changing value at position " + chRec.position() + + " from " + compMethodVal + " to " + ++compMethodVal); + chRec.put(chRec.position(), (byte)compMethodVal); } return super.createHandshakePacket(ba, socketAddr); diff --git a/test/jdk/javax/net/ssl/SSLSession/ServerNameRejectedTLSSessionResumption.java b/test/jdk/javax/net/ssl/SSLSession/ServerNameRejectedTLSSessionResumption.java new file mode 100644 index 00000000000..f80f3402c7e --- /dev/null +++ b/test/jdk/javax/net/ssl/SSLSession/ServerNameRejectedTLSSessionResumption.java @@ -0,0 +1,247 @@ +/* + * 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.nio.ByteBuffer; +import java.util.List; +import java.util.Objects; + +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIMatcher; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.StandardConstants; + +/* + * @test + * @bug 8301686 + * @summary verifies that if the server rejects session resumption due to SNI + * mismatch, during TLS handshake, then the subsequent communication + * between the server and the client happens correctly without any + * errors + * @library /javax/net/ssl/templates + * @run main/othervm -Djavax.net.debug=all + * ServerNameRejectedTLSSessionResumption + */ +public class ServerNameRejectedTLSSessionResumption + extends SSLContextTemplate { + + private static final String CLIENT_REQUESTED_SNI = "client.local"; + // dummy host, no connection is attempted in this test + private static final String PEER_HOST = "foobar"; + // dummy port, no connection is attempted in this test + private static final int PEER_PORT = 12345; + + public static void main(final String[] args) throws Exception { + new ServerNameRejectedTLSSessionResumption().runTest(); + } + + private void runTest() throws Exception { + final SSLContext clientSSLContext = createClientSSLContext(); + final SSLContext serverSSLContext = createServerSSLContext(); + // create client and server SSLEngine(s) + final SSLEngine clientEngine = createClientSSLEngine(clientSSLContext); + // use a SNIMatcher on the server's SSLEngine which accepts the + // SNI name presented by the client SSLEngine + final SSLEngine serverEngine = createServerSSLEngine(serverSSLContext, + new TestSNIMatcher(CLIENT_REQUESTED_SNI)); + // establish communication, which involves TLS handshake, between the + // client and server engines. this communication expected to be + // successful. + communicate(clientEngine, serverEngine); + // now that the communication has been successful, we expect the client + // SSLContext's (internal) cache to have created and cached a + // SSLSession against the peer host:port + + // now create the SSLEngine(s) again with the same SSLContext + // instances as before, so that the SSLContext instance attempts + // to reuse the cached SSLSession against the peer host:port + final SSLEngine secondClientEngine = + createClientSSLEngine(clientSSLContext); + // the newly created SSLEngine for the server will not use any + // SNIMatcher so as to reject the session resumption (of the + // cached SSLSession) + final SSLEngine secondServerEngine = + createServerSSLEngine(serverSSLContext, null); + // attempt communication, which again involves TLS handshake + // since these are new engine instances. The session resumption + // should be rejected and a fresh session should get created and + // communication should succeed without any errors + communicate(secondClientEngine, secondServerEngine); + } + + private static void communicate(final SSLEngine clientEngine, + final SSLEngine serverEngine) + throws Exception { + + final ByteBuffer msgFromClient = + ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); + final ByteBuffer msgFromServer = + ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); + final ByteBuffer clientBuffer = ByteBuffer.allocate(1 << 15); + final ByteBuffer serverBuffer = ByteBuffer.allocate(1 << 15); + /* + * For data transport, this test uses local ByteBuffers + */ + final ByteBuffer clientToServerTransport = + ByteBuffer.allocateDirect(1 << 16); + final ByteBuffer serverToClientTransport = + ByteBuffer.allocateDirect(1 << 16); + boolean isClientToServer = true; + while (true) { + if (isClientToServer) { + // send client's message over the transport, will initiate a + // TLS handshake if necessary + SSLEngineResult result = clientEngine.wrap(msgFromClient, + clientToServerTransport); + // run any delegated tasks + final HandshakeStatus hsStatus = checkAndRunTasks(clientEngine, + result.getHandshakeStatus()); + clientToServerTransport.flip(); // will now contain the + // network data from + // client to server + + // read from the client generated network data into + // server's buffer + result = serverEngine.unwrap(clientToServerTransport, + serverBuffer); + checkAndRunTasks(serverEngine, result.getHandshakeStatus()); + clientToServerTransport.compact(); + + if (hsStatus == HandshakeStatus.NEED_UNWRAP) { + isClientToServer = false; + } else if (hsStatus == HandshakeStatus.FINISHED) { + break; + } else if (hsStatus != HandshakeStatus.NEED_WRAP) { + throw new Exception("Unexpected handshake result " + + result); + } + } else { + // send server's message over the transport + SSLEngineResult result = serverEngine.wrap(msgFromServer, + serverToClientTransport); + // run any delegated tasks on the server side + final HandshakeStatus hsStatus = checkAndRunTasks(serverEngine, + result.getHandshakeStatus()); + serverToClientTransport.flip(); // will now contain the + // network data from + // server to client + + // read from the server generated network data into + // client's buffer + result = clientEngine.unwrap(serverToClientTransport, + clientBuffer); + // run any delegated tasks on the client side + checkAndRunTasks(clientEngine, result.getHandshakeStatus()); + serverToClientTransport.compact(); + + if (hsStatus == HandshakeStatus.NEED_UNWRAP) { + isClientToServer = true; + } else if (hsStatus == HandshakeStatus.FINISHED) { + break; + } else if (hsStatus != HandshakeStatus.NEED_WRAP) { + throw new Exception("Unexpected handshake result " + + result); + } + } + } + serverEngine.wrap(msgFromServer, serverToClientTransport); + serverToClientTransport.flip(); + clientEngine.unwrap(serverToClientTransport, clientBuffer); + serverToClientTransport.compact(); + } + + private static SSLEngine createServerSSLEngine( + final SSLContext sslContext, final SNIMatcher sniMatcher) { + final SSLEngine serverEngine = sslContext.createSSLEngine(); + serverEngine.setUseClientMode(false); + if (sniMatcher != null) { + final SSLParameters sslParameters = + serverEngine.getSSLParameters(); // returns a copy + sslParameters.setSNIMatchers(List.of(sniMatcher)); + // use the updated params + serverEngine.setSSLParameters(sslParameters); + } + return serverEngine; + } + + private static SSLEngine createClientSSLEngine( + final SSLContext sslContext) { + final SSLEngine clientEngine = sslContext.createSSLEngine(PEER_HOST, + PEER_PORT); + clientEngine.setUseClientMode(true); + final SSLParameters params = + clientEngine.getSSLParameters(); // returns a copy + // setup SNI name that will be used by the client during TLS handshake + params.setServerNames(List.of(new SNIHostName(CLIENT_REQUESTED_SNI))); + clientEngine.setSSLParameters(params); // use the updated params + return clientEngine; + } + + private static HandshakeStatus checkAndRunTasks( + final SSLEngine engine, final HandshakeStatus handshakeStatus) { + if (handshakeStatus != HandshakeStatus.NEED_TASK) { + return handshakeStatus; + } + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + System.out.println("Running task " + runnable); + runnable.run(); + } + return engine.getHandshakeStatus(); + } + + private static final class TestSNIMatcher extends SNIMatcher { + + private final String recognizedSNIServerName; + + private TestSNIMatcher(final String recognizedSNIServerName) { + super(StandardConstants.SNI_HOST_NAME); + this.recognizedSNIServerName = recognizedSNIServerName; + } + + @Override + public boolean matches(final SNIServerName clientRequestedSNI) { + Objects.requireNonNull(clientRequestedSNI); + System.out.println("Attempting SNI match against client" + + " request SNI name: " + clientRequestedSNI + + " against server recognized SNI name " + + recognizedSNIServerName); + if (!SNIHostName.class.isInstance(clientRequestedSNI)) { + System.out.println("SNI match failed - client request" + + " SNI isn't a SNIHostName"); + // we only support SNIHostName type + return false; + } + final String requestedName = + ((SNIHostName) clientRequestedSNI).getAsciiName(); + final boolean matches = + recognizedSNIServerName.equals(requestedName); + System.out.println("SNI match " + (matches ? "passed" : "failed")); + return matches; + } + } +} diff --git a/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java b/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java index ee50f21ae27..0867925f135 100644 --- a/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java +++ b/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -34,7 +34,15 @@ public class MFLNTest extends SSLEngineTestCase { public static void main(String[] args) { setUpAndStartKDCIfNeeded(); System.setProperty("jsse.enableMFLNExtension", "true"); - for (int mfl = 4096; mfl >= 256; mfl /= 2) { + String testMode = System.getProperty("test.mode", "norm"); + int mflLen; + if (testMode.equals("norm_sni")) { + mflLen = 512; + } else { + mflLen = 256; + } + + for (int mfl = 4096; mfl >= mflLen; mfl /= 2) { System.out.println("==============================================" + "=============="); System.out.printf("Testsing DTLS handshake with MFL = %d%n", mfl); diff --git a/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java index 7bb3e2c8d2b..855e34b57f0 100644 --- a/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java +++ b/test/jdk/javax/net/ssl/ciphersuites/DisabledAlgorithms.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8076221 8211883 + * @bug 8076221 8211883 8279164 * @summary Check if weak cipher suites are disabled * @modules jdk.crypto.ec * @run main/othervm DisabledAlgorithms default @@ -60,9 +60,9 @@ public class DisabledAlgorithms { System.getProperty("test.src", "./") + "/" + pathToStores + "/" + trustStoreFile; - // supported RC4, NULL, and anon cipher suites - // it does not contain KRB5 cipher suites because they need a KDC - private static final String[] rc4_null_anon_ciphersuites = new String[] { + // disabled RC4, NULL, anon, and ECDH cipher suites + private static final String[] disabled_ciphersuites + = new String[] { "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_RC4_128_SHA", @@ -94,7 +94,19 @@ public class DisabledAlgorithms { "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "TLS_ECDH_anon_WITH_NULL_SHA", - "TLS_ECDH_anon_WITH_RC4_128_SHA" + "TLS_ECDH_anon_WITH_RC4_128_SHA", + "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA" }; public static void main(String[] args) throws Exception { @@ -113,9 +125,8 @@ public static void main(String[] args) throws Exception { System.out.println("jdk.tls.disabledAlgorithms = " + Security.getProperty("jdk.tls.disabledAlgorithms")); - // check if RC4, NULL, and anon cipher suites - // can't be used by default - checkFailure(rc4_null_anon_ciphersuites); + // check that disabled cipher suites can't be used by default + checkFailure(disabled_ciphersuites); break; case "empty": // reset jdk.tls.disabledAlgorithms @@ -123,9 +134,9 @@ public static void main(String[] args) throws Exception { System.out.println("jdk.tls.disabledAlgorithms = " + Security.getProperty("jdk.tls.disabledAlgorithms")); - // check if RC4, NULL, and anon cipher suites can be used - // if jdk.tls.disabledAlgorithms is empty - checkSuccess(rc4_null_anon_ciphersuites); + // check that disabled cipher suites can be used if + // jdk.{tls,certpath}.disabledAlgorithms is empty + checkSuccess(disabled_ciphersuites); break; default: throw new RuntimeException("Wrong parameter: " + args[0]); @@ -151,11 +162,12 @@ private static void checkFailure(String[] ciphersuites) throws Exception { throw new RuntimeException("Expected SSLHandshakeException " + "not thrown"); } catch (SSLHandshakeException e) { - System.out.println("Expected exception on client side: " + System.out.println("Got expected exception on client side: " + e); } } + server.stop(); while (server.isRunning()) { sleep(); } @@ -251,7 +263,6 @@ public void run() { } catch (SSLHandshakeException e) { System.out.println("Server: run: " + e); sslError = true; - stopped = true; } catch (IOException e) { if (!stopped) { System.out.println("Server: run: unexpected exception: " diff --git a/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java b/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java index 92bb2e03228..e56f99c58da 100644 --- a/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java +++ b/test/jdk/javax/net/ssl/sanity/ciphersuites/CheckCipherSuites.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2019, 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,7 +23,7 @@ /* * @test - * @bug 4750141 4895631 8217579 8163326 + * @bug 4750141 4895631 8217579 8163326 8279164 * @summary Check enabled and supported ciphersuites are correct * @run main/othervm CheckCipherSuites default * @run main/othervm CheckCipherSuites limited @@ -51,54 +51,38 @@ public class CheckCipherSuites { // Not suite B, but we want it to position the suite early "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", - // AES_256(GCM) - ECDHE - forward screcy + // AES_256(GCM) - ECDHE - forward secrecy "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - // AES_128(GCM) - ECDHE - forward screcy + // AES_128(GCM) - ECDHE - forward secrecy "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - // AES_256(GCM) - DHE - forward screcy + // AES_256(GCM) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", - // AES_128(GCM) - DHE - forward screcy + // AES_128(GCM) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", - // AES_256(CBC) - ECDHE - forward screcy + // AES_256(CBC) - ECDHE - forward secrecy "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", - // AES_256(CBC) - ECDHE - forward screcy + // AES_256(CBC) - ECDHE - forward secrecy "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - // AES_256(CBC) - DHE - forward screcy + // AES_256(CBC) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", - // AES_128(CBC) - DHE - forward screcy + // AES_128(CBC) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", - // AES_256(GCM) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", - - // AES_128(GCM) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", - - // AES_256(CBC) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", - - // AES_128(CBC) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", - // AES_256(CBC) - ECDHE - using SHA "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", @@ -115,14 +99,6 @@ public class CheckCipherSuites { "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - // AES_256(CBC) - using SHA, not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", - - // AES_128(CBC) - using SHA, not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", - // deprecated "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256", @@ -146,16 +122,10 @@ public class CheckCipherSuites { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", @@ -178,54 +148,38 @@ public class CheckCipherSuites { // Not suite B, but we want it to position the suite early "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", - // AES_256(GCM) - ECDHE - forward screcy + // AES_256(GCM) - ECDHE - forward secrecy "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", - // AES_128(GCM) - ECDHE - forward screcy + // AES_128(GCM) - ECDHE - forward secrecy "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - // AES_256(GCM) - DHE - forward screcy + // AES_256(GCM) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", - // AES_128(GCM) - DHE - forward screcy + // AES_128(GCM) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", - // AES_256(CBC) - ECDHE - forward screcy + // AES_256(CBC) - ECDHE - forward secrecy "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", - // AES_256(CBC) - ECDHE - forward screcy + // AES_256(CBC) - ECDHE - forward secrecy "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - // AES_256(CBC) - DHE - forward screcy + // AES_256(CBC) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", - // AES_128(CBC) - DHE - forward screcy + // AES_128(CBC) - DHE - forward secrecy "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", - // AES_256(GCM) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", - - // AES_128(GCM) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", - - // AES_256(CBC) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", - - // AES_128(CBC) - not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", - // AES_256(CBC) - ECDHE - using SHA "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", @@ -242,14 +196,6 @@ public class CheckCipherSuites { "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - // AES_256(CBC) - using SHA, not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", - - // AES_128(CBC) - using SHA, not forward screcy - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", - // deprecated "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256", @@ -273,16 +219,10 @@ public class CheckCipherSuites { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", diff --git a/test/jdk/javax/print/DialogMargins.java b/test/jdk/javax/print/DialogMargins.java index cb5b488369c..ca45a1690a6 100644 --- a/test/jdk/javax/print/DialogMargins.java +++ b/test/jdk/javax/print/DialogMargins.java @@ -24,12 +24,12 @@ /** * @test * @bug 4485755 6361370 6448717 5080051 6939417 8016343 + * @key printer * @summary dialog doesn't have way to specify margins * for 6361370, verify exception for offline printer in Windows * for 6448717, faster display of print dialog * for 6500903, verify status of printer if accepting jobs or not * for 8016343, verify printing to non-default printer - * @author prr * @run main/manual DialogMargins */ diff --git a/test/jdk/javax/print/LookupServices.java b/test/jdk/javax/print/LookupServices.java index 7f6b9f1f604..a5685dbc57c 100644 --- a/test/jdk/javax/print/LookupServices.java +++ b/test/jdk/javax/print/LookupServices.java @@ -24,6 +24,7 @@ /* * @test * @bug 4510477 6520186 + * @key printer * @summary No crash with HP OfficeJet 600 installed. * @run main LookupServices */ diff --git a/test/jdk/javax/print/PrintServiceLookup/GetPrintServices.java b/test/jdk/javax/print/PrintServiceLookup/GetPrintServices.java index 544428566cb..092f7f17da5 100644 --- a/test/jdk/javax/print/PrintServiceLookup/GetPrintServices.java +++ b/test/jdk/javax/print/PrintServiceLookup/GetPrintServices.java @@ -29,6 +29,7 @@ /* * @test + * @key printer * @bug 8013810 8025439 * @summary Test that print service returned without filter are of the same class * as with name filter diff --git a/test/jdk/javax/print/PrintSubInputStream/Example.java b/test/jdk/javax/print/PrintSubInputStream/Example.java index 27acae8af6e..b2aa913dccb 100644 --- a/test/jdk/javax/print/PrintSubInputStream/Example.java +++ b/test/jdk/javax/print/PrintSubInputStream/Example.java @@ -23,9 +23,9 @@ /** * @test + * key printer * @bug 4700712 4707777 * @summary Should submit only 1 job in Windows and print only 1 page. - * @author jgodinez * @run main/manual Example */ import java.awt.*; diff --git a/test/jdk/javax/print/ServiceUIPropBtnTest.java b/test/jdk/javax/print/ServiceUIPropBtnTest.java index ccb57ba4037..189b9b3ac25 100644 --- a/test/jdk/javax/print/ServiceUIPropBtnTest.java +++ b/test/jdk/javax/print/ServiceUIPropBtnTest.java @@ -23,6 +23,7 @@ /* @test @bug 8246742 + @key printer @summary Verifies ServiceUI.printDialog does not support properties dialog @run main/manual ServiceUIPropBtnTest */ diff --git a/test/jdk/javax/print/TextFlavorTest.java b/test/jdk/javax/print/TextFlavorTest.java index f703e8aacc5..18b5e08f6b4 100644 --- a/test/jdk/javax/print/TextFlavorTest.java +++ b/test/jdk/javax/print/TextFlavorTest.java @@ -24,6 +24,7 @@ /* @test @bug 6334074 8022536 + @key printer @summary test supported text flavors reported properly @run main TextFlavorTest */ diff --git a/test/jdk/javax/print/attribute/Chroma.java b/test/jdk/javax/print/attribute/Chroma.java index 5968f951409..60ce9f6719a 100644 --- a/test/jdk/javax/print/attribute/Chroma.java +++ b/test/jdk/javax/print/attribute/Chroma.java @@ -21,7 +21,7 @@ * questions. */ /* - * @test 1.3 01/05/11 + * @test * @bug 4456750 * @summary Test for supported chromaticity values with null DocFlavor. * No exception should be thrown. diff --git a/test/jdk/javax/print/attribute/CollateAttr.java b/test/jdk/javax/print/attribute/CollateAttr.java index 5ea8a78be37..0c6cef60bce 100644 --- a/test/jdk/javax/print/attribute/CollateAttr.java +++ b/test/jdk/javax/print/attribute/CollateAttr.java @@ -24,6 +24,7 @@ /** * @test * @bug 6574117 + * @key printer * @summary Verify no NPE testing service support of SheetCollate * @run main CollateAttr */ diff --git a/test/jdk/javax/print/attribute/PSCopiesFlavorTest.java b/test/jdk/javax/print/attribute/PSCopiesFlavorTest.java index 25b5d955fa5..3f78fa38537 100644 --- a/test/jdk/javax/print/attribute/PSCopiesFlavorTest.java +++ b/test/jdk/javax/print/attribute/PSCopiesFlavorTest.java @@ -24,6 +24,7 @@ /** * @test * @bug 6527316 6732647 + * @key printer * @summary Copies isn't supported for PS flavors. * @run main PSCopiesFlavorTest */ diff --git a/test/jdk/javax/print/attribute/PrintResAttr.java b/test/jdk/javax/print/attribute/PrintResAttr.java index c5467b32532..dee94ae1b78 100644 --- a/test/jdk/javax/print/attribute/PrintResAttr.java +++ b/test/jdk/javax/print/attribute/PrintResAttr.java @@ -23,6 +23,7 @@ /** * @test + * @key printer * @bug 8048328 * @summary CUPS Printing does not report supported printer resolutions. * @run main PrintResAttr diff --git a/test/jdk/javax/print/attribute/ServiceDialogTest.java b/test/jdk/javax/print/attribute/ServiceDialogTest.java index bce24e749cf..626325ab1a4 100644 --- a/test/jdk/javax/print/attribute/ServiceDialogTest.java +++ b/test/jdk/javax/print/attribute/ServiceDialogTest.java @@ -25,6 +25,7 @@ /** * @test * @bug 4910388 4871089 4998624 + * @key printer * @summary Confirm that * 1. After choosing Reverse Landscape in the system default print * Print Service (2nd in the list), it diff --git a/test/jdk/javax/print/attribute/ServiceDialogValidateTest.java b/test/jdk/javax/print/attribute/ServiceDialogValidateTest.java index 929f76869cf..3598d4ae7bd 100644 --- a/test/jdk/javax/print/attribute/ServiceDialogValidateTest.java +++ b/test/jdk/javax/print/attribute/ServiceDialogValidateTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 5049012 8163922 + * @key printer * @summary Verify if PrintToFile option is disabled for flavors that do not * support Destination * @requires (os.family == "linux") diff --git a/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java b/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java index ab17680cefc..5d38d4b1e11 100644 --- a/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java +++ b/test/jdk/javax/print/attribute/ServiceDlgPageRangeTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 5080098 8164205 + * @key printer * @summary Verify if PageRanges option is disabled for Non service-formatted * flavors. * @run main/manual ServiceDlgPageRangeTest diff --git a/test/jdk/javax/print/attribute/ServiceDlgSheetCollateTest.java b/test/jdk/javax/print/attribute/ServiceDlgSheetCollateTest.java index 4f80acc29aa..dd569639f25 100644 --- a/test/jdk/javax/print/attribute/ServiceDlgSheetCollateTest.java +++ b/test/jdk/javax/print/attribute/ServiceDlgSheetCollateTest.java @@ -23,6 +23,7 @@ /* * @test * @bug 5080830 + * @key printer * @summary Verify if SheetCollate option is disabled for flavors that do not * support SheetCollate * @run main/manual ServiceDlgSheetCollateTest diff --git a/test/jdk/javax/print/attribute/Services_getDocFl.java b/test/jdk/javax/print/attribute/Services_getDocFl.java index ca926b39928..63986e46a93 100644 --- a/test/jdk/javax/print/attribute/Services_getDocFl.java +++ b/test/jdk/javax/print/attribute/Services_getDocFl.java @@ -28,6 +28,7 @@ /* * @test + * @key printer * @bug 4901243 8040139 8167291 * @summary JPG, GIF, and PNG DocFlavors (URL) should be supported if Postscript is supported. * @run main Services_getDocFl diff --git a/test/jdk/javax/print/attribute/SidesAttributeTest.java b/test/jdk/javax/print/attribute/SidesAttributeTest.java index 60454f30407..09890c7211b 100644 --- a/test/jdk/javax/print/attribute/SidesAttributeTest.java +++ b/test/jdk/javax/print/attribute/SidesAttributeTest.java @@ -25,6 +25,7 @@ /* * @test * @bug JDK-8311033 + * @key printer * @summary [macos] PrinterJob does not take into account Sides attribute * @run main/manual SidesAttributeTest */ diff --git a/test/jdk/javax/print/attribute/TestUnsupportedResolution.java b/test/jdk/javax/print/attribute/TestUnsupportedResolution.java index 0ecf842af2a..78e7e93c5f5 100644 --- a/test/jdk/javax/print/attribute/TestUnsupportedResolution.java +++ b/test/jdk/javax/print/attribute/TestUnsupportedResolution.java @@ -25,6 +25,7 @@ /** * @test * @bug 8033277 + * @key printer * @summary Confirm that scaling of printout is correct. Manual comparison with printout using a supported resolution is needed. * @run main/manual TestUnsupportedResolution */ diff --git a/test/jdk/javax/print/attribute/autosense/PrintAutoSenseData.java b/test/jdk/javax/print/attribute/autosense/PrintAutoSenseData.java index f5ddc0d15e2..008e0917342 100644 --- a/test/jdk/javax/print/attribute/autosense/PrintAutoSenseData.java +++ b/test/jdk/javax/print/attribute/autosense/PrintAutoSenseData.java @@ -24,6 +24,7 @@ /* * @test * @bug 4468109 8021583 + * @key printer * @summary Test for printing AUTOSENSE DocFlavor. No exception should be thrown. * @run main PrintAutoSenseData */ diff --git a/test/jdk/javax/swing/JButton/bug4234034.java b/test/jdk/javax/swing/JButton/bug4234034.java new file mode 100644 index 00000000000..e1e65345f73 --- /dev/null +++ b/test/jdk/javax/swing/JButton/bug4234034.java @@ -0,0 +1,72 @@ +/* + * 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 + * 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 4234034 + * @summary Tests NullPointerException when ToolTip invoked via keyboard + * @key headful + * @run main bug4234034 + */ + +import java.awt.Robot; +import java.awt.event.KeyEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class bug4234034 { + static JFrame frame; + static JButton button; + + public static void main(String args[]) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4323121"); + button = new JButton("Press tab, then Ctrl+F1"); + button.setToolTipText("Tooltip for button"); + frame.getContentPane().add(button); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_F1); + robot.keyRelease(KeyEvent.VK_F1); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JButton/bug4323121.java b/test/jdk/javax/swing/JButton/bug4323121.java new file mode 100644 index 00000000000..0b352ce57eb --- /dev/null +++ b/test/jdk/javax/swing/JButton/bug4323121.java @@ -0,0 +1,125 @@ +/* + * 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 + * 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 4323121 + * @summary Tests whether any button that extends JButton always + returns true for isArmed() + * @key headful + * @run main bug4323121 + */ + +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class bug4323121 { + + static JFrame frame; + static testButton button; + static volatile Point pt; + static volatile int buttonW; + static volatile int buttonH; + static volatile boolean failed = false; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4323121"); + button = new testButton("gotcha"); + frame.getContentPane().add(button); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + pt = button.getLocationOnScreen(); + buttonW = button.getSize().width; + buttonH = button.getSize().height; + }); + robot.mouseMove(pt.x + buttonW / 2, pt.y + buttonH / 2); + robot.waitForIdle(); + if (failed) { + throw new RuntimeException("Any created button returns " + + "true for isArmed()"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class testButton extends JButton implements MouseMotionListener, MouseListener { + public testButton(String label) { + super(label); + addMouseMotionListener(this); + addMouseListener(this); + } + + protected void paintComponent(Graphics g) { + super.paintComponent(g); + } + + protected void paintBorder(Graphics g) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseDragged(MouseEvent e) { + } + + public void mouseMoved(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + if (getModel().isArmed()) { + failed = true; + } + } + + public void mouseExited(MouseEvent e) { + } + + public void mouseClicked(MouseEvent e) { + } + } +} diff --git a/test/jdk/javax/swing/JButton/bug4490179.java b/test/jdk/javax/swing/JButton/bug4490179.java new file mode 100644 index 00000000000..94b141e5030 --- /dev/null +++ b/test/jdk/javax/swing/JButton/bug4490179.java @@ -0,0 +1,95 @@ +/* + * 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 + * 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 4490179 + * @summary Tests that JButton only responds to left mouse clicks. + * @key headful + * @run main bug4490179 + */ + +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class bug4490179 { + static JFrame frame; + static JButton button; + static volatile Point pt; + static volatile int buttonW; + static volatile int buttonH; + static volatile boolean passed = true; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4490179"); + button = new JButton("Button"); + frame.getContentPane().add(button); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + passed = false; + } + }); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + pt = button.getLocationOnScreen(); + buttonW = button.getSize().width; + buttonH = button.getSize().height; + }); + + robot.mouseMove(pt.x + buttonW / 2, pt.y + buttonH / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + if (!passed) { + throw new RuntimeException("Test Failed"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JComponent/6683775/bug6683775.java b/test/jdk/javax/swing/JComponent/6683775/bug6683775.java index c5572e83919..5c8445dc479 100644 --- a/test/jdk/javax/swing/JComponent/6683775/bug6683775.java +++ b/test/jdk/javax/swing/JComponent/6683775/bug6683775.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -46,6 +46,10 @@ public class bug6683775 { static final int LOC = 100, SIZE = 200; + static JFrame testFrame; + static JFrame backgroundFrame; + static BufferedImage capture; + public static void main(String[] args) throws Exception { GraphicsConfiguration gc = getGC(); if (gc == null || !gc.getDevice().isWindowTranslucencySupported( @@ -53,35 +57,39 @@ public static void main(String[] args) throws Exception { return; } Robot robot = new Robot(); - final JFrame testFrame = new JFrame(gc); - - SwingUtilities.invokeAndWait(() -> { - JFrame backgroundFrame = new JFrame("Background frame"); - backgroundFrame.setUndecorated(true); - JPanel panel = new JPanel(); - panel.setBackground(Color.RED); - backgroundFrame.add(panel); - backgroundFrame.setBounds(LOC, LOC, SIZE, SIZE); - backgroundFrame.setVisible(true); - - testFrame.setUndecorated(true); - JPanel p = new JPanel(); - p.setOpaque(false); - testFrame.add(p); - setOpaque(testFrame, false); - testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - testFrame.setBounds(LOC, LOC, SIZE, SIZE); - testFrame.setVisible(true); - }); - - robot.waitForIdle(); - Thread.sleep(1500); - - //robot.getPixelColor() didn't work right for some reason - BufferedImage capture = - robot.createScreenCapture(new Rectangle(LOC, LOC, SIZE, SIZE)); - - SwingUtilities.invokeAndWait(testFrame::dispose); + + try { + SwingUtilities.invokeAndWait(() -> { + testFrame = new JFrame(gc); + backgroundFrame = new JFrame("Background frame"); + backgroundFrame.setUndecorated(true); + JPanel panel = new JPanel(); + panel.setBackground(Color.RED); + backgroundFrame.add(panel); + backgroundFrame.setBounds(LOC, LOC, SIZE, SIZE); + backgroundFrame.setVisible(true); + + testFrame.setUndecorated(true); + JPanel p = new JPanel(); + p.setOpaque(false); + testFrame.add(p); + setOpaque(testFrame, false); + testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + testFrame.setBounds(LOC, LOC, SIZE, SIZE); + testFrame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(1000); + + //robot.getPixelColor() didn't work right for some reason + capture = + robot.createScreenCapture(new Rectangle(LOC, LOC, SIZE, SIZE)); + + } finally { + SwingUtilities.invokeAndWait(testFrame::dispose); + SwingUtilities.invokeAndWait(backgroundFrame::dispose); + } int redRGB = Color.RED.getRGB(); if (redRGB != capture.getRGB(SIZE/2, SIZE/2)) { diff --git a/test/jdk/javax/swing/JEditorPane/EditorPaneCharset.java b/test/jdk/javax/swing/JEditorPane/EditorPaneCharset.java new file mode 100644 index 00000000000..cf2edf0e1a1 --- /dev/null +++ b/test/jdk/javax/swing/JEditorPane/EditorPaneCharset.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +import javax.swing.JEditorPane; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; + +/* + * @test + * @bug 8328953 + * @summary Verifies JEditorPane.read doesn't throw ChangedCharSetException + but handles it and reads HTML in the specified encoding + * @run main EditorPaneCharset + */ + +public final class EditorPaneCharset { + private static final String CYRILLIC_TEXT = + "\u041F\u0440\u0438\u0432\u0435\u0442, \u043C\u0438\u0440!"; + private static final String HTML_CYRILLIC = + "\n" + + "\n" + + " \n" + + "\n" + + "

      " + CYRILLIC_TEXT + "

      \n" + + "\n"; + + public static void main(String[] args) throws IOException, BadLocationException { + JEditorPane editorPane = new JEditorPane(); + editorPane.setContentType("text/html"); + Document document = editorPane.getDocument(); + + // Shouldn't throw ChangedCharSetException + editorPane.read( + new ByteArrayInputStream( + HTML_CYRILLIC.getBytes( + Charset.forName("windows-1251"))), + document); + + Element root = document.getDefaultRootElement(); + Element body = root.getElement(1); + Element p = body.getElement(0); + String pText = document.getText(p.getStartOffset(), + p.getEndOffset() - p.getStartOffset() - 1); + if (!CYRILLIC_TEXT.equals(pText)) { + throw new RuntimeException("Text doesn't match"); + } + } +} diff --git a/test/jdk/javax/swing/JFileChooser/8041694/bug8041694.java b/test/jdk/javax/swing/JFileChooser/8041694/bug8041694.java index 3e3cb3b9658..23ed9163fa5 100644 --- a/test/jdk/javax/swing/JFileChooser/8041694/bug8041694.java +++ b/test/jdk/javax/swing/JFileChooser/8041694/bug8041694.java @@ -102,11 +102,11 @@ public void run() { } System.out.println(String.format( "The selected directory is '%s'.", selectedDir.getAbsolutePath())); - if (selectedDir.getName().equals("d")) { + if (selectedDir.getName().toLowerCase().equals("d")) { throw new RuntimeException( "JFileChooser removed trailing spaces in the selected directory name. " + "Expected 'd ' got '" + selectedDir.getName() + "'."); - } else if (!selectedDir.getName().equals("d ")) { + } else if (!selectedDir.getName().toLowerCase().equals("d ")) { throw new RuntimeException("The selected directory name is not " + "the expected 'd ' but '" + selectedDir.getName() + "'."); } diff --git a/test/jdk/javax/swing/JFileChooser/8046391/bug8046391.java b/test/jdk/javax/swing/JFileChooser/8046391/bug8046391.java index 60e80d33116..db9b89cbdc3 100644 --- a/test/jdk/javax/swing/JFileChooser/8046391/bug8046391.java +++ b/test/jdk/javax/swing/JFileChooser/8046391/bug8046391.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8046391 + * @bug 8046391 8293862 * @requires (os.family == "windows") * @summary JFileChooser hangs if displayed in Windows L&F * @author Alexey Ivanov diff --git a/test/jdk/javax/swing/JFileChooser/FileSystemView/NoIconExeNPE.java b/test/jdk/javax/swing/JFileChooser/FileSystemView/NoIconExeNPE.java new file mode 100644 index 00000000000..5da17d04537 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/FileSystemView/NoIconExeNPE.java @@ -0,0 +1,274 @@ +/* + * 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. + */ + +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import javax.swing.Icon; +import javax.swing.UIManager; +import javax.swing.filechooser.FileSystemView; + +/* + * @test + * @bug 8320692 + * @requires (os.family == "windows") + * @summary NullPointerException is thrown for .exe file without icon + * @run main/othervm NoIconExeNPE + */ +public class NoIconExeNPE { + + /** + * Bytes of a short {@code Hello.exe} which has no icon. + */ + private static final byte[] bytes = { + (byte) 0x4d, (byte) 0x5a, (byte) 0x80, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x00, + (byte) 0x10, (byte) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0x00, + (byte) 0x00, (byte) 0x40, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0e, + (byte) 0x1f, (byte) 0xba, (byte) 0x0e, (byte) 0x00, (byte) 0xb4, + (byte) 0x09, (byte) 0xcd, (byte) 0x21, (byte) 0xb8, (byte) 0x01, + (byte) 0x4c, (byte) 0xcd, (byte) 0x21, (byte) 0x54, (byte) 0x68, + (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x70, (byte) 0x72, + (byte) 0x6f, (byte) 0x67, (byte) 0x72, (byte) 0x61, (byte) 0x6d, + (byte) 0x20, (byte) 0x63, (byte) 0x61, (byte) 0x6e, (byte) 0x6e, + (byte) 0x6f, (byte) 0x74, (byte) 0x20, (byte) 0x62, (byte) 0x65, + (byte) 0x20, (byte) 0x72, (byte) 0x75, (byte) 0x6e, (byte) 0x20, + (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x44, (byte) 0x4f, + (byte) 0x53, (byte) 0x20, (byte) 0x6d, (byte) 0x6f, (byte) 0x64, + (byte) 0x65, (byte) 0x2e, (byte) 0x0d, (byte) 0x0a, (byte) 0x24, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x50, (byte) 0x45, + (byte) 0x00, (byte) 0x00, (byte) 0x4c, (byte) 0x01, (byte) 0x01, + (byte) 0x00, (byte) 0x55, (byte) 0x96, (byte) 0x96, (byte) 0x65, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xe0, (byte) 0x00, + (byte) 0x0f, (byte) 0x01, (byte) 0x0b, (byte) 0x01, (byte) 0x01, + (byte) 0x49, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00, (byte) 0x00, + (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x20, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, + (byte) 0x00, (byte) 0xac, (byte) 0xa3, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, + (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x40, (byte) 0x10, (byte) 0x00, (byte) 0x00, + (byte) 0x86, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x2e, (byte) 0x74, (byte) 0x65, (byte) 0x78, + (byte) 0x74, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xc6, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x00, + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x6a, (byte) 0x00, (byte) 0x68, + (byte) 0x29, (byte) 0x10, (byte) 0x40, (byte) 0x00, (byte) 0x68, + (byte) 0x1c, (byte) 0x10, (byte) 0x40, (byte) 0x00, (byte) 0x6a, + (byte) 0x00, (byte) 0xff, (byte) 0x15, (byte) 0x88, (byte) 0x10, + (byte) 0x40, (byte) 0x00, (byte) 0x6a, (byte) 0x00, (byte) 0xff, + (byte) 0x15, (byte) 0x80, (byte) 0x10, (byte) 0x40, (byte) 0x00, + (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f, + (byte) 0x20, (byte) 0x57, (byte) 0x6f, (byte) 0x72, (byte) 0x6c, + (byte) 0x64, (byte) 0x21, (byte) 0x00, (byte) 0x48, (byte) 0x65, + (byte) 0x6c, (byte) 0x6c, (byte) 0x6f, (byte) 0x20, (byte) 0x66, + (byte) 0x61, (byte) 0x73, (byte) 0x6d, (byte) 0x00, (byte) 0x90, + (byte) 0x90, (byte) 0x90, (byte) 0x90, (byte) 0x90, (byte) 0x90, + (byte) 0x90, (byte) 0x90, (byte) 0x90, (byte) 0x90, (byte) 0x90, + (byte) 0x90, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x90, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x10, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x9d, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x88, (byte) 0x10, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x90, (byte) 0x90, (byte) 0x90, (byte) 0x90, + (byte) 0xa8, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xb8, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x6b, (byte) 0x65, (byte) 0x72, (byte) 0x6e, + (byte) 0x65, (byte) 0x6c, (byte) 0x33, (byte) 0x32, (byte) 0x2e, + (byte) 0x64, (byte) 0x6c, (byte) 0x6c, (byte) 0x00, (byte) 0x75, + (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x33, (byte) 0x32, + (byte) 0x2e, (byte) 0x64, (byte) 0x6c, (byte) 0x6c, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x45, (byte) 0x78, (byte) 0x69, + (byte) 0x74, (byte) 0x50, (byte) 0x72, (byte) 0x6f, (byte) 0x63, + (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x00, (byte) 0x90, + (byte) 0x90, (byte) 0x00, (byte) 0x00, (byte) 0x4d, (byte) 0x65, + (byte) 0x73, (byte) 0x73, (byte) 0x61, (byte) 0x67, (byte) 0x65, + (byte) 0x42, (byte) 0x6f, (byte) 0x78, (byte) 0x41, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 + }; + + public static void main(String[] args) throws Exception { + final Path temp = Files.createTempDirectory("no-icon"); + final Path hello = temp.resolve("Hello.exe"); + + try { + try (OutputStream out = Files.newOutputStream(hello)) { + out.write(bytes); + } + + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + FileSystemView fsv = FileSystemView.getFileSystemView(); + // No NullPointerException is expected + Icon icon = fsv.getSystemIcon(hello.toFile()); + if (icon == null) { + throw new RuntimeException("Null icon returned by FileSystemView.getSystemIcon()"); + } + } finally { + Files.deleteIfExists(hello); + Files.delete(temp); + } + } +} diff --git a/test/jdk/javax/swing/JFileChooser/FileSystemView/Win32FolderSort.java b/test/jdk/javax/swing/JFileChooser/FileSystemView/Win32FolderSort.java new file mode 100644 index 00000000000..7ed5e0f1705 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/FileSystemView/Win32FolderSort.java @@ -0,0 +1,158 @@ +/* + * 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. + */ + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/* + * @test + * @bug 8305072 + * @requires (os.family == "windows") + * @modules java.desktop/sun.awt.shell + * @summary Verifies consistency of Win32ShellFolder2.compareTo + * @run main/othervm --add-opens java.desktop/sun.awt.shell=ALL-UNNAMED Win32FolderSort + */ +public class Win32FolderSort { + public static void main(String[] args) throws Exception { + Class folderManager = Class.forName("sun.awt.shell.Win32ShellFolderManager2"); + Class folder = Class.forName("sun.awt.shell.Win32ShellFolder2"); + + Method getDesktop = folderManager.getDeclaredMethod("getDesktop"); + getDesktop.setAccessible(true); + Method getPersonal = folderManager.getDeclaredMethod("getPersonal"); + getPersonal.setAccessible(true); + + Method createShellFolder = folderManager.getDeclaredMethod("createShellFolder", folder, File.class); + createShellFolder.setAccessible(true); + + Method isFileSystem = folder.getMethod("isFileSystem"); + isFileSystem.setAccessible(true); + Method isSpecial = folder.getMethod("isSpecial"); + isSpecial.setAccessible(true); + Method getChildByPath = folder.getDeclaredMethod("getChildByPath", String.class); + getChildByPath.setAccessible(true); + + File desktop = (File) getDesktop.invoke(null); + File personal = (File) getPersonal.invoke(null); + if (!((Boolean) isSpecial.invoke(personal))) { + throw new RuntimeException("personal is not special"); + } + File fakePersonal = (File) getChildByPath.invoke(desktop, personal.getPath()); + if (fakePersonal == null) { + fakePersonal = (File) createShellFolder.invoke(null, desktop, + new File(personal.getPath())); + } + if ((Boolean) isSpecial.invoke(fakePersonal)) { + throw new RuntimeException("fakePersonal is special"); + } + File homeDir = (File) createShellFolder.invoke(null, desktop, + new File(System.getProperty("user.home"))); + + File[] files = {fakePersonal, personal, homeDir}; + for (File f : files) { + if (!((Boolean) isFileSystem.invoke(f))) { + throw new RuntimeException(f + " is not on file system"); + } + } + + List errors = new ArrayList<>(2); + for (File f1 : files) { + for (File f2 : files) { + for (File f3 : files) { + String result = verifyCompareTo(f1, f2, f3); + if (result != null) { + String error = result + "\nwhere" + + "\n a = " + formatFile(f1, isSpecial) + + "\n b = " + formatFile(f2, isSpecial) + + "\n c = " + formatFile(f3, isSpecial); + errors.add(error); + } + } + } + } + + + System.out.println("Unsorted:"); + for (File f : files) { + System.out.println(formatFile(f, isSpecial)); + } + System.out.println(); + + Arrays.sort(files); + System.out.println("Sorted:"); + for (File f : files) { + System.out.println(formatFile(f, isSpecial)); + } + + + if (!errors.isEmpty()) { + System.err.println("Implementation of Win32ShellFolder2.compareTo is inconsistent:"); + errors.forEach(System.err::println); + throw new RuntimeException("Inconsistencies found: " + errors.size() + + " - " + errors.get(0)); + } + } + + /** + * Verifies consistency of {@code Comparable} implementation. + * + * @param a the first object + * @param b the second object + * @param c the third object + * @return error message if inconsistency is found, + * or {@code null } otherwise + */ + private static String verifyCompareTo(File a, File b, File c) { + // a < b & b < c => a < c + if (a.compareTo(b) < 0 && b.compareTo(c) < 0) { + if (a.compareTo(c) >= 0) { + return "a < b & b < c but a >= c"; + } + } + + // a > b & b > c => a > c + if (a.compareTo(b) > 0 && b.compareTo(c) > 0) { + if (a.compareTo(c) <= 0) { + return "a > b & b > c but a <= c"; + } + } + + // a = b & b = c => a = c + if (a.compareTo(b) == 0 && b.compareTo(c) == 0) { + if (a.compareTo(c) != 0) { + return "a = b & b = c but a != c"; + } + } + + return null; + } + + private static String formatFile(File f, Method isSpecial) + throws InvocationTargetException, IllegalAccessException { + return f + "(" + isSpecial.invoke(f) + ")"; + } +} diff --git a/test/jdk/javax/swing/JFileChooser/FileSystemView/WindowsDefaultIconSizeTest.java b/test/jdk/javax/swing/JFileChooser/FileSystemView/WindowsDefaultIconSizeTest.java new file mode 100644 index 00000000000..081277ca2d4 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/FileSystemView/WindowsDefaultIconSizeTest.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.filechooser.FileSystemView; +import java.awt.Image; +import java.awt.image.MultiResolutionImage; +import java.io.File; +import java.io.IOException; + +/* + * @test + * @bug 8282526 + * @summary Default icon is not painted properly + * @requires (os.family == "windows") + * @run main WindowsDefaultIconSizeTest + */ + +public class WindowsDefaultIconSizeTest { + public static void main(String[] args) { + WindowsDefaultIconSizeTest test = new WindowsDefaultIconSizeTest(); + test.test(); + } + + public void test() { + String sep = System.getProperty("file.separator"); + String dir = System.getProperty("test.src", "."); + String filename = "test.not"; + + File testFile = new File(dir + sep + filename); + try { + if (!testFile.exists()) { + testFile.createNewFile(); + testFile.deleteOnExit(); + } + FileSystemView fsv = FileSystemView.getFileSystemView(); + Icon icon = fsv.getSystemIcon(new File(dir + sep + filename)); + if (icon instanceof ImageIcon) { + Image image = ((ImageIcon) icon).getImage(); + if (image instanceof MultiResolutionImage) { + Image variant = ((MultiResolutionImage) image).getResolutionVariant(16, 16); + if (variant.getWidth(null) != 16) { + throw new RuntimeException("Default file icon has size of " + + variant.getWidth(null) + " instead of 16"); + } + } + } + } catch (IOException ioe) { + throw new RuntimeException("Unexpected error while creating the test file: " + ioe.getLocalizedMessage()); + } + } +} diff --git a/test/jdk/javax/swing/JFrame/DefaultCloseOperation.java b/test/jdk/javax/swing/JFrame/DefaultCloseOperation.java new file mode 100644 index 00000000000..098000a8bab --- /dev/null +++ b/test/jdk/javax/swing/JFrame/DefaultCloseOperation.java @@ -0,0 +1,221 @@ +/* + * 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 + * 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.awt.Frame; +import java.awt.FlowLayout; +import java.awt.Window; +import java.awt.event.ItemEvent; +import java.awt.event.WindowEvent; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; + +/* + * @test + * @summary test for defaultCloseOperation property for Swing JFrame and JDialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultCloseOperation + */ + +public class DefaultCloseOperation extends JPanel { + + private static final String INSTRUCTIONS = """ + Do the following steps: + + - Click the "Open Frame" button (a TestFrame will appear) + - On the TestFrame, select "Close" from the system menu (the window should go away) + - Select "Do Nothing" from the "JFrame Default Close Operation" ComboBox + - Click the "Open Frame" button + - On the TestFrame, select "Close" from the system menu (the window should remain open) + - Select "Dispose" from the "JFrame Default Close Operation" ComboBox + - On the TestFrame, select "Close" from the system menu (the window should go away) + + + - Click the "Open Frame" button + - Click the "Open Dialog" button (a TestDialog will appear) + - On the TestDialog, select "Close" from the system menu (the window should go away) + - Select "Do Nothing" from the "JDialog Default Close Operation" ComboBox + - Click the "Open Dialog" button + - On the TestDialog, select "Close" from the system menu (the window should remain open) + - Select "Dispose" from the "JDialog Default Close Operation" ComboBox + - On the TestDialog, select "Close" from the system menu (the window should go away) + """; + + JComboBox frameCloseOp; + + CloseOpDialog testDialog; + JComboBox dialogCloseOp; + + public static void main(String[] args) throws Exception { + + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("DefaultCloseOperation Manual Test") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(20) + .columns(70) + .build(); + + SwingUtilities.invokeAndWait(() -> { + DefaultCloseOperation dco = new DefaultCloseOperation(); + dco.init(); + + JFrame frame = new JFrame("DefaultCloseOperation"); + frame.add(dco); + frame.setSize(500,200); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame + .positionTestWindow(frame, PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } + + public void init() { + setLayout(new FlowLayout()); + + CloseOpFrame testFrame = new CloseOpFrame(); + testFrame.setLocationRelativeTo(null); + PassFailJFrame.addTestWindow(testFrame); + + add(new JLabel("JFrame Default Close Operation:")); + frameCloseOp = new JComboBox<>(); + frameCloseOp.addItem("Hide"); + frameCloseOp.addItem("Do Nothing"); + frameCloseOp.addItem("Dispose"); + frameCloseOp.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + String item = (String)e.getItem(); + switch (item) { + case "Do Nothing" -> testFrame + .setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + case "Hide" -> testFrame + .setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); + case "Dispose" -> testFrame + .setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + } + } + }); + add(frameCloseOp); + + JButton b = new JButton("Open Frame..."); + b.addActionListener(e -> testFrame.setVisible(true)); + add(b); + + testDialog = new CloseOpDialog(testFrame); + testDialog.setLocationRelativeTo(null); + PassFailJFrame.addTestWindow(testDialog); + + add(new JLabel("JDialog Default Close Operation:")); + dialogCloseOp = new JComboBox<>(); + dialogCloseOp.addItem("Hide"); + dialogCloseOp.addItem("Do Nothing"); + dialogCloseOp.addItem("Dispose"); + dialogCloseOp.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + String item = (String)e.getItem(); + switch (item) { + case "Do Nothing" -> testDialog + .setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + case "Hide" -> testDialog + .setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); + case "Dispose" -> testDialog + .setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + } + } + }); + add(dialogCloseOp); + + b = new JButton("Open Dialog..."); + b.addActionListener(e -> testDialog.setVisible(true)); + add(b); + } + + public static void verifyCloseOperation(Window window, int op) { + switch (op) { + case WindowConstants.DO_NOTHING_ON_CLOSE -> { + if (!window.isVisible()) { + PassFailJFrame + .forceFail("defaultCloseOperation=DoNothing failed"); + } + } + case WindowConstants.HIDE_ON_CLOSE -> { + if (window.isVisible()) { + PassFailJFrame + .forceFail("defaultCloseOperation=Hide failed"); + } + } + case WindowConstants.DISPOSE_ON_CLOSE -> { + if (window.isVisible() || window.isDisplayable()) { + PassFailJFrame + .forceFail("defaultCloseOperation=Dispose failed"); + } + } + } + } +} + +class CloseOpFrame extends JFrame { + + public CloseOpFrame() { + super("DefaultCloseOperation Test"); + getContentPane().add("Center", new JLabel("Test Frame")); + pack(); + } + + protected void processWindowEvent(WindowEvent e) { + super.processWindowEvent(e); + + if (e.getID() == WindowEvent.WINDOW_CLOSING) { + DefaultCloseOperation + .verifyCloseOperation(this, getDefaultCloseOperation()); + } + } +} + +class CloseOpDialog extends JDialog { + + public CloseOpDialog(Frame owner) { + super(owner, "DefaultCloseOperation Test Dialog"); + getContentPane().add("Center", new JLabel("Test Dialog")); + pack(); + } + + protected void processWindowEvent(WindowEvent e) { + super.processWindowEvent(e); + + if (e.getID() == WindowEvent.WINDOW_CLOSING) { + DefaultCloseOperation + .verifyCloseOperation(this, getDefaultCloseOperation()); + } + } +} diff --git a/test/jdk/javax/swing/JFrame/bug4419914.java b/test/jdk/javax/swing/JFrame/bug4419914.java new file mode 100644 index 00000000000..4574b1ac3d6 --- /dev/null +++ b/test/jdk/javax/swing/JFrame/bug4419914.java @@ -0,0 +1,82 @@ +/* + * 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 + * 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 4419914 + * @summary Tests that tab movement is correct in RTL component orientation. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4419914 +*/ + +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import javax.swing.JButton; +import javax.swing.JFrame; +import java.util.Locale; + +public class bug4419914 { + private static final String INSTRUCTIONS = """ + 1. You will see a frame with five buttons. + 2. Confirm that each button is placed as follows: + NORTH + END CENTER START + SOUTH + 3. Press the "NORTH" button and confirm the button is focused. + 4. Press TAB repeatedly and confirm that the TAB focus moves from right to left. + (NORTH - START - CENTER - END - SOUTH - NORTH - START - CENTER - ...) + + If there's anything different from the above items, click Fail else click Pass."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Tab movement Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(48) + .testUI(bug4419914::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("bug4419914"); + frame.setFocusCycleRoot(true); + frame.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + frame.setLocale(Locale.ENGLISH); + frame.enableInputMethods(false); + + frame.getContentPane().setComponentOrientation( + ComponentOrientation.RIGHT_TO_LEFT); + frame.getContentPane().setLocale(Locale.ENGLISH); + frame.getContentPane().setLayout(new BorderLayout()); + frame.add(new JButton("SOUTH"), BorderLayout.SOUTH); + frame.add(new JButton("CENTER"), BorderLayout.CENTER); + frame.add(new JButton("END"), BorderLayout.LINE_END); + frame.add(new JButton("START"), BorderLayout.LINE_START); + frame.add(new JButton("NORTH"), BorderLayout.NORTH); + frame.setSize(300, 150); + return frame; + } +} diff --git a/test/jdk/javax/swing/JMenuBar/RightLeftOrientation.java b/test/jdk/javax/swing/JMenuBar/RightLeftOrientation.java new file mode 100644 index 00000000000..80779c9ce1d --- /dev/null +++ b/test/jdk/javax/swing/JMenuBar/RightLeftOrientation.java @@ -0,0 +1,154 @@ +/* + * 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 + * 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 4211731 4214512 + * @summary + * This test checks if menu bars lay out correctly when their + * ComponentOrientation property is set to RIGHT_TO_LEFT. This test is + * manual. The tester is asked to compare left-to-right and + * right-to-left menu bars and judge whether they are mirror images of each + * other. + * @library /test/jdk/java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RightLeftOrientation + */ + +import java.awt.ComponentOrientation; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class RightLeftOrientation { + + static JFrame ltrFrame; + static JFrame rtlFrame; + + private static final String INSTRUCTIONS = """ + This test checks menu bars for correct Right-To-Left Component Orientation. + + You should see two frames, each containing a menu bar. + + One frame will be labelled "Left To Right" and will contain + a menu bar with menus starting on its left side. + The other frame will be labelled "Right To Left" and will + contain a menu bar with menus starting on its right side. + + The test will also contain radio buttons that can be used to set + the look and feel of the menu bars. + For each look and feel, you should compare the two menu + bars and make sure they are mirror images of each other. """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("RTL test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(30) + .testUI(RightLeftOrientation::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("RightLeftOrientation"); + JPanel panel = new JPanel(); + + ButtonGroup group = new ButtonGroup(); + JRadioButton rb; + ActionListener plafChanger = new PlafChanger(); + + UIManager.LookAndFeelInfo[] lafInfos = UIManager.getInstalledLookAndFeels(); + for (int i = 0; i < lafInfos.length; i++) { + rb = new JRadioButton(lafInfos[i].getName()); + rb.setActionCommand(lafInfos[i].getClassName()); + rb.addActionListener(plafChanger); + group.add(rb); + panel.add(rb); + if (i == 0) { + rb.setSelected(true); + } + } + + frame.add(panel); + + ltrFrame = new JFrame("Left To Right"); + ltrFrame.setJMenuBar(createMenuBar(ComponentOrientation.LEFT_TO_RIGHT)); + ltrFrame.setSize(400, 100); + ltrFrame.setLocation(new Point(10, 10)); + ltrFrame.setVisible(true); + + rtlFrame = new JFrame("Right To Left"); + rtlFrame.setJMenuBar(createMenuBar(ComponentOrientation.RIGHT_TO_LEFT)); + rtlFrame.setSize(400, 100); + rtlFrame.setLocation(new Point(10, 120)); + rtlFrame.setVisible(true); + frame.pack(); + return frame; + } + + static class PlafChanger implements ActionListener { + public void actionPerformed(ActionEvent e) { + String lnfName = e.getActionCommand(); + + try { + UIManager.setLookAndFeel(lnfName); + SwingUtilities.updateComponentTreeUI(ltrFrame); + SwingUtilities.updateComponentTreeUI(rtlFrame); + } + catch (Exception exc) { + System.err.println("Could not load LookAndFeel: " + lnfName); + } + + } + } + + + static JMenuBar createMenuBar(ComponentOrientation o) { + JMenuBar menuBar = new JMenuBar(); + menuBar.setComponentOrientation(o); + + JMenu menu = new JMenu("One"); + menu.setComponentOrientation(o); + menuBar.add(menu); + + menu = new JMenu("Two"); + menu.setComponentOrientation(o); + menuBar.add(menu); + + menu = new JMenu("Three"); + menu.setComponentOrientation(o); + menuBar.add(menu); + + return menuBar; + } + +} diff --git a/test/jdk/javax/swing/JPopupMenu/6580930/bug6580930.java b/test/jdk/javax/swing/JPopupMenu/6580930/bug6580930.java index a3537af6b65..912e70195fe 100644 --- a/test/jdk/javax/swing/JPopupMenu/6580930/bug6580930.java +++ b/test/jdk/javax/swing/JPopupMenu/6580930/bug6580930.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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,17 +24,26 @@ * @test * @key headful * @bug 6580930 7184956 + * @requires (os.family != "mac") * @summary Swing Popups should overlap taskbar - * @author Alexander Potochkin * @library /lib/client * @build ExtendedRobot * @run main bug6580930 */ -import javax.swing.*; -import java.awt.*; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Insets; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.Window; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; public class bug6580930 { private static ExtendedRobot robot; diff --git a/test/jdk/javax/swing/JPopupMenu/FocusablePopupDismissTest.java b/test/jdk/javax/swing/JPopupMenu/FocusablePopupDismissTest.java new file mode 100644 index 00000000000..2704c9789e3 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/FocusablePopupDismissTest.java @@ -0,0 +1,94 @@ +/* + * 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 + * @key headful + * @bug 8319103 + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests if the focusable popup can be dismissed when the parent + * window or the popup itself loses focus in Wayland. + * @run main/manual FocusablePopupDismissTest + */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPopupMenu; +import javax.swing.JTextField; +import java.awt.Window; +import java.util.List; + +public class FocusablePopupDismissTest { + private static final String INSTRUCTIONS = """ + A frame with a "Click me" button should appear next to the window + with this instruction. + + Click on the "Click me" button. + + If the JTextField popup with "Some text" is not showing on the screen, + click Fail. + + The following steps require some focusable system window to be displayed + on the screen. This could be a system settings window, file manager, etc. + + Click on the "Click me" button if the popup is not displayed + on the screen. + + While the popup is displayed, click on some other window on the desktop. + If the popup has disappeared, click Pass, otherwise click Fail. + """; + + public static void main(String[] args) throws Exception { + if (System.getenv("WAYLAND_DISPLAY") == null) { + //test is valid only when running on Wayland. + return; + } + + PassFailJFrame.builder() + .title("FocusablePopupDismissTest") + .instructions(INSTRUCTIONS) + .rows(20) + .columns(45) + .testUI(FocusablePopupDismissTest::createTestUI) + .build() + .awaitAndCheck(); + } + + static List createTestUI() { + JFrame frame = new JFrame("FocusablePopupDismissTest"); + JButton button = new JButton("Click me"); + frame.add(button); + + button.addActionListener(e -> { + JPopupMenu popupMenu = new JPopupMenu(); + JTextField textField = new JTextField("Some text", 10); + popupMenu.add(textField); + popupMenu.show(button, 0, button.getHeight()); + }); + frame.pack(); + + return List.of(frame); + } +} diff --git a/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java b/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java index 3168fe5ca06..06622f71819 100644 --- a/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java +++ b/test/jdk/javax/swing/JRadioButton/8033699/bug8033699.java @@ -297,8 +297,8 @@ private static void hitKey(Robot robot, int keycode) { private static void hitKey(Robot robot, int mode, int keycode) { robot.keyPress(mode); robot.keyPress(keycode); - robot.keyRelease(mode); robot.keyRelease(keycode); + robot.keyRelease(mode); robot.waitForIdle(); } } diff --git a/test/jdk/javax/swing/JRadioButton/bug4823809.java b/test/jdk/javax/swing/JRadioButton/bug4823809.java new file mode 100644 index 00000000000..810f4f42939 --- /dev/null +++ b/test/jdk/javax/swing/JRadioButton/bug4823809.java @@ -0,0 +1,93 @@ +/* + * 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 + * 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 javax.swing.JFrame; +import javax.swing.JRadioButton; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ButtonUI; +import javax.swing.plaf.metal.MetalRadioButtonUI; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Robot; + +/* + * @test + * @bug 4823809 + * @summary No Mnemonic or Focus Indicator when using HTML for a Component Text + * @key headful + * @run main bug4823809 + */ + +public class bug4823809 { + private static ButtonUI testUI; + private static volatile boolean passed = false; + private static JFrame frame; + private static Robot robot; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("RadioButton Test"); + testUI = new TestRadioButtonUI(); + JRadioButton radio = new TestRadioButton("This is a radiobutton test!"); + + frame.getContentPane().add(radio); + frame.pack(); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + + if (!passed) { + throw new Error("Focus isn't painted for JRadioButton with HTML text."); + } + System.out.println("Test Passed!"); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class TestRadioButton extends JRadioButton { + public TestRadioButton(String s) { + super(s); + } + + public void setUI(ButtonUI ui) { + super.setUI(testUI); + } + } + + static class TestRadioButtonUI extends MetalRadioButtonUI { + protected void paintFocus(Graphics g, Rectangle t, Dimension d) { + super.paintFocus(g, t, d); + passed = true; + } + } + +} diff --git a/test/jdk/javax/swing/JRootPane/DefaultButtonTest.java b/test/jdk/javax/swing/JRootPane/DefaultButtonTest.java index 560c5d82331..784cd00c244 100644 --- a/test/jdk/javax/swing/JRootPane/DefaultButtonTest.java +++ b/test/jdk/javax/swing/JRootPane/DefaultButtonTest.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 @@ -83,6 +83,7 @@ private void createUI() { public void runTest() throws Exception { Robot robot = new Robot(); + robot.setAutoWaitForIdle(true); robot.setAutoDelay(100); for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) { try { @@ -100,6 +101,8 @@ public void runTest() throws Exception { createUI(); }); robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_ENTER); robot.keyRelease(KeyEvent.VK_ENTER); robot.waitForIdle(); diff --git a/test/jdk/javax/swing/JSpinner/8008657/bug8008657.java b/test/jdk/javax/swing/JSpinner/8008657/bug8008657.java index cbeb0c185bb..26ed0e0082c 100644 --- a/test/jdk/javax/swing/JSpinner/8008657/bug8008657.java +++ b/test/jdk/javax/swing/JSpinner/8008657/bug8008657.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -39,7 +39,6 @@ * @test * @key headful * @bug 8008657 - * @author Alexander Scherbatiy * @summary JSpinner setComponentOrientation doesn't affect on text orientation * @run main bug8008657 */ @@ -137,6 +136,7 @@ static void createDateSpinner() { calendar.add(Calendar.YEAR, -1); Date earliestDate = calendar.getTime(); calendar.add(Calendar.YEAR, 1); + calendar.add(Calendar.DAY_OF_MONTH, 1); Date latestDate = calendar.getTime(); SpinnerModel dateModel = new SpinnerDateModel(initDate, earliestDate, diff --git a/test/jdk/javax/swing/JSplitPane/bug4147653.java b/test/jdk/javax/swing/JSplitPane/bug4147653.java new file mode 100644 index 00000000000..ed84776d46d --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/bug4147653.java @@ -0,0 +1,58 @@ +/* + * 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 + * 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 javax.swing.JSplitPane; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/* + * @test + * @bug 4147653 + * @summary JSplitPane.DIVIDER_LOCATION_PROPERTY is a property, + * you can use that to know when the position changes. + * @run main bug4147653 + */ + +public class bug4147653 { + private static volatile boolean flag = false; + + static class DevMoved implements PropertyChangeListener { + public void propertyChange(PropertyChangeEvent evt) { + flag = true; + } + } + + public static void main(String[] args) throws Exception { + JSplitPane sp = new JSplitPane(); + + DevMoved pl = new DevMoved(); + sp.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, pl); + sp.setDividerLocation(sp.getDividerLocation() + 10); + Thread.sleep(1000); + + if (!flag) { + throw new RuntimeException("Divider property was not changed..."); + } + System.out.println("Test Passed!"); + } +} diff --git a/test/jdk/javax/swing/JSplitPane/bug4870674.java b/test/jdk/javax/swing/JSplitPane/bug4870674.java new file mode 100644 index 00000000000..1984d747f76 --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/bug4870674.java @@ -0,0 +1,181 @@ +/* + * 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 + * 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 javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JSplitPane; +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; +import java.awt.GridLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; + +/* + * @test + * @bug 4870674 + * @summary JSplitPane's one-touch buttons should deal with resized split panes better + * @key headful + * @run main bug4870674 + */ + +public class bug4870674 { + private static JSplitPane jsp0, jsp1; + private static JButton[] leftOneTouchButton = new JButton[2]; + private static JButton[] rightOneTouchButton = new JButton[2]; + private static JFrame frame; + private static Robot robot; + private static volatile boolean passed = true; + private static volatile Point rightBtnPos0; + private static volatile Point leftBtnPos0; + private static volatile Point rightBtnPos1; + private static volatile Point leftBtnPos1; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Test"); + frame.getContentPane().setLayout(new GridLayout(2, 1)); + + jsp0 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + new JButton("Left"), + new JButton("Right")); + frame.getContentPane().add(jsp0); + + jsp0.setUI(new TestSplitPaneUI(0)); + jsp0.setOneTouchExpandable(true); + + jsp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + new JButton("Left"), + new JButton("Right")); + frame.getContentPane().add(jsp1); + + jsp1.setUI(new TestSplitPaneUI(1)); + jsp1.setOneTouchExpandable(true); + + frame.setSize(300, 100); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + rightBtnPos0 = rightOneTouchButton[0].getLocationOnScreen(); + rightBtnPos0.x += rightOneTouchButton[0].getWidth() / 2; + rightBtnPos0.y += rightOneTouchButton[0].getHeight() / 2; + + leftBtnPos1 = leftOneTouchButton[1].getLocationOnScreen(); + leftBtnPos1.x += leftOneTouchButton[0].getWidth() / 2; + leftBtnPos1.y += leftOneTouchButton[0].getHeight() / 2; + + leftBtnPos0 = leftOneTouchButton[0].getLocationOnScreen(); + leftBtnPos0.x += leftOneTouchButton[0].getWidth() / 2; + leftBtnPos0.y += leftOneTouchButton[0].getHeight() / 2; + + rightBtnPos1 = rightOneTouchButton[1].getLocationOnScreen(); + rightBtnPos1.x += rightOneTouchButton[0].getWidth() / 2; + rightBtnPos1.y += rightOneTouchButton[0].getHeight() / 2; + + jsp0.setDividerLocation(250); + }); + robot.mouseMove(rightBtnPos0.x, rightBtnPos0.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + SwingUtilities.invokeAndWait(() -> { + jsp1.setDividerLocation(250); + }); + robot.waitForIdle(); + robot.delay(100); + robot.mouseMove(leftBtnPos1.x, leftBtnPos1.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + SwingUtilities.invokeAndWait(() -> { + frame.setSize(200, 100); + }); + robot.waitForIdle(); + robot.delay(100); + robot.mouseMove(leftBtnPos0.x, leftBtnPos0.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(100); + robot.mouseMove(rightBtnPos1.x, rightBtnPos1.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(100); + + SwingUtilities.invokeAndWait(() -> { + if (jsp0.getDividerLocation() > jsp0.getMaximumDividerLocation() || + jsp1.getDividerLocation() > jsp1.getMaximumDividerLocation()) { + passed = false; + } + }); + + if (!passed) { + throw new RuntimeException("The divider location couldn't " + + "be greater then its maximum location"); + } + System.out.println("Test Passed!"); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class TestSplitPaneUI extends BasicSplitPaneUI { + int i; + + public TestSplitPaneUI(int i) { + super(); + this.i = i; + } + + public BasicSplitPaneDivider createDefaultDivider() { + return new TestSplitPaneDivider(this, i); + } + } + + static class TestSplitPaneDivider extends BasicSplitPaneDivider { + int i = 0; + + public TestSplitPaneDivider(BasicSplitPaneUI ui, int i) { + super(ui); + this.i = i; + } + + protected JButton createLeftOneTouchButton() { + leftOneTouchButton[i] = super.createLeftOneTouchButton(); + return leftOneTouchButton[i]; + } + + protected JButton createRightOneTouchButton() { + rightOneTouchButton[i] = super.createRightOneTouchButton(); + return rightOneTouchButton[i]; + } + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/GetComponentAtTest.java b/test/jdk/javax/swing/JTabbedPane/GetComponentAtTest.java new file mode 100644 index 00000000000..ddefd541f40 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/GetComponentAtTest.java @@ -0,0 +1,96 @@ +/* + * 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 + * 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 4122109 + * @summary Ensure SwingUtilities.getDeepestComponentAt() works correctly + * (in this particular case, with JTabbedPane) + * @key headful + * @run main GetComponentAtTest + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Robot; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +public class GetComponentAtTest { + static JFrame f; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("GetComponentAtTest"); + JTabbedPane tabbedpane = new JTabbedPane(); + f.getContentPane().add(tabbedpane, BorderLayout.CENTER); + + JPanel panel1 = new JPanel(); + panel1.setName("Panel 1"); + panel1.setLayout(new BorderLayout()); + tabbedpane.add(panel1); + JPanel subPanel = new JPanel(); + subPanel.setName("Sub-Panel"); + subPanel.setBackground(Color.green); + panel1.add(subPanel); // add sub panel to 1st tab + + JPanel panel2 = new JPanel(); + panel2.setName("Panel 2"); + tabbedpane.add(panel2); + + f.setSize(150, 150); + f.setVisible(true); + + robot.delay(1000); + + tabbedpane.setSelectedIndex(1); // display 2nd tab + tabbedpane.invalidate(); + tabbedpane.validate(); + if (SwingUtilities.getDeepestComponentAt(tabbedpane, 50, 50) != panel2) { + throw new RuntimeException("SwingUtilities.getDeepestComponentAt() " + + "returned incorrect component! (1)"); + } + + tabbedpane.setSelectedIndex(0); // display 1st tab + tabbedpane.invalidate(); + tabbedpane.validate(); + if (SwingUtilities.getDeepestComponentAt(tabbedpane, 50, 50) != subPanel) { + throw new RuntimeException("SwingUtilities.getDeepestComponentAt() " + + "returned incorrect component! (2)"); + } + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/ReplaceCompTab.java b/test/jdk/javax/swing/JTabbedPane/ReplaceCompTab.java new file mode 100644 index 00000000000..92e9ffeff13 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/ReplaceCompTab.java @@ -0,0 +1,97 @@ +/* + * 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 + * 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 4213896 4228439 + * @summary Ensure that inserting a new tab with a component + * where that component already exists as another tab is handled + * properly. The old tab should be removed and the new tab added. + * @key headful + * @run main ReplaceCompTab + */ + +import java.awt.BorderLayout; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +public class ReplaceCompTab { + static JFrame f; + + public static void main(String[] args) throws Exception { + try { + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("ReplaceCompTab"); + JTabbedPane tabbedpane = new JTabbedPane(); + f.getContentPane().add(tabbedpane, BorderLayout.CENTER); + + JPanel comp = new JPanel(); + + // Add first tab + tabbedpane.addTab("First(temp)", comp); + + // Add second tab with same component (should just replace first one) + tabbedpane.insertTab("First", null, comp, "component added next", 0); + + // Check to ensure only a single tab exists + if (tabbedpane.getTabCount() > 1) { + throw new RuntimeException("Only one tab should exist"); + } + // Check to make sure second tab correctly replaced the first + if (!(tabbedpane.getTitleAt(0).equals("First"))) { + throw new RuntimeException("Tab not replaced correctly"); + } + // Check to make sure adding null continues to work + try { + tabbedpane.addTab("Second", null); + } catch (Exception e) { + throw new RuntimeException("Adding first null " + + "component failed:", e); + } + try { + tabbedpane.addTab("Third", null); + } catch (Exception e) { + throw new RuntimeException("Adding subsequent null " + + "component failed: ", e); + } + try { + tabbedpane.setComponentAt(1, new JLabel("Second Component")); + tabbedpane.setComponentAt(2, new JLabel("Third Component")); + } catch (Exception e) { + throw new RuntimeException("Setting null component " + + "to non-null failed: ", e); + } + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/bug4703690.java b/test/jdk/javax/swing/JTabbedPane/bug4703690.java new file mode 100644 index 00000000000..08be30def82 --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/bug4703690.java @@ -0,0 +1,148 @@ +/* + * 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 + * 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 4703690 + * @summary JTabbedPane should focus proper component at the tab container + * @key headful + * @run main bug4703690 + */ + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; + +public class bug4703690 { + static JFrame frame; + static JTabbedPane tabbedPane; + static JButton one, two; + + static final CountDownLatch focusButtonTwo = new CountDownLatch(1); + static final CountDownLatch switchToTabTwo = new CountDownLatch(1); + static final CountDownLatch focusButtonOne = new CountDownLatch(1); + static Robot robot; + + static Point p; + static Rectangle rect; + + public static void main(String[] args) throws Exception { + bug4703690 test = new bug4703690(); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("bug4703690"); + + JPanel panel = new JPanel(); + one = new JButton("Button 1"); + panel.add(one); + two = new JButton("Button 2"); + panel.add(two); + + tabbedPane = new JTabbedPane(); + frame.getContentPane().add(tabbedPane); + tabbedPane.addTab("Tab one", panel); + tabbedPane.addTab("Tab two", new JPanel()); + + two.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + focusButtonTwo.countDown(); + } + }); + + tabbedPane.addChangeListener(e -> { + if (tabbedPane.getSelectedIndex() == 1) { + switchToTabTwo.countDown(); + } + }); + + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + test.execute(); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + public void execute() throws Exception { + robot = new Robot(); + robot.setAutoDelay(50); + + SwingUtilities.invokeAndWait(two::requestFocus); + + if (!focusButtonTwo.await(1, TimeUnit.SECONDS)) { + throw new RuntimeException("Button two didn't receive focus"); + } + + one.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + focusButtonOne.countDown(); + } + }); + + // Switch to tab two + SwingUtilities.invokeAndWait(() -> { + p = tabbedPane.getLocationOnScreen(); + rect = tabbedPane.getBoundsAt(1); + }); + robot.mouseMove(p.x + rect.x + rect.width / 2, + p.y + rect.y + rect.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (!switchToTabTwo.await(1, TimeUnit.SECONDS)) { + throw new RuntimeException("Switching to tab two failed"); + } + + // Switch to tab one + SwingUtilities.invokeAndWait(() -> { + p = tabbedPane.getLocationOnScreen(); + rect = tabbedPane.getBoundsAt(0); + }); + robot.mouseMove(p.x + rect.x + rect.width / 2, + p.y + rect.y + rect.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (!focusButtonOne.await(1, TimeUnit.SECONDS)) { + throw new RuntimeException("The 'Button 1' button doesn't have focus"); + } + } +} diff --git a/test/jdk/javax/swing/JTable/4275046/bug4275046.java b/test/jdk/javax/swing/JTable/4275046/bug4275046.java index e6c647ae778..78c80cc3557 100644 --- a/test/jdk/javax/swing/JTable/4275046/bug4275046.java +++ b/test/jdk/javax/swing/JTable/4275046/bug4275046.java @@ -87,6 +87,7 @@ private void createGUI() { table.getColumnModel().getColumn(1).setCellEditor(comboEditor); frame.add(table); + frame.setLocationRelativeTo(null); frame.pack(); frame.setSize(550, 400); frame.setVisible(true); @@ -117,6 +118,7 @@ public void run() { private void runTest() throws Exception { robot.waitForIdle(); + robot.delay(1000); // Click the first cell in the "color" column SwingUtilities.invokeAndWait(new Runnable() { @@ -175,6 +177,7 @@ private void checkResult() throws Exception { public void run() { // Read the edited value of from the cell editedValue = table.getModel().getValueAt(0, 1); + editedValue = ((String)editedValue).toLowerCase(); System.out.println("The edited value is = " + editedValue); testResult = editedValue.equals(EXPECTED_VALUE); if (testResult) { diff --git a/test/jdk/javax/swing/JTextArea/bug4849868.java b/test/jdk/javax/swing/JTextArea/bug4849868.java new file mode 100644 index 00000000000..efd94b46336 --- /dev/null +++ b/test/jdk/javax/swing/JTextArea/bug4849868.java @@ -0,0 +1,103 @@ +/* + * 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 + * 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 4849868 + * @summary Tests if JTextArea.getSelectionEnd works correctly + * @key headful + * @run main bug4849868 + */ + +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; + +public class bug4849868 { + + private static volatile boolean passed = false; + + private static JTextArea textArea; + private static JFrame f; + private static Point p; + + private static int end; + private static int len; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(100); + robot.setAutoWaitForIdle(true); + + SwingUtilities.invokeAndWait(() -> { + f = new JFrame("bug4849868"); + textArea = new JTextArea("1234"); + textArea.setLineWrap(true); + JScrollPane pane = new JScrollPane(textArea, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + f.getContentPane().add(pane); + f.setSize(300, 300); + f.setLocationRelativeTo(null); + f.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> + p = textArea.getLocationOnScreen()); + + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseMove(p.x + 350, p.y); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + end = textArea.getSelectionEnd(); + len = textArea.getDocument().getLength(); + }); + passed = (end <= len); + + System.out.println("end: " + end); + System.out.println("len: " + len); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + + if (!passed) { + throw new RuntimeException("Test failed."); + } + } +} diff --git a/src/hotspot/share/metaprogramming/isPointer.hpp b/test/jdk/javax/swing/JTextField/bug4244613.java similarity index 54% rename from src/hotspot/share/metaprogramming/isPointer.hpp rename to test/jdk/javax/swing/JTextField/bug4244613.java index aa1bdefa16b..6a1a8bb9d23 100644 --- a/src/hotspot/share/metaprogramming/isPointer.hpp +++ b/test/jdk/javax/swing/JTextField/bug4244613.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 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 @@ -19,22 +19,34 @@ * 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" +/* + * @test + * @bug 4244613 + * @summary Tests that JTextField has setAction(Action) constructor + * @run main bug4244613 + */ -// This metafunction returns true iff the type T is (irrespective of CV qualifiers) -// a pointer type. +import java.awt.event.ActionEvent; -template class IsPointer: public FalseType {}; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JTextField; -template class IsPointer: public TrueType {}; -template class IsPointer: public TrueType {}; -template class IsPointer: public TrueType {}; -template class IsPointer: public TrueType {}; +public class bug4244613 { + /** Auxilliary class implementing Action + */ + static class NullAction extends AbstractAction { + @Override + public void actionPerformed(ActionEvent e) {} + public Object getValue(String key) { return null; } + public boolean isEnabled() { return false; } + } -#endif // SHARE_METAPROGRAMMING_ISPOINTER_HPP + public static void main(String[] args) { + JTextField tf = new JTextField("bug4244613"); + Action action = new NullAction(); + tf.setAction(action); + } +} diff --git a/test/jdk/javax/swing/JToolBar/RightLeftOrientation.java b/test/jdk/javax/swing/JToolBar/RightLeftOrientation.java new file mode 100644 index 00000000000..8822a86f79a --- /dev/null +++ b/test/jdk/javax/swing/JToolBar/RightLeftOrientation.java @@ -0,0 +1,172 @@ +/* + * 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 + * 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 4214514 + * @summary + * This test checks if tool bars lay out correctly when their + * ComponentOrientation property is set to RIGHT_TO_LEFT. This test is + * manual. The tester is asked to compare left-to-right and + * right-to-left tool bars and judge whether they are mirror images of each + * other. + * @library /test/jdk/java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RightLeftOrientation + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +public class RightLeftOrientation { + + static JFrame ltrFrame; + static JFrame rtlFrame; + + private static final String INSTRUCTIONS = """ + This test checks tool bars for correct Right-To-Left Component Orientation. + + You should see two frames, each containing a tool bar. + + One frame will be labelled "Left To Right" and will contain + a tool bar with buttons starting on its left side. + The other frame will be labelled "Right To Left" and will + contain a tool bar with buttons starting on its right side. + + The test will also contain radio buttons that can be used to set + the look and feel of the tool bars. + For each look and feel, you should compare the two tool bars and + make sure they are mirror images of each other. + You should also drag the tool bars to each corner of the frame + to make sure the docking behavior is consistent between the two frames."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("RTL test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(RightLeftOrientation::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("RightLeftOrientation"); + JPanel panel = new JPanel(); + + ButtonGroup group = new ButtonGroup(); + JRadioButton rb; + ActionListener plafChanger = new PlafChanger(); + + UIManager.LookAndFeelInfo[] lafInfos = UIManager.getInstalledLookAndFeels(); + for (int i = 0; i < lafInfos.length; i++) { + rb = new JRadioButton(lafInfos[i].getName()); + rb.setActionCommand(lafInfos[i].getClassName()); + rb.addActionListener(plafChanger); + group.add(rb); + panel.add(rb); + if (i == 0) { + rb.setSelected(true); + } + } + + frame.add(panel); + + ltrFrame = new JFrame("Left To Right"); + Container contentPane = ltrFrame.getContentPane(); + contentPane.setLayout(new BorderLayout()); + panel = new JPanel(); + panel.setBackground(Color.white); + contentPane.add("Center",panel); + contentPane.add("North", + createToolBar(ComponentOrientation.LEFT_TO_RIGHT)); + ltrFrame.setSize(400, 140); + ltrFrame.setLocation(new Point(10, 10)); + ltrFrame.setVisible(true); + + rtlFrame = new JFrame("Right To Left"); + contentPane = rtlFrame.getContentPane(); + contentPane.setLayout(new BorderLayout()); + panel = new JPanel(); + panel.setBackground(Color.white); + contentPane.add("Center",panel); + contentPane.add("North", + createToolBar(ComponentOrientation.RIGHT_TO_LEFT)); + rtlFrame.setSize(400, 140); + rtlFrame.setLocation(new Point(420, 10)); + rtlFrame.setVisible(true); + + frame.pack(); + return frame; + } + + static class PlafChanger implements ActionListener { + public void actionPerformed(ActionEvent e) { + String lnfName = e.getActionCommand(); + + try { + UIManager.setLookAndFeel(lnfName); + SwingUtilities.updateComponentTreeUI(ltrFrame); + SwingUtilities.updateComponentTreeUI(rtlFrame); + } + catch (Exception exc) { + System.err.println("Could not load LookAndFeel: " + lnfName); + } + + } + } + + + static JToolBar createToolBar(ComponentOrientation o) { + JToolBar toolBar = new JToolBar(); + toolBar.setComponentOrientation(o); + + JButton button = new JButton("One"); + button.setComponentOrientation(o); + toolBar.add(button); + + button = new JButton("Two"); + button.setComponentOrientation(o); + toolBar.add(button); + + button = new JButton("Three"); + button.setComponentOrientation(o); + toolBar.add(button); + + return toolBar; + } + +} diff --git a/test/jdk/javax/swing/JToolBar/bug4203039.java b/test/jdk/javax/swing/JToolBar/bug4203039.java new file mode 100644 index 00000000000..af8b1f122c0 --- /dev/null +++ b/test/jdk/javax/swing/JToolBar/bug4203039.java @@ -0,0 +1,82 @@ +/* + * 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 + * 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.awt.BorderLayout; + +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4203039 + * @summary JToolBar needs a way to limit docking to a particular orientation + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4203039 + */ + +public class bug4203039 { + private static final String instructionsText = """ + This test is used to verify that application-installed + components prevent the toolbar from docking in + those locations. + + This test has installed components on the SOUTH + and EAST, so verify the toolbar cannot dock in those + locations but can dock on the NORTH and WEST"""; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = new PassFailJFrame.Builder() + .title("bug4203039 Instructions") + .instructions(instructionsText) + .testTimeOut(5) + .rows(10) + .columns(35) + .build(); + + SwingUtilities.invokeAndWait(() -> { + JFrame frame = new JFrame("bug4203039"); + frame.setSize(300, 200); + + JToolBar toolbar = new JToolBar(); + JLabel label = new JLabel("This is the toolbar"); + toolbar.add(label); + + frame.add(toolbar, BorderLayout.NORTH); + + frame.add(new JComponent(){}, BorderLayout.SOUTH); + frame.add(new JComponent(){}, BorderLayout.EAST); + + PassFailJFrame.addTestWindow(frame); + PassFailJFrame.positionTestWindow(frame, + PassFailJFrame.Position.HORIZONTAL); + + frame.setVisible(true); + }); + + passFailJFrame.awaitAndCheck(); + } +} diff --git a/test/jdk/javax/swing/JTree/8003400/Test8003400.java b/test/jdk/javax/swing/JTree/8003400/Test8003400.java index 95f5e826aa9..dcc9b0fce6e 100644 --- a/test/jdk/javax/swing/JTree/8003400/Test8003400.java +++ b/test/jdk/javax/swing/JTree/8003400/Test8003400.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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 @@ -109,6 +109,10 @@ public void run() { Robot robot = new Robot(); robot.setAutoDelay(100); + robot.setAutoWaitForIdle(true); + robot.waitForIdle(); + robot.delay(500); + SwingUtilities.invokeAndWait(() -> { point = tree.getLocationOnScreen(); rect = tree.getBounds(); diff --git a/test/jdk/javax/swing/border/Test4129681.html b/test/jdk/javax/swing/border/Test4129681.html deleted file mode 100644 index c80b417075a..00000000000 --- a/test/jdk/javax/swing/border/Test4129681.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - -When applet starts, you'll see a checkbox and a label with a titled border. -Turn on the checkbox to disable the label. -The test passes if the title of the border is disabled as well as the label. - - - - - diff --git a/test/jdk/javax/swing/border/Test4129681.java b/test/jdk/javax/swing/border/Test4129681.java index 330126d3f3f..993dc2d139c 100644 --- a/test/jdk/javax/swing/border/Test4129681.java +++ b/test/jdk/javax/swing/border/Test4129681.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -21,39 +21,62 @@ * questions. */ +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Point; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.UIManager; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; + /* * @test * @bug 4129681 - * @summary Tests enabling/disabling of titled border's caption - * @author Sergey Malenkov - * @run applet/manual=yesno Test4129681.html + * @summary Tests disabling of titled border's caption + * @run main/othervm -Dsun.java2d.uiScale=1 Test4129681 */ -import java.awt.BorderLayout; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JCheckBox; -import javax.swing.JLabel; +public class Test4129681 { + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + int correctColoredPixels = 0; + int totalPixels = 0; + int tolerance = 20; + JLabel label; + Color labelDisableColor = Color.RED; + Dimension SIZE = new Dimension(100, 40); + Point startPoint = new Point(8, 4); + Point endPoint = new Point(18, 14); -public class Test4129681 extends JApplet implements ItemListener { - private JLabel label; + label = new JLabel("Label"); + label.setBorder(BorderFactory.createTitledBorder("\u2588".repeat(5))); + UIManager.getDefaults().put("Label.disabledForeground", labelDisableColor); + label.setSize(SIZE); + label.setEnabled(false); + BufferedImage image = new BufferedImage(label.getWidth(), label.getHeight(), + BufferedImage.TYPE_INT_ARGB); - @Override - public void init() { - JCheckBox check = new JCheckBox("disable"); - check.addItemListener(this); + Graphics2D g2d = image.createGraphics(); + label.paint(g2d); + g2d.dispose(); - this.label = new JLabel("message"); - this.label.setBorder(BorderFactory.createTitledBorder("label")); - this.label.setEnabled(!check.isSelected()); - - add(BorderLayout.NORTH, check); - add(BorderLayout.CENTER, this.label); - } + for (int x = startPoint.x; x < endPoint.x; x++) { + for (int y = startPoint.y; y < endPoint.y; y++) { + if (image.getRGB(x, y) == labelDisableColor.getRGB()) { + correctColoredPixels++; + } + totalPixels++; + } + } - public void itemStateChanged(ItemEvent event) { - this.label.setEnabled(ItemEvent.DESELECTED == event.getStateChange()); + if (((double) correctColoredPixels / totalPixels * 100) <= tolerance) { + ImageIO.write(image, "png", new File("failureImage.png")); + throw new RuntimeException("Label with border is not disabled"); + } + System.out.println("Test Passed"); } } diff --git a/test/jdk/javax/swing/plaf/basic/BasicDirectoryModel/ConcurrentModification.java b/test/jdk/javax/swing/plaf/basic/BasicDirectoryModel/ConcurrentModification.java new file mode 100644 index 00000000000..0c23ee23b5b --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicDirectoryModel/ConcurrentModification.java @@ -0,0 +1,273 @@ +/* + * 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. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +import javax.swing.JFileChooser; + +/* + * @test + * @bug 8323670 8307091 8240690 + * @requires os.family == "mac" | os.family == "linux" + * @summary Verifies thread-safety of BasicDirectoryModel (JFileChooser) + * @run main/othervm -Djava.awt.headless=true ConcurrentModification + */ +public final class ConcurrentModification extends ThreadGroup { + /** Initial number of files. */ + private static final long NUMBER_OF_FILES = 50; + /** Maximum number of files created on a timer tick. */ + private static final long LIMIT_FILES = 10; + + /** Timer period (delay) for creating new files. */ + private static final long TIMER_PERIOD = 250; + + /** + * Number of threads running {@code fileChooser.rescanCurrentDirectory()}. + */ + private static final int NUMBER_OF_THREADS = 5; + /** Number of repeated calls to {@code rescanCurrentDirectory}. */ + private static final int NUMBER_OF_REPEATS = 2_000; + /** Maximum amount a thread waits before initiating rescan. */ + private static final long LIMIT_SLEEP = 100; + + + /** The barrier to start all the scanner threads simultaneously. */ + private static final CyclicBarrier start = new CyclicBarrier(NUMBER_OF_THREADS); + /** The barrier to wait for all the scanner threads to complete, plus main thread. */ + private static final CyclicBarrier end = new CyclicBarrier(NUMBER_OF_THREADS + 1); + + /** List of scanner threads. */ + private static final List threads = new ArrayList<>(NUMBER_OF_THREADS); + + /** + * Stores an exception caught by any of the threads. + * If more exceptions are caught, they're added as suppressed exceptions. + */ + private static final AtomicReference exception = + new AtomicReference<>(); + + /** + * Stores an {@code IOException} thrown while removing the files. + */ + private static final AtomicReference ioException = + new AtomicReference<>(); + + + public static void main(String[] args) throws Throwable { + try { + // Start the test in its own thread group to catch and handle + // all thrown exceptions, in particular in + // BasicDirectoryModel.FilesLoader which is created by Swing. + ThreadGroup threadGroup = new ConcurrentModification(); + Thread runner = new Thread(threadGroup, + ConcurrentModification::wrapper, + "Test Runner"); + runner.start(); + runner.join(); + } catch (Throwable throwable) { + handleException(throwable); + } + + if (ioException.get() != null) { + System.err.println("An error occurred while removing files:"); + ioException.get().printStackTrace(); + } + + if (exception.get() != null) { + throw exception.get(); + } + } + + private static void wrapper() { + final long timeStart = System.currentTimeMillis(); + try { + runTest(timeStart); + } catch (Throwable throwable) { + handleException(throwable); + } finally { + System.out.printf("Duration: %,d\n", + (System.currentTimeMillis() - timeStart)); + } + } + + private static void runTest(final long timeStart) throws Throwable { + final Path temp = Files.createDirectory(Paths.get("fileChooser-concurrency-" + timeStart)); + + final Timer timer = new Timer("File creator"); + + try { + createFiles(temp); + + final JFileChooser fc = new JFileChooser(temp.toFile()); + + IntStream.range(0, NUMBER_OF_THREADS) + .forEach(i -> { + Thread thread = new Thread(new Scanner(fc)); + threads.add(thread); + thread.start(); + }); + + timer.scheduleAtFixedRate(new CreateFilesTimerTask(temp), + 0, TIMER_PERIOD); + + end.await(); + } catch (Throwable e) { + threads.forEach(Thread::interrupt); + throw e; + } finally { + timer.cancel(); + + deleteFiles(temp); + deleteFile(temp); + } + } + + + private ConcurrentModification() { + super("bdmConcurrency"); + } + + @Override + public void uncaughtException(Thread t, Throwable e) { + handleException(t, e); + } + + private static void handleException(Throwable throwable) { + handleException(Thread.currentThread(), throwable); + } + + private static void handleException(final Thread thread, + final Throwable throwable) { + System.err.println("Exception in " + thread.getName() + ": " + + throwable.getClass() + + (throwable.getMessage() != null + ? ": " + throwable.getMessage() + : "")); + if (!exception.compareAndSet(null, throwable)) { + exception.get().addSuppressed(throwable); + } + threads.stream() + .filter(t -> t != thread) + .forEach(Thread::interrupt); + } + + + private record Scanner(JFileChooser fileChooser) + implements Runnable { + + @Override + public void run() { + try { + start.await(); + + int counter = 0; + try { + do { + fileChooser.rescanCurrentDirectory(); + Thread.sleep((long) (Math.random() * LIMIT_SLEEP)); + } while (++counter < NUMBER_OF_REPEATS + && !Thread.interrupted()); + } catch (InterruptedException e) { + // Just exit the loop + } + } catch (Throwable throwable) { + handleException(throwable); + } finally { + try { + end.await(); + } catch (InterruptedException | BrokenBarrierException e) { + handleException(e); + } + } + } + } + + private static void createFiles(final Path parent) { + createFiles(parent, 0, NUMBER_OF_FILES); + } + + private static void createFiles(final Path parent, + final long start, + final long end) { + LongStream.range(start, end) + .forEach(n -> createFile(parent.resolve(n + ".file"))); + } + + private static void createFile(final Path file) { + try { + Files.createFile(file); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static void deleteFiles(final Path parent) throws IOException { + try (var stream = Files.walk(parent)) { + stream.filter(p -> p != parent) + .forEach(ConcurrentModification::deleteFile); + } + } + + private static void deleteFile(final Path file) { + try { + Files.delete(file); + } catch (IOException e) { + if (!ioException.compareAndSet(null, e)) { + ioException.get().addSuppressed(e); + } + } + } + + private static final class CreateFilesTimerTask extends TimerTask { + private final Path temp; + private long no; + + public CreateFilesTimerTask(Path temp) { + this.temp = temp; + no = NUMBER_OF_FILES; + } + + @Override + public void run() { + try { + long count = (long) (Math.random() * LIMIT_FILES); + createFiles(temp, no, no + count); + no += count; + } catch (Throwable t) { + handleException(t); + } + } + } +} diff --git a/test/jdk/javax/swing/text/PaintTest.java b/test/jdk/javax/swing/text/PaintTest.java new file mode 100644 index 00000000000..9df7e54b22e --- /dev/null +++ b/test/jdk/javax/swing/text/PaintTest.java @@ -0,0 +1,108 @@ +/* + * 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 + * 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 4210250 + * @summary Tests that PlainView repaints the necessary lines of text. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PaintTest + */ + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; + +public class PaintTest { + + private static final String INSTRUCTIONS = """ + Click the paint button. + If half of the second line is erased, + that is you can only see the bottom half of the second line + with the top half painted over in white, click fail, else click pass."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("PlainView Repaint Instructions") + .instructions(INSTRUCTIONS) + .rows(7) + .columns(35) + .testUI(PaintTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("PaintTest"); + + new PaintTest().create(frame.getContentPane()); + frame.pack(); + return frame; + } + + + void create(Container parent) { + parent.setLayout(new FlowLayout()); + + final JTextArea ta = new JTextArea + ("A sample textarea\nwith a couple of lines\nof text") { + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + if (getFont() != null) { + size.height += getFontMetrics(getFont()) + .getHeight() / 2; + } + return size; + } + }; + JButton button = new JButton("paint"); + + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + Rectangle taBounds = ta.getBounds(); + int fontHeight = + ta.getFontMetrics(ta.getFont()).getHeight(); + + taBounds.height = fontHeight + fontHeight / 2; + ta.repaint(taBounds); + } + }); + } + }); + + parent.add(new JScrollPane(ta)); + parent.add(button); + } +} diff --git a/test/jdk/javax/swing/text/bug4148489.java b/test/jdk/javax/swing/text/bug4148489.java new file mode 100644 index 00000000000..12fc5849d47 --- /dev/null +++ b/test/jdk/javax/swing/text/bug4148489.java @@ -0,0 +1,109 @@ +/* + * 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 + * 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 4148489 + * @summary Text gets deleted with negative values for setFirstLineIndent. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4148489 + */ + +import java.awt.BorderLayout; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; +import javax.swing.UIManager; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.JTextComponent; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; +import javax.swing.text.Style; + +public class bug4148489 { + + static StyleContext sc; + static DefaultStyledDocument doc; + + private static final String INSTRUCTIONS = """ + Put the cursor at the beginning of the first text line and move the + cursor to the right using arrow key. + If the text is not corrupted then click Pass + If the text disappear while cursor moves click Fail."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Text traversal Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(35) + .testUI(bug4148489::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + try { + UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + } catch (Exception e) { + System.err.println("Error loading L&F: " + e); + } + JPanel testPanel = new JPanel(); + testPanel.setLayout(new BorderLayout()); + sc = new StyleContext(); + doc = new DefaultStyledDocument(sc); + + setParagraph(); + JTextComponent editor = new JTextPane(doc); + JScrollPane scroller = new JScrollPane(); + scroller.getViewport().add(editor); + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.add("Center", scroller); + testPanel.add("Center", panel); + JFrame frame = new JFrame("Styled Document"); + frame.add(testPanel); + frame.pack(); + return frame; + } + + static void setParagraph() { + Style sty = sc.addStyle("normal", sc.getStyle(StyleContext.DEFAULT_STYLE)); + //here sets the negative value for setFirstLineIndent + StyleConstants.setFirstLineIndent(sty, -50); + StyleConstants.setLeftIndent(sty, 50); + String data = "Here I wrote some text for test. You can ignore this text because of it's a senseless text."; + try { + Style s = null; + doc.insertString(doc.getLength(), data, s); + Style ls = sc.getStyle("normal"); + doc.setLogicalStyle(doc.getLength() - 1, ls); + doc.insertString(doc.getLength(), "\n", null); + } catch (BadLocationException e) { + throw new RuntimeException("BadLocationException occures while calls insertString()...", e); + } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLDocument/HTMLStrikeOnly.java b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLStrikeOnly.java new file mode 100644 index 00000000000..5ec32f74d40 --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLStrikeOnly.java @@ -0,0 +1,135 @@ +/* + * 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. + */ + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.text.View; +import javax.swing.text.html.CSS; + +/* + * @test + * @bug 8326734 + * @summary Tests different combinations of setting 'line-through' + * @run main HTMLStrikeOnly + */ +public class HTMLStrikeOnly { + private static final String HTML = """ + + + + + line-through + + + +

      line-through?

      +

      line-through?

      +

      line-through?

      +

      line-through?

      + +

      line-through?

      +

      line-through?

      +

      line-through?

      +

      line-through?

      + +

      line-through?

      +

      line-through?

      +

      line-through?

      + +

      line-through

      +

      line-through

      +

      line-through

      +

      line-through

      + + + """; + public static void main(String[] args) { + final JEditorPane html = new JEditorPane("text/html", HTML); + html.setEditable(false); + + final Dimension size = html.getPreferredSize(); + html.setSize(size); + + BufferedImage image = new BufferedImage(size.width, size.height, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.createGraphics(); + // Paint the editor pane to ensure all views are created + html.paint(g); + g.dispose(); + + int errorCount = 0; + String firstError = null; + + System.out.println("----- Views -----"); + final View bodyView = html.getUI() + .getRootView(html) + .getView(1) + .getView(1); + for (int i = 0; i < bodyView.getViewCount(); i++) { + View pView = bodyView.getView(i); + View contentView = getContentView(pView); + + String decoration = + contentView.getAttributes() + .getAttribute(CSS.Attribute.TEXT_DECORATION) + .toString(); + + System.out.println(i + ": " + decoration); + if (!decoration.contains("line-through") + || decoration.contains("underline")) { + errorCount++; + if (firstError == null) { + firstError = "Line " + i + ": " + decoration; + } + } + } + + if (errorCount > 0) { + saveImage(image); + throw new RuntimeException(errorCount + " error(s) found, " + + "the first one: " + firstError); + } + } + + private static View getContentView(View parent) { + View view = parent.getView(0); + return view.getViewCount() > 0 + ? getContentView(view) + : view; + } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", + new File("html.png")); + } catch (IOException ignored) { } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecoration.java b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecoration.java new file mode 100644 index 00000000000..a8868d05a5a --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecoration.java @@ -0,0 +1,157 @@ +/* + * 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. + */ + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.text.View; +import javax.swing.text.html.CSS; + +/* + * @test + * @bug 8323801 8326734 + * @summary Tests different combination of 'underline' and 'line-through'; + * the text should render with both 'underline' and 'line-through'. + * @run main HTMLTextDecoration + */ +public final class HTMLTextDecoration { + private static final String HTML = """ + + + + + underline + line-through text + + + +

      underline + line-through?

      +

      underline + line-through?

      +

      underline + line-through?

      + +

      underline + line-through?

      +

      underline + line-through?

      +

      underline + line-through?

      + +

      underline + line-through?

      +

      underline + line-through?

      + +

      underline + line-through?

      +

      underline + line-through?

      +

      underline + line-through?

      + +

      underline + line-through?

      +

      underline + line-through?

      + +

      underline + line-through?

      +

      underline + line-through?

      +

      underline + line-through?

      + +

      underline + line-through?

      +

      underline + line-through?

      + +
      underline + line-through?
      +
      underline + line-through?
      +
      underline + line-through?
      + +
      underline + line-through?
      +
      underline + line-through?
      + +

      underline + line-through?

      +

      underline + line-through?

      + +
      underline + line-through?
      +
      underline + line-through?
      + + + """; + + public static void main(String[] args) { + final JEditorPane html = new JEditorPane("text/html", HTML); + html.setEditable(false); + + final Dimension size = html.getPreferredSize(); + html.setSize(size); + + BufferedImage image = new BufferedImage(size.width, size.height, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.createGraphics(); + // Paint the editor pane to ensure all views are created + html.paint(g); + g.dispose(); + + int errorCount = 0; + String firstError = null; + + System.out.println("----- Views -----"); + final View bodyView = html.getUI() + .getRootView(html) + .getView(1) + .getView(1); + for (int i = 0; i < bodyView.getViewCount(); i++) { + View pView = bodyView.getView(i); + View contentView = getContentView(pView); + + String decoration = + contentView.getAttributes() + .getAttribute(CSS.Attribute.TEXT_DECORATION) + .toString(); + + System.out.println(i + ": " + decoration); + if (!decoration.contains("underline") + || !decoration.contains("line-through")) { + errorCount++; + if (firstError == null) { + firstError = "Line " + i + ": " + decoration; + } + } + } + + if (errorCount > 0) { + saveImage(image); + throw new RuntimeException(errorCount + " error(s) found, " + + "the first one: " + firstError); + } + } + + private static View getContentView(View parent) { + View view = parent.getView(0); + return view.getViewCount() > 0 + ? getContentView(view) + : view; + } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", + new File("html.png")); + } catch (IOException ignored) { } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java new file mode 100644 index 00000000000..318626e5110 --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java @@ -0,0 +1,128 @@ +/* + * 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. + */ + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.text.View; +import javax.swing.text.html.CSS; + +/* + * @test + * @bug 8335967 + * @summary Tests 'text-decoration: none' is respected + * @run main HTMLTextDecorationNone + */ +public final class HTMLTextDecorationNone { + private static final String HTML = """ + + + + + text-decoration: none (<a>) + + + +

      underlined

      +

      not underlined

      +

      not underlined

      +

      underlined?

      +

      underlined?

      + + + """; + + private static final boolean[] underlined = {true, false, false, true, true}; + + public static void main(String[] args) { + final JEditorPane html = new JEditorPane("text/html", HTML); + html.setEditable(false); + + final Dimension size = html.getPreferredSize(); + html.setSize(size); + + BufferedImage image = new BufferedImage(size.width, size.height, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.createGraphics(); + // Paint the editor pane to ensure all views are created + html.paint(g); + g.dispose(); + + int errorCount = 0; + String firstError = null; + + System.out.println("----- Views -----"); + final View bodyView = html.getUI() + .getRootView(html) + .getView(1) + .getView(1); + for (int i = 0; i < bodyView.getViewCount(); i++) { + View pView = bodyView.getView(i); + View contentView = getContentView(pView); + + Object decorationAttr = + contentView.getAttributes() + .getAttribute(CSS.Attribute.TEXT_DECORATION); + String decoration = decorationAttr == null + ? "none" : decorationAttr.toString(); + + System.out.println(i + ": " + decoration); + if (decoration.contains("underline") != underlined[i]) { + errorCount++; + if (firstError == null) { + firstError = "Line " + i + ": " + decoration + " vs " + + (underlined[i] ? "underline" : "none"); + } + } + } + + if (errorCount > 0) { + saveImage(image); + throw new RuntimeException(errorCount + " error(s) found, " + + "the first one: " + firstError); + } + } + + private static View getContentView(View parent) { + View view = parent.getView(0); + return view.getViewCount() > 0 + ? getContentView(view) + : view; + } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", + new File("html.png")); + } catch (IOException ignored) { } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLDocument/HTMLUnderlineOnly.java b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLUnderlineOnly.java new file mode 100644 index 00000000000..e142203b458 --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLUnderlineOnly.java @@ -0,0 +1,129 @@ +/* + * 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. + */ + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.text.View; +import javax.swing.text.html.CSS; + +/* + * @test + * @bug 8326734 + * @summary Tests different combinations of setting 'underline' + * @run main HTMLUnderlineOnly + */ +public class HTMLUnderlineOnly { + private static final String HTML = """ + + + + + underline + + + +

      underline?

      +

      underline?

      + +

      underline?

      +

      underline?

      + +

      underline?

      +

      underline?

      + +

      underline

      +

      underline

      +

      underline

      + + + """; + public static void main(String[] args) { + final JEditorPane html = new JEditorPane("text/html", HTML); + html.setEditable(false); + + final Dimension size = html.getPreferredSize(); + html.setSize(size); + + BufferedImage image = new BufferedImage(size.width, size.height, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.createGraphics(); + // Paint the editor pane to ensure all views are created + html.paint(g); + g.dispose(); + + int errorCount = 0; + String firstError = null; + + System.out.println("----- Views -----"); + final View bodyView = html.getUI() + .getRootView(html) + .getView(1) + .getView(1); + for (int i = 0; i < bodyView.getViewCount(); i++) { + View pView = bodyView.getView(i); + View contentView = getContentView(pView); + + String decoration = + contentView.getAttributes() + .getAttribute(CSS.Attribute.TEXT_DECORATION) + .toString(); + + System.out.println(i + ": " + decoration); + if (!decoration.contains("underline") + || decoration.contains("line-through")) { + errorCount++; + if (firstError == null) { + firstError = "Line " + i + ": " + decoration; + } + } + } + + if (errorCount > 0) { + saveImage(image); + throw new RuntimeException(errorCount + " error(s) found, " + + "the first one: " + firstError); + } + } + + private static View getContentView(View parent) { + View view = parent.getView(0); + return view.getViewCount() > 0 + ? getContentView(view) + : view; + } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", + new File("html.png")); + } catch (IOException ignored) { } + } +} diff --git a/test/jdk/javax/swing/text/html/HTMLDocument/HTMLUnderlineStrike.java b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLUnderlineStrike.java new file mode 100644 index 00000000000..79aa5c81281 --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLUnderlineStrike.java @@ -0,0 +1,89 @@ +/* + * 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. + */ + +import java.io.StringReader; + +import javax.swing.text.AttributeSet; +import javax.swing.text.Element; +import javax.swing.text.html.CSS; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; + +/* + * @test + * @bug 8323801 8326734 + * @summary Tests that '' produce underlined and struck-through text + */ +public final class HTMLUnderlineStrike { + private static final String HTML = """ + + + + + Strike-through text + + +

      struck?

      +

      struck?

      + +

      struck?

      +

      struck?

      + + + """; + + public static void main(String[] args) throws Exception { + HTMLEditorKit kit = new HTMLEditorKit(); + HTMLDocument doc = new HTMLDocument(); + + try (StringReader reader = new StringReader(HTML)) { + kit.read(reader, doc, 0); + } + + StringBuilder errors = new StringBuilder(); + + Element root = doc.getDefaultRootElement(); + Element body = root.getElement(1); + for (int i = 0; i < body.getElementCount(); i++) { + Element p = body.getElement(i); + Element content = p.getElement(0); + AttributeSet attr = content.getAttributes(); + Object decoration = attr.getAttribute(CSS.Attribute.TEXT_DECORATION); + String strDecoration = decoration.toString(); + System.out.println(i + ": " + decoration); + if (!strDecoration.contains("line-through") + || !strDecoration.contains("underline")) { + errors.append("

      [") + .append(i) + .append("], "); + } + } + + if (!errors.isEmpty()) { + errors.delete(errors.length() - 2, errors.length()); + throw new RuntimeException(errors + " must have both " + + "'line-through' and 'underline' in " + + "'text-decoration'"); + } + } +} diff --git a/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.css b/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.css new file mode 100644 index 00000000000..ab220defb71 --- /dev/null +++ b/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.css @@ -0,0 +1,14 @@ +.base { + font: 16px sans-serif; + font-size: 16; +} + +.bigger { + font-weight: bold; + font-size: 150%; +} + +.smaller { + font-style: italic; + font-size: 75%; +} diff --git a/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.html b/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.html new file mode 100644 index 00000000000..4fde9ab7bc1 --- /dev/null +++ b/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.html @@ -0,0 +1,21 @@ + + + + + JDK-8292948: JEditorPane ignores font-size styles in external linked css-file + + + + +

      + +

      Bigger text (24)

      + +

      Normal size text (16)

      + +

      Smaller text (12)

      + +
      + + + diff --git a/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.java b/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.java new file mode 100644 index 00000000000..0b9b034c5b5 --- /dev/null +++ b/test/jdk/javax/swing/text/html/StyleSheet/TestExternalCSSFontSize.java @@ -0,0 +1,171 @@ +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URL; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.SwingUtilities; +import javax.swing.text.GlyphView; +import javax.swing.text.View; + +/* + * @test + * @bug 8292948 + * @summary Tests font-size declarations from an external style sheet. + * @run main TestExternalCSSFontSize + */ +public class TestExternalCSSFontSize { + + private static final int[] expectedFontSizes = { 24, 16, 12 }; + + private volatile JEditorPane editor; + + private volatile CountDownLatch loadLatch; + + TestExternalCSSFontSize() {} + + void setUp() { + String fileName = getClass().getName().replace('.', '/') + ".html"; + URL htmlFile = getClass().getClassLoader().getResource(fileName); + if (htmlFile == null) { + throw new IllegalStateException("Resource not found: " + fileName); + } + + loadLatch = new CountDownLatch(1); + editor = new JEditorPane(); + editor.setContentType("text/html"); + editor.addPropertyChangeListener("page", evt -> { + System.out.append("Loaded: ").println(evt.getNewValue()); + loadLatch.countDown(); + }); + try { + editor.setPage(htmlFile); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + void verify() { + editor.setSize(editor.getPreferredSize()); // Do lay out text + + scanFontSizes(editor.getUI().getRootView(editor), 0); + } + + private int scanFontSizes(View view, int branchIndex) { + int currentIndex = branchIndex; + for (int i = 0; i < view.getViewCount(); i++) { + View child = view.getView(i); + if (child instanceof GlyphView) { + if (child.getElement() + .getAttributes().getAttribute("CR") == Boolean.TRUE) { + continue; + } + assertFontSize((GlyphView) child, currentIndex++); + } else { + currentIndex = scanFontSizes(child, currentIndex); + } + } + return currentIndex; + } + + private void assertFontSize(GlyphView child, int index) { + printSource(child); + if (index >= expectedFontSizes.length) { + throw new AssertionError("Unexpected text run #" + + index + " (>= " + expectedFontSizes.length + ")"); + } + + int actualFontSize = child.getFont().getSize(); + if (actualFontSize != expectedFontSizes[index]) { + throw new AssertionError("Font size expected [" + + expectedFontSizes[index] + "] but found [" + actualFontSize +"]"); + } + } + + private void printSource(View textRun) { + try { + editor.getEditorKit().write(System.out, + editor.getDocument(), textRun.getStartOffset(), + textRun.getEndOffset() - textRun.getStartOffset()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + void run() throws Throwable { + SwingUtilities.invokeAndWait(this::setUp); + if (loadLatch.await(5, TimeUnit.SECONDS)) { + SwingUtilities.invokeAndWait(this::verify); + } else { + throw new IllegalStateException("Page loading timed out"); + } + } + + public static void main(String[] args) throws Throwable { + TestExternalCSSFontSize test = new TestExternalCSSFontSize(); + boolean success = false; + try { + test.run(); + success = true; + } finally { + if (!success || hasOpt(args, "-capture")) { + if (test.editor == null) { + System.err.println("Can't save image (test.editor is null)"); + } else { + String suffix = success ? "-success" : "-failure"; + SwingUtilities.invokeAndWait(() -> test.captureImage(suffix)); + } + } + } + } + + private static boolean hasOpt(String[] args, String opt) { + return Arrays.asList(args).contains(opt); + } + + private void captureImage(String suffix) { + try { + BufferedImage capture = + new BufferedImage(editor.getWidth(), editor.getHeight(), + BufferedImage.TYPE_INT_ARGB); + Graphics g = capture.getGraphics(); + editor.paint(g); + g.dispose(); + + ImageIO.write(capture, "png", + new File(getClass().getSimpleName() + suffix + ".png")); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/test/jdk/javax/swing/text/html/StyleSheet/bug4803145.java b/test/jdk/javax/swing/text/html/StyleSheet/bug4803145.java new file mode 100644 index 00000000000..2d701657234 --- /dev/null +++ b/test/jdk/javax/swing/text/html/StyleSheet/bug4803145.java @@ -0,0 +1,97 @@ +/* + * 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 + * 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 4803145 + * @summary Tests if bullets for HTML
        are on the correct side for Arabic and Hebrew in JEditorPane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4803145 +*/ + +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.text.html.HTMLEditorKit; + +public class bug4803145 { + + private static final String INSTRUCTIONS = """ + A JEditorPane with some html list in Hebrew appears. + The bullets should be on the left side of the list items. + Press the "switch text orientation" button. + After the text relayouts: + + - If the bullets are to the right of the list items then test PASSED. + + - If the bullets remained on the left side then test FAILED."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("JEditorPane Instructions") + .instructions(INSTRUCTIONS) + .rows(10) + .columns(30) + .testUI(bug4803145::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + + String text = + "
          " + + "
        • מבוא" + + "
        • אחסון" + + "
        • (new code) הקוד הישן (Old Code)" + + "
        "; + + JFrame f = new JFrame("bug4803145"); + JEditorPane jep = new JEditorPane(); + jep.setEditorKit(new HTMLEditorKit()); + jep.setEditable(false); + + jep.setText(text); + + f.setSize(500, 500); + f.add(jep); + + JButton switchButton = new JButton("switch text orientation"); + switchButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + boolean isLeftToRight = jep.getComponentOrientation().isLeftToRight(); + jep.setComponentOrientation(isLeftToRight ? ComponentOrientation.RIGHT_TO_LEFT : + ComponentOrientation.LEFT_TO_RIGHT); + } + }); + f.add(switchButton, BorderLayout.SOUTH); + f.pack(); + return f; + } + +} diff --git a/test/jdk/jdk/incubator/vector/Double128VectorTests.java b/test/jdk/jdk/incubator/vector/Double128VectorTests.java index b00b0cbaaab..bf9c717cc59 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorTests.java @@ -59,6 +59,8 @@ public class Double128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -986,6 +1001,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2135,7 +2158,7 @@ static void ADDReduceDouble128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll); + Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { double res = 0; @@ -2179,7 +2202,7 @@ static void ADDReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double128VectorTests::ADDReduceMasked, Double128VectorTests::ADDReduceAllMasked); + Double128VectorTests::ADDReduceMasked, Double128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { double res = 1; @@ -2220,7 +2243,7 @@ static void MULReduceDouble128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll); + Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { double res = 1; @@ -2264,7 +2287,7 @@ static void MULReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double128VectorTests::MULReduceMasked, Double128VectorTests::MULReduceAllMasked); + Double128VectorTests::MULReduceMasked, Double128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { double res = Double.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/Double256VectorTests.java b/test/jdk/jdk/incubator/vector/Double256VectorTests.java index 008b4dcc348..bb4694966d8 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorTests.java @@ -59,6 +59,8 @@ public class Double256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -986,6 +1001,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2135,7 +2158,7 @@ static void ADDReduceDouble256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll); + Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { double res = 0; @@ -2179,7 +2202,7 @@ static void ADDReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double256VectorTests::ADDReduceMasked, Double256VectorTests::ADDReduceAllMasked); + Double256VectorTests::ADDReduceMasked, Double256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { double res = 1; @@ -2220,7 +2243,7 @@ static void MULReduceDouble256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll); + Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { double res = 1; @@ -2264,7 +2287,7 @@ static void MULReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double256VectorTests::MULReduceMasked, Double256VectorTests::MULReduceAllMasked); + Double256VectorTests::MULReduceMasked, Double256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { double res = Double.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/Double512VectorTests.java b/test/jdk/jdk/incubator/vector/Double512VectorTests.java index ce25f8ea703..72bb1a49c36 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorTests.java @@ -59,6 +59,8 @@ public class Double512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -986,6 +1001,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2135,7 +2158,7 @@ static void ADDReduceDouble512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll); + Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { double res = 0; @@ -2179,7 +2202,7 @@ static void ADDReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double512VectorTests::ADDReduceMasked, Double512VectorTests::ADDReduceAllMasked); + Double512VectorTests::ADDReduceMasked, Double512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { double res = 1; @@ -2220,7 +2243,7 @@ static void MULReduceDouble512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll); + Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { double res = 1; @@ -2264,7 +2287,7 @@ static void MULReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double512VectorTests::MULReduceMasked, Double512VectorTests::MULReduceAllMasked); + Double512VectorTests::MULReduceMasked, Double512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { double res = Double.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/Double64VectorTests.java b/test/jdk/jdk/incubator/vector/Double64VectorTests.java index c4270d0f268..b445995543f 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorTests.java @@ -59,6 +59,8 @@ public class Double64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -986,6 +1001,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2135,7 +2158,7 @@ static void ADDReduceDouble64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll); + Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { double res = 0; @@ -2179,7 +2202,7 @@ static void ADDReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double64VectorTests::ADDReduceMasked, Double64VectorTests::ADDReduceAllMasked); + Double64VectorTests::ADDReduceMasked, Double64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { double res = 1; @@ -2220,7 +2243,7 @@ static void MULReduceDouble64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll); + Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { double res = 1; @@ -2264,7 +2287,7 @@ static void MULReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } assertReductionArraysEqualsMasked(r, ra, a, mask, - Double64VectorTests::MULReduceMasked, Double64VectorTests::MULReduceAllMasked); + Double64VectorTests::MULReduceMasked, Double64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { double res = Double.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java index df037495d89..9db85a2f20e 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java @@ -65,6 +65,9 @@ static VectorShape getMaxBit() { private static final int Max = 256; // juts so we can do N/Max + // for floating point reduction ops that may introduce rounding errors + private static final double RELATIVE_ROUNDING_ERROR = (double)0.000001; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); interface FUnOp { @@ -123,15 +126,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(double[] r, double rc, double[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (double)0.0); + } + + static void assertReductionArraysEquals(double[] r, double rc, double[] a, + FReductionOp f, FReductionAllOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -145,15 +154,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (double)0.0); + } + + static void assertReductionArraysEqualsMasked(double[] r, double rc, double[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + double relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -991,6 +1007,14 @@ static long bits(double e) { return fill(s * BUFFER_REPS, i -> (((double)(i + 1) == 0) ? 1 : (double)(i + 1))); }), + withToString("double[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (double)0.01 + ((double)i / (i + 1))); + }), + withToString("double[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (double)0.01 + ((double)i / (i + 1))); + }), withToString("double[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2140,7 +2164,7 @@ static void ADDReduceDoubleMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll); + DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { double res = 0; @@ -2184,7 +2208,7 @@ static void ADDReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - DoubleMaxVectorTests::ADDReduceMasked, DoubleMaxVectorTests::ADDReduceAllMasked); + DoubleMaxVectorTests::ADDReduceMasked, DoubleMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MULReduce(double[] a, int idx) { double res = 1; @@ -2225,7 +2249,7 @@ static void MULReduceDoubleMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll); + DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static double MULReduceMasked(double[] a, int idx, boolean[] mask) { double res = 1; @@ -2269,7 +2293,7 @@ static void MULReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } assertReductionArraysEqualsMasked(r, ra, a, mask, - DoubleMaxVectorTests::MULReduceMasked, DoubleMaxVectorTests::MULReduceAllMasked); + DoubleMaxVectorTests::MULReduceMasked, DoubleMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static double MINReduce(double[] a, int idx) { double res = Double.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/Float128VectorTests.java b/test/jdk/jdk/incubator/vector/Float128VectorTests.java index ae38b606da9..c8a9aa4a80c 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorTests.java @@ -59,6 +59,8 @@ public class Float128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -996,6 +1011,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2145,7 +2168,7 @@ static void ADDReduceFloat128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll); + Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { float res = 0; @@ -2189,7 +2212,7 @@ static void ADDReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float128VectorTests::ADDReduceMasked, Float128VectorTests::ADDReduceAllMasked); + Float128VectorTests::ADDReduceMasked, Float128VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { float res = 1; @@ -2230,7 +2253,7 @@ static void MULReduceFloat128VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll); + Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { float res = 1; @@ -2274,7 +2297,7 @@ static void MULReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float128VectorTests::MULReduceMasked, Float128VectorTests::MULReduceAllMasked); + Float128VectorTests::MULReduceMasked, Float128VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { float res = Float.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/Float256VectorTests.java b/test/jdk/jdk/incubator/vector/Float256VectorTests.java index be55fa2b458..fcbf7df409b 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorTests.java @@ -59,6 +59,8 @@ public class Float256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -996,6 +1011,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2145,7 +2168,7 @@ static void ADDReduceFloat256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll); + Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { float res = 0; @@ -2189,7 +2212,7 @@ static void ADDReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float256VectorTests::ADDReduceMasked, Float256VectorTests::ADDReduceAllMasked); + Float256VectorTests::ADDReduceMasked, Float256VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { float res = 1; @@ -2230,7 +2253,7 @@ static void MULReduceFloat256VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll); + Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { float res = 1; @@ -2274,7 +2297,7 @@ static void MULReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float256VectorTests::MULReduceMasked, Float256VectorTests::MULReduceAllMasked); + Float256VectorTests::MULReduceMasked, Float256VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { float res = Float.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/Float512VectorTests.java b/test/jdk/jdk/incubator/vector/Float512VectorTests.java index f1f1b884d6b..597a590098a 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorTests.java @@ -59,6 +59,8 @@ public class Float512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -996,6 +1011,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2145,7 +2168,7 @@ static void ADDReduceFloat512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll); + Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { float res = 0; @@ -2189,7 +2212,7 @@ static void ADDReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float512VectorTests::ADDReduceMasked, Float512VectorTests::ADDReduceAllMasked); + Float512VectorTests::ADDReduceMasked, Float512VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { float res = 1; @@ -2230,7 +2253,7 @@ static void MULReduceFloat512VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll); + Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { float res = 1; @@ -2274,7 +2297,7 @@ static void MULReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float512VectorTests::MULReduceMasked, Float512VectorTests::MULReduceAllMasked); + Float512VectorTests::MULReduceMasked, Float512VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { float res = Float.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/Float64VectorTests.java b/test/jdk/jdk/incubator/vector/Float64VectorTests.java index 47f0cf3f15b..b5877b9de46 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorTests.java @@ -59,6 +59,8 @@ public class Float64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); @@ -118,15 +120,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -140,15 +148,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -996,6 +1011,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2145,7 +2168,7 @@ static void ADDReduceFloat64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll); + Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { float res = 0; @@ -2189,7 +2212,7 @@ static void ADDReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float64VectorTests::ADDReduceMasked, Float64VectorTests::ADDReduceAllMasked); + Float64VectorTests::ADDReduceMasked, Float64VectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { float res = 1; @@ -2230,7 +2253,7 @@ static void MULReduceFloat64VectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll); + Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { float res = 1; @@ -2274,7 +2297,7 @@ static void MULReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } assertReductionArraysEqualsMasked(r, ra, a, mask, - Float64VectorTests::MULReduceMasked, Float64VectorTests::MULReduceAllMasked); + Float64VectorTests::MULReduceMasked, Float64VectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { float res = Float.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java index 511afbb680e..9a40694f869 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java @@ -65,6 +65,9 @@ static VectorShape getMaxBit() { private static final int Max = 256; // juts so we can do N/Max + // for floating point reduction ops that may introduce rounding errors + private static final float RELATIVE_ROUNDING_ERROR = (float)0.000001; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); interface FUnOp { @@ -123,15 +126,21 @@ interface FReductionAllOp { static void assertReductionArraysEquals(float[] r, float rc, float[] a, FReductionOp f, FReductionAllOp fa) { + assertReductionArraysEquals(r, rc, a, f, fa, (float)0.0); + } + + static void assertReductionArraysEquals(float[] r, float rc, float[] a, + FReductionOp f, FReductionAllOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a)); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i)); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -145,15 +154,22 @@ interface FReductionAllMaskedOp { static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, (float)0.0); + } + + static void assertReductionArraysEqualsMasked(float[] r, float rc, float[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + float relativeError) { int i = 0; try { - Assert.assertEquals(rc, fa.apply(a, mask)); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); for (; i < a.length; i += SPECIES.length()) { - Assert.assertEquals(r[i], f.apply(a, i, mask)); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); } } catch (AssertionError e) { - Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); - Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); } } @@ -1001,6 +1017,14 @@ static int bits(float e) { return fill(s * BUFFER_REPS, i -> (((float)(i + 1) == 0) ? 1 : (float)(i + 1))); }), + withToString("float[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> (float)0.01 + ((float)i / (i + 1))); + }), + withToString("float[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : (float)0.01 + ((float)i / (i + 1))); + }), withToString("float[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); @@ -2150,7 +2174,7 @@ static void ADDReduceFloatMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll); + FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR); } static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { float res = 0; @@ -2194,7 +2218,7 @@ static void ADDReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - FloatMaxVectorTests::ADDReduceMasked, FloatMaxVectorTests::ADDReduceAllMasked); + FloatMaxVectorTests::ADDReduceMasked, FloatMaxVectorTests::ADDReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MULReduce(float[] a, int idx) { float res = 1; @@ -2235,7 +2259,7 @@ static void MULReduceFloatMaxVectorTests(IntFunction fa) { } assertReductionArraysEquals(r, ra, a, - FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll); + FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR); } static float MULReduceMasked(float[] a, int idx, boolean[] mask) { float res = 1; @@ -2279,7 +2303,7 @@ static void MULReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } assertReductionArraysEqualsMasked(r, ra, a, mask, - FloatMaxVectorTests::MULReduceMasked, FloatMaxVectorTests::MULReduceAllMasked); + FloatMaxVectorTests::MULReduceMasked, FloatMaxVectorTests::MULReduceAllMasked, RELATIVE_ROUNDING_ERROR); } static float MINReduce(float[] a, int idx) { float res = Float.POSITIVE_INFINITY; diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template index 70ffa79bf6f..1ded00af608 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-Masked-op.template @@ -2,5 +2,9 @@ static void [[TEST]]Reduce$vectorteststype$Masked(IntFunction<$type$[]> fa, IntFunction fm) { [[KERNEL]] assertReductionArraysEqualsMasked(r, ra, a, mask, +#if[FP] + $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked, RELATIVE_ROUNDING_ERROR); +#else[FP] $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked); +#end[FP] } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template index b86248f3f09..15a3ba8c66c 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template @@ -2,5 +2,9 @@ static void [[TEST]]Reduce$vectorteststype$(IntFunction<$type$[]> fa) { [[KERNEL]] assertReductionArraysEquals(r, ra, a, +#if[FP] + $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll, RELATIVE_ROUNDING_ERROR); +#else[FP] $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); +#end[FP] } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index 3e78d5897d5..089f9fef8d2 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -88,6 +88,10 @@ public class $vectorteststype$ extends AbstractVectorTest { private static final int Max = 256; // juts so we can do N/$bits$ #end[MaxBit] +#if[FP] + // for floating point reduction ops that may introduce rounding errors + private static final $type$ RELATIVE_ROUNDING_ERROR = ($type$)0.000001; +#end[FP] static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / $bits$); @@ -147,6 +151,9 @@ public class $vectorteststype$ extends AbstractVectorTest { static void assertReductionArraysEquals($type$[] r, $type$ rc, $type$[] a, FReductionOp f, FReductionAllOp fa) { +#if[FP] + assertReductionArraysEquals(r, rc, a, f, fa, ($type$)0.0); +#else[FP] int i = 0; try { Assert.assertEquals(rc, fa.apply(a)); @@ -157,7 +164,25 @@ public class $vectorteststype$ extends AbstractVectorTest { Assert.assertEquals(rc, fa.apply(a), "Final result is incorrect!"); Assert.assertEquals(r[i], f.apply(a, i), "at index #" + i); } +#end[FP] + } +#if[FP] + + static void assertReductionArraysEquals($type$[] r, $type$ rc, $type$[] a, + FReductionOp f, FReductionAllOp fa, + $type$ relativeError) { + int i = 0; + try { + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError)); + for (; i < a.length; i += SPECIES.length()) { + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError)); + } + } catch (AssertionError e) { + Assert.assertEquals(rc, fa.apply(a), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i), Math.abs(r[i] * relativeError), "at index #" + i); + } } +#end[FP] interface FReductionMaskedOp { $type$ apply($type$[] a, int idx, boolean[] mask); @@ -169,6 +194,9 @@ public class $vectorteststype$ extends AbstractVectorTest { static void assertReductionArraysEqualsMasked($type$[] r, $type$ rc, $type$[] a, boolean[] mask, FReductionMaskedOp f, FReductionAllMaskedOp fa) { +#if[FP] + assertReductionArraysEqualsMasked(r, rc, a, mask, f, fa, ($type$)0.0); +#else[FP] int i = 0; try { Assert.assertEquals(rc, fa.apply(a, mask)); @@ -179,7 +207,26 @@ public class $vectorteststype$ extends AbstractVectorTest { Assert.assertEquals(rc, fa.apply(a, mask), "Final result is incorrect!"); Assert.assertEquals(r[i], f.apply(a, i, mask), "at index #" + i); } +#end[FP] + } +#if[FP] + + static void assertReductionArraysEqualsMasked($type$[] r, $type$ rc, $type$[] a, boolean[] mask, + FReductionMaskedOp f, FReductionAllMaskedOp fa, + $type$ relativeError) { + int i = 0; + try { + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError)); + for (; i < a.length; i += SPECIES.length()) { + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * +relativeError)); + } + } catch (AssertionError e) { + Assert.assertEquals(rc, fa.apply(a, mask), Math.abs(rc * relativeError), "Final result is incorrect!"); + Assert.assertEquals(r[i], f.apply(a, i, mask), Math.abs(r[i] * relativeError), "at index #" + i); + } } +#end[FP] #if[!Long] interface FReductionOpLong { @@ -1051,6 +1098,16 @@ public class $vectorteststype$ extends AbstractVectorTest { return fill(s * BUFFER_REPS, i -> ((($type$)(i + 1) == 0) ? 1 : ($type$)(i + 1))); }), +#if[FP] + withToString("$type$[0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> ($type$)0.01 + (($type$)i / (i + 1))); + }), + withToString("$type$[i -> i % 17 == 0 ? cornerCaseValue(i) : 0.01 + (i / (i + 1))]", (int s) -> { + return fill(s * BUFFER_REPS, + i -> i % 17 == 0 ? cornerCaseValue(i) : ($type$)0.01 + (($type$)i / (i + 1))); + }), +#end[FP] withToString("$type$[cornerCaseValue(i)]", (int s) -> { return fill(s * BUFFER_REPS, i -> cornerCaseValue(i)); diff --git a/test/jdk/jdk/internal/loader/URLClassPath/LargeClasspathWithPkgPrefix.java b/test/jdk/jdk/internal/loader/URLClassPath/LargeClasspathWithPkgPrefix.java new file mode 100644 index 00000000000..17def65a6ba --- /dev/null +++ b/test/jdk/jdk/internal/loader/URLClassPath/LargeClasspathWithPkgPrefix.java @@ -0,0 +1,140 @@ +/* + * 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.io.File; +import java.nio.file.Files; +import java.nio.file.Path; + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.compiler.CompilerUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.JarBuilder; + +/* + * @test + * @bug 8308184 + * @summary Verify that an application can be launched when the classpath contains large number of + * jars and the java.protocol.handler.pkgs system property is set + * @library /test/lib/ + * @build jdk.test.lib.util.JarBuilder jdk.test.lib.compiler.CompilerUtils + * jdk.test.lib.process.ProcessTools + * @run driver LargeClasspathWithPkgPrefix + */ +public class LargeClasspathWithPkgPrefix { + + private static final Path CWD = Path.of("."); + + private static final String JAVA_MAIN_CONTENT = """ + public class Foo { + public static void main(String[] args) throws Exception { + if (args.length != 0) { + System.out.println("unexpected args: " + java.util.Arrays.toString(args)); + System.exit(1); + } + System.out.println("Running application on Java version: " + + System.getProperty("java.version")); + System.out.println("Application launched with java.protocol.handler.pkgs=" + + System.getProperty("java.protocol.handler.pkgs")); + System.out.println("Application launched with classpath: " + + System.getProperty("java.class.path")); + System.out.println("Hello World"); + } + } + """; + + public static void main(final String[] args) throws Exception { + // dir to which the application main's .class file will be compiled to + Path classesDir = Files.createTempDirectory(CWD, "8308184-classes").toAbsolutePath(); + // dir contains many jars + Path libDir = Files.createTempDirectory(CWD, "8308184-libs").toAbsolutePath(); + Files.createDirectories(libDir); + + // trivial jar file + Path jarPath = Path.of(libDir.toString(), "8308184-dummy.jar"); + createJar(jarPath); + + // create multiple such jar files in the lib dir + int numCopies = 750; + long start = System.currentTimeMillis(); + for (int i = 1; i <= numCopies; i++) { + Path dest = Path.of(libDir.toString(), "8308184-dummy-" + i + ".jar"); + Files.copy(jarPath, dest); + } + long end = System.currentTimeMillis(); + System.out.println("Created " + numCopies + " jars under " + libDir + + ", took " + (end - start) + " milli seconds"); + + // create the application's main java file + Path fooJavaSrcFile = Path.of(classesDir.toString(), "Foo.java"); + Files.writeString(fooJavaSrcFile, JAVA_MAIN_CONTENT); + + // compile this java file + compile(fooJavaSrcFile, classesDir); + + // Create the classpath string. It is important that the classes directory which contains + // application's main class, is at the end of the classpath (or too far into the classpath). + // The initial entries in the classpath should be jar files. + // constructed classpath is of the form -cp lib/*:classes/ + // (the * in lib/* is parsed/interpreted by the java launcher and includes all jars in that + // directory) + String classpath = File.pathSeparator + libDir.toString() + "/*" + + File.pathSeparator + classesDir.toString(); + // launch the application + launchApplication(classpath); + // test passed successfully, we don't need the lib directory which has too many jars, + // anymore. we let the dir stay only if the test fails, for debug purpose + libDir.toFile().deleteOnExit(); + } + + // creates a trivial jar file + private static void createJar(Path p) throws Exception { + JarBuilder jb = new JarBuilder(p.toString()); + jb.addEntry("foobar.txt", "foobar".getBytes()); + jb.build(); + System.out.println("Created jar at " + p); + } + + // compile to + private static void compile(Path javaFile, Path destDir) throws Exception { + boolean compiled = CompilerUtils.compile(javaFile, destDir); + if (!compiled) { + // compilation failure log/reason would already be available on System.out/err + throw new AssertionError("Compilation failed for " + javaFile); + } + } + + // java -Djava.protocol.handler.pkgs=foo.bar.some.nonexistent.pkg -cp Foo + private static void launchApplication(String classPath) throws Exception { + String java = JDKToolFinder.getJDKTool("java"); + ProcessBuilder pb = new ProcessBuilder(java, + "-Djava.protocol.handler.pkgs=foo.bar.some.nonexistent.pkg", + "-cp", classPath, + "Foo"); + pb.directory(CWD.toFile()); + System.out.println("Launching java application: " + pb.command()); + OutputAnalyzer analyzer = ProcessTools.executeProcess(pb); + analyzer.shouldHaveExitValue(0); + analyzer.shouldContain("Hello World"); + } +} diff --git a/test/jdk/jdk/jfr/api/consumer/TestRecordedClass.java b/test/jdk/jdk/jfr/api/consumer/TestRecordedClass.java new file mode 100644 index 00000000000..19c63704919 --- /dev/null +++ b/test/jdk/jdk/jfr/api/consumer/TestRecordedClass.java @@ -0,0 +1,115 @@ +/* + * 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. + */ + +package jdk.jfr.api.consumer; + +import java.lang.reflect.Modifier; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Verifies methods of RecordedClass + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm jdk.jfr.api.consumer.TestRecordedClass + */ +public class TestRecordedClass { + + static class TestEvent extends Event { + Class typeA; + Class typeB; + } + + private static class TypeA { + } + + public final static class TypeB { + } + + public static void main(String[] args) throws Exception { + try (Recording recording = new Recording()) { + recording.start(); + TestEvent event = new TestEvent(); + event.typeA = TypeA.class; + event.typeB = TypeB.class; + event.commit(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent recordedEvent : events) { + RecordedClass typeA = recordedEvent.getClass("typeA"); + RecordedClass typeB = recordedEvent.getClass("typeB"); + assertModifiers(typeA, TypeA.class); + assertModifiers(typeB, TypeB.class); + assertName(typeA, TypeA.class); + assertName(typeB, TypeB.class); + assertClassLoader(typeA, TypeA.class.getClassLoader()); + assertClassLoader(typeB, TypeB.class.getClassLoader()); + assertId(typeA); + assertId(typeB); + if (typeA.getId() == typeB.getId()) { + throw new Exception("Same ID for different classes"); + } + } + } + } + + private static void assertId(RecordedClass recordedClass) throws Exception { + long id = recordedClass.getId(); + if (id < 1 || id >= 1024 * 1024) { + throw new Exception("Expected class ID to be above 1 and below 1 M"); + } + } + + private static void assertClassLoader(RecordedClass recordedClass, ClassLoader classLoader) throws Exception { + String expected = classLoader.getClass().getName(); + String actual = recordedClass.getClassLoader().getType().getName(); + if (!expected.equals(actual)) { + throw new Exception("Expected class loader to be " + expected + ", was " + actual); + } + } + + private static void assertName(RecordedClass recordedClass, Class clazz) throws Exception { + String className = clazz.getClass().getName(); + if (className.equals(recordedClass.getName())) { + throw new Exception("Expected class to be named " + className); + } + } + + private static void assertModifiers(RecordedClass recordedClass, Class clazz) throws Exception { + int modifiers = clazz.getModifiers(); + if (modifiers != recordedClass.getModifiers()) { + String expected = Modifier.toString(modifiers); + String actual = Modifier.toString(recordedClass.getModifiers()); + throw new Exception("Expected modifier to be '" + expected + "', was '" + actual + "'"); + } + } +} diff --git a/test/jdk/jdk/jfr/api/consumer/filestream/TestOrdered.java b/test/jdk/jdk/jfr/api/consumer/filestream/TestOrdered.java index e4eea51d754..8ddc5357b37 100644 --- a/test/jdk/jdk/jfr/api/consumer/filestream/TestOrdered.java +++ b/test/jdk/jdk/jfr/api/consumer/filestream/TestOrdered.java @@ -66,6 +66,11 @@ public void run() { e.printStackTrace(); throw new Error("Unexpected exception in barrier"); } + Instant timestamp = Instant.now(); + // Wait for clock to increment + while (Instant.now().equals(timestamp)) { + ; + } OrderedEvent e2 = new OrderedEvent(); e2.commit(); } @@ -108,6 +113,7 @@ private static void testSetOrderedFalse(Path p) throws Exception { es.setOrdered(false); es.onEvent(e -> { Instant endTime = e.getEndTime(); + System.out.println("testSetOrderedFalse: endTime: " + endTime); if (endTime.isBefore(timestamp.get())) { unoreded.set(true); es.close(); diff --git a/test/jdk/jdk/jfr/event/oldobject/TestListenerLeak.java b/test/jdk/jdk/jfr/event/oldobject/TestListenerLeak.java index 67ef11b2740..92610283525 100644 --- a/test/jdk/jdk/jfr/event/oldobject/TestListenerLeak.java +++ b/test/jdk/jdk/jfr/event/oldobject/TestListenerLeak.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 @@ -71,15 +71,17 @@ public void onListen() { public static void main(String[] args) throws Exception { WhiteBox.setWriteAllObjectSamples(true); - - try (Recording r = new Recording()) { - r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); - r.start(); - listenerLeak(); - r.stop(); - List events = Events.fromRecording(r); - if (OldObjects.countMatchingEvents(events, Stuff[].class, null, null, -1, "listenerLeak") == 0) { - throw new Exception("Could not find leak with " + Stuff[].class); + while (true) { + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + listenerLeak(); + r.stop(); + List events = Events.fromRecording(r); + if (OldObjects.countMatchingEvents(events, Stuff[].class, null, null, -1, "listenerLeak") != 0) { + return; // Success + } + System.out.println("Could not find leak with " + Stuff[].class + ". Retrying."); } } } diff --git a/test/jdk/jdk/jfr/event/oldobject/TestSanityDefault.java b/test/jdk/jdk/jfr/event/oldobject/TestSanityDefault.java index d3d8377183d..7480acb1d1d 100644 --- a/test/jdk/jdk/jfr/event/oldobject/TestSanityDefault.java +++ b/test/jdk/jdk/jfr/event/oldobject/TestSanityDefault.java @@ -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 @@ -37,7 +37,7 @@ * @requires vm.hasJFR * @library /test/lib /test/jdk * @summary Purpose of this test is to run leak profiler without command line tweaks or WhiteBox hacks until we succeed - * @run main/othervm jdk.jfr.event.oldobject.TestSanityDefault + * @run main/othervm -Xmx1G jdk.jfr.event.oldobject.TestSanityDefault */ public class TestSanityDefault { diff --git a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java index 889926077a9..307ee9b212b 100644 --- a/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java +++ b/test/jdk/security/infra/java/security/cert/CertPathValidator/certification/CAInterop.java @@ -533,6 +533,28 @@ * @run main/othervm/manual -Djava.security.debug=certpath CAInterop globalsigne46 CRL */ +/* + * @test id=ssltlsrootecc2022 + * @bug 8341057 + * @summary Interoperability tests with SSL TLS 2022 root CAs + * @library /test/lib + * @build jtreg.SkippedException ValidatePathWithURL CAInterop + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop ssltlsrootecc2022 DEFAULT + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop ssltlsrootecc2022 DEFAULT + * @run main/othervm/manual -Djava.security.debug=certpath CAInterop ssltlsrootecc2022 CRL + */ + +/* + * @test id=ssltlsrootrsa2022 + * @bug 8341057 + * @summary Interoperability tests with SSL TLS 2022 root CAs + * @library /test/lib + * @build jtreg.SkippedException ValidatePathWithURL CAInterop + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop ssltlsrootrsa2022 DEFAULT + * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop ssltlsrootrsa2022 DEFAULT + * @run main/othervm/manual -Djava.security.debug=certpath CAInterop ssltlsrootrsa2022 CRL + */ + /** * Collection of certificate validation tests for interoperability with external CAs. * These tests are marked as manual as they depend on external infrastructure and may fail @@ -711,6 +733,13 @@ private CATestURLs getTestURLs(String alias) { new CATestURLs("https://valid.e46.roots.globalsign.com", "https://revoked.e46.roots.globalsign.com"); + case "ssltlsrootecc2022" -> + new CATestURLs("https://test-root-2022-ecc.ssl.com", + "https://revoked-root-2022-ecc.ssl.com"); + case "ssltlsrootrsa2022" -> + new CATestURLs("https://test-root-2022-rsa.ssl.com", + "https://revoked-root-2022-rsa.ssl.com"); + default -> throw new RuntimeException("No test setup found for: " + alias); }; } diff --git a/test/jdk/sm/ssl/TLCP/ALPNTest.java b/test/jdk/sm/ssl/TLCP/ALPNTest.java new file mode 100644 index 00000000000..24a2be568dc --- /dev/null +++ b/test/jdk/sm/ssl/TLCP/ALPNTest.java @@ -0,0 +1,700 @@ +/* + * 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 + * 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. + */ + +// +// Please run in othervm mode. SunJSSE does not support dynamic system +// properties, no way to re-use system properties in samevm/agentvm mode. +// + +/* + * @test + * @summary TLCP 1.1 with Application Layer Protocol Negotiation. + * @compile ../../Utils.java + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * ALPNTest h2 HTTP/1.1,h2 h2 + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * ALPNTest HTTP/1.1,h2 h2,HTTP/1.1 HTTP/1.1 + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * ALPNTest h2 HTTP/1.1 none + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ALPNTest { + + private static final String TEST_BASE = System.getProperty("test.src"); + private static final Path CERT_DIR = Paths.get(TEST_BASE) + .resolve("..").resolve("..").resolve("tlcp-certs"); + + public static void main(String[] args) throws Exception { +// System.setProperty("javax.net.debug", "all"); + + String[] serverAppProtocols = args[0].split(","); + String[] clientAppProtocols = args[1].split(","); + String expectedNegotiated = args[2]; + new ALPNTest(serverAppProtocols, clientAppProtocols, expectedNegotiated).run(); + } + + private final String[] serverAppProtocols; + private final String[] clientAppProtocols; + private final String expectedNegotiated; + + private final boolean expectedFail; + + public ALPNTest(String[] serverAppProtocols, String[] clientAppProtocols, + String expectedNegotiated) { + this.serverAppProtocols = serverAppProtocols; + this.clientAppProtocols = clientAppProtocols; + this.expectedNegotiated = expectedNegotiated; + + expectedFail = "none".equals(expectedNegotiated); + } + + /* + * Run the test case. + */ + public void run() throws Exception { + bootup(); + } + + /* + * Define the server side application of the test for the specified socket. + */ + protected void runServerApplication(SSLSocket socket) throws Exception { + // here comes the test logic + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + try { + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + + if (expectedFail) { + throw new RuntimeException("Expected SSLHandshakeException wasn't thrown"); + } + } catch (SSLHandshakeException se) { + if (expectedFail) { + System.out.println("Expected SSLHandshakeException: " + se.getMessage()); + } else { + throw se; + } + } + } + + /* + * Define the client side application of the test for the specified socket. + * This method is used if the returned value of + * isCustomizedClientConnection() is false. + * + * @param socket may be null is no client socket is generated. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(SSLSocket socket) throws Exception { + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + try { + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + + if (expectedFail) { + throw new RuntimeException("Expected SSLHandshakeException wasn't thrown"); + } + } catch (SSLHandshakeException se) { + if (expectedFail) { + System.out.println("Expected SSLHandshakeException: " + se.getMessage()); + } else { + throw se; + } + } + } + + /* + * Define the client side application of the test for the specified + * server port. This method is used if the returned value of + * isCustomizedClientConnection() is true. + * + * Note that the client need to connect to the server port by itself + * for the actual message exchange. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(int serverPort) throws Exception { + // blank + } + + /* + * Create an instance of SSLContext for client use. + */ + protected SSLContext createClientSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, CLIENT_CERTS, + getClientContextParameters()); + } + + /* + * Create an instance of SSLContext for server use. + */ + protected SSLContext createServerSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, SERVER_CERTS, + getServerContextParameters()); + } + + /* + * The parameters used to configure SSLContext. + */ + protected static final class ContextParameters { + final String contextProtocol; + final String tmAlgorithm; + final String kmAlgorithm; + + ContextParameters(String contextProtocol, + String tmAlgorithm, String kmAlgorithm) { + + this.contextProtocol = contextProtocol; + this.tmAlgorithm = tmAlgorithm; + this.kmAlgorithm = kmAlgorithm; + } + } + + /* + * Get the client side parameters of SSLContext. + */ + protected ContextParameters getClientContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Get the server side parameters of SSLContext. + */ + protected ContextParameters getServerContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Does the client side use customized connection other than + * explicit Socket.connect(), for example, URL.openConnection()? + */ + protected boolean isCustomizedClientConnection() { + return false; + } + + /* + * Configure the client side socket. + */ + protected void configureClientSocket(SSLSocket socket) { + SSLParameters params = socket.getSSLParameters(); + params.setApplicationProtocols(clientAppProtocols); + socket.setSSLParameters(params); + } + + /* + * Configure the server side socket. + */ + protected void configureServerSocket(SSLServerSocket socket) { + socket.setNeedClientAuth(true); + SSLParameters params = socket.getSSLParameters(); + params.setApplicationProtocols(serverAppProtocols); + socket.setSSLParameters(params); + } + + /* + * ============================================= + * Define the client and server side operations. + * + * If the client or server is doing some kind of object creation + * that the other side depends on, and that thread prematurely + * exits, you may experience a hang. The test harness will + * terminate all hung threads after its timeout has expired, + * currently 3 minutes by default, but you might try to be + * smart about it.... + */ + + /* + * Is the server ready to serve? + */ + protected final CountDownLatch serverCondition = new CountDownLatch(1); + + /* + * Is the client ready to handshake? + */ + protected final CountDownLatch clientCondition = new CountDownLatch(1); + + /* + * What's the server port? Use any free port by default + */ + protected volatile int serverPort = 0; + + /* + * What's the server address? null means binding to the wildcard. + */ + protected volatile InetAddress serverAddress = null; + + /* + * Define the server side of the test. + */ + protected void doServerSide() throws Exception { + // kick start the server side service + SSLContext context = createServerSSLContext(); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + InetAddress serverAddress = this.serverAddress; + SSLServerSocket sslServerSocket = serverAddress == null ? + (SSLServerSocket)sslssf.createServerSocket(serverPort) + : (SSLServerSocket)sslssf.createServerSocket(); + if (serverAddress != null) { + sslServerSocket.bind(new InetSocketAddress(serverAddress, serverPort)); + } + configureServerSocket(sslServerSocket); + serverPort = sslServerSocket.getLocalPort(); + + // Signal the client, the server is ready to accept connection. + serverCondition.countDown(); + + // Try to accept a connection in 30 seconds. + SSLSocket sslSocket; + try { + sslServerSocket.setSoTimeout(300000); + sslSocket = (SSLSocket)sslServerSocket.accept(); + } catch (SocketTimeoutException ste) { + // Ignore the test case if no connection within 30 seconds. + System.out.println( + "No incoming client connection in 30 seconds. " + + "Ignore in server side."); + return; + } finally { + sslServerSocket.close(); + } + + // handle the connection + try { + // Is it the expected client connection? + // + // Naughty test cases or third party routines may try to + // connection to this server port unintentionally. In + // order to mitigate the impact of unexpected client + // connections and avoid intermittent failure, it should + // be checked that the accepted connection is really linked + // to the expected client. + boolean clientIsReady = + clientCondition.await(30L, TimeUnit.SECONDS); + + if (clientIsReady) { + // Run the application in server side. + runServerApplication(sslSocket); + } else { // Otherwise, ignore + // We don't actually care about plain socket connections + // for TLS communication testing generally. Just ignore + // the test if the accepted connection is not linked to + // the expected client or the client connection timeout + // in 30 seconds. + System.out.println( + "The client is not the expected one or timeout. " + + "Ignore in server side."); + } + } finally { + sslSocket.close(); + } + } + + /* + * Define the client side of the test. + */ + protected void doClientSide() throws Exception { + + // Wait for server to get started. + // + // The server side takes care of the issue if the server cannot + // get started in 90 seconds. The client side would just ignore + // the test case if the serer is not ready. + boolean serverIsReady = + serverCondition.await(90L, TimeUnit.SECONDS); + if (!serverIsReady) { + System.out.println( + "The server is not ready yet in 90 seconds. " + + "Ignore in client side."); + return; + } + + if (isCustomizedClientConnection()) { + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // Run the application in client side. + runClientApplication(serverPort); + + return; + } + + SSLContext context = createClientSSLContext(); + SSLSocketFactory sslsf = context.getSocketFactory(); + + try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { + try { + configureClientSocket(sslSocket); + InetAddress serverAddress = this.serverAddress; + InetSocketAddress connectAddress = serverAddress == null + ? new InetSocketAddress("localhost", serverPort) + : new InetSocketAddress(serverAddress, serverPort); + sslSocket.connect(connectAddress, 15000); + } catch (IOException ioe) { + // The server side may be impacted by naughty test cases or + // third party routines, and cannot accept connections. + // + // Just ignore the test if the connection cannot be + // established. + System.out.println( + "Cannot make a connection in 15 seconds. " + + "Ignore in client side."); + return; + } + + // OK, here the client and server get connected. + + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // There is still a chance in theory that the server thread may + // wait client-ready timeout and then quit. The chance should + // be really rare so we don't consider it until it becomes a + // real problem. + + // Run the application in client side. + runClientApplication(sslSocket); + + String appProtocol = sslSocket.getApplicationProtocol(); + appProtocol = appProtocol == null ? "none" : appProtocol; + if (!expectedNegotiated.equals(appProtocol)) { + throw new RuntimeException("Unexpected negotiated protocol: " + + appProtocol); + } else { + System.out.println("Negotiated protocol: " + appProtocol); + } + } + } + + /* + * ============================================= + * Stuffs to customize the SSLContext instances. + */ + + /* + * ======================================= + * Certificates and keys used in the test. + */ + // Trusted certificates. + protected final static Cert[] TRUSTED_CERTS = { + Cert.CA_CERT}; + + // End entity certificate. + protected final static Cert[] SERVER_CERTS = { + Cert.SERVER_SIGN_CERT, Cert.SERVER_ENC_CERT}; + protected final static Cert[] CLIENT_CERTS = { + Cert.CLIENT_SIGN_CERT, Cert.CLIENT_ENC_CERT}; + + /* + * Create an instance of SSLContext with the specified trust/key materials. + */ + public static SSLContext createSSLContext( + Cert[] trustedCerts, + Cert[] endEntityCerts, + ContextParameters params) throws Exception { + + KeyStore ts = null; // trust store + KeyStore ks = null; // key store + char passphrase[] = "passphrase".toCharArray(); + + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trused certs. + ByteArrayInputStream is; + if (trustedCerts != null && trustedCerts.length != 0) { + ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + + Certificate[] trustedCert = new Certificate[trustedCerts.length]; + for (int i = 0; i < trustedCerts.length; i++) { + is = new ByteArrayInputStream(trustedCerts[i].certStr.getBytes()); + try { + trustedCert[i] = cf.generateCertificate(is); + } finally { + is.close(); + } + + ts.setCertificateEntry( + "trusted-cert-" + trustedCerts[i].name(), trustedCert[i]); + } + } + + // Import the key materials. + if (endEntityCerts != null && endEntityCerts.length != 0) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + for (int i = 0; i < endEntityCerts.length; i++) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(endEntityCerts[i].privKeyStr)); + KeyFactory kf = KeyFactory.getInstance(endEntityCerts[i].keyAlgo); + PrivateKey priKey = kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream( + endEntityCerts[i].certStr.getBytes()); + Certificate keyCert = null; + try { + keyCert = cf.generateCertificate(is); + } finally { + is.close(); + } + + Certificate[] chain = new Certificate[] { keyCert }; + + // import the key entry. + ks.setKeyEntry("cert-" + endEntityCerts[i].name(), + priKey, passphrase, chain); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(params.tmAlgorithm); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance(params.contextProtocol); + if (endEntityCerts != null && endEntityCerts.length != 0 && ks != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(params.kmAlgorithm); + kmf.init(ks, passphrase); + + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } else { + context.init(null, tmf.getTrustManagers(), null); + } + + return context; + } + + /* + * ================================================= + * Stuffs to boot up the client-server mode testing. + */ + private Thread clientThread = null; + private Thread serverThread = null; + private volatile Exception serverException = null; + private volatile Exception clientException = null; + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + private final boolean separateServerThread = false; + + /* + * Boot up the testing, used to drive remainder of the test. + */ + private void bootup() throws Exception { + Exception startException = null; + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + startException = e; + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + if (serverThread != null) { + serverThread.join(); + } + } else { + if (clientThread != null) { + clientThread.join(); + } + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + } else { + remote = clientException; + local = serverException; + } + + Exception exception = null; + + /* + * Check various exception conditions. + */ + if ((local != null) && (remote != null)) { + // If both failed, return the curthread's exception. + local.initCause(remote); + exception = local; + } else if (local != null) { + exception = local; + } else if (remote != null) { + exception = remote; + } else if (startException != null) { + exception = startException; + } + + /* + * If there was an exception *AND* a startException, + * output it. + */ + if (exception != null) { + if (exception != startException && startException != null) { + exception.addSuppressed(startException); + } + throw exception; + } + + // Fall-through: no exception to throw! + } + + private void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + @Override + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + logException("Server died", e); + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + logException("Server failed", e); + serverException = e; + } + } + } + + private void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + @Override + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + logException("Client died", e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + logException("Client failed", e); + clientException = e; + } + } + } + + private synchronized void logException(String prefix, Throwable cause) { + System.out.println(prefix + ": " + cause); + cause.printStackTrace(System.out); + } + + public enum Cert { + + CA_CERT("tlcp-intca.crt", "tlcp-intca.key"), + SERVER_SIGN_CERT("tlcp-server-sign.crt", "tlcp-server-sign.key"), + SERVER_ENC_CERT("tlcp-server-enc.crt", "tlcp-server-enc.key"), + CLIENT_SIGN_CERT("tlcp-client-sign.crt", "tlcp-client-sign.key"), + CLIENT_ENC_CERT("tlcp-client-enc.crt", "tlcp-client-enc.key"); + + final String keyAlgo = "EC"; + final String certStr; + final String privKeyStr; + + Cert(String certName, String privKeyName) { + try { + certStr = Utils.certStr(CERT_DIR.resolve(certName)); + privKeyStr = Utils.keyStr(CERT_DIR.resolve(privKeyName)); + } catch (IOException e) { + throw new RuntimeException("Cannot load cert or key", e); + } + } + } +} diff --git a/test/jdk/sm/ssl/TLCP/ConstraintTest.java b/test/jdk/sm/ssl/TLCP/ConstraintTest.java new file mode 100644 index 00000000000..bca10a38b01 --- /dev/null +++ b/test/jdk/sm/ssl/TLCP/ConstraintTest.java @@ -0,0 +1,647 @@ +/* + * 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 + * 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. + */ + +// +// Please run in othervm mode. SunJSSE does not support dynamic system +// properties, no way to re-use system properties in samevm/agentvm mode. +// + +/* + * @test + * @summary TLCP 1.1 would be restricted by the system property on cipher suites. + * @compile ../../Utils.java + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_GCM_SM3 + * -Djdk.tls.server.cipherSuites=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * ConstraintTest + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ConstraintTest { + + private static final String TEST_BASE = System.getProperty("test.src"); + private static final Path CERT_DIR = Paths.get(TEST_BASE) + .resolve("..").resolve("..").resolve("tlcp-certs"); + + public static void main(String[] args) throws Exception { +// System.setProperty("javax.net.debug", "all"); + + try { + new ConstraintTest().run(); + throw new RuntimeException( + "Expected SSLHandshakeException was not thrown"); + } catch (SSLHandshakeException e) { + System.out.println("Expected SSLHandshakeException: " + e.getMessage()); + } + } + + /* + * Run the test case. + */ + public void run() throws Exception { + bootup(); + } + + /* + * Define the server side application of the test for the specified socket. + */ + protected void runServerApplication(SSLSocket socket) throws Exception { + // here comes the test logic + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + } + + /* + * Define the client side application of the test for the specified socket. + * This method is used if the returned value of + * isCustomizedClientConnection() is false. + * + * @param socket may be null is no client socket is generated. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(SSLSocket socket) throws Exception { + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + } + + /* + * Define the client side application of the test for the specified + * server port. This method is used if the returned value of + * isCustomizedClientConnection() is true. + * + * Note that the client need to connect to the server port by itself + * for the actual message exchange. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(int serverPort) throws Exception { + // blank + } + + /* + * Create an instance of SSLContext for client use. + */ + protected SSLContext createClientSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, CLIENT_CERTS, + getClientContextParameters()); + } + + /* + * Create an instance of SSLContext for server use. + */ + protected SSLContext createServerSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, SERVER_CERTS, + getServerContextParameters()); + } + + /* + * The parameters used to configure SSLContext. + */ + protected static final class ContextParameters { + final String contextProtocol; + final String tmAlgorithm; + final String kmAlgorithm; + + ContextParameters(String contextProtocol, + String tmAlgorithm, String kmAlgorithm) { + + this.contextProtocol = contextProtocol; + this.tmAlgorithm = tmAlgorithm; + this.kmAlgorithm = kmAlgorithm; + } + } + + /* + * Get the client side parameters of SSLContext. + */ + protected ContextParameters getClientContextParameters() { + return new ContextParameters("TLCP", "PKIX", "NewSunX509"); + } + + /* + * Get the server side parameters of SSLContext. + */ + protected ContextParameters getServerContextParameters() { + return new ContextParameters("TLCP", "PKIX", "NewSunX509"); + } + + /* + * Does the client side use customized connection other than + * explicit Socket.connect(), for example, URL.openConnection()? + */ + protected boolean isCustomizedClientConnection() { + return false; + } + + /* + * Configure the client side socket. + */ + protected void configureClientSocket(SSLSocket socket) { + + } + + /* + * Configure the server side socket. + */ + protected void configureServerSocket(SSLServerSocket socket) { + socket.setNeedClientAuth(true); + } + + /* + * ============================================= + * Define the client and server side operations. + * + * If the client or server is doing some kind of object creation + * that the other side depends on, and that thread prematurely + * exits, you may experience a hang. The test harness will + * terminate all hung threads after its timeout has expired, + * currently 3 minutes by default, but you might try to be + * smart about it.... + */ + + /* + * Is the server ready to serve? + */ + protected final CountDownLatch serverCondition = new CountDownLatch(1); + + /* + * Is the client ready to handshake? + */ + protected final CountDownLatch clientCondition = new CountDownLatch(1); + + /* + * What's the server port? Use any free port by default + */ + protected volatile int serverPort = 0; + + /* + * What's the server address? null means binding to the wildcard. + */ + protected volatile InetAddress serverAddress = null; + + /* + * Define the server side of the test. + */ + protected void doServerSide() throws Exception { + // kick start the server side service + SSLContext context = createServerSSLContext(); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + InetAddress serverAddress = this.serverAddress; + SSLServerSocket sslServerSocket = serverAddress == null ? + (SSLServerSocket)sslssf.createServerSocket(serverPort) + : (SSLServerSocket)sslssf.createServerSocket(); + if (serverAddress != null) { + sslServerSocket.bind(new InetSocketAddress(serverAddress, serverPort)); + } + configureServerSocket(sslServerSocket); + serverPort = sslServerSocket.getLocalPort(); + + // Signal the client, the server is ready to accept connection. + serverCondition.countDown(); + + // Try to accept a connection in 30 seconds. + SSLSocket sslSocket; + try { + sslServerSocket.setSoTimeout(300000); + sslSocket = (SSLSocket)sslServerSocket.accept(); + } catch (SocketTimeoutException ste) { + // Ignore the test case if no connection within 30 seconds. + System.out.println( + "No incoming client connection in 30 seconds. " + + "Ignore in server side."); + return; + } finally { + sslServerSocket.close(); + } + + // handle the connection + try { + // Is it the expected client connection? + // + // Naughty test cases or third party routines may try to + // connection to this server port unintentionally. In + // order to mitigate the impact of unexpected client + // connections and avoid intermittent failure, it should + // be checked that the accepted connection is really linked + // to the expected client. + boolean clientIsReady = + clientCondition.await(30L, TimeUnit.SECONDS); + + if (clientIsReady) { + // Run the application in server side. + runServerApplication(sslSocket); + } else { // Otherwise, ignore + // We don't actually care about plain socket connections + // for TLS communication testing generally. Just ignore + // the test if the accepted connection is not linked to + // the expected client or the client connection timeout + // in 30 seconds. + System.out.println( + "The client is not the expected one or timeout. " + + "Ignore in server side."); + } + } finally { + sslSocket.close(); + } + } + + /* + * Define the client side of the test. + */ + protected void doClientSide() throws Exception { + + // Wait for server to get started. + // + // The server side takes care of the issue if the server cannot + // get started in 90 seconds. The client side would just ignore + // the test case if the serer is not ready. + boolean serverIsReady = + serverCondition.await(90L, TimeUnit.SECONDS); + if (!serverIsReady) { + System.out.println( + "The server is not ready yet in 90 seconds. " + + "Ignore in client side."); + return; + } + + if (isCustomizedClientConnection()) { + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // Run the application in client side. + runClientApplication(serverPort); + + return; + } + + SSLContext context = createClientSSLContext(); + SSLSocketFactory sslsf = context.getSocketFactory(); + + try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { + try { + configureClientSocket(sslSocket); + InetAddress serverAddress = this.serverAddress; + InetSocketAddress connectAddress = serverAddress == null + ? new InetSocketAddress("localhost", serverPort) + : new InetSocketAddress(serverAddress, serverPort); + sslSocket.connect(connectAddress, 15000); + } catch (IOException ioe) { + // The server side may be impacted by naughty test cases or + // third party routines, and cannot accept connections. + // + // Just ignore the test if the connection cannot be + // established. + System.out.println( + "Cannot make a connection in 15 seconds. " + + "Ignore in client side."); + return; + } + + // OK, here the client and server get connected. + + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // There is still a chance in theory that the server thread may + // wait client-ready timeout and then quit. The chance should + // be really rare so we don't consider it until it becomes a + // real problem. + + // Run the application in client side. + runClientApplication(sslSocket); + } + } + + /* + * ============================================= + * Stuffs to customize the SSLContext instances. + */ + + /* + * ======================================= + * Certificates and keys used in the test. + */ + // Trusted certificates. + protected final static Cert[] TRUSTED_CERTS = { + Cert.CA_CERT}; + + // End entity certificate. + protected final static Cert[] SERVER_CERTS = { + Cert.SERVER_SIGN_CERT, Cert.SERVER_ENC_CERT}; + protected final static Cert[] CLIENT_CERTS = { + Cert.CLIENT_SIGN_CERT, Cert.CLIENT_ENC_CERT}; + + /* + * Create an instance of SSLContext with the specified trust/key materials. + */ + public static SSLContext createSSLContext( + Cert[] trustedCerts, + Cert[] endEntityCerts, + ContextParameters params) throws Exception { + + KeyStore ts = null; // trust store + KeyStore ks = null; // key store + char passphrase[] = "passphrase".toCharArray(); + + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trused certs. + ByteArrayInputStream is; + if (trustedCerts != null && trustedCerts.length != 0) { + ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + + Certificate[] trustedCert = new Certificate[trustedCerts.length]; + for (int i = 0; i < trustedCerts.length; i++) { + is = new ByteArrayInputStream(trustedCerts[i].certStr.getBytes()); + try { + trustedCert[i] = cf.generateCertificate(is); + } finally { + is.close(); + } + + ts.setCertificateEntry( + "trusted-cert-" + trustedCerts[i].name(), trustedCert[i]); + } + } + + // Import the key materials. + if (endEntityCerts != null && endEntityCerts.length != 0) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + for (int i = 0; i < endEntityCerts.length; i++) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(endEntityCerts[i].privKeyStr)); + KeyFactory kf = KeyFactory.getInstance(endEntityCerts[i].keyAlgo); + PrivateKey priKey = kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream( + endEntityCerts[i].certStr.getBytes()); + Certificate keyCert = null; + try { + keyCert = cf.generateCertificate(is); + } finally { + is.close(); + } + + Certificate[] chain = new Certificate[] { keyCert }; + + // import the key entry. + ks.setKeyEntry("cert-" + endEntityCerts[i].name(), + priKey, passphrase, chain); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(params.tmAlgorithm); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance(params.contextProtocol); + if (endEntityCerts != null && endEntityCerts.length != 0 && ks != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(params.kmAlgorithm); + kmf.init(ks, passphrase); + + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } else { + context.init(null, tmf.getTrustManagers(), null); + } + + return context; + } + + /* + * ================================================= + * Stuffs to boot up the client-server mode testing. + */ + private Thread clientThread = null; + private Thread serverThread = null; + private volatile Exception serverException = null; + private volatile Exception clientException = null; + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + private final boolean separateServerThread = false; + + /* + * Boot up the testing, used to drive remainder of the test. + */ + private void bootup() throws Exception { + Exception startException = null; + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + startException = e; + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + if (serverThread != null) { + serverThread.join(); + } + } else { + if (clientThread != null) { + clientThread.join(); + } + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + } else { + remote = clientException; + local = serverException; + } + + Exception exception = null; + + /* + * Check various exception conditions. + */ + if ((local != null) && (remote != null)) { + // If both failed, return the curthread's exception. + local.initCause(remote); + exception = local; + } else if (local != null) { + exception = local; + } else if (remote != null) { + exception = remote; + } else if (startException != null) { + exception = startException; + } + + /* + * If there was an exception *AND* a startException, + * output it. + */ + if (exception != null) { + if (exception != startException && startException != null) { + exception.addSuppressed(startException); + } + throw exception; + } + + // Fall-through: no exception to throw! + } + + private void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + @Override + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + logException("Server died", e); + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + logException("Server failed", e); + serverException = e; + } + } + } + + private void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + @Override + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + logException("Client died", e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + logException("Client failed", e); + clientException = e; + } + } + } + + private synchronized void logException(String prefix, Throwable cause) { + System.out.println(prefix + ": " + cause); + cause.printStackTrace(System.out); + } + + public enum Cert { + + CA_CERT("tlcp-intca.crt", "tlcp-intca.key"), + SERVER_SIGN_CERT("tlcp-server-sign.crt", "tlcp-server-sign.key"), + SERVER_ENC_CERT("tlcp-server-enc.crt", "tlcp-server-enc.key"), + CLIENT_SIGN_CERT("tlcp-client-sign.crt", "tlcp-client-sign.key"), + CLIENT_ENC_CERT("tlcp-client-enc.crt", "tlcp-client-enc.key"); + + final String keyAlgo = "EC"; + final String certStr; + final String privKeyStr; + + Cert(String certName, String privKeyName) { + try { + certStr = Utils.certStr(CERT_DIR.resolve(certName)); + privKeyStr = Utils.keyStr(CERT_DIR.resolve(privKeyName)); + } catch (IOException e) { + throw new RuntimeException("Cannot load cert or key", e); + } + } + } +} diff --git a/test/jdk/sm/ssl/TLCP/NoConstraintTest.java b/test/jdk/sm/ssl/TLCP/NoConstraintTest.java new file mode 100644 index 00000000000..7a1040e47b5 --- /dev/null +++ b/test/jdk/sm/ssl/TLCP/NoConstraintTest.java @@ -0,0 +1,642 @@ +/* + * 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 + * 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. + */ + +// +// Please run in othervm mode. SunJSSE does not support dynamic system +// properties, no way to re-use system properties in samevm/agentvm mode. +// + +/* + * @test + * @summary TLCP 1.1 would not be restricted by the system properties on named groups + * and signature schemes, since this protocol always work with only + * curveSM2 and sm2sig_sm3. + * @compile ../../Utils.java + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_GCM_SM3 + * -Djdk.tls.client.SignatureSchemes=ecdsa_secp256r1_sha256 + * -Djdk.tls.namedGroups=secp256r1 + * NoConstraintTest + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class NoConstraintTest { + + private static final String TEST_BASE = System.getProperty("test.src"); + private static final Path CERT_DIR = Paths.get(TEST_BASE) + .resolve("..").resolve("..").resolve("tlcp-certs"); + + public static void main(String[] args) throws Exception { +// System.setProperty("javax.net.debug", "all"); + new NoConstraintTest().run(); + } + + /* + * Run the test case. + */ + public void run() throws Exception { + bootup(); + } + + /* + * Define the server side application of the test for the specified socket. + */ + protected void runServerApplication(SSLSocket socket) throws Exception { + // here comes the test logic + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + } + + /* + * Define the client side application of the test for the specified socket. + * This method is used if the returned value of + * isCustomizedClientConnection() is false. + * + * @param socket may be null is no client socket is generated. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(SSLSocket socket) throws Exception { + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + } + + /* + * Define the client side application of the test for the specified + * server port. This method is used if the returned value of + * isCustomizedClientConnection() is true. + * + * Note that the client need to connect to the server port by itself + * for the actual message exchange. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(int serverPort) throws Exception { + // blank + } + + /* + * Create an instance of SSLContext for client use. + */ + protected SSLContext createClientSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, CLIENT_CERTS, + getClientContextParameters()); + } + + /* + * Create an instance of SSLContext for server use. + */ + protected SSLContext createServerSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, SERVER_CERTS, + getServerContextParameters()); + } + + /* + * The parameters used to configure SSLContext. + */ + protected static final class ContextParameters { + final String contextProtocol; + final String tmAlgorithm; + final String kmAlgorithm; + + ContextParameters(String contextProtocol, + String tmAlgorithm, String kmAlgorithm) { + + this.contextProtocol = contextProtocol; + this.tmAlgorithm = tmAlgorithm; + this.kmAlgorithm = kmAlgorithm; + } + } + + /* + * Get the client side parameters of SSLContext. + */ + protected ContextParameters getClientContextParameters() { + return new ContextParameters("TLCP", "PKIX", "NewSunX509"); + } + + /* + * Get the server side parameters of SSLContext. + */ + protected ContextParameters getServerContextParameters() { + return new ContextParameters("TLCP", "PKIX", "NewSunX509"); + } + + /* + * Does the client side use customized connection other than + * explicit Socket.connect(), for example, URL.openConnection()? + */ + protected boolean isCustomizedClientConnection() { + return false; + } + + /* + * Configure the client side socket. + */ + protected void configureClientSocket(SSLSocket socket) { + + } + + /* + * Configure the server side socket. + */ + protected void configureServerSocket(SSLServerSocket socket) { + socket.setNeedClientAuth(true); + } + + /* + * ============================================= + * Define the client and server side operations. + * + * If the client or server is doing some kind of object creation + * that the other side depends on, and that thread prematurely + * exits, you may experience a hang. The test harness will + * terminate all hung threads after its timeout has expired, + * currently 3 minutes by default, but you might try to be + * smart about it.... + */ + + /* + * Is the server ready to serve? + */ + protected final CountDownLatch serverCondition = new CountDownLatch(1); + + /* + * Is the client ready to handshake? + */ + protected final CountDownLatch clientCondition = new CountDownLatch(1); + + /* + * What's the server port? Use any free port by default + */ + protected volatile int serverPort = 0; + + /* + * What's the server address? null means binding to the wildcard. + */ + protected volatile InetAddress serverAddress = null; + + /* + * Define the server side of the test. + */ + protected void doServerSide() throws Exception { + // kick start the server side service + SSLContext context = createServerSSLContext(); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + InetAddress serverAddress = this.serverAddress; + SSLServerSocket sslServerSocket = serverAddress == null ? + (SSLServerSocket)sslssf.createServerSocket(serverPort) + : (SSLServerSocket)sslssf.createServerSocket(); + if (serverAddress != null) { + sslServerSocket.bind(new InetSocketAddress(serverAddress, serverPort)); + } + configureServerSocket(sslServerSocket); + serverPort = sslServerSocket.getLocalPort(); + + // Signal the client, the server is ready to accept connection. + serverCondition.countDown(); + + // Try to accept a connection in 30 seconds. + SSLSocket sslSocket; + try { + sslServerSocket.setSoTimeout(300000); + sslSocket = (SSLSocket)sslServerSocket.accept(); + } catch (SocketTimeoutException ste) { + // Ignore the test case if no connection within 30 seconds. + System.out.println( + "No incoming client connection in 30 seconds. " + + "Ignore in server side."); + return; + } finally { + sslServerSocket.close(); + } + + // handle the connection + try { + // Is it the expected client connection? + // + // Naughty test cases or third party routines may try to + // connection to this server port unintentionally. In + // order to mitigate the impact of unexpected client + // connections and avoid intermittent failure, it should + // be checked that the accepted connection is really linked + // to the expected client. + boolean clientIsReady = + clientCondition.await(30L, TimeUnit.SECONDS); + + if (clientIsReady) { + // Run the application in server side. + runServerApplication(sslSocket); + } else { // Otherwise, ignore + // We don't actually care about plain socket connections + // for TLS communication testing generally. Just ignore + // the test if the accepted connection is not linked to + // the expected client or the client connection timeout + // in 30 seconds. + System.out.println( + "The client is not the expected one or timeout. " + + "Ignore in server side."); + } + } finally { + sslSocket.close(); + } + } + + /* + * Define the client side of the test. + */ + protected void doClientSide() throws Exception { + + // Wait for server to get started. + // + // The server side takes care of the issue if the server cannot + // get started in 90 seconds. The client side would just ignore + // the test case if the serer is not ready. + boolean serverIsReady = + serverCondition.await(90L, TimeUnit.SECONDS); + if (!serverIsReady) { + System.out.println( + "The server is not ready yet in 90 seconds. " + + "Ignore in client side."); + return; + } + + if (isCustomizedClientConnection()) { + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // Run the application in client side. + runClientApplication(serverPort); + + return; + } + + SSLContext context = createClientSSLContext(); + SSLSocketFactory sslsf = context.getSocketFactory(); + + try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { + try { + configureClientSocket(sslSocket); + InetAddress serverAddress = this.serverAddress; + InetSocketAddress connectAddress = serverAddress == null + ? new InetSocketAddress("localhost", serverPort) + : new InetSocketAddress(serverAddress, serverPort); + sslSocket.connect(connectAddress, 15000); + } catch (IOException ioe) { + // The server side may be impacted by naughty test cases or + // third party routines, and cannot accept connections. + // + // Just ignore the test if the connection cannot be + // established. + System.out.println( + "Cannot make a connection in 15 seconds. " + + "Ignore in client side."); + return; + } + + // OK, here the client and server get connected. + + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // There is still a chance in theory that the server thread may + // wait client-ready timeout and then quit. The chance should + // be really rare so we don't consider it until it becomes a + // real problem. + + // Run the application in client side. + runClientApplication(sslSocket); + } + } + + /* + * ============================================= + * Stuffs to customize the SSLContext instances. + */ + + /* + * ======================================= + * Certificates and keys used in the test. + */ + // Trusted certificates. + protected final static Cert[] TRUSTED_CERTS = { + Cert.CA_CERT}; + + // End entity certificate. + protected final static Cert[] SERVER_CERTS = { + Cert.SERVER_SIGN_CERT, Cert.SERVER_ENC_CERT}; + protected final static Cert[] CLIENT_CERTS = { + Cert.CLIENT_SIGN_CERT, Cert.CLIENT_ENC_CERT}; + + /* + * Create an instance of SSLContext with the specified trust/key materials. + */ + public static SSLContext createSSLContext( + Cert[] trustedCerts, + Cert[] endEntityCerts, + ContextParameters params) throws Exception { + + KeyStore ts = null; // trust store + KeyStore ks = null; // key store + char passphrase[] = "passphrase".toCharArray(); + + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trused certs. + ByteArrayInputStream is; + if (trustedCerts != null && trustedCerts.length != 0) { + ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + + Certificate[] trustedCert = new Certificate[trustedCerts.length]; + for (int i = 0; i < trustedCerts.length; i++) { + is = new ByteArrayInputStream(trustedCerts[i].certStr.getBytes()); + try { + trustedCert[i] = cf.generateCertificate(is); + } finally { + is.close(); + } + + ts.setCertificateEntry( + "trusted-cert-" + trustedCerts[i].name(), trustedCert[i]); + } + } + + // Import the key materials. + if (endEntityCerts != null && endEntityCerts.length != 0) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + for (int i = 0; i < endEntityCerts.length; i++) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(endEntityCerts[i].privKeyStr)); + KeyFactory kf = KeyFactory.getInstance(endEntityCerts[i].keyAlgo); + PrivateKey priKey = kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream( + endEntityCerts[i].certStr.getBytes()); + Certificate keyCert = null; + try { + keyCert = cf.generateCertificate(is); + } finally { + is.close(); + } + + Certificate[] chain = new Certificate[] { keyCert }; + + // import the key entry. + ks.setKeyEntry("cert-" + endEntityCerts[i].name(), + priKey, passphrase, chain); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(params.tmAlgorithm); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance(params.contextProtocol); + if (endEntityCerts != null && endEntityCerts.length != 0 && ks != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(params.kmAlgorithm); + kmf.init(ks, passphrase); + + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } else { + context.init(null, tmf.getTrustManagers(), null); + } + + return context; + } + + /* + * ================================================= + * Stuffs to boot up the client-server mode testing. + */ + private Thread clientThread = null; + private Thread serverThread = null; + private volatile Exception serverException = null; + private volatile Exception clientException = null; + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + private final boolean separateServerThread = false; + + /* + * Boot up the testing, used to drive remainder of the test. + */ + private void bootup() throws Exception { + Exception startException = null; + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + startException = e; + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + if (serverThread != null) { + serverThread.join(); + } + } else { + if (clientThread != null) { + clientThread.join(); + } + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + } else { + remote = clientException; + local = serverException; + } + + Exception exception = null; + + /* + * Check various exception conditions. + */ + if ((local != null) && (remote != null)) { + // If both failed, return the curthread's exception. + local.initCause(remote); + exception = local; + } else if (local != null) { + exception = local; + } else if (remote != null) { + exception = remote; + } else if (startException != null) { + exception = startException; + } + + /* + * If there was an exception *AND* a startException, + * output it. + */ + if (exception != null) { + if (exception != startException && startException != null) { + exception.addSuppressed(startException); + } + throw exception; + } + + // Fall-through: no exception to throw! + } + + private void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + @Override + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + logException("Server died", e); + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + logException("Server failed", e); + serverException = e; + } + } + } + + private void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + @Override + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + logException("Client died", e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + logException("Client failed", e); + clientException = e; + } + } + } + + private synchronized void logException(String prefix, Throwable cause) { + System.out.println(prefix + ": " + cause); + cause.printStackTrace(System.out); + } + + public enum Cert { + + CA_CERT("tlcp-intca.crt", "tlcp-intca.key"), + SERVER_SIGN_CERT("tlcp-server-sign.crt", "tlcp-server-sign.key"), + SERVER_ENC_CERT("tlcp-server-enc.crt", "tlcp-server-enc.key"), + CLIENT_SIGN_CERT("tlcp-client-sign.crt", "tlcp-client-sign.key"), + CLIENT_ENC_CERT("tlcp-client-enc.crt", "tlcp-client-enc.key"); + + final String keyAlgo = "EC"; + final String certStr; + final String privKeyStr; + + Cert(String certName, String privKeyName) { + try { + certStr = Utils.certStr(CERT_DIR.resolve(certName)); + privKeyStr = Utils.keyStr(CERT_DIR.resolve(privKeyName)); + } catch (IOException e) { + throw new RuntimeException("Cannot load cert or key", e); + } + } + } +} diff --git a/test/jdk/sm/ssl/TLCP/ResumptionTest.java b/test/jdk/sm/ssl/TLCP/ResumptionTest.java new file mode 100644 index 00000000000..dd65c6eb02c --- /dev/null +++ b/test/jdk/sm/ssl/TLCP/ResumptionTest.java @@ -0,0 +1,704 @@ +/* + * 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 + * 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. + */ + +// +// Please run in othervm mode. SunJSSE does not support dynamic system +// properties, no way to re-use system properties in samevm/agentvm mode. +// + +/* + * @test + * @summary Session resumption on TLCP 1.1. + * @compile ../../Utils.java + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_CBC_SM3 + * ResumptionTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_GCM_SM3 + * ResumptionTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECDHE_SM4_CBC_SM3 + * ResumptionTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECDHE_SM4_GCM_SM3 + * ResumptionTest + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ResumptionTest { + + private static final String TEST_BASE = System.getProperty("test.src"); + private static final Path CERT_DIR = Paths.get(TEST_BASE) + .resolve("..").resolve("..").resolve("tlcp-certs"); + + public static void main(String[] args) throws Exception { +// System.setProperty("javax.net.debug", "all"); + new ResumptionTest().run(); + } + + /* + * Run the test case. + */ + public void run() throws Exception { + bootup(); + } + + /* + * Define the server side application of the test for the specified socket. + */ + protected void runServerApplication(SSLSocket socket) throws Exception { + // here comes the test logic + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + } + + /* + * Define the client side application of the test for the specified socket. + * This method is used if the returned value of + * isCustomizedClientConnection() is false. + * + * @param socket may be null is no client socket is generated. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(SSLSocket socket) throws Exception { + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + } + + /* + * Create an instance of SSLContext for client use. + */ + protected SSLContext createClientSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, CLIENT_CERTS, + getClientContextParameters()); + } + + /* + * Create an instance of SSLContext for server use. + */ + protected SSLContext createServerSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, SERVER_CERTS, + getServerContextParameters()); + } + + /* + * The parameters used to configure SSLContext. + */ + protected static final class ContextParameters { + final String contextProtocol; + final String tmAlgorithm; + final String kmAlgorithm; + + ContextParameters(String contextProtocol, + String tmAlgorithm, String kmAlgorithm) { + + this.contextProtocol = contextProtocol; + this.tmAlgorithm = tmAlgorithm; + this.kmAlgorithm = kmAlgorithm; + } + } + + /* + * Get the client side parameters of SSLContext. + */ + protected ContextParameters getClientContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Get the server side parameters of SSLContext. + */ + protected ContextParameters getServerContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Configure the client side socket. + */ + protected void configureClientSocket(SSLSocket socket) { + + } + + /* + * Configure the server side socket. + */ + protected void configureServerSocket(SSLServerSocket socket) { + socket.setNeedClientAuth(true); + } + + /* + * ============================================= + * Define the client and server side operations. + * + * If the client or server is doing some kind of object creation + * that the other side depends on, and that thread prematurely + * exits, you may experience a hang. The test harness will + * terminate all hung threads after its timeout has expired, + * currently 3 minutes by default, but you might try to be + * smart about it.... + */ + + /* + * Is the server ready to serve? + */ + protected final CountDownLatch serverCondition = new CountDownLatch(1); + + /* + * Is the client ready to handshake? + */ + protected final CountDownLatch firstClientCondition = new CountDownLatch(1); + protected final CountDownLatch secondClientCondition = new CountDownLatch(1); + + /* + * What's the server port? Use any free port by default + */ + protected volatile int serverPort = 0; + + /* + * What's the server address? null means binding to the wildcard. + */ + protected volatile InetAddress serverAddress = null; + + /* + * Define the server side of the test. + */ + protected void doServerSide() throws Exception { + // kick start the server side service + SSLContext context = createServerSSLContext(); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + InetAddress serverAddress = this.serverAddress; + SSLServerSocket sslServerSocket = serverAddress == null ? + (SSLServerSocket)sslssf.createServerSocket(serverPort) + : (SSLServerSocket)sslssf.createServerSocket(); + if (serverAddress != null) { + sslServerSocket.bind(new InetSocketAddress(serverAddress, serverPort)); + } + configureServerSocket(sslServerSocket); + serverPort = sslServerSocket.getLocalPort(); + + // Signal the client, the server is ready to accept connection. + serverCondition.countDown(); + + // Try to accept the first connection in 30 seconds. + SSLSocket firstSSLSocket; + try { + sslServerSocket.setSoTimeout(300000); + firstSSLSocket = (SSLSocket)sslServerSocket.accept(); + } catch (SocketTimeoutException ste) { + // Ignore the test case if no connection within 30 seconds. + System.out.println( + "No incoming client connection in 30 seconds. " + + "Ignore in server side."); + return; + } + + // handle the first connection + try { + // Is it the expected client connection? + // + // Naughty test cases or third party routines may try to + // connection to this server port unintentionally. In + // order to mitigate the impact of unexpected client + // connections and avoid intermittent failure, it should + // be checked that the accepted connection is really linked + // to the expected client. + boolean clientIsReady = + firstClientCondition.await(30L, TimeUnit.SECONDS); + + if (clientIsReady) { + // Run the application in server side. + runServerApplication(firstSSLSocket); + } else { // Otherwise, ignore + // We don't actually care about plain socket connections + // for TLS communication testing generally. Just ignore + // the test if the accepted connection is not linked to + // the expected client or the client connection timeout + // in 30 seconds. + System.out.println( + "The client is not the expected one or timeout. " + + "Ignore in server side."); + } + } finally { + firstSSLSocket.close(); + } + + // Try to accept the second connection in 30 seconds. + SSLSocket secondSSLSocket; + try { + secondSSLSocket = (SSLSocket)sslServerSocket.accept(); + } catch (SocketTimeoutException ste) { + // Ignore the test case if no connection within 30 seconds. + System.out.println( + "No incoming client connection in 30 seconds. " + + "Ignore in server side."); + return; + } + + // handle the second connection + try { + // Is it the expected client connection? + // + // Naughty test cases or third party routines may try to + // connection to this server port unintentionally. In + // order to mitigate the impact of unexpected client + // connections and avoid intermittent failure, it should + // be checked that the accepted connection is really linked + // to the expected client. + boolean clientIsReady = + secondClientCondition.await(30L, TimeUnit.SECONDS); + + if (clientIsReady) { + // Run the application in server side. + runServerApplication(secondSSLSocket); + } else { // Otherwise, ignore + // We don't actually care about plain socket connections + // for TLS communication testing generally. Just ignore + // the test if the accepted connection is not linked to + // the expected client or the client connection timeout + // in 30 seconds. + System.out.println( + "The client is not the expected one or timeout. " + + "Ignore in server side."); + } + } finally { + secondSSLSocket.close(); + } + + sslServerSocket.close(); + } + + /* + * Define the client side of the test. + */ + protected void doClientSide() throws Exception { + + // Wait for server to get started. + // + // The server side takes care of the issue if the server cannot + // get started in 90 seconds. The client side would just ignore + // the test case if the serer is not ready. + boolean serverIsReady = + serverCondition.await(90L, TimeUnit.SECONDS); + if (!serverIsReady) { + System.out.println( + "The server is not ready yet in 90 seconds. " + + "Ignore in client side."); + return; + } + + SSLContext context = createClientSSLContext(); + SSLSocketFactory sslsf = context.getSocketFactory(); + + long firstCreationTime; + try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { + try { + configureClientSocket(sslSocket); + InetAddress serverAddress = this.serverAddress; + InetSocketAddress connectAddress = serverAddress == null + ? new InetSocketAddress("localhost", serverPort) + : new InetSocketAddress(serverAddress, serverPort); + sslSocket.connect(connectAddress, 15000); + } catch (IOException ioe) { + // The server side may be impacted by naughty test cases or + // third party routines, and cannot accept connections. + // + // Just ignore the test if the connection cannot be + // established. + System.out.println( + "Cannot make a connection in 15 seconds. " + + "Ignore in client side."); + return; + } + + // OK, here the client and server get connected. + + // Signal the server, the client is ready to communicate. + firstClientCondition.countDown(); + + // There is still a chance in theory that the server thread may + // wait client-ready timeout and then quit. The chance should + // be really rare so we don't consider it until it becomes a + // real problem. + + // Run the application in client side. + runClientApplication(sslSocket); + firstCreationTime = sslSocket.getSession().getCreationTime(); + } + + long secondCreationTime; + try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { + try { + configureClientSocket(sslSocket); + InetAddress serverAddress = this.serverAddress; + InetSocketAddress connectAddress = serverAddress == null + ? new InetSocketAddress("localhost", serverPort) + : new InetSocketAddress(serverAddress, serverPort); + sslSocket.connect(connectAddress, 15000); + } catch (IOException ioe) { + // The server side may be impacted by naughty test cases or + // third party routines, and cannot accept connections. + // + // Just ignore the test if the connection cannot be + // established. + System.out.println( + "Cannot make a connection in 15 seconds. " + + "Ignore in client side."); + return; + } + + // OK, here the client and server get connected. + + // Signal the server, the client is ready to communicate. + secondClientCondition.countDown(); + + // There is still a chance in theory that the server thread may + // wait client-ready timeout and then quit. The chance should + // be really rare so we don't consider it until it becomes a + // real problem. + + // Run the application in client side. + runClientApplication(sslSocket); + secondCreationTime = sslSocket.getSession().getCreationTime(); + } + + if (firstCreationTime == -1L || firstCreationTime != secondCreationTime) { + throw new RuntimeException("Incorrect session creation time: " + + "firstCreationTime=" + firstCreationTime + + ", secondCreationTime=" + secondCreationTime); + } else { + System.out.println("Session resumed!"); + } + } + + /* + * ============================================= + * Stuffs to customize the SSLContext instances. + */ + + /* + * ======================================= + * Certificates and keys used in the test. + */ + // Trusted certificates. + protected final static Cert[] TRUSTED_CERTS = { + Cert.CA_CERT}; + + // End entity certificate. + protected final static Cert[] SERVER_CERTS = { + Cert.SERVER_SIGN_CERT, Cert.SERVER_ENC_CERT}; + protected final static Cert[] CLIENT_CERTS = { + Cert.CLIENT_SIGN_CERT, Cert.CLIENT_ENC_CERT}; + + /* + * Create an instance of SSLContext with the specified trust/key materials. + */ + public static SSLContext createSSLContext( + Cert[] trustedCerts, + Cert[] endEntityCerts, + ContextParameters params) throws Exception { + + KeyStore ts = null; // trust store + KeyStore ks = null; // key store + char passphrase[] = "passphrase".toCharArray(); + + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trused certs. + ByteArrayInputStream is; + if (trustedCerts != null && trustedCerts.length != 0) { + ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + + Certificate[] trustedCert = new Certificate[trustedCerts.length]; + for (int i = 0; i < trustedCerts.length; i++) { + is = new ByteArrayInputStream(trustedCerts[i].certStr.getBytes()); + try { + trustedCert[i] = cf.generateCertificate(is); + } finally { + is.close(); + } + + ts.setCertificateEntry( + "trusted-cert-" + trustedCerts[i].name(), trustedCert[i]); + } + } + + // Import the key materials. + if (endEntityCerts != null && endEntityCerts.length != 0) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + for (int i = 0; i < endEntityCerts.length; i++) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(endEntityCerts[i].privKeyStr)); + KeyFactory kf = KeyFactory.getInstance(endEntityCerts[i].keyAlgo); + PrivateKey priKey = kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream( + endEntityCerts[i].certStr.getBytes()); + Certificate keyCert = null; + try { + keyCert = cf.generateCertificate(is); + } finally { + is.close(); + } + + Certificate[] chain = new Certificate[] { keyCert }; + + // import the key entry. + ks.setKeyEntry("cert-" + endEntityCerts[i].name(), + priKey, passphrase, chain); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(params.tmAlgorithm); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance(params.contextProtocol); + if (endEntityCerts != null && endEntityCerts.length != 0 && ks != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(params.kmAlgorithm); + kmf.init(ks, passphrase); + + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } else { + context.init(null, tmf.getTrustManagers(), null); + } + + return context; + } + + /* + * ================================================= + * Stuffs to boot up the client-server mode testing. + */ + private Thread clientThread = null; + private Thread serverThread = null; + private volatile Exception serverException = null; + private volatile Exception clientException = null; + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + private final boolean separateServerThread = false; + + /* + * Boot up the testing, used to drive remainder of the test. + */ + private void bootup() throws Exception { + Exception startException = null; + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + startException = e; + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + if (serverThread != null) { + serverThread.join(); + } + } else { + if (clientThread != null) { + clientThread.join(); + } + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + } else { + remote = clientException; + local = serverException; + } + + Exception exception = null; + + /* + * Check various exception conditions. + */ + if ((local != null) && (remote != null)) { + // If both failed, return the curthread's exception. + local.initCause(remote); + exception = local; + } else if (local != null) { + exception = local; + } else if (remote != null) { + exception = remote; + } else if (startException != null) { + exception = startException; + } + + /* + * If there was an exception *AND* a startException, + * output it. + */ + if (exception != null) { + if (exception != startException && startException != null) { + exception.addSuppressed(startException); + } + throw exception; + } + + // Fall-through: no exception to throw! + } + + private void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + @Override + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + logException("Server died", e); + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + logException("Server failed", e); + serverException = e; + } + } + } + + private void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + @Override + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + logException("Client died", e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + logException("Client failed", e); + clientException = e; + } + } + } + + private synchronized void logException(String prefix, Throwable cause) { + System.out.println(prefix + ": " + cause); + cause.printStackTrace(System.out); + } + + public enum Cert { + + CA_CERT("tlcp-intca.crt", "tlcp-intca.key"), + SERVER_SIGN_CERT("tlcp-server-sign.crt", "tlcp-server-sign.key"), + SERVER_ENC_CERT("tlcp-server-enc.crt", "tlcp-server-enc.key"), + CLIENT_SIGN_CERT("tlcp-client-sign.crt", "tlcp-client-sign.key"), + CLIENT_ENC_CERT("tlcp-client-enc.crt", "tlcp-client-enc.key"); + + final String keyAlgo = "EC"; + final String certStr; + final String privKeyStr; + + Cert(String certName, String privKeyName) { + try { + certStr = Utils.certStr(CERT_DIR.resolve(certName)); + privKeyStr = Utils.keyStr(CERT_DIR.resolve(privKeyName)); + } catch (IOException e) { + throw new RuntimeException("Cannot load cert or key", e); + } + } + } +} diff --git a/test/jdk/sm/ssl/TLCP/SNITest.java b/test/jdk/sm/ssl/TLCP/SNITest.java new file mode 100644 index 00000000000..e280923d96e --- /dev/null +++ b/test/jdk/sm/ssl/TLCP/SNITest.java @@ -0,0 +1,699 @@ +/* + * 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 + * 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. + */ + +// +// Please run in othervm mode. SunJSSE does not support dynamic system +// properties, no way to re-use system properties in samevm/agentvm mode. +// + +/* + * @test + * @summary TLCP 1.1 with Server Name Indication. + * @compile ../../Utils.java + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * SNITest foo foo success + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * SNITest foo bar fail + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIMatcher; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class SNITest { + + private static final String TEST_BASE = System.getProperty("test.src"); + private static final Path CERT_DIR = Paths.get(TEST_BASE) + .resolve("..").resolve("..").resolve("tlcp-certs"); + + public static void main(String[] args) throws Exception { +// System.setProperty("javax.net.debug", "all"); + + String serverHostRegex = args[0]; + String clientHost = args[1]; + boolean expectedFail = "fail".equals(args[2]); + new SNITest(serverHostRegex, clientHost, expectedFail).run(); + } + + private final String serverHostRegex; + private final String clientHost; + private final boolean expectedFail; + + public SNITest(String serverHostRegex, String clientHost, + boolean expectedFail) { + this.serverHostRegex = serverHostRegex; + this.clientHost = clientHost; + this.expectedFail = expectedFail; + } + + /* + * Run the test case. + */ + public void run() throws Exception { + bootup(); + } + + /* + * Define the server side application of the test for the specified socket. + */ + protected void runServerApplication(SSLSocket socket) throws Exception { + // here comes the test logic + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + try { + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + + if (expectedFail) { + throw new RuntimeException( + "Expected SSLHandshakeException wasn't thrown"); + } + } catch (SSLHandshakeException se) { + if (expectedFail) { + System.out.println( + "Expected SSLHandshakeException: " + se.getMessage()); + } else { + throw se; + } + } + } + + /* + * Define the client side application of the test for the specified socket. + * This method is used if the returned value of + * isCustomizedClientConnection() is false. + * + * @param socket may be null is no client socket is generated. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(SSLSocket socket) throws Exception { + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + try { + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + + if (expectedFail) { + throw new RuntimeException( + "Expected SSLHandshakeException wasn't thrown"); + } + } catch (SSLHandshakeException se) { + if (expectedFail) { + System.out.println( + "Expected SSLHandshakeException: " + se.getMessage()); + } else { + throw se; + } + } + } + + /* + * Define the client side application of the test for the specified + * server port. This method is used if the returned value of + * isCustomizedClientConnection() is true. + * + * Note that the client need to connect to the server port by itself + * for the actual message exchange. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(int serverPort) throws Exception { + // blank + } + + /* + * Create an instance of SSLContext for client use. + */ + protected SSLContext createClientSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, CLIENT_CERTS, + getClientContextParameters()); + } + + /* + * Create an instance of SSLContext for server use. + */ + protected SSLContext createServerSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, SERVER_CERTS, + getServerContextParameters()); + } + + /* + * The parameters used to configure SSLContext. + */ + protected static final class ContextParameters { + final String contextProtocol; + final String tmAlgorithm; + final String kmAlgorithm; + + ContextParameters(String contextProtocol, + String tmAlgorithm, String kmAlgorithm) { + + this.contextProtocol = contextProtocol; + this.tmAlgorithm = tmAlgorithm; + this.kmAlgorithm = kmAlgorithm; + } + } + + /* + * Get the client side parameters of SSLContext. + */ + protected ContextParameters getClientContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Get the server side parameters of SSLContext. + */ + protected ContextParameters getServerContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Does the client side use customized connection other than + * explicit Socket.connect(), for example, URL.openConnection()? + */ + protected boolean isCustomizedClientConnection() { + return false; + } + + /* + * Configure the client side socket. + */ + protected void configureClientSocket(SSLSocket socket) { + SSLParameters params = socket.getSSLParameters(); + List serverNames = new ArrayList<>(); + serverNames.add(new SNIHostName(clientHost)); + params.setServerNames(serverNames); + socket.setSSLParameters(params); + } + + /* + * Configure the server side socket. + */ + protected void configureServerSocket(SSLServerSocket socket) { + socket.setNeedClientAuth(true); + + SSLParameters params = socket.getSSLParameters(); + List sniMatchers = new ArrayList<>(); + sniMatchers.add(SNIHostName.createSNIMatcher(serverHostRegex)); + params.setSNIMatchers(sniMatchers); + socket.setSSLParameters(params); + } + + /* + * ============================================= + * Define the client and server side operations. + * + * If the client or server is doing some kind of object creation + * that the other side depends on, and that thread prematurely + * exits, you may experience a hang. The test harness will + * terminate all hung threads after its timeout has expired, + * currently 3 minutes by default, but you might try to be + * smart about it.... + */ + + /* + * Is the server ready to serve? + */ + protected final CountDownLatch serverCondition = new CountDownLatch(1); + + /* + * Is the client ready to handshake? + */ + protected final CountDownLatch clientCondition = new CountDownLatch(1); + + /* + * What's the server port? Use any free port by default + */ + protected volatile int serverPort = 0; + + /* + * What's the server address? null means binding to the wildcard. + */ + protected volatile InetAddress serverAddress = null; + + /* + * Define the server side of the test. + */ + protected void doServerSide() throws Exception { + // kick start the server side service + SSLContext context = createServerSSLContext(); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + InetAddress serverAddress = this.serverAddress; + SSLServerSocket sslServerSocket = serverAddress == null ? + (SSLServerSocket)sslssf.createServerSocket(serverPort) + : (SSLServerSocket)sslssf.createServerSocket(); + if (serverAddress != null) { + sslServerSocket.bind(new InetSocketAddress(serverAddress, serverPort)); + } + configureServerSocket(sslServerSocket); + serverPort = sslServerSocket.getLocalPort(); + + // Signal the client, the server is ready to accept connection. + serverCondition.countDown(); + + // Try to accept a connection in 30 seconds. + SSLSocket sslSocket; + try { + sslServerSocket.setSoTimeout(300000); + sslSocket = (SSLSocket)sslServerSocket.accept(); + } catch (SocketTimeoutException ste) { + // Ignore the test case if no connection within 30 seconds. + System.out.println( + "No incoming client connection in 30 seconds. " + + "Ignore in server side."); + return; + } finally { + sslServerSocket.close(); + } + + // handle the connection + try { + // Is it the expected client connection? + // + // Naughty test cases or third party routines may try to + // connection to this server port unintentionally. In + // order to mitigate the impact of unexpected client + // connections and avoid intermittent failure, it should + // be checked that the accepted connection is really linked + // to the expected client. + boolean clientIsReady = + clientCondition.await(30L, TimeUnit.SECONDS); + + if (clientIsReady) { + // Run the application in server side. + runServerApplication(sslSocket); + } else { // Otherwise, ignore + // We don't actually care about plain socket connections + // for TLS communication testing generally. Just ignore + // the test if the accepted connection is not linked to + // the expected client or the client connection timeout + // in 30 seconds. + System.out.println( + "The client is not the expected one or timeout. " + + "Ignore in server side."); + } + } finally { + sslSocket.close(); + } + } + + /* + * Define the client side of the test. + */ + protected void doClientSide() throws Exception { + + // Wait for server to get started. + // + // The server side takes care of the issue if the server cannot + // get started in 90 seconds. The client side would just ignore + // the test case if the serer is not ready. + boolean serverIsReady = + serverCondition.await(90L, TimeUnit.SECONDS); + if (!serverIsReady) { + System.out.println( + "The server is not ready yet in 90 seconds. " + + "Ignore in client side."); + return; + } + + if (isCustomizedClientConnection()) { + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // Run the application in client side. + runClientApplication(serverPort); + + return; + } + + SSLContext context = createClientSSLContext(); + SSLSocketFactory sslsf = context.getSocketFactory(); + + try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { + try { + configureClientSocket(sslSocket); + InetAddress serverAddress = this.serverAddress; + InetSocketAddress connectAddress = serverAddress == null + ? new InetSocketAddress("localhost", serverPort) + : new InetSocketAddress(serverAddress, serverPort); + sslSocket.connect(connectAddress, 15000); + } catch (IOException ioe) { + // The server side may be impacted by naughty test cases or + // third party routines, and cannot accept connections. + // + // Just ignore the test if the connection cannot be + // established. + System.out.println( + "Cannot make a connection in 15 seconds. " + + "Ignore in client side."); + return; + } + + // OK, here the client and server get connected. + + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // There is still a chance in theory that the server thread may + // wait client-ready timeout and then quit. The chance should + // be really rare so we don't consider it until it becomes a + // real problem. + + // Run the application in client side. + runClientApplication(sslSocket); + } + } + + /* + * ============================================= + * Stuffs to customize the SSLContext instances. + */ + + /* + * ======================================= + * Certificates and keys used in the test. + */ + // Trusted certificates. + protected final static Cert[] TRUSTED_CERTS = { + Cert.CA_CERT}; + + // End entity certificate. + protected final static Cert[] SERVER_CERTS = { + Cert.SERVER_SIGN_CERT, Cert.SERVER_ENC_CERT}; + protected final static Cert[] CLIENT_CERTS = { + Cert.CLIENT_SIGN_CERT, Cert.CLIENT_ENC_CERT}; + + /* + * Create an instance of SSLContext with the specified trust/key materials. + */ + public static SSLContext createSSLContext( + Cert[] trustedCerts, + Cert[] endEntityCerts, + ContextParameters params) throws Exception { + + KeyStore ts = null; // trust store + KeyStore ks = null; // key store + char passphrase[] = "passphrase".toCharArray(); + + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trused certs. + ByteArrayInputStream is; + if (trustedCerts != null && trustedCerts.length != 0) { + ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + + Certificate[] trustedCert = new Certificate[trustedCerts.length]; + for (int i = 0; i < trustedCerts.length; i++) { + is = new ByteArrayInputStream(trustedCerts[i].certStr.getBytes()); + try { + trustedCert[i] = cf.generateCertificate(is); + } finally { + is.close(); + } + + ts.setCertificateEntry( + "trusted-cert-" + trustedCerts[i].name(), trustedCert[i]); + } + } + + // Import the key materials. + if (endEntityCerts != null && endEntityCerts.length != 0) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + for (int i = 0; i < endEntityCerts.length; i++) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(endEntityCerts[i].privKeyStr)); + KeyFactory kf = KeyFactory.getInstance(endEntityCerts[i].keyAlgo); + PrivateKey priKey = kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream( + endEntityCerts[i].certStr.getBytes()); + Certificate keyCert = null; + try { + keyCert = cf.generateCertificate(is); + } finally { + is.close(); + } + + Certificate[] chain = new Certificate[] { keyCert }; + + // import the key entry. + ks.setKeyEntry("cert-" + endEntityCerts[i].name(), + priKey, passphrase, chain); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(params.tmAlgorithm); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance(params.contextProtocol); + if (endEntityCerts != null && endEntityCerts.length != 0 && ks != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(params.kmAlgorithm); + kmf.init(ks, passphrase); + + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } else { + context.init(null, tmf.getTrustManagers(), null); + } + + return context; + } + + /* + * ================================================= + * Stuffs to boot up the client-server mode testing. + */ + private Thread clientThread = null; + private Thread serverThread = null; + private volatile Exception serverException = null; + private volatile Exception clientException = null; + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + private final boolean separateServerThread = false; + + /* + * Boot up the testing, used to drive remainder of the test. + */ + private void bootup() throws Exception { + Exception startException = null; + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + startException = e; + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + if (serverThread != null) { + serverThread.join(); + } + } else { + if (clientThread != null) { + clientThread.join(); + } + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + } else { + remote = clientException; + local = serverException; + } + + Exception exception = null; + + /* + * Check various exception conditions. + */ + if ((local != null) && (remote != null)) { + // If both failed, return the curthread's exception. + local.initCause(remote); + exception = local; + } else if (local != null) { + exception = local; + } else if (remote != null) { + exception = remote; + } else if (startException != null) { + exception = startException; + } + + /* + * If there was an exception *AND* a startException, + * output it. + */ + if (exception != null) { + if (exception != startException && startException != null) { + exception.addSuppressed(startException); + } + throw exception; + } + + // Fall-through: no exception to throw! + } + + private void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + @Override + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + logException("Server died", e); + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + logException("Server failed", e); + serverException = e; + } + } + } + + private void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + @Override + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + logException("Client died", e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + logException("Client failed", e); + clientException = e; + } + } + } + + private synchronized void logException(String prefix, Throwable cause) { + System.out.println(prefix + ": " + cause); + cause.printStackTrace(System.out); + } + + public enum Cert { + + CA_CERT("tlcp-intca.crt", "tlcp-intca.key"), + SERVER_SIGN_CERT("tlcp-server-sign.crt", "tlcp-server-sign.key"), + SERVER_ENC_CERT("tlcp-server-enc.crt", "tlcp-server-enc.key"), + CLIENT_SIGN_CERT("tlcp-client-sign.crt", "tlcp-client-sign.key"), + CLIENT_ENC_CERT("tlcp-client-enc.crt", "tlcp-client-enc.key"); + + final String keyAlgo = "EC"; + final String certStr; + final String privKeyStr; + + Cert(String certName, String privKeyName) { + try { + certStr = Utils.certStr(CERT_DIR.resolve(certName)); + privKeyStr = Utils.keyStr(CERT_DIR.resolve(privKeyName)); + } catch (IOException e) { + throw new RuntimeException("Cannot load cert or key", e); + } + } + } +} diff --git a/test/jdk/sm/ssl/TLCP/SSLEngineTest.java b/test/jdk/sm/ssl/TLCP/SSLEngineTest.java new file mode 100644 index 00000000000..692dfcb6fa3 --- /dev/null +++ b/test/jdk/sm/ssl/TLCP/SSLEngineTest.java @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * 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. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @summary TLCP 1.1 with SSLEngine. + * @compile ../../Utils.java + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_CBC_SM3 + * SSLEngineTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_GCM_SM3 + * SSLEngineTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECDHE_SM4_CBC_SM3 + * SSLEngineTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECDHE_SM4_GCM_SM3 + * SSLEngineTest + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +public class SSLEngineTest { + + private static final String TEST_BASE = System.getProperty("test.src"); + private static final Path CERT_DIR = Paths.get(TEST_BASE) + .resolve("..").resolve("..").resolve("tlcp-certs"); + + public static void main(String[] args) throws Exception { +// System.setProperty("javax.net.debug", "all"); + new SSLEngineTest().runTest(); + } + + protected final SSLEngine clientEngine; // client Engine + protected final ByteBuffer clientOut; // write side of clientEngine + protected final ByteBuffer clientIn; // read side of clientEngine + + protected final SSLEngine serverEngine; // server Engine + protected final ByteBuffer serverOut; // write side of serverEngine + protected final ByteBuffer serverIn; // read side of serverEngine + + // For data transport, this example uses local ByteBuffers. This + // isn't really useful, but the purpose of this example is to show + // SSLEngine concepts, not how to do network transport. + protected final ByteBuffer cTOs; // "reliable" transport client->server + protected final ByteBuffer sTOc; // "reliable" transport server->client + + // Trusted certificates. + protected final static Cert[] TRUSTED_CERTS = { Cert.CA_CERT} ; + + // End entity certificate. + protected final static Cert[] SERVER_CERTS = { + Cert.SERVER_SIGN_CERT, Cert.SERVER_ENC_CERT}; + protected final static Cert[] CLIENT_CERTS = { + Cert.CLIENT_SIGN_CERT, Cert.CLIENT_ENC_CERT}; + + protected SSLEngineTest() throws Exception { + serverEngine = configureServerEngine( + createServerSSLContext().createSSLEngine()); + + clientEngine = configureClientEngine( + createClientSSLContext().createSSLEngine()); + + // We'll assume the buffer sizes are the same + // between client and server. + SSLSession session = clientEngine.getSession(); + int appBufferMax = session.getApplicationBufferSize(); + int netBufferMax = session.getPacketBufferSize(); + + // We'll make the input buffers a bit bigger than the max needed + // size, so that unwrap()s following a successful data transfer + // won't generate BUFFER_OVERFLOWS. + // + // We'll use a mix of direct and indirect ByteBuffers for + // tutorial purposes only. In reality, only use direct + // ByteBuffers when they give a clear performance enhancement. + clientIn = ByteBuffer.allocate(appBufferMax + 50); + serverIn = ByteBuffer.allocate(appBufferMax + 50); + + cTOs = ByteBuffer.allocateDirect(netBufferMax); + sTOc = ByteBuffer.allocateDirect(netBufferMax); + + clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); + serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); + } + + /* + * Create an instance of SSLContext with the specified trust/key materials. + */ + public static SSLContext createSSLContext( + Cert[] trustedCerts, + Cert[] endEntityCerts, + ContextParameters params) throws Exception { + + KeyStore ts = null; // trust store + KeyStore ks = null; // key store + char[] passphrase = "passphrase".toCharArray(); + + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trused certs. + ByteArrayInputStream is; + if (trustedCerts != null && trustedCerts.length != 0) { + ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + + Certificate[] trustedCert = new Certificate[trustedCerts.length]; + for (int i = 0; i < trustedCerts.length; i++) { + is = new ByteArrayInputStream(trustedCerts[i].certStr.getBytes()); + try { + trustedCert[i] = cf.generateCertificate(is); + } finally { + is.close(); + } + + ts.setCertificateEntry( + "trusted-cert-" + trustedCerts[i].name(), trustedCert[i]); + } + } + + // Import the key materials. + if (endEntityCerts != null && endEntityCerts.length != 0) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + for (int i = 0; i < endEntityCerts.length; i++) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(endEntityCerts[i].privKeyStr)); + KeyFactory kf = KeyFactory.getInstance(endEntityCerts[i].keyAlgo); + PrivateKey priKey = kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream( + endEntityCerts[i].certStr.getBytes()); + Certificate keyCert = null; + try { + keyCert = cf.generateCertificate(is); + } finally { + is.close(); + } + + Certificate[] chain = new Certificate[] { keyCert }; + + // import the key entry. + ks.setKeyEntry("cert-" + endEntityCerts[i].name(), + priKey, passphrase, chain); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(params.tmAlgorithm); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance(params.contextProtocol); + if (endEntityCerts != null && endEntityCerts.length != 0 && ks != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(params.kmAlgorithm); + kmf.init(ks, passphrase); + + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } else { + context.init(null, tmf.getTrustManagers(), null); + } + + return context; + } + + /* + * Create an instance of SSLContext for client use. + */ + protected SSLContext createClientSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, CLIENT_CERTS, + getClientContextParameters()); + } + + /* + * Create an instance of SSLContext for server use. + */ + protected SSLContext createServerSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, SERVER_CERTS, + getServerContextParameters()); + } + + /* + * The parameters used to configure SSLContext. + */ + protected static final class ContextParameters { + final String contextProtocol; + final String tmAlgorithm; + final String kmAlgorithm; + + ContextParameters(String contextProtocol, + String tmAlgorithm, String kmAlgorithm) { + + this.contextProtocol = contextProtocol; + this.tmAlgorithm = tmAlgorithm; + this.kmAlgorithm = kmAlgorithm; + } + } + + /* + * Get the client side parameters of SSLContext. + */ + protected ContextParameters getClientContextParameters() { + return new ContextParameters("TLCP", "PKIX", "NewSunX509"); + } + + /* + * Get the server side parameters of SSLContext. + */ + protected ContextParameters getServerContextParameters() { + return new ContextParameters("TLCP", "PKIX", "NewSunX509"); + } + + // + // Protected methods could be used to customize the test case. + // + + /* + * Configure the client side engine. + */ + protected SSLEngine configureClientEngine(SSLEngine clientEngine) { + clientEngine.setUseClientMode(true); + + // Get/set parameters if needed + // SSLParameters paramsClient = clientEngine.getSSLParameters(); + // clientEngine.setSSLParameters(paramsClient); + + return clientEngine; + } + + /* + * Configure the server side engine. + */ + protected SSLEngine configureServerEngine(SSLEngine serverEngine) { + serverEngine.setUseClientMode(false); + serverEngine.setNeedClientAuth(true); + + // Get/set parameters if needed + // + // SSLParameters paramsServer = serverEngine.getSSLParameters(); + // serverEngine.setSSLParameters(paramsServer); + + return serverEngine; + } + + // + // Private methods that used to build the common part of the test. + // + + private void runTest() throws Exception { + SSLEngineResult clientResult; + SSLEngineResult serverResult; + + boolean dataDone = false; + while (isOpen(clientEngine) || isOpen(serverEngine)) { + log("================="); + + // client wrap + log("---Client Wrap---"); + clientResult = clientEngine.wrap(clientOut, cTOs); + logEngineStatus(clientEngine, clientResult); + runDelegatedTasks(clientEngine); + + // server wrap + log("---Server Wrap---"); + serverResult = serverEngine.wrap(serverOut, sTOc); + logEngineStatus(serverEngine, serverResult); + runDelegatedTasks(serverEngine); + + cTOs.flip(); + sTOc.flip(); + + Integer protocolVersion = protocolVersion(sTOc); + if(protocolVersion != null && protocolVersion != 0x0101) { + throw new RuntimeException(String.format( + "Not TLCP 1.1: 0x%04X", protocolVersion)); + } + + // client unwrap + log("---Client Unwrap---"); + clientResult = clientEngine.unwrap(sTOc, clientIn); + logEngineStatus(clientEngine, clientResult); + runDelegatedTasks(clientEngine); + + // server unwrap + log("---Server Unwrap---"); + serverResult = serverEngine.unwrap(cTOs, serverIn); + logEngineStatus(serverEngine, serverResult); + runDelegatedTasks(serverEngine); + + cTOs.compact(); + sTOc.compact(); + + // After we've transferred all application data between the client + // and server, we close the clientEngine's outbound stream. + // This generates a close_notify handshake message, which the + // server engine receives and responds by closing itself. + if (!dataDone && (clientOut.limit() == serverIn.position()) && + (serverOut.limit() == clientIn.position())) { + + // A sanity check to ensure we got what was sent. + checkTransfer(serverOut, clientIn); + checkTransfer(clientOut, serverIn); + + log("\tClosing clientEngine's *OUTBOUND*..."); + clientEngine.closeOutbound(); + logEngineStatus(clientEngine); + + dataDone = true; + log("\tClosing serverEngine's *OUTBOUND*..."); + serverEngine.closeOutbound(); + logEngineStatus(serverEngine); + } + } + } + + static boolean isOpen(SSLEngine engine) { + return (!engine.isOutboundDone() || !engine.isInboundDone()); + } + + private static void logEngineStatus(SSLEngine engine) { + log("\tCurrent HS State: " + engine.getHandshakeStatus()); + log("\tisInboundDone() : " + engine.isInboundDone()); + log("\tisOutboundDone(): " + engine.isOutboundDone()); + } + + private static void logEngineStatus( + SSLEngine engine, SSLEngineResult result) { + log("\tResult Status : " + result.getStatus()); + log("\tResult HS Status : " + result.getHandshakeStatus()); + log("\tEngine HS Status : " + engine.getHandshakeStatus()); + log("\tisInboundDone() : " + engine.isInboundDone()); + log("\tisOutboundDone() : " + engine.isOutboundDone()); + log("\tMore Result : " + result); + } + + private static void log(String message) { + System.err.println(message); + } + + // If the result indicates that we have outstanding tasks to do, + // go ahead and run them in this thread. + protected static void runDelegatedTasks(SSLEngine engine) throws Exception { + if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + log(" running delegated task..."); + runnable.run(); + } + HandshakeStatus hsStatus = engine.getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_TASK) { + throw new Exception( + "handshake shouldn't need additional tasks"); + } + logEngineStatus(engine); + } + } + + // Simple check to make sure everything came across as expected. + static void checkTransfer(ByteBuffer a, ByteBuffer b) + throws Exception { + a.flip(); + b.flip(); + + if (!a.equals(b)) { + throw new Exception("Data didn't transfer cleanly"); + } else { + log("\tData transferred cleanly"); + } + + a.position(a.limit()); + b.position(b.limit()); + a.limit(a.capacity()); + b.limit(b.capacity()); + } + + private static Integer protocolVersion(ByteBuffer buf) { + int bufLen = buf.remaining(); + if (bufLen > 0) { + buf.mark(); + + buf.get(); // record type + int majorVersion = Byte.toUnsignedInt(buf.get()); + int minorVersion = Byte.toUnsignedInt(buf.get()); + + buf.reset(); + + return (majorVersion << 8) + minorVersion; + } + + return null; // not protocol version + } + + public enum Cert { + + CA_CERT("tlcp-intca.crt", "tlcp-intca.key"), + SERVER_SIGN_CERT("tlcp-server-sign.crt", "tlcp-server-sign.key"), + SERVER_ENC_CERT("tlcp-server-enc.crt", "tlcp-server-enc.key"), + CLIENT_SIGN_CERT("tlcp-client-sign.crt", "tlcp-client-sign.key"), + CLIENT_ENC_CERT("tlcp-client-enc.crt", "tlcp-client-enc.key"); + + final String keyAlgo = "EC"; + final String certStr; + final String privKeyStr; + + Cert(String certName, String privKeyName) { + try { + certStr = Utils.certStr(CERT_DIR.resolve(certName)); + privKeyStr = Utils.keyStr(CERT_DIR.resolve(privKeyName)); + } catch (IOException e) { + throw new RuntimeException("Cannot load cert or key", e); + } + } + } +} diff --git a/test/jdk/sm/ssl/TLCP/SSLSocketTest.java b/test/jdk/sm/ssl/TLCP/SSLSocketTest.java new file mode 100644 index 00000000000..7b20451b47e --- /dev/null +++ b/test/jdk/sm/ssl/TLCP/SSLSocketTest.java @@ -0,0 +1,647 @@ +/* + * 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 + * 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. + */ + +// +// Please run in othervm mode. SunJSSE does not support dynamic system +// properties, no way to re-use system properties in samevm/agentvm mode. +// + +/* + * @test + * @summary TLCP 1.1 with SSLSocket. + * @compile ../../Utils.java + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_CBC_SM3 + * SSLSocketTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECC_SM4_GCM_SM3 + * SSLSocketTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECDHE_SM4_CBC_SM3 + * SSLSocketTest + * @run main/othervm -Djdk.tls.client.protocols=TLCPv1.1 + * -Djdk.tls.client.cipherSuites=TLCP_ECDHE_SM4_GCM_SM3 + * SSLSocketTest + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class SSLSocketTest { + + private static final String TEST_BASE = System.getProperty("test.src"); + private static final Path CERT_DIR = Paths.get(TEST_BASE) + .resolve("..").resolve("..").resolve("tlcp-certs"); + + public static void main(String[] args) throws Exception { +// System.setProperty("javax.net.debug", "all"); + new SSLSocketTest().run(); + } + + /* + * Run the test case. + */ + public void run() throws Exception { + bootup(); + } + + /* + * Define the server side application of the test for the specified socket. + */ + protected void runServerApplication(SSLSocket socket) throws Exception { + // here comes the test logic + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslIS.read(); + sslOS.write(85); + sslOS.flush(); + } + + /* + * Define the client side application of the test for the specified socket. + * This method is used if the returned value of + * isCustomizedClientConnection() is false. + * + * @param socket may be null is no client socket is generated. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(SSLSocket socket) throws Exception { + InputStream sslIS = socket.getInputStream(); + OutputStream sslOS = socket.getOutputStream(); + + sslOS.write(280); + sslOS.flush(); + sslIS.read(); + } + + /* + * Define the client side application of the test for the specified + * server port. This method is used if the returned value of + * isCustomizedClientConnection() is true. + * + * Note that the client need to connect to the server port by itself + * for the actual message exchange. + * + * @see #isCustomizedClientConnection() + */ + protected void runClientApplication(int serverPort) throws Exception { + // blank + } + + /* + * Create an instance of SSLContext for client use. + */ + protected SSLContext createClientSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, CLIENT_CERTS, + getClientContextParameters()); + } + + /* + * Create an instance of SSLContext for server use. + */ + protected SSLContext createServerSSLContext() throws Exception { + return createSSLContext(TRUSTED_CERTS, SERVER_CERTS, + getServerContextParameters()); + } + + /* + * The parameters used to configure SSLContext. + */ + protected static final class ContextParameters { + final String contextProtocol; + final String tmAlgorithm; + final String kmAlgorithm; + + ContextParameters(String contextProtocol, + String tmAlgorithm, String kmAlgorithm) { + + this.contextProtocol = contextProtocol; + this.tmAlgorithm = tmAlgorithm; + this.kmAlgorithm = kmAlgorithm; + } + } + + /* + * Get the client side parameters of SSLContext. + */ + protected ContextParameters getClientContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Get the server side parameters of SSLContext. + */ + protected ContextParameters getServerContextParameters() { + return new ContextParameters("TLCP", "PKIX", "PKIX"); + } + + /* + * Does the client side use customized connection other than + * explicit Socket.connect(), for example, URL.openConnection()? + */ + protected boolean isCustomizedClientConnection() { + return false; + } + + /* + * Configure the client side socket. + */ + protected void configureClientSocket(SSLSocket socket) { + + } + + /* + * Configure the server side socket. + */ + protected void configureServerSocket(SSLServerSocket socket) { + socket.setNeedClientAuth(true); + } + + /* + * ============================================= + * Define the client and server side operations. + * + * If the client or server is doing some kind of object creation + * that the other side depends on, and that thread prematurely + * exits, you may experience a hang. The test harness will + * terminate all hung threads after its timeout has expired, + * currently 3 minutes by default, but you might try to be + * smart about it.... + */ + + /* + * Is the server ready to serve? + */ + protected final CountDownLatch serverCondition = new CountDownLatch(1); + + /* + * Is the client ready to handshake? + */ + protected final CountDownLatch clientCondition = new CountDownLatch(1); + + /* + * What's the server port? Use any free port by default + */ + protected volatile int serverPort = 0; + + /* + * What's the server address? null means binding to the wildcard. + */ + protected volatile InetAddress serverAddress = null; + + /* + * Define the server side of the test. + */ + protected void doServerSide() throws Exception { + // kick start the server side service + SSLContext context = createServerSSLContext(); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + InetAddress serverAddress = this.serverAddress; + SSLServerSocket sslServerSocket = serverAddress == null ? + (SSLServerSocket)sslssf.createServerSocket(serverPort) + : (SSLServerSocket)sslssf.createServerSocket(); + if (serverAddress != null) { + sslServerSocket.bind(new InetSocketAddress(serverAddress, serverPort)); + } + configureServerSocket(sslServerSocket); + serverPort = sslServerSocket.getLocalPort(); + + // Signal the client, the server is ready to accept connection. + serverCondition.countDown(); + + // Try to accept a connection in 30 seconds. + SSLSocket sslSocket; + try { + sslServerSocket.setSoTimeout(300000); + sslSocket = (SSLSocket)sslServerSocket.accept(); + } catch (SocketTimeoutException ste) { + // Ignore the test case if no connection within 30 seconds. + System.out.println( + "No incoming client connection in 30 seconds. " + + "Ignore in server side."); + return; + } finally { + sslServerSocket.close(); + } + + // handle the connection + try { + // Is it the expected client connection? + // + // Naughty test cases or third party routines may try to + // connection to this server port unintentionally. In + // order to mitigate the impact of unexpected client + // connections and avoid intermittent failure, it should + // be checked that the accepted connection is really linked + // to the expected client. + boolean clientIsReady = + clientCondition.await(30L, TimeUnit.SECONDS); + + if (clientIsReady) { + // Run the application in server side. + runServerApplication(sslSocket); + } else { // Otherwise, ignore + // We don't actually care about plain socket connections + // for TLS communication testing generally. Just ignore + // the test if the accepted connection is not linked to + // the expected client or the client connection timeout + // in 30 seconds. + System.out.println( + "The client is not the expected one or timeout. " + + "Ignore in server side."); + } + } finally { + sslSocket.close(); + } + } + + /* + * Define the client side of the test. + */ + protected void doClientSide() throws Exception { + + // Wait for server to get started. + // + // The server side takes care of the issue if the server cannot + // get started in 90 seconds. The client side would just ignore + // the test case if the serer is not ready. + boolean serverIsReady = + serverCondition.await(90L, TimeUnit.SECONDS); + if (!serverIsReady) { + System.out.println( + "The server is not ready yet in 90 seconds. " + + "Ignore in client side."); + return; + } + + if (isCustomizedClientConnection()) { + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // Run the application in client side. + runClientApplication(serverPort); + + return; + } + + SSLContext context = createClientSSLContext(); + SSLSocketFactory sslsf = context.getSocketFactory(); + + try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { + try { + configureClientSocket(sslSocket); + InetAddress serverAddress = this.serverAddress; + InetSocketAddress connectAddress = serverAddress == null + ? new InetSocketAddress("localhost", serverPort) + : new InetSocketAddress(serverAddress, serverPort); + sslSocket.connect(connectAddress, 15000); + } catch (IOException ioe) { + // The server side may be impacted by naughty test cases or + // third party routines, and cannot accept connections. + // + // Just ignore the test if the connection cannot be + // established. + System.out.println( + "Cannot make a connection in 15 seconds. " + + "Ignore in client side."); + return; + } + + // OK, here the client and server get connected. + + // Signal the server, the client is ready to communicate. + clientCondition.countDown(); + + // There is still a chance in theory that the server thread may + // wait client-ready timeout and then quit. The chance should + // be really rare so we don't consider it until it becomes a + // real problem. + + // Run the application in client side. + runClientApplication(sslSocket); + } + } + + /* + * ============================================= + * Stuffs to customize the SSLContext instances. + */ + + /* + * ======================================= + * Certificates and keys used in the test. + */ + // Trusted certificates. + protected final static Cert[] TRUSTED_CERTS = { + Cert.CA_CERT}; + + // End entity certificate. + protected final static Cert[] SERVER_CERTS = { + Cert.SERVER_SIGN_CERT, Cert.SERVER_ENC_CERT}; + protected final static Cert[] CLIENT_CERTS = { + Cert.CLIENT_SIGN_CERT, Cert.CLIENT_ENC_CERT}; + + /* + * Create an instance of SSLContext with the specified trust/key materials. + */ + public static SSLContext createSSLContext( + Cert[] trustedCerts, + Cert[] endEntityCerts, + ContextParameters params) throws Exception { + + KeyStore ts = null; // trust store + KeyStore ks = null; // key store + char passphrase[] = "passphrase".toCharArray(); + + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trused certs. + ByteArrayInputStream is; + if (trustedCerts != null && trustedCerts.length != 0) { + ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + + Certificate[] trustedCert = new Certificate[trustedCerts.length]; + for (int i = 0; i < trustedCerts.length; i++) { + is = new ByteArrayInputStream(trustedCerts[i].certStr.getBytes()); + try { + trustedCert[i] = cf.generateCertificate(is); + } finally { + is.close(); + } + + ts.setCertificateEntry( + "trusted-cert-" + trustedCerts[i].name(), trustedCert[i]); + } + } + + // Import the key materials. + if (endEntityCerts != null && endEntityCerts.length != 0) { + ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + for (int i = 0; i < endEntityCerts.length; i++) { + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + Base64.getMimeDecoder().decode(endEntityCerts[i].privKeyStr)); + KeyFactory kf = KeyFactory.getInstance(endEntityCerts[i].keyAlgo); + PrivateKey priKey = kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream( + endEntityCerts[i].certStr.getBytes()); + Certificate keyCert = null; + try { + keyCert = cf.generateCertificate(is); + } finally { + is.close(); + } + + Certificate[] chain = new Certificate[] { keyCert }; + + // import the key entry. + ks.setKeyEntry("cert-" + endEntityCerts[i].name(), + priKey, passphrase, chain); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(params.tmAlgorithm); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance(params.contextProtocol); + if (endEntityCerts != null && endEntityCerts.length != 0 && ks != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(params.kmAlgorithm); + kmf.init(ks, passphrase); + + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } else { + context.init(null, tmf.getTrustManagers(), null); + } + + return context; + } + + /* + * ================================================= + * Stuffs to boot up the client-server mode testing. + */ + private Thread clientThread = null; + private Thread serverThread = null; + private volatile Exception serverException = null; + private volatile Exception clientException = null; + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + private final boolean separateServerThread = false; + + /* + * Boot up the testing, used to drive remainder of the test. + */ + private void bootup() throws Exception { + Exception startException = null; + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + startException = e; + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + if (serverThread != null) { + serverThread.join(); + } + } else { + if (clientThread != null) { + clientThread.join(); + } + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + } else { + remote = clientException; + local = serverException; + } + + Exception exception = null; + + /* + * Check various exception conditions. + */ + if ((local != null) && (remote != null)) { + // If both failed, return the curthread's exception. + local.initCause(remote); + exception = local; + } else if (local != null) { + exception = local; + } else if (remote != null) { + exception = remote; + } else if (startException != null) { + exception = startException; + } + + /* + * If there was an exception *AND* a startException, + * output it. + */ + if (exception != null) { + if (exception != startException && startException != null) { + exception.addSuppressed(startException); + } + throw exception; + } + + // Fall-through: no exception to throw! + } + + private void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + @Override + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + logException("Server died", e); + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + logException("Server failed", e); + serverException = e; + } + } + } + + private void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + @Override + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + logException("Client died", e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + logException("Client failed", e); + clientException = e; + } + } + } + + private synchronized void logException(String prefix, Throwable cause) { + System.out.println(prefix + ": " + cause); + cause.printStackTrace(System.out); + } + + public enum Cert { + + CA_CERT("tlcp-intca.crt", "tlcp-intca.key"), + SERVER_SIGN_CERT("tlcp-server-sign.crt", "tlcp-server-sign.key"), + SERVER_ENC_CERT("tlcp-server-enc.crt", "tlcp-server-enc.key"), + CLIENT_SIGN_CERT("tlcp-client-sign.crt", "tlcp-client-sign.key"), + CLIENT_ENC_CERT("tlcp-client-enc.crt", "tlcp-client-enc.key"); + + final String keyAlgo = "EC"; + final String certStr; + final String privKeyStr; + + Cert(String certName, String privKeyName) { + try { + certStr = Utils.certStr(CERT_DIR.resolve(certName)); + privKeyStr = Utils.keyStr(CERT_DIR.resolve(privKeyName)); + } catch (IOException e) { + throw new RuntimeException("Cannot load cert or key", e); + } + } + } +} diff --git a/test/jdk/sm/tlcp-certs/ca.ext b/test/jdk/sm/tlcp-certs/ca.ext new file mode 100644 index 00000000000..b46fdf55cc5 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/ca.ext @@ -0,0 +1,4 @@ +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=critical,CA:TRUE +keyUsage=critical,digitalSignature,keyCertSign,cRLSign diff --git a/test/jdk/sm/tlcp-certs/ee-enc.ext b/test/jdk/sm/tlcp-certs/ee-enc.ext new file mode 100644 index 00000000000..8fd44d6658b --- /dev/null +++ b/test/jdk/sm/tlcp-certs/ee-enc.ext @@ -0,0 +1,4 @@ +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=critical,CA:FALSE +keyUsage=critical,keyEncipherment,dataEncipherment,keyAgreement diff --git a/test/jdk/sm/tlcp-certs/ee-sign.ext b/test/jdk/sm/tlcp-certs/ee-sign.ext new file mode 100644 index 00000000000..8f6ef3edea9 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/ee-sign.ext @@ -0,0 +1,4 @@ +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=critical,CA:FALSE +keyUsage=critical,digitalSignature diff --git a/test/jdk/sm/tlcp-certs/tlcp-ca.crt b/test/jdk/sm/tlcp-certs/tlcp-ca.crt new file mode 100644 index 00000000000..aeecb79f7c8 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-ca.crt @@ -0,0 +1,48 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 5a:e5:9b:89:d5:9b:d7:cf:f7:e8:a0:4c:69:0e:c9:dc:da:1a:13:6f + Signature Algorithm: SM2-with-SM3 + Issuer: CN = tlcp-ca + Validity + Not Before: Feb 18 14:47:59 2024 GMT + Not After : Feb 15 14:47:59 2034 GMT + Subject: CN = tlcp-ca + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:c6:b4:61:ab:c5:6c:2f:dc:1d:59:e5:cf:2c:4a: + eb:5b:7a:00:f2:78:09:6d:e2:29:13:c7:ed:b4:ae: + ef:8c:dd:ba:a1:90:2b:e5:e6:b3:47:a6:1f:55:30: + d6:dc:08:22:d1:e2:82:bd:32:a4:50:67:3a:82:e9: + 93:57:59:ae:e5 + ASN1 OID: SM2 + X509v3 extensions: + X509v3 Subject Key Identifier: + DF:37:41:A6:65:54:7E:59:CC:64:36:71:47:57:E6:39:6F:7D:75:20 + X509v3 Authority Key Identifier: + DirName:/CN=tlcp-ca + serial:5A:E5:9B:89:D5:9B:D7:CF:F7:E8:A0:4C:69:0E:C9:DC:DA:1A:13:6F + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Signature Algorithm: SM2-with-SM3 + Signature Value: + 30:44:02:20:08:84:a9:b3:ae:14:da:fd:8d:3a:6b:9b:ca:c9: + 18:67:8f:1d:34:93:cb:83:e2:40:67:33:ca:d8:da:de:10:b4: + 02:20:0a:b1:93:f4:06:6c:f9:e5:45:e1:4d:fa:d0:40:88:74: + d9:15:8d:c8:76:d3:0f:92:31:38:18:c6:c5:1c:57:22 +-----BEGIN CERTIFICATE----- +MIIBoDCCAUegAwIBAgIUWuWbidWb18/36KBMaQ7J3NoaE28wCgYIKoEcz1UBg3Uw +EjEQMA4GA1UEAwwHdGxjcC1jYTAeFw0yNDAyMTgxNDQ3NTlaFw0zNDAyMTUxNDQ3 +NTlaMBIxEDAOBgNVBAMMB3RsY3AtY2EwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNC +AATGtGGrxWwv3B1Z5c8sSutbegDyeAlt4ikTx+20ru+M3bqhkCvl5rNHph9VMNbc +CCLR4oK9MqRQZzqC6ZNXWa7lo3sweTAdBgNVHQ4EFgQU3zdBpmVUflnMZDZxR1fm +OW99dSAwNwYDVR0jBDAwLqEWpBQwEjEQMA4GA1UEAwwHdGxjcC1jYYIUWuWbidWb +18/36KBMaQ7J3NoaE28wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw +CgYIKoEcz1UBg3UDRwAwRAIgCISps64U2v2NOmubyskYZ48dNJPLg+JAZzPK2Nre +ELQCIAqxk/QGbPnlReFN+tBAiHTZFY3IdtMPkjE4GMbFHFci +-----END CERTIFICATE----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-ca.key b/test/jdk/sm/tlcp-certs/tlcp-ca.key new file mode 100644 index 00000000000..5dfcf3d9cd4 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-ca.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgBKmiXA1M7KBBnloq +bvNEcOnA1efe+RSfCAYy5i3u5GihRANCAATGtGGrxWwv3B1Z5c8sSutbegDyeAlt +4ikTx+20ru+M3bqhkCvl5rNHph9VMNbcCCLR4oK9MqRQZzqC6ZNXWa7l +-----END PRIVATE KEY----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-client-enc.crt b/test/jdk/sm/tlcp-certs/tlcp-client-enc.crt new file mode 100644 index 00000000000..19bb1e0e1ec --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-client-enc.crt @@ -0,0 +1,47 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 4b:c0:c7:c5:57:24:00:30:c4:e9:91:5c:42:c4:90:69:28:08:d0:3a + Signature Algorithm: SM2-with-SM3 + Issuer: CN = tlcp-intca + Validity + Not Before: Feb 18 14:48:00 2024 GMT + Not After : Feb 15 14:48:00 2034 GMT + Subject: CN = tlcp-client-enc + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:b3:69:25:bf:72:60:7b:44:fd:60:ae:df:c7:5b: + 23:8f:73:c3:4e:df:66:e1:ff:33:f6:4a:7f:15:64: + d9:be:9f:e0:b1:04:a3:73:f1:c4:7d:96:f4:4b:31: + d2:95:b9:76:81:78:90:ac:e4:71:bc:b6:64:86:91: + 5c:ee:76:f4:7f + ASN1 OID: SM2 + X509v3 extensions: + X509v3 Subject Key Identifier: + C4:45:48:97:37:B7:8F:23:87:BA:DA:D2:C3:7B:E9:91:F8:5B:8B:A4 + X509v3 Authority Key Identifier: + 34:4B:2B:54:8E:09:53:6B:6B:72:29:7D:B5:41:6E:DC:AF:34:69:E5 + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Key Encipherment, Data Encipherment, Key Agreement + Signature Algorithm: SM2-with-SM3 + Signature Value: + 30:45:02:21:00:f9:f9:fb:d7:41:ed:af:93:64:4f:31:17:a3: + c1:c9:ea:17:fe:bc:fa:41:83:ec:2d:05:c7:64:07:87:b1:42: + ed:02:20:64:f4:16:91:37:d1:71:59:e1:dc:cb:64:e5:6a:9d: + 27:23:60:d9:0a:b5:78:eb:58:39:cc:97:39:77:cc:07:48 +-----BEGIN CERTIFICATE----- +MIIBkTCCATegAwIBAgIUS8DHxVckADDE6ZFcQsSQaSgI0DowCgYIKoEcz1UBg3Uw +FTETMBEGA1UEAwwKdGxjcC1pbnRjYTAeFw0yNDAyMTgxNDQ4MDBaFw0zNDAyMTUx +NDQ4MDBaMBoxGDAWBgNVBAMMD3RsY3AtY2xpZW50LWVuYzBZMBMGByqGSM49AgEG +CCqBHM9VAYItA0IABLNpJb9yYHtE/WCu38dbI49zw07fZuH/M/ZKfxVk2b6f4LEE +o3PxxH2W9Esx0pW5doF4kKzkcby2ZIaRXO529H+jYDBeMB0GA1UdDgQWBBTERUiX +N7ePI4e62tLDe+mR+FuLpDAfBgNVHSMEGDAWgBQ0SytUjglTa2tyKX21QW7crzRp +5TAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIDODAKBggqgRzPVQGDdQNIADBF +AiEA+fn710Htr5NkTzEXo8HJ6hf+vPpBg+wtBcdkB4exQu0CIGT0FpE30XFZ4dzL +ZOVqnScjYNkKtXjrWDnMlzl3zAdI +-----END CERTIFICATE----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-client-enc.key b/test/jdk/sm/tlcp-certs/tlcp-client-enc.key new file mode 100644 index 00000000000..4ba60063947 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-client-enc.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgVeddNFyElrhTFjRX +VJqeHqMsb9F3DJqEziTR6WpynzyhRANCAASzaSW/cmB7RP1grt/HWyOPc8NO32bh +/zP2Sn8VZNm+n+CxBKNz8cR9lvRLMdKVuXaBeJCs5HG8tmSGkVzudvR/ +-----END PRIVATE KEY----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-client-sign.crt b/test/jdk/sm/tlcp-certs/tlcp-client-sign.crt new file mode 100644 index 00000000000..814972a8f3f --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-client-sign.crt @@ -0,0 +1,47 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 78:16:98:17:b9:f2:6a:56:50:bd:42:39:c5:09:5d:37:dc:0c:b9:70 + Signature Algorithm: SM2-with-SM3 + Issuer: CN = tlcp-intca + Validity + Not Before: Feb 18 14:48:00 2024 GMT + Not After : Feb 15 14:48:00 2034 GMT + Subject: CN = tlcp-client-sign + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:54:eb:96:73:3a:93:c5:c9:41:76:fc:e5:bf:88: + eb:8b:54:e2:64:4b:1d:16:34:4c:14:62:27:a2:a7: + c5:93:c3:0b:1f:36:d9:ad:61:8d:cc:e5:87:d4:e7: + 22:e5:74:b7:05:82:6e:cf:f1:0f:7f:35:cb:50:7e: + cb:ae:b8:5b:ff + ASN1 OID: SM2 + X509v3 extensions: + X509v3 Subject Key Identifier: + 39:13:B5:B5:DF:8A:D3:F7:50:A4:33:CE:9F:99:0B:9C:EC:CF:5A:BA + X509v3 Authority Key Identifier: + 34:4B:2B:54:8E:09:53:6B:6B:72:29:7D:B5:41:6E:DC:AF:34:69:E5 + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature + Signature Algorithm: SM2-with-SM3 + Signature Value: + 30:46:02:21:00:f3:f9:92:87:fa:00:53:40:ca:f4:61:5b:d9: + 6f:5b:69:35:fb:8b:93:74:ca:fe:20:bd:a1:46:c5:d7:f5:a4: + 74:02:21:00:91:2d:2f:45:b3:ca:57:99:92:55:b6:85:50:48: + 06:8b:c4:52:f8:13:f3:84:ac:eb:1b:82:82:bc:3d:c2:00:1c +-----BEGIN CERTIFICATE----- +MIIBkzCCATigAwIBAgIUeBaYF7nyalZQvUI5xQldN9wMuXAwCgYIKoEcz1UBg3Uw +FTETMBEGA1UEAwwKdGxjcC1pbnRjYTAeFw0yNDAyMTgxNDQ4MDBaFw0zNDAyMTUx +NDQ4MDBaMBsxGTAXBgNVBAMMEHRsY3AtY2xpZW50LXNpZ24wWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAARU65ZzOpPFyUF2/OW/iOuLVOJkSx0WNEwUYieip8WTwwsf +NtmtYY3M5YfU5yLldLcFgm7P8Q9/NctQfsuuuFv/o2AwXjAdBgNVHQ4EFgQUORO1 +td+K0/dQpDPOn5kLnOzPWrowHwYDVR0jBBgwFoAUNEsrVI4JU2trcil9tUFu3K80 +aeUwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwCgYIKoEcz1UBg3UDSQAw +RgIhAPP5kof6AFNAyvRhW9lvW2k1+4uTdMr+IL2hRsXX9aR0AiEAkS0vRbPKV5mS +VbaFUEgGi8RS+BPzhKzrG4KCvD3CABw= +-----END CERTIFICATE----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-client-sign.key b/test/jdk/sm/tlcp-certs/tlcp-client-sign.key new file mode 100644 index 00000000000..4a10987f5e3 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-client-sign.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg2w/SH1+bzMphNFm4 +uohTwzaAT/js9RoPQWmduGXuaKOhRANCAARU65ZzOpPFyUF2/OW/iOuLVOJkSx0W +NEwUYieip8WTwwsfNtmtYY3M5YfU5yLldLcFgm7P8Q9/NctQfsuuuFv/ +-----END PRIVATE KEY----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-gen-certs.sh b/test/jdk/sm/tlcp-certs/tlcp-gen-certs.sh new file mode 100644 index 00000000000..2a73a40999c --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-gen-certs.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2022, 2024, 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. +# + +echo "Generate X.509 version 3 extensions for CA" +cat > ca.ext << EOF +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=critical,CA:TRUE +keyUsage=critical,digitalSignature,keyCertSign,cRLSign +EOF + +echo "Generate X.509 version 3 extensions for sign EE" +cat > ee-sign.ext << EOF +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=critical,CA:FALSE +keyUsage=critical,digitalSignature +EOF + +echo "Generate X.509 version 3 extensions for enc EE" +cat > ee-enc.ext << EOF +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +basicConstraints=critical,CA:FALSE +keyUsage=critical,keyEncipherment,dataEncipherment,keyAgreement +EOF + +OPENSSL=tongsuo + +##### CA +$OPENSSL genpkey -algorithm ec -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out tlcp-ca.key +$OPENSSL req -new -key tlcp-ca.key -subj "/CN=tlcp-ca" -sm3 -out tlcp-ca.csr +$OPENSSL x509 -extfile ca.ext -req -CAcreateserial -days 3650 -in tlcp-ca.csr -sm3 \ + -signkey tlcp-ca.key -out tlcp-ca.crt.tmp +$OPENSSL x509 -text -in tlcp-ca.crt.tmp > tlcp-ca.crt + +##### Intermediate CA +$OPENSSL genpkey -algorithm ec -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out tlcp-intca.key +$OPENSSL req -new -key tlcp-intca.key -subj "/CN=tlcp-intca" -sm3 -out tlcp-intca.csr +$OPENSSL x509 -extfile ca.ext -req -CAcreateserial -days 3650 -in tlcp-intca.csr -sm3 \ + -CA tlcp-ca.crt -CAkey tlcp-ca.key -out tlcp-intca.crt.tmp +$OPENSSL x509 -text -in tlcp-intca.crt.tmp > tlcp-intca.crt + +##### Sign EE (Server) +$OPENSSL genpkey -algorithm ec -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out tlcp-server-sign.key +$OPENSSL req -new -key tlcp-server-sign.key -subj "/CN=tlcp-server-sign" -sm3 -out tlcp-server-sign.csr +$OPENSSL x509 -extfile ee-sign.ext -req -CAcreateserial -days 3650 -in tlcp-server-sign.csr -sm3 \ + -CA tlcp-intca.crt -CAkey tlcp-intca.key -out tlcp-server-sign.crt.tmp +$OPENSSL x509 -text -in tlcp-server-sign.crt.tmp > tlcp-server-sign.crt + +##### Sign EE (Client) +$OPENSSL genpkey -algorithm ec -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out tlcp-client-sign.key +$OPENSSL req -new -key tlcp-client-sign.key -subj "/CN=tlcp-client-sign" -sm3 -out tlcp-client-sign.csr +$OPENSSL x509 -extfile ee-sign.ext -req -CAcreateserial -days 3650 -in tlcp-client-sign.csr -sm3 \ + -CA tlcp-intca.crt -CAkey tlcp-intca.key -out tlcp-client-sign.crt.tmp +$OPENSSL x509 -text -in tlcp-client-sign.crt.tmp > tlcp-client-sign.crt + +##### Enc EE (Server) +$OPENSSL genpkey -algorithm ec -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out tlcp-server-enc.key +$OPENSSL req -new -key tlcp-server-enc.key -subj "/CN=tlcp-server-enc" -sm3 -out tlcp-server-enc.csr +$OPENSSL x509 -extfile ee-enc.ext -req -CAcreateserial -days 3650 -in tlcp-server-enc.csr -sm3 \ + -CA tlcp-intca.crt -CAkey tlcp-intca.key -out tlcp-server-enc.crt.tmp +$OPENSSL x509 -text -in tlcp-server-enc.crt.tmp > tlcp-server-enc.crt + +##### Enc EE (Client) +$OPENSSL genpkey -algorithm ec -pkeyopt ec_paramgen_curve:SM2 -pkeyopt ec_param_enc:named_curve -out tlcp-client-enc.key +$OPENSSL req -new -key tlcp-client-enc.key -subj "/CN=tlcp-client-enc" -sm3 -out tlcp-client-enc.csr +$OPENSSL x509 -extfile ee-enc.ext -req -CAcreateserial -days 3650 -in tlcp-client-enc.csr -sm3 \ + -CA tlcp-intca.crt -CAkey tlcp-intca.key -out tlcp-client-enc.crt.tmp +$OPENSSL x509 -text -in tlcp-client-enc.crt.tmp > tlcp-client-enc.crt + +rm *.tmp *.csr diff --git a/test/jdk/sm/tlcp-certs/tlcp-intca.crt b/test/jdk/sm/tlcp-certs/tlcp-intca.crt new file mode 100644 index 00000000000..a71b9ad4ae6 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-intca.crt @@ -0,0 +1,47 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 48:0f:8d:28:d6:08:df:ad:d5:69:e2:4d:57:c2:4d:5b:f0:39:a6:26 + Signature Algorithm: SM2-with-SM3 + Issuer: CN = tlcp-ca + Validity + Not Before: Feb 18 14:48:00 2024 GMT + Not After : Feb 15 14:48:00 2034 GMT + Subject: CN = tlcp-intca + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:7b:7e:7f:d8:79:59:5f:94:6d:ca:de:50:5c:e9: + e8:06:44:51:97:65:fb:74:88:54:23:47:b4:c5:fb: + 19:3c:44:7e:11:fb:ca:1e:cd:e2:35:12:29:ad:ec: + 6b:09:4e:4a:92:1f:a6:31:42:2a:79:0a:ba:2b:87: + da:40:42:d4:ed + ASN1 OID: SM2 + X509v3 extensions: + X509v3 Subject Key Identifier: + 34:4B:2B:54:8E:09:53:6B:6B:72:29:7D:B5:41:6E:DC:AF:34:69:E5 + X509v3 Authority Key Identifier: + DF:37:41:A6:65:54:7E:59:CC:64:36:71:47:57:E6:39:6F:7D:75:20 + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Signature Algorithm: SM2-with-SM3 + Signature Value: + 30:45:02:21:00:81:22:ce:eb:a3:26:34:66:29:d7:67:55:b6: + 20:78:3a:77:73:48:34:d5:2a:ad:e7:f3:d8:8f:3d:e7:57:32: + 9a:02:20:17:76:48:b6:41:2c:fe:0a:f7:7a:7f:cf:70:a4:32: + 7d:04:bc:a5:83:f3:27:df:11:85:57:fc:86:e1:46:bf:01 +-----BEGIN CERTIFICATE----- +MIIBjDCCATKgAwIBAgIUSA+NKNYI363VaeJNV8JNW/A5piYwCgYIKoEcz1UBg3Uw +EjEQMA4GA1UEAwwHdGxjcC1jYTAeFw0yNDAyMTgxNDQ4MDBaFw0zNDAyMTUxNDQ4 +MDBaMBUxEzARBgNVBAMMCnRsY3AtaW50Y2EwWTATBgcqhkjOPQIBBggqgRzPVQGC +LQNCAAR7fn/YeVlflG3K3lBc6egGRFGXZft0iFQjR7TF+xk8RH4R+8oezeI1Eimt +7GsJTkqSH6YxQip5Crorh9pAQtTto2MwYTAdBgNVHQ4EFgQUNEsrVI4JU2trcil9 +tUFu3K80aeUwHwYDVR0jBBgwFoAU3zdBpmVUflnMZDZxR1fmOW99dSAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwCgYIKoEcz1UBg3UDSAAwRQIhAIEi +zuujJjRmKddnVbYgeDp3c0g01Sqt5/PYjz3nVzKaAiAXdki2QSz+Cvd6f89wpDJ9 +BLylg/Mn3xGFV/yG4Ua/AQ== +-----END CERTIFICATE----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-intca.key b/test/jdk/sm/tlcp-certs/tlcp-intca.key new file mode 100644 index 00000000000..30f2fcd1ed4 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-intca.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgY223TUABKTGzV8B0 +XVg3QFtVXhKsWGnFLfiUTeE9mKChRANCAAR7fn/YeVlflG3K3lBc6egGRFGXZft0 +iFQjR7TF+xk8RH4R+8oezeI1Eimt7GsJTkqSH6YxQip5Crorh9pAQtTt +-----END PRIVATE KEY----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-server-enc.crt b/test/jdk/sm/tlcp-certs/tlcp-server-enc.crt new file mode 100644 index 00000000000..ac3f9a5e37c --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-server-enc.crt @@ -0,0 +1,47 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 5c:24:ac:c2:0a:d1:e1:64:a8:03:71:9e:06:20:98:96:4a:b1:87:95 + Signature Algorithm: SM2-with-SM3 + Issuer: CN = tlcp-intca + Validity + Not Before: Feb 18 14:48:00 2024 GMT + Not After : Feb 15 14:48:00 2034 GMT + Subject: CN = tlcp-server-enc + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:47:cb:b5:1c:fc:f4:48:4e:53:ec:91:d0:e3:c5: + b3:29:98:2c:bd:75:8b:88:26:58:4d:37:af:8a:98: + 69:c1:dd:5e:0d:ff:28:7d:99:db:be:c7:52:a3:d1: + 85:cf:44:03:fa:8b:90:1f:64:63:ea:a8:1d:b8:a6: + bc:a4:29:7f:e6 + ASN1 OID: SM2 + X509v3 extensions: + X509v3 Subject Key Identifier: + 7E:42:B8:A1:97:41:1A:A3:B0:70:22:88:1F:2C:FB:36:77:21:7C:7B + X509v3 Authority Key Identifier: + 34:4B:2B:54:8E:09:53:6B:6B:72:29:7D:B5:41:6E:DC:AF:34:69:E5 + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Key Encipherment, Data Encipherment, Key Agreement + Signature Algorithm: SM2-with-SM3 + Signature Value: + 30:46:02:21:00:ac:f9:45:6b:a3:06:17:cd:7e:9d:a9:80:ee: + 83:08:3f:69:c1:c0:b8:f9:27:b7:e6:39:88:f2:06:7c:e2:67: + 30:02:21:00:f7:cf:75:1c:8e:8f:94:93:0c:6b:21:4e:ed:86: + c2:21:b5:26:28:60:41:93:8a:53:3f:75:f3:26:79:2b:86:35 +-----BEGIN CERTIFICATE----- +MIIBkjCCATegAwIBAgIUXCSswgrR4WSoA3GeBiCYlkqxh5UwCgYIKoEcz1UBg3Uw +FTETMBEGA1UEAwwKdGxjcC1pbnRjYTAeFw0yNDAyMTgxNDQ4MDBaFw0zNDAyMTUx +NDQ4MDBaMBoxGDAWBgNVBAMMD3RsY3Atc2VydmVyLWVuYzBZMBMGByqGSM49AgEG +CCqBHM9VAYItA0IABEfLtRz89EhOU+yR0OPFsymYLL11i4gmWE03r4qYacHdXg3/ +KH2Z277HUqPRhc9EA/qLkB9kY+qoHbimvKQpf+ajYDBeMB0GA1UdDgQWBBR+Qrih +l0Eao7BwIogfLPs2dyF8ezAfBgNVHSMEGDAWgBQ0SytUjglTa2tyKX21QW7crzRp +5TAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIDODAKBggqgRzPVQGDdQNJADBG +AiEArPlFa6MGF81+namA7oMIP2nBwLj5J7fmOYjyBnziZzACIQD3z3Ucjo+Ukwxr +IU7thsIhtSYoYEGTilM/dfMmeSuGNQ== +-----END CERTIFICATE----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-server-enc.key b/test/jdk/sm/tlcp-certs/tlcp-server-enc.key new file mode 100644 index 00000000000..be4e292ed90 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-server-enc.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg0GXgsa0fNoq/tuxa +BMGV4/2UMhdewxi124M+jW79BI2hRANCAARHy7Uc/PRITlPskdDjxbMpmCy9dYuI +JlhNN6+KmGnB3V4N/yh9mdu+x1Kj0YXPRAP6i5AfZGPqqB24prykKX/m +-----END PRIVATE KEY----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-server-sign.crt b/test/jdk/sm/tlcp-certs/tlcp-server-sign.crt new file mode 100644 index 00000000000..acf5097015a --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-server-sign.crt @@ -0,0 +1,47 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 77:de:85:77:a9:80:06:40:e7:99:1a:49:85:84:ee:d0:92:31:dd:55 + Signature Algorithm: SM2-with-SM3 + Issuer: CN = tlcp-intca + Validity + Not Before: Feb 18 14:48:00 2024 GMT + Not After : Feb 15 14:48:00 2034 GMT + Subject: CN = tlcp-server-sign + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:23:84:6d:67:9c:d0:f8:f7:ca:ab:dc:50:3a:bb: + c1:d4:9c:60:2c:82:f3:f2:84:36:7c:dc:1a:e8:a9: + 6e:77:d8:6a:f2:c8:fe:3a:92:4d:ae:b0:ac:a8:e7: + 49:f3:9e:39:a0:fe:fc:6d:18:cc:f1:6e:7c:54:2c: + f6:92:1b:93:99 + ASN1 OID: SM2 + X509v3 extensions: + X509v3 Subject Key Identifier: + EA:18:7E:17:B7:61:95:68:42:0C:45:06:A5:40:5D:DB:58:87:2C:92 + X509v3 Authority Key Identifier: + 34:4B:2B:54:8E:09:53:6B:6B:72:29:7D:B5:41:6E:DC:AF:34:69:E5 + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature + Signature Algorithm: SM2-with-SM3 + Signature Value: + 30:45:02:21:00:9f:ff:32:de:e2:2a:1d:87:73:5a:bb:89:81: + 89:41:c3:59:47:f3:b0:65:c9:d1:ed:fe:5a:ba:45:e7:c4:b6: + 59:02:20:06:e7:8b:8a:ac:51:d6:8e:e0:f0:ff:4a:1f:b7:33: + fa:4d:b3:53:de:e9:4e:c1:e0:89:20:dd:b8:80:bb:7d:32 +-----BEGIN CERTIFICATE----- +MIIBkjCCATigAwIBAgIUd96Fd6mABkDnmRpJhYTu0JIx3VUwCgYIKoEcz1UBg3Uw +FTETMBEGA1UEAwwKdGxjcC1pbnRjYTAeFw0yNDAyMTgxNDQ4MDBaFw0zNDAyMTUx +NDQ4MDBaMBsxGTAXBgNVBAMMEHRsY3Atc2VydmVyLXNpZ24wWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAAQjhG1nnND498qr3FA6u8HUnGAsgvPyhDZ83BroqW532Gry +yP46kk2usKyo50nznjmg/vxtGMzxbnxULPaSG5OZo2AwXjAdBgNVHQ4EFgQU6hh+ +F7dhlWhCDEUGpUBd21iHLJIwHwYDVR0jBBgwFoAUNEsrVI4JU2trcil9tUFu3K80 +aeUwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwCgYIKoEcz1UBg3UDSAAw +RQIhAJ//Mt7iKh2Hc1q7iYGJQcNZR/OwZcnR7f5aukXnxLZZAiAG54uKrFHWjuDw +/0oftzP6TbNT3ulOweCJIN24gLt9Mg== +-----END CERTIFICATE----- diff --git a/test/jdk/sm/tlcp-certs/tlcp-server-sign.key b/test/jdk/sm/tlcp-certs/tlcp-server-sign.key new file mode 100644 index 00000000000..eb92b96a5b8 --- /dev/null +++ b/test/jdk/sm/tlcp-certs/tlcp-server-sign.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgM3F7pwfi/NkOLxVh +QUw/R8uPVzuwGrIn25jOZjm1646hRANCAAQjhG1nnND498qr3FA6u8HUnGAsgvPy +hDZ83BroqW532GryyP46kk2usKyo50nznjmg/vxtGMzxbnxULPaSG5OZ +-----END PRIVATE KEY----- diff --git a/test/jdk/sun/java2d/marlin/ScaleTest.java b/test/jdk/sun/java2d/marlin/ScaleTest.java index bfd56422ba4..c9778a6477a 100644 --- a/test/jdk/sun/java2d/marlin/ScaleTest.java +++ b/test/jdk/sun/java2d/marlin/ScaleTest.java @@ -21,13 +21,17 @@ * questions. */ +/* @test + * @summary Circle is rendered in C shape + * @bug 6829659 8311666 + */ + import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; - public class ScaleTest { public static void main(String[] args) throws Exception { BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); diff --git a/test/jdk/sun/java2d/marlin/StrokeShapeTest.java b/test/jdk/sun/java2d/marlin/StrokeShapeTest.java index 41413bff3e4..bc23f29e15d 100644 --- a/test/jdk/sun/java2d/marlin/StrokeShapeTest.java +++ b/test/jdk/sun/java2d/marlin/StrokeShapeTest.java @@ -20,6 +20,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +/* @test + * @summary StrokeShapeTest: createStrokedShape() behaves differently + * @bug 6829678 8311666 + */ + import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; diff --git a/test/jdk/sun/java2d/marlin/ThinLineTest.java b/test/jdk/sun/java2d/marlin/ThinLineTest.java index a1a033c0bd4..7450f4e421d 100644 --- a/test/jdk/sun/java2d/marlin/ThinLineTest.java +++ b/test/jdk/sun/java2d/marlin/ThinLineTest.java @@ -20,6 +20,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + +/* @test + * @summary ThinLineTest: A line < 1 pixel disappears. + * @bug 6829673 8311666 + */ + import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java index 45ebd6930a3..59f3f8e182a 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, 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 @@ -195,7 +195,7 @@ private int doTest(String... args) throws Exception { System.out.println("test output:"); System.out.println(output.getOutput()); - if (!output.getOutput().contains("Exception thrown by the agent : " + + if (!output.getOutput().contains("Exception thrown by the agent: " + "java.rmi.server.ExportException: Port already in use")) { return output.getExitValue(); } diff --git a/test/jdk/sun/management/jmxremote/bootstrap/exelauncher.c b/test/jdk/sun/management/jmxremote/bootstrap/exelauncher.c index 46f30d9e157..dc4971fed02 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/exelauncher.c +++ b/test/jdk/sun/management/jmxremote/bootstrap/exelauncher.c @@ -61,8 +61,9 @@ int main(int argc, char**argv) { fprintf(stderr, "Usage: %s jvm-path classpath class\n", argv[0]); return -1; } - cp_prop = (char*)malloc(strlen(CP_PROP)+strlen(argv[2]) +1); - sprintf(cp_prop, "%s%s", CP_PROP, argv[2]); + size_t propLen = strlen(CP_PROP) + strlen(argv[2]) + 1; + cp_prop = (char*)malloc(propLen); + snprintf(cp_prop, propLen, "%s%s", CP_PROP, argv[2]); options[0].optionString = cp_prop; vm_args.version = 0x00010002; diff --git a/test/jdk/sun/management/windows/exerevokeall.c b/test/jdk/sun/management/windows/exerevokeall.c index 16c932f9b02..ac6f6183dbb 100644 --- a/test/jdk/sun/management/windows/exerevokeall.c +++ b/test/jdk/sun/management/windows/exerevokeall.c @@ -99,12 +99,13 @@ static char *getTextualSid(SID* sid) { } // S-SID_REVISION - sprintf(name, "S-%lu-", SID_REVISION ); + snprintf(name, len, "S-%lu-", SID_REVISION ); // Identifier authority if ((sia->Value[0] != 0) || (sia->Value[1] != 0)) { - sprintf(name + strlen(name), "0x%02hx%02hx%02hx%02hx%02hx%02hx", + snprintf(name + strlen(name), len - strlen(name), + "0x%02hx%02hx%02hx%02hx%02hx%02hx", (USHORT)sia->Value[0], (USHORT)sia->Value[1], (USHORT)sia->Value[2], @@ -114,7 +115,7 @@ static char *getTextualSid(SID* sid) { } else { - sprintf(name + strlen(name), "%lu", + snprintf(name + strlen(name), len - strlen(name), "%lu", (ULONG)(sia->Value[5] ) + (ULONG)(sia->Value[4] << 8) + (ULONG)(sia->Value[3] << 16) + @@ -123,7 +124,7 @@ static char *getTextualSid(SID* sid) { // finally, the sub-authorities for (i=0 ; i -1) hostAddr = "[" + hostAddr + "]"; - String baseURLStr = "http://" + hostAddr + ":" + server.getLocalPort() + "/"; + String baseURLStr = "http://" + hostAddr + ":" + server.getAddress().getPort() + "/"; URL bigDataURL = new URL (baseURLStr + "firstCall"); URL smallDataURL = new URL (baseURLStr + "secondCall"); @@ -98,7 +108,7 @@ public static void clientHttpCalls() { uc = (HttpURLConnection)smallDataURL.openConnection(Proxy.NO_PROXY); uc.getResponseCode(); - if (SimpleHttpTransaction.failed) + if (SimpleHttpTransactionHandler.failed) throw new RuntimeException("Failed: Initial Keep Alive Connection is not being reused"); // Part 2 @@ -137,7 +147,7 @@ public static void clientHttpCalls() { } catch (IOException e) { e.printStackTrace(); } finally { - server.terminate(); + server.stop(1); } if (!uncaught.isEmpty()) { throw new RuntimeException("Unhandled exception:", uncaught.get(0)); @@ -145,9 +155,9 @@ public static void clientHttpCalls() { } } -class SimpleHttpTransaction implements HttpCallback +class SimpleHttpTransactionHandler implements HttpHandler { - static boolean failed = false; + static volatile boolean failed = false; // Need to have enough data here that is too large for the socket buffer to hold. // Also http.KeepAlive.remainingData must be greater than this value, default is 256K. @@ -155,48 +165,46 @@ class SimpleHttpTransaction implements HttpCallback int port1; - public void request(HttpTransaction trans) { + public void handle(HttpExchange trans) { try { String path = trans.getRequestURI().getPath(); if (path.equals("/firstCall")) { - port1 = trans.channel().socket().getPort(); + port1 = trans.getRemoteAddress().getPort(); System.out.println("First connection on client port = " + port1); byte[] responseBody = new byte[RESPONSE_DATA_LENGTH]; for (int i=0; i total_len) { - byte[] total1 = new byte [total_len * 2]; - System.arraycopy (total, 0, total1, 0, len); - total = total1; - total_len = total_len * 2; - } - System.arraycopy (buf, 0, total, len, c); - len += c; - } - setResponseEntityBody (total, len); - } - - /* chunked */ - - /** - * Set the entity response body with the given array of strings - * The content encoding is set to "chunked" and each array element - * is sent as one chunk. - * @param body the array of string chunks to send in the response - */ - public void setResponseEntityBody (String[] body) { - StringBuffer buf = new StringBuffer (); - int len = 0; - for (int i=0; i - * It must be instantiated with a {@link HttpCallback} object to which - * requests are given and must be handled. - *

        - * Simple synchronization between the client(s) and server can be done - * using the {@link #waitForCondition(String)}, {@link #setCondition(String)} and - * {@link #rendezvous(String,int)} methods. - * - * NOTE NOTE NOTE NOTE NOTE NOTE NOTE - * - * If changes are made here, please sure they are propagated to - * the HTTPS equivalent in the JSSE regression test suite. - * - * NOTE NOTE NOTE NOTE NOTE NOTE NOTE - */ - -public class TestHttpServer { - - ServerSocketChannel schan; - int threads; - int cperthread; - HttpCallback cb; - Server[] servers; - - /** - * Create a TestHttpServer instance with the specified callback object - * for handling requests. One thread is created to handle requests, - * and up to ten TCP connections will be handled simultaneously. - * @param cb the callback object which is invoked to handle each - * incoming request - */ - - public TestHttpServer (HttpCallback cb) throws IOException { - this (cb, 1, 10, 0); - } - - /** - * Create a TestHttpServer instance with the specified callback object - * for handling requests. One thread is created to handle requests, - * and up to ten TCP connections will be handled simultaneously. - * @param cb the callback object which is invoked to handle each - * incoming request - * @param address the address to bind the server to. Null - * means bind to the wildcard address. - * @param port the port number to bind the server to. Zero - * means choose any free port. - */ - - public TestHttpServer (HttpCallback cb, InetAddress address, int port) throws IOException { - this (cb, 1, 10, address, 0); - } - - /** - * Create a TestHttpServer instance with the specified number of - * threads and maximum number of connections per thread. This functions - * the same as the 4 arg constructor, where the port argument is set to zero. - * @param cb the callback object which is invoked to handle each - * incoming request - * @param threads the number of threads to create to handle requests - * in parallel - * @param cperthread the number of simultaneous TCP connections to - * handle per thread - */ - - public TestHttpServer (HttpCallback cb, int threads, int cperthread) - throws IOException { - this (cb, threads, cperthread, 0); - } - - /** - * Create a TestHttpServer instance with the specified number - * of threads and maximum number of connections per thread and running on - * the specified port. The specified number of threads are created to - * handle incoming requests, and each thread is allowed - * to handle a number of simultaneous TCP connections. - * @param cb the callback object which is invoked to handle - * each incoming request - * @param threads the number of threads to create to handle - * requests in parallel - * @param cperthread the number of simultaneous TCP connections - * to handle per thread - * @param port the port number to bind the server to. Zero - * means choose any free port. - */ - - public TestHttpServer (HttpCallback cb, int threads, int cperthread, int port) - throws IOException { - this(cb, threads, cperthread, null, port); - } - - /** - * Create a TestHttpServer instance with the specified number - * of threads and maximum number of connections per thread and running on - * the specified port. The specified number of threads are created to - * handle incoming requests, and each thread is allowed - * to handle a number of simultaneous TCP connections. - * @param cb the callback object which is invoked to handle - * each incoming request - * @param threads the number of threads to create to handle - * requests in parallel - * @param cperthread the number of simultaneous TCP connections - * to handle per thread - * @param address the address to bind the server to. Null - * means bind to the wildcard address. - * @param port the port number to bind the server to. Zero - * means choose any free port. - */ - - public TestHttpServer (HttpCallback cb, int threads, int cperthread, - InetAddress address, int port) - throws IOException { - schan = ServerSocketChannel.open (); - InetSocketAddress addr = new InetSocketAddress (address, port); - schan.socket().bind (addr); - this.threads = threads; - this.cb = cb; - this.cperthread = cperthread; - servers = new Server [threads]; - for (int i=0; i -1) hostaddr = "[" + hostaddr + "]"; - return hostaddr + ":" + getLocalPort(); - } - - static class Server extends Thread { - - ServerSocketChannel schan; - Selector selector; - SelectionKey listenerKey; - SelectionKey key; /* the current key being processed */ - HttpCallback cb; - ByteBuffer consumeBuffer; - int maxconn; - int nconn; - ClosedChannelList clist; - volatile boolean shutdown; - - Server (HttpCallback cb, ServerSocketChannel schan, int maxconn) { - this.schan = schan; - this.maxconn = maxconn; - this.cb = cb; - nconn = 0; - consumeBuffer = ByteBuffer.allocate (512); - clist = new ClosedChannelList (); - try { - selector = Selector.open (); - schan.configureBlocking (false); - listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT); - } catch (IOException e) { - System.err.println ("Server could not start: " + e); - throw new RuntimeException("Server could not start: " + e, e); - } - } - - /* Stop the thread as soon as possible */ - public void terminate () { - shutdown = true; - } - - public void run () { - try { - while (true) { - selector.select(1000); - Set selected = selector.selectedKeys(); - Iterator iter = selected.iterator(); - while (iter.hasNext()) { - key = iter.next(); - if (key.equals (listenerKey)) { - SocketChannel sock = schan.accept (); - if (sock == null) { - /* false notification */ - iter.remove(); - continue; - } - sock.configureBlocking (false); - sock.register (selector, SelectionKey.OP_READ); - nconn ++; - System.out.println("SERVER: new connection. chan[" + sock + "]"); - if (nconn == maxconn) { - /* deregister */ - listenerKey.cancel (); - listenerKey = null; - } - } else { - if (key.isReadable()) { - boolean closed; - SocketChannel chan = (SocketChannel) key.channel(); - System.out.println("SERVER: connection readable. chan[" + chan + "]"); - if (key.attachment() != null) { - System.out.println("Server: consume"); - closed = consume (chan); - } else { - closed = read (chan, key); - } - if (closed) { - chan.close (); - key.cancel (); - if (nconn == maxconn) { - listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT); - } - nconn --; - } - } - } - iter.remove(); - } - clist.check(); - if (shutdown) { - System.out.println("Force to Shutdown"); - SelectionKey sKey = schan.keyFor(selector); - if (sKey != null) { - sKey.cancel(); - } - - clist.terminate (); - selector.close(); - schan.socket().close(); - schan.close(); - return; - } - } - } catch (IOException e) { - System.out.println ("Server exception: " + e); - // TODO finish - } - } - - /* read all the data off the channel without looking at it - * return true if connection closed - */ - boolean consume (SocketChannel chan) { - try { - consumeBuffer.clear (); - int c = chan.read (consumeBuffer); - if (c == -1) - return true; - } catch (IOException e) { - return true; - } - return false; - } - - /* return true if the connection is closed, false otherwise */ - - private boolean read (SocketChannel chan, SelectionKey key) { - HttpTransaction msg; - boolean res; - try { - InputStream is = new BufferedInputStream (new NioInputStream (chan)); - String requestline = readLine (is); - MessageHeader mhead = new MessageHeader (is); - String clen = mhead.findValue ("Content-Length"); - String trferenc = mhead.findValue ("Transfer-Encoding"); - String data = null; - if (trferenc != null && trferenc.equals ("chunked")) - data = new String (readChunkedData (is)); - else if (clen != null) - data = new String (readNormalData (is, Integer.parseInt (clen))); - String[] req = requestline.split (" "); - if (req.length < 2) { - /* invalid request line */ - return false; - } - String cmd = req[0]; - URI uri = null; - try { - uri = new URI (req[1]); - msg = new HttpTransaction (this, cmd, uri, mhead, data, null, key); - cb.request (msg); - } catch (URISyntaxException e) { - System.err.println ("Invalid URI: " + e); - msg = new HttpTransaction (this, cmd, null, null, null, null, key); - msg.sendResponse (501, "Whatever"); - } - res = false; - } catch (IOException e) { - res = true; - } - return res; - } - - byte[] readNormalData (InputStream is, int len) throws IOException { - byte [] buf = new byte [len]; - int c, off=0, remain=len; - while (remain > 0 && ((c=is.read (buf, off, remain))>0)) { - remain -= c; - off += c; - } - return buf; - } - - private void readCRLF(InputStream is) throws IOException { - int cr = is.read(); - int lf = is.read(); - - if (((cr & 0xff) != 0x0d) || - ((lf & 0xff) != 0x0a)) { - throw new IOException( - "Expected : got '" + cr + "/" + lf + "'"); - } - } - - byte[] readChunkedData (InputStream is) throws IOException { - LinkedList l = new LinkedList (); - int total = 0; - for (int len=readChunkLen(is); len!=0; len=readChunkLen(is)) { - l.add (readNormalData(is, len)); - total += len; - readCRLF(is); // CRLF at end of chunk - } - readCRLF(is); // CRLF at end of Chunked Stream. - byte[] buf = new byte [total]; - Iterator i = l.iterator(); - int x = 0; - while (i.hasNext()) { - byte[] b = (byte[])i.next(); - System.arraycopy (b, 0, buf, x, b.length); - x += b.length; - } - return buf; - } - - private int readChunkLen (InputStream is) throws IOException { - int c, len=0; - boolean done=false, readCR=false; - while (!done) { - c = is.read (); - if (c == '\n' && readCR) { - done = true; - } else { - if (c == '\r' && !readCR) { - readCR = true; - } else { - int x=0; - if (c >= 'a' && c <= 'f') { - x = c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - x = c - 'A' + 10; - } else if (c >= '0' && c <= '9') { - x = c - '0'; - } - len = len * 16 + x; - } - } - } - return len; - } - - private String readLine (InputStream is) throws IOException { - boolean done=false, readCR=false; - byte[] b = new byte [512]; - int c, l = 0; - - while (!done) { - c = is.read (); - if (c == '\n' && readCR) { - done = true; - } else { - if (c == '\r' && !readCR) { - readCR = true; - } else { - b[l++] = (byte)c; - } - } - } - return new String (b); - } - - /** close the channel associated with the current key by: - * 1. shutdownOutput (send a FIN) - * 2. mark the key so that incoming data is to be consumed and discarded - * 3. After a period, close the socket - */ - - synchronized void orderlyCloseChannel (SelectionKey key) throws IOException { - SocketChannel ch = (SocketChannel)key.channel (); - System.out.println("SERVER: orderlyCloseChannel chan[" + ch + "]"); - ch.socket().shutdownOutput(); - key.attach (this); - clist.add (key); - } - - synchronized void abortiveCloseChannel (SelectionKey key) throws IOException { - SocketChannel ch = (SocketChannel)key.channel (); - System.out.println("SERVER: abortiveCloseChannel chan[" + ch + "]"); - - Socket s = ch.socket (); - s.setSoLinger (true, 0); - ch.close(); - } - } - - - /** - * Implements blocking reading semantics on top of a non-blocking channel - */ - - static class NioInputStream extends InputStream { - SocketChannel channel; - Selector selector; - ByteBuffer chanbuf; - SelectionKey key; - int available; - byte[] one; - boolean closed; - ByteBuffer markBuf; /* reads may be satisifed from this buffer */ - boolean marked; - boolean reset; - int readlimit; - - public NioInputStream (SocketChannel chan) throws IOException { - this.channel = chan; - selector = Selector.open(); - chanbuf = ByteBuffer.allocate (1024); - key = chan.register (selector, SelectionKey.OP_READ); - available = 0; - one = new byte[1]; - closed = marked = reset = false; - } - - public synchronized int read (byte[] b) throws IOException { - return read (b, 0, b.length); - } - - public synchronized int read () throws IOException { - return read (one, 0, 1); - } - - public synchronized int read (byte[] b, int off, int srclen) throws IOException { - - int canreturn, willreturn; - - if (closed) - return -1; - - if (reset) { /* satisfy from markBuf */ - canreturn = markBuf.remaining (); - willreturn = canreturn>srclen ? srclen : canreturn; - markBuf.get(b, off, willreturn); - if (canreturn == willreturn) { - reset = false; - } - } else { /* satisfy from channel */ - canreturn = available(); - if (canreturn == 0) { - block (); - canreturn = available(); - } - willreturn = canreturn>srclen ? srclen : canreturn; - chanbuf.get(b, off, willreturn); - available -= willreturn; - - if (marked) { /* copy into markBuf */ - try { - markBuf.put (b, off, willreturn); - } catch (BufferOverflowException e) { - marked = false; - } - } - } - return willreturn; - } - - public synchronized int available () throws IOException { - if (closed) - throw new IOException ("Stream is closed"); - - if (reset) - return markBuf.remaining(); - - if (available > 0) - return available; - - chanbuf.clear (); - available = channel.read (chanbuf); - if (available > 0) - chanbuf.flip(); - else if (available == -1) - throw new IOException ("Stream is closed"); - return available; - } - - /** - * block() only called when available==0 and buf is empty - */ - private synchronized void block () throws IOException { - //assert available == 0; - int n = selector.select (); - //assert n == 1; - selector.selectedKeys().clear(); - available (); - } - - public void close () throws IOException { - if (closed) - return; - channel.close (); - closed = true; - } - - public synchronized void mark (int readlimit) { - if (closed) - return; - this.readlimit = readlimit; - markBuf = ByteBuffer.allocate (readlimit); - marked = true; - reset = false; - } - - public synchronized void reset () throws IOException { - if (closed ) - return; - if (!marked) - throw new IOException ("Stream not marked"); - marked = false; - reset = true; - markBuf.flip (); - } - } - - static class NioOutputStream extends OutputStream { - SocketChannel channel; - ByteBuffer buf; - SelectionKey key; - Selector selector; - boolean closed; - byte[] one; - - public NioOutputStream (SocketChannel channel) throws IOException { - this.channel = channel; - selector = Selector.open (); - key = channel.register (selector, SelectionKey.OP_WRITE); - closed = false; - one = new byte [1]; - } - - public synchronized void write (int b) throws IOException { - one[0] = (byte)b; - write (one, 0, 1); - } - - public synchronized void write (byte[] b) throws IOException { - write (b, 0, b.length); - } - - public synchronized void write (byte[] b, int off, int len) throws IOException { - if (closed) - throw new IOException ("stream is closed"); - - buf = ByteBuffer.allocate (len); - buf.put (b, off, len); - buf.flip (); - int n; - while ((n = channel.write (buf)) < len) { - len -= n; - if (len == 0) - return; - selector.select (); - selector.selectedKeys().clear (); - } - } - - public void close () throws IOException { - if (closed) - return; - channel.close (); - closed = true; - } - } - - /** - * Utilities for synchronization. A condition is - * identified by a string name, and is initialized - * upon first use (ie. setCondition() or waitForCondition()). Threads - * are blocked until some thread calls (or has called) setCondition() for the same - * condition. - *

        - * A rendezvous built on a condition is also provided for synchronizing - * N threads. - */ - - private static HashMap conditions = new HashMap(); - - /* - * Modifiable boolean object - */ - private static class BValue { - boolean v; - } - - /* - * Modifiable int object - */ - private static class IValue { - int v; - IValue (int i) { - v =i; - } - } - - - private static BValue getCond (String condition) { - synchronized (conditions) { - BValue cond = (BValue) conditions.get (condition); - if (cond == null) { - cond = new BValue(); - conditions.put (condition, cond); - } - return cond; - } - } - - /** - * Set the condition to true. Any threads that are currently blocked - * waiting on the condition, will be unblocked and allowed to continue. - * Threads that subsequently call waitForCondition() will not block. - * If the named condition did not exist prior to the call, then it is created - * first. - */ - - public static void setCondition (String condition) { - BValue cond = getCond (condition); - synchronized (cond) { - if (cond.v) { - return; - } - cond.v = true; - cond.notifyAll(); - } - } - - /** - * If the named condition does not exist, then it is created and initialized - * to false. If the condition exists or has just been created and its value - * is false, then the thread blocks until another thread sets the condition. - * If the condition exists and is already set to true, then this call returns - * immediately without blocking. - */ - - public static void waitForCondition (String condition) { - BValue cond = getCond (condition); - synchronized (cond) { - if (!cond.v) { - try { - cond.wait(); - } catch (InterruptedException e) {} - } - } - } - - /* conditions must be locked when accessing this */ - static HashMap rv = new HashMap(); - - /** - * Force N threads to rendezvous (ie. wait for each other) before proceeding. - * The first thread(s) to call are blocked until the last - * thread makes the call. Then all threads continue. - *

        - * All threads that call with the same condition name, must use the same value - * for N (or the results may be not be as expected). - *

        - * Obviously, if fewer than N threads make the rendezvous then the result - * will be a hang. - */ - - public static void rendezvous (String condition, int N) { - BValue cond; - IValue iv; - String name = "RV_"+condition; - - /* get the condition */ - - synchronized (conditions) { - cond = (BValue)conditions.get (name); - if (cond == null) { - /* we are first caller */ - if (N < 2) { - throw new RuntimeException ("rendezvous must be called with N >= 2"); - } - cond = new BValue (); - conditions.put (name, cond); - iv = new IValue (N-1); - rv.put (name, iv); - } else { - /* already initialised, just decrement the counter */ - iv = (IValue) rv.get (name); - iv.v --; - } - } - - if (iv.v > 0) { - waitForCondition (name); - } else { - setCondition (name); - synchronized (conditions) { - clearCondition (name); - rv.remove (name); - } - } - } - - /** - * If the named condition exists and is set then remove it, so it can - * be re-initialized and used again. If the condition does not exist, or - * exists but is not set, then the call returns without doing anything. - * Note, some higher level synchronization - * may be needed between clear and the other operations. - */ - - public static void clearCondition(String condition) { - BValue cond; - synchronized (conditions) { - cond = (BValue) conditions.get (condition); - if (cond == null) { - return; - } - synchronized (cond) { - if (cond.v) { - conditions.remove (condition); - } - } - } - } -} diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index 12a177fd37d..1c99897f53b 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -19,17 +19,16 @@ * 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 8189131 8198240 8191844 8189949 8191031 8196141 8204923 8195774 8199779 * 8209452 8209506 8210432 8195793 8216577 8222089 8222133 8222137 8222136 * 8223499 8225392 8232019 8234245 8233223 8225068 8225069 8243321 8243320 * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 * 8305975 8304760 8307134 8295894 8314960 8317373 8317374 8318759 8319187 - * 8321408 8316138 + * 8321408 8316138 8341057 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -48,12 +47,12 @@ public class VerifyCACerts { + File.separator + "security" + File.separator + "cacerts"; // The numbers of certs now. - private static final int COUNT = 110; + private static final int COUNT = 112; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 private static final String CHECKSUM - = "C1:68:B4:AC:51:BF:B5:C6:FD:20:69:17:E1:AF:E4:5B:01:9B:AA:3F:C3:9A:80:A8:51:53:74:2C:A2:04:B0:FF"; + = "8F:E0:6F:7F:21:59:33:A6:43:F3:48:FD:A3:4A:8E:28:35:AA:DD:6E:A5:43:56:F1:28:34:48:DF:5C:D2:7C:72"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -282,6 +281,10 @@ public class VerifyCACerts { "4F:A3:12:6D:8D:3A:11:D1:C4:85:5A:4F:80:7C:BA:D6:CF:91:9D:3A:5A:88:B0:3B:EA:2C:63:72:D9:3C:40:C9"); put("globalsigne46 [jdk]", "CB:B9:C4:4D:84:B8:04:3E:10:50:EA:31:A6:9F:51:49:55:D7:BF:D2:E2:C6:B4:93:01:01:9A:D6:1D:9F:50:58"); + put("ssltlsrootecc2022 [jdk]", + "C3:2F:FD:9F:46:F9:36:D1:6C:36:73:99:09:59:43:4B:9A:D6:0A:AF:BB:9E:7C:F3:36:54:F1:44:CC:1B:A1:43"); + put("ssltlsrootrsa2022 [jdk]", + "8F:AF:7D:2E:2C:B4:70:9B:B8:E0:B3:36:66:BF:75:A5:DD:45:B5:DE:48:0F:8E:A8:D4:BF:E6:BE:BC:17:F2:ED"); } }; diff --git a/test/jdk/sun/security/ssl/InputRecord/ClientHelloRead.java b/test/jdk/sun/security/ssl/InputRecord/ClientHelloRead.java index e08230f325d..06cba109139 100644 --- a/test/jdk/sun/security/ssl/InputRecord/ClientHelloRead.java +++ b/test/jdk/sun/security/ssl/InputRecord/ClientHelloRead.java @@ -32,12 +32,22 @@ * system properties in samevm/agentvm mode. */ -import java.io.*; -import java.net.*; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.net.URL; import java.security.KeyStore; -import javax.net.*; -import javax.net.ssl.*; -import java.security.cert.*; + +import javax.net.ServerSocketFactory; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSession; /* * ClientHelloRead.java -- includes a simple server that can serve diff --git a/test/jdk/sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java index 00ea4fc4ef1..b9426bcfa95 100644 --- a/test/jdk/sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java +++ b/test/jdk/sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java @@ -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 @@ -26,7 +26,6 @@ * @library /test/lib * @summary Test that a New Session Ticket will be generated when a * SSLSessionBindingListener is set (boundValues) - * @key intermittent * @run main/othervm ResumptionUpdateBoundValues */ @@ -256,7 +255,7 @@ public static void main(String[] args) throws Exception { Thread t; while ((t = threads.take()) != Thread.currentThread()) { System.out.printf(" joining: %s%n", t); - t.join(1000L); + t.join(4000L); } serverReady = false; System.gc(); diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/ReuseAddr.java b/test/jdk/sun/security/ssl/SSLSocketImpl/ReuseAddr.java index abad01099bc..f7e677bbbd0 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/ReuseAddr.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/ReuseAddr.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 @@ -34,9 +34,12 @@ */ import java.net.ServerSocket; +import java.net.BindException; public class ReuseAddr extends SSLSocketTemplate { + private static final int MAX_ATTEMPTS = 3; + @Override protected void doServerSide() throws Exception { super.doServerSide(); @@ -50,6 +53,21 @@ protected void doServerSide() throws Exception { } public static void main(String[] args) throws Exception { - new ReuseAddr().run(); + for (int i=1 ; i <= MAX_ATTEMPTS; i++) { + try { + new ReuseAddr().run(); + System.out.println("Test succeeded at attempt " + i); + break; + } catch (BindException x) { + System.out.println("attempt " + i + " failed: " + x); + if (i == MAX_ATTEMPTS) { + String msg = "Could not succeed after " + i + " attempts"; + System.err.println(msg); + throw new AssertionError("Failed to reuse address: " + msg, x); + } else { + System.out.println("Retrying..."); + } + } + } } } diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketBruceForceClose.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketBruceForceClose.java deleted file mode 100644 index bbc8a4f8bf5..00000000000 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketBruceForceClose.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 - * 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. - */ - -// -// Please run in othervm mode. SunJSSE does not support dynamic system -// properties, no way to re-use system properties in samevm/agentvm mode. -// - -/* - * @test - * @bug 8209333 - * @summary Socket reset issue for TLS 1.3 socket close - * @library /javax/net/ssl/templates - * @run main/othervm SSLSocketBruteForceClose - */ - -import javax.net.ssl.*; -import java.io.*; -import java.net.SocketException; - -public class SSLSocketBruteForceClose extends SSLSocketTemplate { - - public static void main(String[] args) throws Exception { - for (int i = 0; i<= 10; i++) { - System.err.println("==================================="); - System.err.println("loop " + i); - System.err.println("==================================="); - new SSLSocketBruteForceClose().run(); - } - } - - @Override - protected void configureServerSocket(SSLServerSocket socket) { - socket.setNeedClientAuth(false); - socket.setEnableSessionCreation(true); - socket.setUseClientMode(false); - } - - @Override - protected void runServerApplication(SSLSocket socket) throws Exception { - System.err.println("Reading data from client"); - BufferedReader serverReader = new BufferedReader( - new InputStreamReader(socket.getInputStream())); - String data = serverReader.readLine(); - System.err.println("Received data from client: " + data); - - System.err.println("Reading more data from client"); - data = serverReader.readLine(); - System.err.println("Received data from client: " + data); - } - - @Override - protected void configureClientSocket(SSLSocket socket) { - try { - socket.setSoLinger(true, 3); - socket.setSoTimeout(1000); - } catch (SocketException exc) { - throw new RuntimeException("Could not configure client socket", exc); - } - } - - @Override - protected void runClientApplication(SSLSocket socket) throws Exception { - String clientData = "Hi, I am client"; - - System.err.println("Sending data to server ..."); - BufferedWriter os = new BufferedWriter( - new OutputStreamWriter(socket.getOutputStream())); - os.write(clientData, 0, clientData.length()); - os.newLine(); - os.flush(); - - System.err.println("Sending more data to server ..."); - os.write(clientData, 0, clientData.length()); - os.newLine(); - os.flush(); - - socket.close(); - System.err.println("client socket closed"); - } -} - diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/Distrust.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/Distrust.java new file mode 100644 index 00000000000..1c1fcee3609 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/Distrust.java @@ -0,0 +1,249 @@ +/* + * 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. + */ + +import java.io.*; +import java.math.BigInteger; +import java.security.*; +import java.security.cert.*; +import java.time.*; +import java.util.*; +import javax.net.ssl.*; +import sun.security.validator.Validator; +import sun.security.validator.ValidatorException; + +import jdk.test.lib.security.SecurityUtils; + +/** + * @test + * @bug 8337664 8341059 + * @summary Check that TLS Server certificates chaining back to distrusted + * Entrust roots are invalid + * @library /test/lib + * @modules java.base/sun.security.validator + * @run main/othervm Distrust after policyOn invalid + * @run main/othervm Distrust after policyOff valid + * @run main/othervm Distrust before policyOn valid + * @run main/othervm Distrust before policyOff valid + */ + +public class Distrust { + + private static final String TEST_SRC = System.getProperty("test.src", "."); + private static CertificateFactory cf; + + // Each of the roots have a test certificate chain stored in a file + // named "-chain.pem". + private static String[] rootsToTest = new String[] { + "entrustevca", "entrustrootcaec1", "entrustrootcag2", "entrustrootcag4", + "entrust2048ca", "affirmtrustcommercialca", "affirmtrustnetworkingca", + "affirmtrustpremiumca", "affirmtrustpremiumeccca" }; + + // A date that is after the restrictions take effect + private static final Date NOVEMBER_12_2024 = + Date.from(LocalDate.of(2024, 11, 12) + .atStartOfDay(ZoneOffset.UTC) + .toInstant()); + + // A date that is a second before the restrictions take effect + private static final Date BEFORE_NOVEMBER_12_2024 = + Date.from(LocalDate.of(2024, 11, 12) + .atStartOfDay(ZoneOffset.UTC) + .minusSeconds(1) + .toInstant()); + + public static void main(String[] args) throws Exception { + + cf = CertificateFactory.getInstance("X.509"); + + boolean before = args[0].equals("before"); + boolean policyOn = args[1].equals("policyOn"); + boolean isValid = args[2].equals("valid"); + + if (!policyOn) { + // disable policy (default is on) + Security.setProperty("jdk.security.caDistrustPolicies", ""); + } + + Date notBefore = before ? BEFORE_NOVEMBER_12_2024 : NOVEMBER_12_2024; + + X509TrustManager pkixTM = getTMF("PKIX", null); + X509TrustManager sunX509TM = getTMF("SunX509", null); + for (String test : rootsToTest) { + System.err.println("Testing " + test); + X509Certificate[] chain = loadCertificateChain(test); + + testTM(sunX509TM, chain, notBefore, isValid); + testTM(pkixTM, chain, notBefore, isValid); + } + } + + private static X509TrustManager getTMF(String type, + PKIXBuilderParameters params) throws Exception { + TrustManagerFactory tmf = TrustManagerFactory.getInstance(type); + if (params == null) { + tmf.init((KeyStore)null); + } else { + tmf.init(new CertPathTrustManagerParameters(params)); + } + TrustManager[] tms = tmf.getTrustManagers(); + for (TrustManager tm : tms) { + X509TrustManager xtm = (X509TrustManager)tm; + return xtm; + } + throw new Exception("No TrustManager for " + type); + } + + private static PKIXBuilderParameters getParams() throws Exception { + PKIXBuilderParameters pbp = + new PKIXBuilderParameters(SecurityUtils.getCacertsKeyStore(), + new X509CertSelector()); + pbp.setRevocationEnabled(false); + return pbp; + } + + private static void testTM(X509TrustManager xtm, X509Certificate[] chain, + Date notBefore, boolean valid) throws Exception { + // Check if TLS Server certificate (the first element of the chain) + // is issued after the specified notBefore date (should be rejected + // unless distrust property is false). To do this, we need to + // fake the notBefore date since none of the test certs are issued + // after then. + chain[0] = new DistrustedTLSServerCert(chain[0], notBefore); + + try { + xtm.checkServerTrusted(chain, "ECDHE_RSA"); + if (!valid) { + throw new Exception("chain should be invalid"); + } + } catch (CertificateException ce) { + // expired TLS certificates should not be treated as failure + if (expired(ce)) { + System.err.println("Test is N/A, chain is expired"); + return; + } + if (valid) { + throw new Exception("Unexpected exception, chain " + + "should be valid", ce); + } + if (ce instanceof ValidatorException) { + ValidatorException ve = (ValidatorException)ce; + if (ve.getErrorType() != ValidatorException.T_UNTRUSTED_CERT) { + ce.printStackTrace(System.err); + throw new Exception("Unexpected exception: " + ce); + } + } else { + throw new Exception("Unexpected exception: " + ce); + } + } + } + + // check if a cause of exception is an expired cert + private static boolean expired(CertificateException ce) { + if (ce instanceof CertificateExpiredException) { + return true; + } + Throwable t = ce.getCause(); + while (t != null) { + if (t instanceof CertificateExpiredException) { + return true; + } + t = t.getCause(); + } + return false; + } + + private static X509Certificate[] loadCertificateChain(String name) + throws Exception { + try (InputStream in = new FileInputStream(TEST_SRC + File.separator + + name + "-chain.pem")) { + Collection certs = + (Collection)cf.generateCertificates(in); + return certs.toArray(new X509Certificate[0]); + } + } + + private static class DistrustedTLSServerCert extends X509Certificate { + private final X509Certificate cert; + private final Date notBefore; + DistrustedTLSServerCert(X509Certificate cert, Date notBefore) { + this.cert = cert; + this.notBefore = notBefore; + } + public Set getCriticalExtensionOIDs() { + return cert.getCriticalExtensionOIDs(); + } + public byte[] getExtensionValue(String oid) { + return cert.getExtensionValue(oid); + } + public Set getNonCriticalExtensionOIDs() { + return cert.getNonCriticalExtensionOIDs(); + } + public boolean hasUnsupportedCriticalExtension() { + return cert.hasUnsupportedCriticalExtension(); + } + public void checkValidity() throws CertificateExpiredException, + CertificateNotYetValidException { + // always pass + } + public void checkValidity(Date date) throws CertificateExpiredException, + CertificateNotYetValidException { + // always pass + } + public int getVersion() { return cert.getVersion(); } + public BigInteger getSerialNumber() { return cert.getSerialNumber(); } + public Principal getIssuerDN() { return cert.getIssuerDN(); } + public Principal getSubjectDN() { return cert.getSubjectDN(); } + public Date getNotBefore() { return notBefore; } + public Date getNotAfter() { return cert.getNotAfter(); } + public byte[] getTBSCertificate() throws CertificateEncodingException { + return cert.getTBSCertificate(); + } + public byte[] getSignature() { return cert.getSignature(); } + public String getSigAlgName() { return cert.getSigAlgName(); } + public String getSigAlgOID() { return cert.getSigAlgOID(); } + public byte[] getSigAlgParams() { return cert.getSigAlgParams(); } + public boolean[] getIssuerUniqueID() { + return cert.getIssuerUniqueID(); + } + public boolean[] getSubjectUniqueID() { + return cert.getSubjectUniqueID(); + } + public boolean[] getKeyUsage() { return cert.getKeyUsage(); } + public int getBasicConstraints() { return cert.getBasicConstraints(); } + public byte[] getEncoded() throws CertificateEncodingException { + return cert.getEncoded(); + } + public void verify(PublicKey key) throws CertificateException, + InvalidKeyException, NoSuchAlgorithmException, + NoSuchProviderException, SignatureException { + cert.verify(key); + } + public void verify(PublicKey key, String sigProvider) throws + CertificateException, InvalidKeyException, NoSuchAlgorithmException, + NoSuchProviderException, SignatureException { + cert.verify(key, sigProvider); + } + public PublicKey getPublicKey() { return cert.getPublicKey(); } + public String toString() { return cert.toString(); } + } +} diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustcommercialca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustcommercialca-chain.pem new file mode 100644 index 00000000000..76aa6d14338 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustcommercialca-chain.pem @@ -0,0 +1,77 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: 8608355977964138876 (0x7777062726a9b17c) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Commercial + Validity + Not Before: Jan 29 14:06:06 2010 GMT + Not After : Dec 31 14:06:06 2030 GMT + +-----BEGIN CERTIFICATE----- +MIIHHjCCBgagAwIBAgIQAWZjFOyCvT00u/gtkCvS2TANBgkqhkiG9w0BAQsFADCB +gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT +ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp +cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYxMB4XDTI0MDYyODIx +MzgwNVoXDTI1MDcyODIxMzgwNFowgdgxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP +bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW +BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt +aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 +NDA1NDcxKDAmBgNVBAMTH3ZhbGlkY29tbWVyY2lhbC5hZmZpcm10cnVzdC5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeIT2XO0hJ5wDSbIiIcMvs +P3NpQc7O7v5DqldpME6+Qn2sF5b9hc6j72hgTXREa77uUcP5u1JcMWCSWwYQHMpJ +kFzmIzijhS60wW1epb5QyTgM3ZYh1WKvttFCbHUcrTtd+LoPFYsjw9ZK//K9tPp+ +ddn06/ivWvUO5y5vn0wrCaB9tuLdDn4RCQzK2XoZdDuqhPlBBogJX0vM6lsXjgLy +EbvE+/sKYps/In6VtRvCoYavg3OqaIMeaA7gTiYTb1ZGFOAiltnq7fcp6SZUohK3 +QNihv1DadVc+n8LnEUKKDkgG2YgWEFczaE3qwG3ef6L3MzLGrkgVY+qGHyyv2IE7 +AgMBAAGjggM1MIIDMTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT4ARNL47hAsOpa +96VMgKEY3sLIAjAfBgNVHSMEGDAWgBTb72U3C+VHyzXRkB8DwbyIx6fqgDBsBggr +BgEFBQcBAQRgMF4wJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLmFmZmlybXRydXN0 +LmNvbTAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5hZmZpcm10cnVzdC5jb20vYWZ0 +ZXYxY2EuY3J0MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuYWZmaXJtdHJ1 +c3QuY29tL2NybC9hZnRldjFjYS5jcmwwKgYDVR0RBCMwIYIfdmFsaWRjb21tZXJj +aWFsLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARPME0wBwYFZ4EMAQEwQgYKKwYBBAGC +jwkCATA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5hZmZpcm10cnVzdC5jb20v +cmVwb3NpdG9yeTCCAYAGCisGAQQB1nkCBAIEggFwBIIBbAFqAHcAEvFONL1TckyE +BhnDjz96E/jntWKHiJxtMAWE6+WGJjoAAAGQYMi3wQAABAMASDBGAiEAjvdsU4G2 +o4BZSOOjaH6gOp7zhKtXQByQUvfHfsi2ePcCIQDnnIO2qlHBm+sskUDlXfR0lCUW +yFPVr9nFZ0L9YPpozgB2AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0 +AAABkGDIt9MAAAQDAEcwRQIhANh1zS3Qeo9yKF+j3G52JhmDRYBS+1TM0wykoXCY +llpxAiAG+LAlKSbwwgrboUSTDDXWNeoRYZ7fKbU72kKfHrpZvwB3ABoE/0nQVB1A +r/agw7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDIt9sAAAQDAEgwRgIhAN8OoC4I +zw8bFJy8ACgK40c9ZfsIfFhePTc9CyrL5uDsAiEA4Jn/IqBB9L5DeTgqw9hBaYag +FmY/2gWDip36ga0WUsAwDQYJKoZIhvcNAQELBQADggEBABywPLJP097Emz6LNeFU +/HvfhaUKv2pgIHf/Kvjs5x78RK9G605THPEHr/TeUjNZ4PBd48WBNVWzyd/8FuOt +r+FsYkRJb9CnrOhZHuCwlcdWXvuY8PiuBmT+xB16BWR5yhYbbiGe4hea0Pf6CfHh +jJoGJw4dQKfgneZOV7IcaWnNTKYawlcZOgxvEwFvj+iZM31WphEPKRAV+N+Tp+ZR +nxlEdjmdbOjqBydlYIEzuFIgxgtnPdK5wqCOWb+z2cARUAO/AkiWrOLTPDc7ydQK +GcfDrSqffHOlwaee08C6STFaJWIcpqxZdXE6Jc+8/85bfPEAG1UepgfnBTqW9RGT +Q3s= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEqDCCA5CgAwIBAgIQFylVHtaOf7Ht9XMA811/1TANBgkqhkiG9w0BAQsFADBE +MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHzAdBgNVBAMMFkFm +ZmlybVRydXN0IENvbW1lcmNpYWwwHhcNMTkwMzIxMjAyNzU0WhcNMzAxMjAyMDQw +MDAwWjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYD +VQQLEyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQD +EyhBZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuPBMIa9VuXJGAw0MHvieGciPFA11 +b9T49YJ7T+zVpoMMQO+ueUKVHb2l26oeCiwIhXMQ5LquOVcx+rofouzcKXY3wKDZ +zHIOnAkU+23Ucn/3dRH7aHJULsBufZq+NvwgYSgJJEDKfqvIV/c5HiRyZ2H+nAI5 +10Q2xC0UxgSBsufccQ+Fwkg6BAGDlTXrvi8wi75UaGue6jv/qcKLybeVUrgqKE64 +d9oa9PG5/g89QwSdsIQEdVSFzFvFpOG9YhJbJ177Zg6DGCxU0lWwFrVpyH/2vnXl +jhMQScn8UxzCJdDg3EDqjgaV0JH2yoLug+QVYgURPu5BEb5ut9vAdP7cLwIDAQAB +o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz +cC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFNvvZTcL5UfLNdGQHwPBvIjHp+qA +MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUnZPGU4teyq8/nx4P5ZmV +vCT2lI8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v +d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdENvbW1l +cmNpYWwuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI +KwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAATH11fMrINGmQGQqQW0ATteVnUG +LrmRSN2OlmRm+dkUwKXhcQQEfYYlEggPqgvxSUpw13fXSOqVHqAcj3BIqF957kh+ +m3DmC0RX9KaEKD165pf77P5nZcRmZpBl9cctvzIxN19uzcminchusYwLyeWhBtTZ +xpER9LbrfMNaQ7GnrgalMx54QvdjOhw/GJs9/SqEzYmPshL+DzgZX/oAzY63rQIh +rBblf6/2talZqci96oFzNst8rGfPy/xQ7lgkki1hwIYbORMfloBhP+vAZJo0mxdM +ipu3Z0ToK+KU2iqnBxXVr2/kod+CpkHnjUHa1wnQuSaefng3XwZ/vqtSL9c= +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustnetworkingca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustnetworkingca-chain.pem new file mode 100644 index 00000000000..7384d31152e --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustnetworkingca-chain.pem @@ -0,0 +1,76 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: 8957382827206547757 (0x7c4f04391cd4992d) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Networking + Validity + Not Before: Jan 29 14:08:24 2010 GMT + Not After : Dec 31 14:08:24 2030 GMT + +-----BEGIN CERTIFICATE----- +MIIHGjCCBgKgAwIBAgIQX2vGPaCJ1tS0ncp2OlBMFjANBgkqhkiG9w0BAQsFADCB +gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT +ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp +cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYzMB4XDTI0MDYyODIx +NDU0OVoXDTI1MDcyODIxNDU0OFowgdgxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP +bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW +BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt +aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 +NDA1NDcxKDAmBgNVBAMTH3ZhbGlkbmV0d29ya2luZy5hZmZpcm10cnVzdC5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkGknE8kFr+CaIybQrDPRw +z9OKXq77p4CnrkF1/g9w/HiIs6Ps8YqTjsiTKM3wYLbvPA+TbO9DpCSyCP2bVyLf +AjUE617KZSpfy9RqzvGjn/1qH/cBKohhEliMfDj4ZHfY4x+1WYTZPVK/g0Ny5RAP +wz9lJHR2SsVGLvpqXzWaVoxifJ8HZWD7n5z/75WeYko+Hubx3WvzJZcN2Xjn+q6a +7wkDaXPayrvn5uWGPlOLQHqJ5z7wts21jASMTfJAToFyzH6dGwvqxkP3bVJGJ8AF +vtMfqVjcOcjWgmmOEHMPAAqs5QKrYuSLccH6hFTwFEUCdMwVqfloznt2sNUSBoKj +AgMBAAGjggMxMIIDLTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTrE0z4fRyx9P9M +0FfA6VgGkJiYVDAfBgNVHSMEGDAWgBR5HrHJF8cerLHHFNfD6H+8uVCbFTBsBggr +BgEFBQcBAQRgMF4wJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLmFmZmlybXRydXN0 +LmNvbTAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5hZmZpcm10cnVzdC5jb20vYWZ0 +ZXYzY2EuY3J0MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuYWZmaXJtdHJ1 +c3QuY29tL2NybC9hZnRldjNjYS5jcmwwKgYDVR0RBCMwIYIfdmFsaWRuZXR3b3Jr +aW5nLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARPME0wBwYFZ4EMAQEwQgYKKwYBBAGC +jwkCAjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5hZmZpcm10cnVzdC5jb20v +cmVwb3NpdG9yeTCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHYADeHyMCvTDcFA +YhIJ6lUu/Ed0fLHX6TDvDkIetH5OqjQAAAGQYM/MjQAABAMARzBFAiBjnehs1mvh +5Xm3uXZ7Bq8gijwiXThwnLSYROQxnWrnbAIhALbgJG+PRZQfzTBbgM/zAwNsBjhe +F5iENnaajJCxzOhaAHUAEvFONL1TckyEBhnDjz96E/jntWKHiJxtMAWE6+WGJjoA +AAGQYM/MgQAABAMARjBEAiAsWOm1IIjaxQP9uaPI9tQmkiJPUOTrBTsTDO+jkgiG ++QIgVNhND82rsFGjrtAAHzzgCVzLDUM3zaHxnP/z3BNuO4QAdQAaBP9J0FQdQK/2 +oMO/8djEZy9O7O4jQGiYaxdALtyJfQAAAZBgz8zLAAAEAwBGMEQCIBIGxtjk7Lw8 +i+oggK7VrPMNTB632t321cwhEm517BbZAiBws3+uytwh59N6qGJUuSFQnOZNPOPj +eQnH2fSdT1J2sDANBgkqhkiG9w0BAQsFAAOCAQEAcSzitESRKlbcUvxvUB7FjK0I +CaBU1Nyu0xDFCoG2pmp7GASJz34wtPYfsiX5+j4hDh/noMcgk7WlD8pzgWYw15Rk ++5kTv2v4U85y/JFjzMOHbz64KjQdGebqhjvC/E/EXxK+AZf4H574/w7rbyJ30vFL +gNvPF9AxS1MuYIO55jXrHMByKnFoQZgPsmAY/x+n+OzMxWOdR18PupypCB5TyJZ8 +pQzwoxmX7qeZHiXyJ8jQUwe1qoQc2SbwfQxfwSPUPSJuQo90N+5nyQMe7vvPBM0Y +/CXaFpfPqh71D4C0Ey+0hYxSt99gYs4P9twUByjIlP0wTyhaoEpt3zw9DdZypQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEqDCCA5CgAwIBAgIQNCSh7Pjwo1/nRrcBHEPoRDANBgkqhkiG9w0BAQsFADBE +MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHzAdBgNVBAMMFkFm +ZmlybVRydXN0IE5ldHdvcmtpbmcwHhcNMTkwMzIxMjAzODU5WhcNMzAxMjAyMDQw +MDAwWjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYD +VQQLEyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQD +EyhBZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYzMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHDl/3xr1qiHoe0Rzb3AGLw56e9J +l2a3X59+PAfI5wGBHuK9Dl7XsyoH65X6QIC/rXyVpuNgKbbwIGHB+rCSplyHzGyC +WeM3LXa2q1US7VteeFDS959nxJVRFfwATR9xAK6YTUWQ/yWdw0dZSm0lQNmEMBwS +qi0ufWokiWXZUzWHOu7A6driCohu9sFDwe1INJUPH6uIlovmzGvG3UYbUSymJcjs +Ka0fXXX9zukco8exlOIKWRJSNLxKtSSPDVASrGLQ1xi3qkiLTKci3+jKMNDFf1vw +foZN99HhUcWKXfr2KlWfANdjTMlsTKCfuhfWl1OBVNHGRrACAQCXI/ji0wIDAQAB +o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz +cC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFHkesckXxx6ssccU18Pof7y5UJsV +MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUBx/S55zawm6iQLSwelAQ +UHTEyL0wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v +d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEkGA1UdHwRCMEAwPqA8oDqG +OGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdE5ldHdv +cmtpbmcuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI +KwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAAhmE4I56hNpnWXQ2Si8a/TgQUZr +X5Jlv1LDvl3rkDyfEIHNZ8dth17SakJYJBWHExph/iIYjCJ9YmeyhghV5rPqT+wF +4yyE2ngenIusfnWT2bTpT9u2VZbCNeACE5XnN2UHSA0J9idPjfLuthViWEvSZZUh +DJ53bX+exO366nDY4AI7owIyhz8hdsWyhZ/0ST+eD+kbgd8osd+GdxzRmyKcfl84 +D1K1uff01T9w2dyUaZglQsFljkaO6xmeXZJsPnhwCp/HlMHWzhAneUQ7I9FZSOW+ +WiYbt4RitmBpysadBReikWM4knECzJQ/fMT9vC0k9BLlqUYRwCH9vr0UnZo= +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumca-chain.pem new file mode 100644 index 00000000000..6f108bc1229 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumca-chain.pem @@ -0,0 +1,88 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: 7893706540734352110 (0x6d8c1446b1a60aee) + Signature Algorithm: sha384WithRSAEncryption + Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Premium + Validity + Not Before: Jan 29 14:10:36 2010 GMT + Not After : Dec 31 14:10:36 2040 GMT + +-----BEGIN CERTIFICATE----- +MIIIFjCCBv6gAwIBAgIQQVOTWr7tEAJXmRDkCSxkajANBgkqhkiG9w0BAQsFADCB +gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT +ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp +cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYyMB4XDTI0MDYyODIx +NDgyN1oXDTI1MDcyODIxNDgyNlowgdUxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP +bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW +BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt +aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 +NDA1NDcxJTAjBgNVBAMTHHZhbGlkcHJlbWl1bS5hZmZpcm10cnVzdC5jb20wggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVRMzwbDq47ivHOKqJdiEJNL2+ +g9Snj/BRctqcQTrIV99RP0pmAh5fHg7vnhVsHqc9sRLVcQWTJk9NuRJ2VnDKWsBa +Xrp5UWaNjS0vaFA4jzCi1gWzTTZgPTQn3VRG3JP1F5CZb405/mtWDaw/CfWkcUqQ +VSilqFlJRsjcPCzQh7ZaXAo+FmzJxNSwjxdP6JSYMeTDRCUpSb3T8PypVI1CEmLZ +jsxrg5oIZn25591g/pzgLE56N0stNY4d3q4YD1t5x46RsqYAJYSkk8rcTN+kHzsY +VSqaRDyPkGbmuCeJUvW24wJ30yQtXQWA+U0dMYLe7LyglJ7dkOzvWNbqrIcvM8My +hxH/wwVH7e4dL/1E58yr1BHENUk7Mp9rzIXj496eLkF5G1lMkNnuVRQqCAOW0rPY +V0rI8yrCMTK52s4mNjQo2J7JOYdTUvAWZ92MKvEjjhQlMH8eK72Km/+mkxpsgGmr +3c6u+Gom7oI5VaLZ+3p2uWaOsutk1tkzWjhzY4L27hwmIdWujfrWMRx8uxcfoJxX +gQ40d1QiSN51BtCPE5UnpLU/YUxMdzWmtUoGUfYIGVqDVToBnunIFMdmFjC0IrNl +hquDQi/OGMpzuOvxX1FoXb+rRwOhhdrcR0BQqUVRTV0U5LlcsDeNMqmqPE9mzGtJ +W69Fsh7crntng/L72wIDAQABo4IDMDCCAywwDAYDVR0TAQH/BAIwADAdBgNVHQ4E +FgQU3PWyi/4usZghgahc/Tj+Q60QLOcwHwYDVR0jBBgwFoAUc3yaOGg8UXxBCP6h +HyoetGHbzTwwbAYIKwYBBQUHAQEEYDBeMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz +cC5hZmZpcm10cnVzdC5jb20wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9haWEuYWZmaXJt +dHJ1c3QuY29tL2FmdGV2MmNhLmNydDA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8v +Y3JsLmFmZmlybXRydXN0LmNvbS9jcmwvYWZ0ZXYyY2EuY3JsMCcGA1UdEQQgMB6C +HHZhbGlkcHJlbWl1bS5hZmZpcm10cnVzdC5jb20wDgYDVR0PAQH/BAQDAgWgMB0G +A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBWBgNVHSAETzBNMAcGBWeBDAEB +MEIGCisGAQQBgo8JAgMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuYWZmaXJt +dHJ1c3QuY29tL3JlcG9zaXRvcnkwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB2 +ABoE/0nQVB1Ar/agw7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDSN7EAAAQDAEcw +RQIgVDWwhv7yG6RNnkMZnVq1YYA7ypn/GSH0ibUKnESHRpYCIQCY8gyCX7VFONUI +QuR8daz7ra2FCUI9TwylrR3eFfIgGgB3AN3cyjSV1+EWBeeVMvrHn/g9HFDf2wA6 +FBJ2Ciysu8gqAAABkGDSN5cAAAQDAEgwRgIhAM1edsSyFUKU0Dj1WxTGwziE6fCW +g2ByfL8kDrP260YXAiEA6YQOpJf04N13Nn263BxAl+laH9Ar0eo03fArlv743TQA +dQAN4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAAAZBg0je+AAAEAwBG +MEQCIExqK4katETAQo+H0+ImuNJCSeFEI9C+9wrjhl6ZnWb9AiBwkC1vpLYOIm/1 +YCLCQIOmTdg2wf8LITlrQNJA8vbBljANBgkqhkiG9w0BAQsFAAOCAQEASOmPu7ot +yl6MoMns19uI6H2KSUjMFh3/fKMcY/ettmEYalgrytexFMrLnD2UniBlD+nJEshp +5/z7o0YDiRoiLhMAs7VqIdX3erNu/ghNh7P2bDnoMWShSoAKxez1XOGL3rRE0NAi +DsWCaNRHH9rnC97275sbGnua7ZYg+8BiF62vpJlqjrxDHjGiej8qAWSjztbB43Af +bwRscpXTxNkMvOBuRFMH+rSxB8CrOV68W+yxmzPuPxVjM7oJH8Qk5BC53NRqFsVz +JhbNfot0+/drj7JT3jlacUVQcD/BzDuC3+qczQlLjLdHgQM2/e4fXsD6C5S6B11d +BDx6ipGpaASofA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIQU3HI6weE/VEI5dTz4yPsRjANBgkqhkiG9w0BAQsFADBB +MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHDAaBgNVBAMME0Fm +ZmlybVRydXN0IFByZW1pdW0wHhcNMTkwMzIxMjA0NjM1WhcNMzAxMjAyMDQwMDAw +WjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQL +EyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhB +ZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvDDZHfxkB1nAGFKdw0VCgV+B/eBtW1o+ +bXzwRcpeFh5saDI+tv1RAMrYFq+AJkXCCJopgMF2Wqfv5myE3JMgxEHuuKUpJz7H +FprrFckVOGCtJKH8Iy9AWPjBwt8lKmxGJF7EZst+QoVt4hMe0qhL0WEKbATFPe41 +DcM7UsyQv6Bvpn424uePy3/1ATIsVL3YmvAbUNR0aqVxYAJzTefvyIet/761bKGc +NyqdOVWFFeTDtr8iL1TBXToAgl0GJ39bFQZsP19VcCpfk9Zj3YHTPRPq5wZOZuUN +F7jiBUEi6DaVOi3Wy4vdySHtWPeBHRYif1I6fcUfdCNORMc4ee6KewIDAQABo4IB +UTCCAU0wNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5h +ZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFHN8mjhoPFF8QQj+oR8qHrRh2808MBIG +A1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUncBnpgwi2Sb1RaumZVIRJ9hF +rGMwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3 +LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEYGA1UdHwQ/MD0wO6A5oDeGNWh0 +dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdFByZW1pdW0u +Y3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH +AwIwDQYJKoZIhvcNAQELBQADggIBABi64UEwl3l0yIiuSACyVQQIBI60BUmhseac +4BzCAsJrR5tE/2U9QAa2y6JpR1nqm76DJvw1QQgvFcNe+fkwpvoViCaSTbZkGGwD +mQe2xRSYJcDSMQUc/GgzLcX2c1CrexQXE1vwV/q33af1en5s1GzLl915aNS/k1ch +G7EMruJ/D4cuH9j4j2i+b+llmVBzavBwelN5rc693o+Ot9id/1sTWNugwAu3uXGb +VlhETMnjXGIciegOLdWYhWBln0izYlt9IwlDEpjMVaZ0HZlj2JBGaSe4PfEFpJPO +beuPcQpLQGw2XpW2ZMG5JcRYaoKWjixXAGktRA3H9nvVW92jvzx/RX484w2ZM5Rt +E+I1ikAuQLAyWG7clht387e2RuC3NZTtefSyjE3L9gQDOPC+Z9ycwr0WJHRsxFvh +FJQi3JnxgFZf5mc5n2mh3qAgALTNOUHuDiHrerjTOWbpF/1/NJmo/c/YZ63vZIhc +EaER4HuhbBqlpf6z3WOIQdZm1ChwXYHrEcLDgfwm9cXoaVK2HZapkMwQbPffPlT1 +E+AxRFB4YmT1y2WzdaHfhFA9nH6ByUdL5+FfrDoIIUO2e8OLOAcrJsf5+unhAhc0 +v7N48JWdmpstjkXCaCIaidrZLJxS+pikNgHB1dXF/TxokLTiPB9jcYKdGaYs3XHb +YKLdwubu +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumeccca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumeccca-chain.pem new file mode 100644 index 00000000000..37b1b787084 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumeccca-chain.pem @@ -0,0 +1,63 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: 8401224907861490260 (0x7497258ac73f7a54) + Signature Algorithm: ecdsa-with-SHA384 + Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Premium ECC + Validity + Not Before: Jan 29 14:20:24 2010 GMT + Not After : Dec 31 14:20:24 2040 GMT + +-----BEGIN CERTIFICATE----- +MIIF0zCCBVmgAwIBAgIQFVwk9nYUM5SYOnBd+IoGtzAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJTZWUg +d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTMwMQYDVQQDEypBZmZpcm1U +cnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVZFQzEwHhcNMjQwNjI4MjE0 +OTUwWhcNMjUwNzI4MjE0OTQ4WjCB2DELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09u +dGFyaW8xDzANBgNVBAcTBk90dGF3YTETMBEGCysGAQQBgjc8AgEDEwJDQTEYMBYG +CysGAQQBgjc8AgECEwdPbnRhcmlvMRwwGgYDVQQKExNBZmZpcm10cnVzdCBMaW1p +dGVkMR0wGwYDVQQPExRQcml2YXRlIE9yZ2FuaXphdGlvbjEQMA4GA1UEBRMHMjU0 +MDU0NzEoMCYGA1UEAxMfdmFsaWRwcmVtaXVtZWNjLmFmZmlybXRydXN0LmNvbTB2 +MBAGByqGSM49AgEGBSuBBAAiA2IABEkLBzBYSJPRENKDaA1iBPQz+jZUV+OoM9nJ +sr9sMfmHaqr3nlWxAMM99b9/usVfYyUxqyi+YL2Z3ZSxjX2dpyhwMtPpIQkL1pMW +Iv55XBIcYRyl2NjcADS9B06G+nnix6OCAzcwggMzMAwGA1UdEwEB/wQCMAAwHQYD +VR0OBBYEFP+37ywf2YJJ/4CEVy1GY4ioGm1yMB8GA1UdIwQYMBaAFMaQjAKD113j +vjucLtVlfSoQYO7lMG4GCCsGAQUFBwEBBGIwYDAnBggrBgEFBQcwAYYbaHR0cDov +L29jc3AuYWZmaXJtdHJ1c3QuY29tMDUGCCsGAQUFBzAChilodHRwOi8vYWlhLmFm +ZmlybXRydXN0LmNvbS9hZnRldmVjMWNhLmNydDA+BgNVHR8ENzA1MDOgMaAvhi1o +dHRwOi8vY3JsLmFmZmlybXRydXN0LmNvbS9jcmwvYWZ0ZXZlYzFjYS5jcmwwKgYD +VR0RBCMwIYIfdmFsaWRwcmVtaXVtZWNjLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8B +Af8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARP +ME0wBwYFZ4EMAQEwQgYKKwYBBAGCjwkCBDA0MDIGCCsGAQUFBwIBFiZodHRwczov +L3d3dy5hZmZpcm10cnVzdC5jb20vcmVwb3NpdG9yeTCCAX4GCisGAQQB1nkCBAIE +ggFuBIIBagFoAHUA5tIxY0B3jMEQQQbXcbnOwdJA9paEhvu6hzId/R43jlAAAAGQ +YNN5tQAABAMARjBEAiAnainEoBGI9czVh+c9QLPL30S3Rtov8zrnhlXfeKLzZQIg +UGkntBMux0MqHt9Aj60qMsS/C4ZWF7AihVVaUKcrEVgAdgAN4fIwK9MNwUBiEgnq +VS78R3R8sdfpMO8OQh60fk6qNAAAAZBg03m1AAAEAwBHMEUCIGI9kBByoozH4cfS +ECW/O2N/ElkdATkt7EwQ52kcc4ICAiEA9QTh8JlJTb/ytYC1ECX0vQbrYVexg+fu +dw7dfToF9nAAdwAS8U40vVNyTIQGGcOPP3oT+Oe1YoeInG0wBYTr5YYmOgAAAZBg +03ndAAAEAwBIMEYCIQCox5nSCcVB2AfNYXco77zsJnYP7KAU2I4VA2GNL7I4wQIh +AP6WEzyfBoGpYYqFmNnJUavyhKBmeNiR7eNtaFwpSc+UMAoGCCqGSM49BAMDA2gA +MGUCMAGSNMXAAKDRk0ZOtydN95Rkja97+70TatCIIxEAsJD8Hu7lfj2LHCYFQjVY +oaWTrQIxAKUudx7E/JnjsthuL6sNqKVHfD3iLUJyQNK9wE0SVt1xAm7Cu1JXZORE +M64KMKoQFQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDXDCCAuKgAwIBAgIQAgKlhME0Bk3J8y0gfqNymDAKBggqhkjOPQQDAzBFMQsw +CQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxIDAeBgNVBAMMF0FmZmly +bVRydXN0IFByZW1pdW0gRUNDMB4XDTE5MDMyMTIwNTUwN1oXDTMwMTIwMjA0MDAw +MFowgYUxCzAJBgNVBAYTAkNBMRQwEgYDVQQKEwtBZmZpcm1UcnVzdDErMCkGA1UE +CxMiU2VlIHd3dy5hZmZpcm10cnVzdC5jb20vcmVwb3NpdG9yeTEzMDEGA1UEAxMq +QWZmaXJtVHJ1c3QgRXh0ZW5kZWQgVmFsaWRhdGlvbiBDQSAtIEVWRUMxMHYwEAYH +KoZIzj0CAQYFK4EEACIDYgAEu9f5NkumdaVlmaNaxpDB+rBk/S6lhqcUU1zTLcRz +4G0dr4290hezjrvZJxGJ/X15aexpdD2V9cwaPD/yuEJcaaz+rg/qDoqQF3+AFqVc +41jw1E0S59+57XVKLtXI7Xh6o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsG +AQUFBzABhhtodHRwOi8vb2NzcC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFMaQ +jAKD113jvjucLtVlfSoQYO7lMBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgw +FoAUmq8pesARNTUmUTAAw2r+QNWu1jwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYI +KwYBBQUHAgEWJmh0dHBzOi8vd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5 +MEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2Ny +bC9BZmZpcm1UcnVzdFByZW1pdW1FQ0MuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNV +HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwCgYIKoZIzj0EAwMDaAAwZQIwHJ5g +a6sHvQ51DGr0bWq34awuwlWbybC2grHoNp5uYapcXr/qTJusb/6n+dczqFdaAjEA +7VQY06fE9ifMnTgT9824jc3+H6kfhMk4PoIj9ouWdYfc1DyTBS/low9Hb8liQyFr +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrust2048ca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrust2048ca-chain.pem new file mode 100644 index 00000000000..253072d00ed --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrust2048ca-chain.pem @@ -0,0 +1,76 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: 946069240 (0x3863def8) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048) + Validity + Not Before: Dec 24 17:50:51 1999 GMT + Not After : Jul 24 14:15:12 2029 GMT + +-----BEGIN CERTIFICATE----- +MIIGiDCCBXCgAwIBAgIQS5P8oVcgTBT74PnIwDQivjANBgkqhkiG9w0BAQsFADCB +ujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsT +H1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAy +MDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwG +A1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxSzAeFw0y +MzEwMDIxOTE4MTBaFw0yNDExMDIxOTE4MDlaMGkxCzAJBgNVBAYTAkNBMRAwDgYD +VQQIEwdPbnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExGDAWBgNVBAoTD0VudHJ1c3Qg +TGltaXRlZDEdMBsGA1UEAxMUMjA0OHRlc3QuZW50cnVzdC5uZXQwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgkrsKoDEHyJjll/fu7mjvtbookb50rzTI +i+jQzvtL8AJOcCfxJL1cVriufc/zRYdSQeRJxkbUb+SqIJkec+27onPpY3xOjJAK +bWdmac1Iv9JPXYMpKJXnOGrooeXEtCcKSKphx4VhHnLA67BGfSNfHLm4JwghX4jY +VpZ8P89gmh8l1eLRP+b3y7OzEkFliwmErALSD8i/bkzE+GxYMnpg/HI2Iw1lakxE +wZOg0ydgl7jHWZUDdnxhAvLS/hfzPVhi9ZwgoXQJiUXUp0JJo6QgVOIC5IztpdZa +3HW1VK7a0eTLhmdFRx39ARn/GbbIyoIqUzLOhAa2cbsGIJjtXjhrAgMBAAGjggLY +MIIC1DAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRHn0CebGnHRqTZTeTYCbPHhiVB +MzAfBgNVHSMEGDAWgBSConB03bxTP8971PfNf6dgxgpMvzBoBggrBgEFBQcBAQRc +MFowIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDMGCCsGAQUF +BzAChidodHRwOi8vYWlhLmVudHJ1c3QubmV0L2wxay1jaGFpbjI1Ni5jZXIwMwYD +VR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbnRydXN0Lm5ldC9sZXZlbDFrLmNy +bDAfBgNVHREEGDAWghQyMDQ4dGVzdC5lbnRydXN0Lm5ldDAOBgNVHQ8BAf8EBAMC +BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBMGA1UdIAQMMAowCAYG +Z4EMAQICMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdwA/F0tP1yJHWJQdZRyE +vg0S7ZA3fx+FauvBvyiF7PhkbgAAAYrx05lbAAAEAwBIMEYCIQDbMXKdzSr90jM+ +TekjpqVTEBDDvub7+AEx/kQYzf9gugIhAKPCjJmIh1NZrKkwK8MsOEL4jkN6FJ/h +4kiiJoze3fB/AHYAdv+IPwq2+5VRwmHM9Ye6NLSkzbsp3GhCCp/mZ0xaOnQAAAGK +8dOZVAAABAMARzBFAiAW11p7sV2byjrpk6AMQrMGwV2CuT3AKNuQVyxva7XQPAIh +AP1P7DfYsZ1aR12Tkg2x2BYjFrlsJCl36n5I/565xQk4AHUA2ra/az+1tiKfm8K7 +XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGK8dOZnwAABAMARjBEAiA43NURCcnHNpkH +XggwpVY9QYNIEAjpHEcPmyXJuQ9y8QIgPqx0MnlKXLuJVReuI5Hzc3iFtcYo070d +UYWH2AuVaFwwDQYJKoZIhvcNAQELBQADggEBAIZoSHApNF6DNYvGKHZJX411QkA0 +5zkq3dcm95BFomaqroEp1QeUeQ8e6xofUs84CURzopE9P81JBHX2Qzb/VeBzZOKy +dekaoz4NGW5ZvpMh7HXXaUpHKU/xZ5uUHVSatBU+cnidPhgn1czntqOwjzsgEZNW +/wbPEjqvIrZvAW4DPak/MSwlENys4ty5gX4453S5gwd18b+NFBq44O/FofR8bvWU +3lJ3VcVeONDzTcXPv+Yd1SlyO1/eXdWlFqloYFjkpcQ4wSLbOEeiWWITkZ0xCAxQ +j8uWuDOSyFQLpaPJvEuG1dlho7RZdor0flUIxYfqg2Nr4Svq1ezskwrdQm0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFKjCCBBKgAwIBAgIQLgRRzl0kJMcrXWV2cWUG2DANBgkqhkiG9w0BAQsFADCB +tDEUMBIGA1UEChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5l +dC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNV +BAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1 +c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0yMjExMjUy +MTE5NDNaFw0yOTA3MjMwMDAwMDBaMIG6MQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +RW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdh +bC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMTIgRW50cnVzdCwgSW5jLiAtIGZvciBh +dXRob3JpemVkIHVzZSBvbmx5MS4wLAYDVQQDEyVFbnRydXN0IENlcnRpZmljYXRp +b24gQXV0aG9yaXR5IC0gTDFLMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA2j+W0E25L0Tn2zlem1DuXKVh2kFnUwmqAJqOV38pa9vH4SEkqjrQjUcj0u1y +FvCRIdJdt7hLqIOPt5EyaM/OJZMssn2XyP7BtBe6CZ4DkJN7fEmDImiKm95HwzGY +ei59QAvS7z7Tsoyqj0ip/wDoKVgG97aTWpRzJiatWA7lQrjV6nN5ZGhTJbiEz5R6 +rgZFDKNrTdDGvuoYpDbwkrK6HIiPOlJ/915tgxyd8B/lw9bdpXiSPbBtLOrJz5RB +GXFEaLpHPATpXbo+8DX3Fbae8i4VHj9HyMg4p3NFXU2wO7GOFyk36t0FASK7lDYq +jVs1/lMZLwhGwSqzGmIdTivZGwIDAQABo4IBLjCCASowEgYDVR0TAQH/BAgwBgEB +/wIBADAdBgNVHQ4EFgQUgqJwdN28Uz/Pe9T3zX+nYMYKTL8wHwYDVR0jBBgwFoAU +VeSB0RGAvtiJuQijMfmhJAkWuXAwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzAB +hhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAyBgNVHR8EKzApMCegJaAjhiFodHRw +Oi8vY3JsLmVudHJ1c3QubmV0LzIwNDhjYS5jcmwwDgYDVR0PAQH/BAQDAgEGMB0G +A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA8BgNVHSAENTAzMDEGBFUdIAAw +KTAnBggrBgEFBQcCARYbaHR0cHM6Ly93d3cuZW50cnVzdC5uZXQvcnBhMA0GCSqG +SIb3DQEBCwUAA4IBAQAuAlHLO8CoKt2a4I23UDkKc7kQI3nUkWqq2RxRh8a/4TEF +C9WSF03EHVBW9JZZcrZ3ZdTDRsNF8vSqmCABz1FLu6vw3D3bEXELonAYlkmeFFV7 +1hiW9AdyMJD92XsXiU0Yr9J76Tk4iknMTTHiZXdZOcPMOXlMwPy++HS5tTIyqO0d +zl1PS8tlCcZrKaNNKbmiIWPhmBUSog9IQt2VKpoAIP8tlvRt5tHf5qW5m7vp7qmG +HF2ou54+qQIXO6jIP8CQ4xWvj0aiLklTNMkvXesaVq0xzNgRkx9ZzhREfbuM6eWc +GQHwG7m+JmfL+u1dCAZhh4Uyn5oLU9gogFM6v4jX +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustevca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustevca-chain.pem new file mode 100644 index 00000000000..e9c06b19c69 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustevca-chain.pem @@ -0,0 +1,79 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: 1164660820 (0x456b5054) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority + Validity + Not Before: Nov 27 20:23:42 2006 GMT + Not After : Nov 27 20:53:42 2026 GMT + +-----BEGIN CERTIFICATE----- +MIIHEjCCBfqgAwIBAgIQFhH4VGskTR+tQK3JbN63kTANBgkqhkiG9w0BAQsFADCB +sTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsT +MHd3dy5lbnRydXN0Lm5ldC9ycGEgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5j +ZTEfMB0GA1UECxMWKGMpIDIwMDkgRW50cnVzdCwgSW5jLjEuMCwGA1UEAxMlRW50 +cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxRTAeFw0yNDA2MjgyMTQw +NDVaFw0yNTA3MjgyMTQwNDRaMIHIMQswCQYDVQQGEwJDQTEQMA4GA1UECBMHT250 +YXJpbzEPMA0GA1UEBxMGT3R0YXdhMRMwEQYLKwYBBAGCNzwCAQMTAkNBMRgwFgYL +KwYBBAGCNzwCAQITB09udGFyaW8xGDAWBgNVBAoTD0VudHJ1c3QgTGltaXRlZDEd +MBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzE5MTM2MDUx +HDAaBgNVBAMTE3ZhbGlkZXYuZW50cnVzdC5uZXQwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDL2vkrBEZ5qeVdac1C01bcNnoeCU3AVU3Fh1Ifldic9/Gw +xqNVOFYQNzTk8M62FnPUvas4MnXmeBkPhhym+dnjsM22EeS2p6gTlvOGtJFVr+Ix +vq1UAKtqK0gYGriW6SexroSYiG1O0aeqEnKSLlEBHYhmacj2jlbx0ToxMfdBMRRq +4UjnIrh/CBochxt7aKv525tChnZGMT06QKAjx71w2cou0C05v83KJ75EI4EAmTfE +z9sKJeST5pH5MI3WKcP6ZmXynKYSIpIGb4Z8B9Ftp8HdzdR9EafOSlRlbIkEn3lm +nq4UCph48/PsUcJoViBCoY6zDLcPGt3gGQVIjq3vAgMBAAGjggMLMIIDBzAMBgNV +HRMBAf8EAjAAMB0GA1UdDgQWBBRF6MZkqXf3sICXuvbrBH1R9I8bAjAfBgNVHSME +GDAWgBRbQYqyxEPBvb/IVEFVneCWrf+5oTBlBggrBgEFBQcBAQRZMFcwIwYIKwYB +BQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDAGCCsGAQUFBzAChiRodHRw +Oi8vYWlhLmVudHJ1c3QubmV0L2wxZS1jaGFpbi5jZXIwMwYDVR0fBCwwKjAooCag +JIYiaHR0cDovL2NybC5lbnRydXN0Lm5ldC9sZXZlbDFlLmNybDAeBgNVHREEFzAV +ghN2YWxpZGV2LmVudHJ1c3QubmV0MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAU +BggrBgEFBQcDAQYIKwYBBQUHAwIwSwYDVR0gBEQwQjAHBgVngQwBATA3BgpghkgB +hvpsCgECMCkwJwYIKwYBBQUHAgEWG2h0dHBzOi8vd3d3LmVudHJ1c3QubmV0L3Jw +YTCCAX0GCisGAQQB1nkCBAIEggFtBIIBaQFnAHUAEvFONL1TckyEBhnDjz96E/jn +tWKHiJxtMAWE6+WGJjoAAAGQYMsp8gAABAMARjBEAiAL794Fw7wyzricvRl+2AON +FbGf2hwDB3wh8RkGLBRQ7AIgTCarii0atho7ZeUO3h66Ug7s7WxnF9onDZrtoMrH +U9MAdQAN4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAAAZBgyyoMAAAE +AwBGMEQCIFaXc4M9C9mNukrV68Sc2E5lw9srQ80nMBCGseY99nFxAiAppQmR9FKC +TE/ROlgZRfimx61W4k+SaQ52eek4JNWXXwB3ABoE/0nQVB1Ar/agw7/x2MRnL07s +7iNAaJhrF0Au3Il9AAABkGDLKi0AAAQDAEgwRgIhAPFUevU47H5uJqYL5y1ClFS7 +mEve7E8350JKnR1VykGLAiEArn7VAJcmRNNCDAegsHCCLlpasz9PLHFd9XHQAwvL +IFwwDQYJKoZIhvcNAQELBQADggEBAHfMCQP5Y+7IYzqOh5D/81WqHagmWOqkJYsN +33uux44fhVGqiG1O5ImTQsxJpt/HmDuK1qLEjG31Y9q89U91KAqLQauCQ5iMXrBg +TlwK8kzO1XYC5KkpO5ZWanqfjOahM9mkEKHPV9srwj6xTbInCq9DabRwuWyohj3b +EKrmB016TOT0hJ94jCb8PWl15oQJdsGlEfrG7amtDSRuoDHVA3nXJIJqx5LVnErB +glfsAmP8TPkWYY8kuNE2Rjr2M6P5LRLEvtRELCQF3lPuY0+xxGksGVM207YqhYKv +GzMmA8c7tF3ZclbE0rUA2T8FuBuweAV8tnWq2TaeAHWIJ4nY17s= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFHjCCBAagAwIBAgIRAIZmsCrBy1RAAAAAAFHTWJwwDQYJKoZIhvcNAQELBQAw +gbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkwNwYDVQQL +EzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVu +dHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xOTA2MTkxNjUy +MDhaFw0yNjExMTkxNzIyMDhaMIGxMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50 +cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L3JwYSBpcyBpbmNv +cnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwOSBFbnRydXN0 +LCBJbmMuMS4wLAYDVQQDEyVFbnRydXN0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gTDFFMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlsEVHfdDiRm +3Cqh24DMXcdf/VIWWNpflAapuLa5YwxHIILsx5VOi7h3Umo9tYep1uHMdOWmyMDU +Vk+NLtYIPgxMQz7wQZNeRu8559llKgx2UCe9Ww0zMwfg96KpnOERM61m/NIseqqj +cxa+k4V1D9c3jPojt2T440xu7bMFveI223zedkTagnJ2tm7/lKHQhvcQzUpai7B1 +jGZSgE5ITEmDpkDXd4ETTV5yfkhGIqoP4j5llDjhcnH+SnEJujV/VYk9gdW4KAEQ +dzZaEIXSvWCEK0lhlAzeTEBqKsR5YIQkgjJpSphL4lYQugNFUSDTz9qOVBtFtnq6 +l5pa2MbRXwIDAQABo4IBLjCCASowDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG +CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUF +BwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZW50cnVzdC5uZXQwMwYD +VR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbnRydXN0Lm5ldC9yb290Y2ExLmNy +bDA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggrBgEFBQcCARYaaHR0cDovL3d3dy5l +bnRydXN0Lm5ldC9ycGEwHQYDVR0OBBYEFFtBirLEQ8G9v8hUQVWd4Jat/7mhMB8G +A1UdIwQYMBaAFGiQ5GekplOAx4ZmpPH3S0P7hL1tMA0GCSqGSIb3DQEBCwUAA4IB +AQAPUNBX97sqIXZl/zLu53iv7a0HK7prvD0cVaZM0yRfVptvARgjIZZzTtv32v6X +wSr4fDeRmpLaTWtipBGSqh3fNkTSVT8GGBq6+h1lrPEYv6jnStDf7VLQxVliKt2w +h34JjgRUx9rdia30tk/EpPavkxxPks8vjoLN3f4dbkIY/sfljyZbseqVLx9kl/we +OvqL6jZgaQOapFQLZJze7VwLiPVuUnW8ddK3JIE1a5YCZs0irIW5+96ttznIgPK2 +aUOmHQp/zasi7SFl49HrKGKWtZuyDB9U56e01H6PDTpSSSTPyLsSVg3JALHBPDzS +bBraAU3wuAyc3BQ4OIOmwwnT +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcaec1-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcaec1-chain.pem new file mode 100644 index 00000000000..8ea22c05b80 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcaec1-chain.pem @@ -0,0 +1,66 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: + a6:8b:79:29:00:00:00:00:50:d0:91:f9 + Signature Algorithm: ecdsa-with-SHA384 + Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2012 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - EC1 + Validity + Not Before: Dec 18 15:25:36 2012 GMT + Not After : Dec 18 15:55:36 2037 GMT + +-----BEGIN CERTIFICATE----- +MIIFzDCCBVOgAwIBAgIQcbNJ8XJLeT3fV8DU3QNYSDAKBggqhkjOPQQDAzCBujEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1Nl +ZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDE2 +IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwGA1UE +AxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxSjAeFw0yNDA2 +MjgyMTM5MzVaFw0yNTA3MjgyMTM5MzRaMIHLMQswCQYDVQQGEwJDQTEQMA4GA1UE +CBMHT250YXJpbzEPMA0GA1UEBxMGT3R0YXdhMRMwEQYLKwYBBAGCNzwCAQMTAkNB +MRgwFgYLKwYBBAGCNzwCAQITB09udGFyaW8xGDAWBgNVBAoTD0VudHJ1c3QgTGlt +aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEzARBgNVBAUTCjEw +MDA0OTI4NzkxHDAaBgNVBAMTE3ZhbGlkZWMuZW50cnVzdC5uZXQwdjAQBgcqhkjO +PQIBBgUrgQQAIgNiAAS90ZyZ86Gl5Fh1qJ/70UwyQWATu3igiQLeVVvZ4G79SBEG +Xc4TcAn0LzBhfJonAzWFkAS860ARjvFHgUj0otyT+Q2/zC9c8CjOsL3bYp3SNUbC +FWBhIV0vhGGY8NafeXCjggMJMIIDBTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTP +DXJE/iZfi5wUSAo4GN4thBCCHDAfBgNVHSMEGDAWgBTD+UUDvsj5CzxFNfPrcuzn +6OuUmzBjBggrBgEFBQcBAQRXMFUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVu +dHJ1c3QubmV0MC4GCCsGAQUFBzAChiJodHRwOi8vYWlhLmVudHJ1c3QubmV0L2wx +ai1lYzEuY2VyMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50cnVzdC5u +ZXQvbGV2ZWwxai5jcmwwHgYDVR0RBBcwFYITdmFsaWRlYy5lbnRydXN0Lm5ldDAO +BgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEsG +A1UdIAREMEIwBwYFZ4EMAQEwNwYKYIZIAYb6bAoBAjApMCcGCCsGAQUFBwIBFhto +dHRwczovL3d3dy5lbnRydXN0Lm5ldC9ycGEwggF9BgorBgEEAdZ5AgQCBIIBbQSC +AWkBZwB1AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0AAABkGDKGokA +AAQDAEYwRAIgZwtzml8YzKjqeP86zX+88q8sHOt//2Qmahr2tk97ozUCIFCOM2nF +s1GJVBjKQZEH8QqkivVp+Cai9pC/57TiOmCOAHUAzPsPaoVxCWX+lZtTzumyfCLp +hVwNl422qX5UwP5MDbAAAAGQYMoamAAABAMARjBEAiEA37X8EgQAUzLxn/Ny1Yx3 +uszQF5D85m8vZ0otf8nHzuwCH168zpAxzKS71Fz6CgmDS0QZOfBSYFBD+Pdcm6e1 +ilkAdwAS8U40vVNyTIQGGcOPP3oT+Oe1YoeInG0wBYTr5YYmOgAAAZBgyhq6AAAE +AwBIMEYCIQCljVuYzRe6oQTZPdx0tGhIQSOwM1JbxoMJu2cW+gEGLAIhAMSSJoni +0KT3KavwtsSWuuHsWjt8atv6TpJtLmVxCIdlMAoGCCqGSM49BAMDA2cAMGQCMBPY +1dn1Js8F9b08aVCZ3vqDGFTKuzTXaxArf/y/WhLtcHdZPLaYVifQcAKzp1WCFQIw +MvpE6RDccmnZi5TX88p16s8ev/qkegpbf7Xuw1JQEfy2NRwrXc+NwA422EjXBTti +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIID5zCCA2ygAwIBAgIQCoPUgD5+n1EAAAAAUdTB9zAKBggqhkjOPQQDAzCBvzEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1Nl +ZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEy +IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UE +AxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4X +DTE2MDQwNTIwMTk1NFoXDTM3MTAwNTIwNDk1NFowgboxCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu +bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNiBFbnRydXN0LCBJbmMu +IC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxLjAsBgNVBAMTJUVudHJ1c3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgLSBMMUowdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAT14eFXmpQX/dEf7NAxrMH13n0btz1KKvH2S1rROGPAKex2CY8yxznbffK/MbCk +F7ByYXGs1+8kL5xmTysU/c+YmjOZx2mMSAk2DPw30fijJ3tRrwChZ+TBpgtB6+A5 +MsCjggEuMIIBKjAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAz +BggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3Qu +bmV0MDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvZWMx +cm9vdC5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6 +Ly93d3cuZW50cnVzdC5uZXQvcnBhMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjAdBgNVHQ4EFgQUw/lFA77I+Qs8RTXz63Ls5+jrlJswHwYDVR0jBBgwFoAU +t2PnGt2N6QimVYOk4GpQQWURQkkwCgYIKoZIzj0EAwMDaQAwZgIxAPnVAOqxKDd7 +v37EBmpPqWCCWBFPKW6HpRx3GUWc9caeQIw8rO2HXYgf92pb/TsJYAIxAJhI0MpR +z5L42xF1R9UIPfQxCMwgsnWBqIqcfMrMO+2DxQy6GIP3cFFj9gRyxguKWw== +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag2-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag2-chain.pem new file mode 100644 index 00000000000..5fcbf9ffc2c --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag2-chain.pem @@ -0,0 +1,80 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: 1246989352 (0x4a538c28) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2009 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G2 + Validity + Not Before: Jul 7 17:25:54 2009 GMT + Not After : Dec 7 17:55:54 2030 GMT + +-----BEGIN CERTIFICATE----- +MIIHOzCCBiOgAwIBAgIQWFfRPoYcAxEc+S0tOlD+ljANBgkqhkiG9w0BAQsFADCB +ujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsT +H1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAy +MDE0IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwG +A1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxTTAeFw0y +NDA2MjgyMTQyMTRaFw0yNTA3MjgyMTQyMTNaMIHLMQswCQYDVQQGEwJDQTEQMA4G +A1UECBMHT250YXJpbzEPMA0GA1UEBxMGT3R0YXdhMRMwEQYLKwYBBAGCNzwCAQMT +AkNBMRgwFgYLKwYBBAGCNzwCAQITB09udGFyaW8xGDAWBgNVBAoTD0VudHJ1c3Qg +TGltaXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEzARBgNVBAUT +CjEwMDA0OTI4NzkxHDAaBgNVBAMTE3ZhbGlkZzIuZW50cnVzdC5uZXQwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZ66eWZS5ytmbHJeHcA9WfnpbGFC04 +Tov7L0NWiStVRPEFrXrGSn6RPriGci6RwrCz5yn47EWjk2AjSD4e5lySDKHwTg+0 +S9pl3lcSd8tQOTbTwVM0EfOxdUlO4IY0jCOSM8rnZUc1JvEIIrXWXWF9AWoDb4BQ +erTefRm/YykFC558PEzn84vU9KoEmDwIP4upWKVutuzBpHWhZW3q9wagg62KifHN +1yaagv4PUGgdkrVkyA1ZO3D7b2RpQjBreOTk+tsTnWtbAkFGtRBOA/2QrEvyqMU7 +eCnpFZMIaj2tKeSLqhIWxzOnrAGUJNp5wLYmVnnhPhHEv1g79pNsZLR3AgMBAAGj +ggMoMIIDJDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRt85gfkWUjfTKgrLytMp8o +VvOe3zAfBgNVHSMEGDAWgBTD99C1KjCtrw2RIXA5VN28iXDHOjBoBggrBgEFBQcB +AQRcMFowIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDMGCCsG +AQUFBzAChidodHRwOi8vYWlhLmVudHJ1c3QubmV0L2wxbS1jaGFpbjI1Ni5jZXIw +MwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbnRydXN0Lm5ldC9sZXZlbDFt +LmNybDA3BgNVHREEMDAughN2YWxpZGcyLmVudHJ1c3QubmV0ghd3d3cudmFsaWRn +Mi5lbnRydXN0Lm5ldDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH +AwEGCCsGAQUFBwMCMEsGA1UdIAREMEIwBwYFZ4EMAQEwNwYKYIZIAYb6bAoBAjAp +MCcGCCsGAQUFBwIBFhtodHRwczovL3d3dy5lbnRydXN0Lm5ldC9ycGEwggF+Bgor +BgEEAdZ5AgQCBIIBbgSCAWoBaAB3ABLxTjS9U3JMhAYZw48/ehP457Vih4icbTAF +hOvlhiY6AAABkGDMhQQAAAQDAEgwRgIhAMzddgbnWlodtosz6EMh2Y89n0JR4eMO +v+W6tUp2gVwYAiEA6UKa2eFlX0KdzuZCvTlPgi8DeK3ZI2wffyV2bYMXtsIAdgAN +4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAAAZBgzIURAAAEAwBHMEUC +IQDmVH2NlaV2/Y3OaPMXhH+BT63zA+Bh/5aCfPiYrJ7K2AIgRADPHzpwS7bfvVZI +k8QxUBSCDXFmZQOrpamBaEko6YIAdQDM+w9qhXEJZf6Vm1PO6bJ8IumFXA2Xjbap +flTA/kwNsAAAAZBgzIUGAAAEAwBGMEQCIA1CHfNw7cCcJSb3s7ik9Wflf3irqE9G +QKxZ+Y9BOIx0AiA6CMvw7OHjG519E1tZgr/HFRXzxKchBp80dfsaEKxY9zANBgkq +hkiG9w0BAQsFAAOCAQEAqvn1CTObiV5zKVY6NWjGK49Wqsr9t1ok/h/yfKRmr36O +UZkMTPANj0uhwM4gtieTze9hnNzEkx1ec6G40JyABRiSX+0dtq3n8wiW3d8G1Qj5 +/s8yZ13/ATrdjjr1mlGOvh0sgWTTPaQpl8ijXTy40GYpZIUXXBK09Rm6W0siq+7m +OHNpJR4APWOBBU4QwiWrHHsFq4KvwxiTjNWWizCOnZwVi3awNBoDD/Iwszn+trOA +8U/1SsHGuPBWKajcGorwi2zQ99JxAwJJ8XNBCekynjbPZYx52KkqfR07Fd2Occbl +3lh3wXrepzzU1a6vdyiQpagX8btyIqQpAzytypzaLQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFLTCCBBWgAwIBAgIMYaHn0gAAAABR02amMA0GCSqGSIb3DQEBCwUAMIG+MQsw +CQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2Vl +IHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMDkg +RW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIwMAYDVQQD +EylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjAeFw0x +NDEyMTUxNTI1MDNaFw0zMDEwMTUxNTU1MDNaMIG6MQswCQYDVQQGEwJVUzEWMBQG +A1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5l +dC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMTQgRW50cnVzdCwgSW5jLiAt +IGZvciBhdXRob3JpemVkIHVzZSBvbmx5MS4wLAYDVQQDEyVFbnRydXN0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gTDFNMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA0IHBOSPCsdHs91fdVSQ2kSAiSPf8ylIKsKs/M7WwhAf23056sPuY +Ij0BrFb7cW2y7rmgD1J3q5iTvjOK64dex6qwymmPQwhqPyK/MzlG1ZTy4kwFItln +gJHxBEoOm3yiydJs/TwJhL39axSagR3nioPvYRZ1R5gTOw2QFpi/iuInMlOZmcP7 +lhw192LtjL1JcdJDQ6Gh4yEqI3CodT2ybEYGYW8YZ+QpfrI8wcVfCR5uRE7sIZlY +FUj0VUgqtzS0BeN8SYwAWN46lsw53GEzVc4qLj/RmWLoquY0djGqr3kplnjLgRSv +adr7BLlZg0SqCU+01CwBnZuUMWstoc/B5QIDAQABo4IBKzCCAScwDgYDVR0PAQH/ +BAQDAgEGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATASBgNVHRMBAf8E +CDAGAQH/AgEAMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29j +c3AuZW50cnVzdC5uZXQwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2NybC5lbnRy +dXN0Lm5ldC9nMmNhLmNybDA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggrBgEFBQcC +ARYaaHR0cDovL3d3dy5lbnRydXN0Lm5ldC9ycGEwHQYDVR0OBBYEFMP30LUqMK2v +DZEhcDlU3byJcMc6MB8GA1UdIwQYMBaAFGpyJnrQHu995ztpUdRsjZ+QEmarMA0G +CSqGSIb3DQEBCwUAA4IBAQC0h8eEIhopwKR47PVPG7SEl2937tTPWa+oQ5YvHVje +pvMVWy7ZQ5xMQrkXFxGttLFBx2YMIoYFp7Qi+8VoaIqIMthx1hGOjlJ+Qgld2dnA +DizvRGsf2yS89byxqsGK5Wbb0CTz34mmi/5e0FC6m3UAyQhKS3Q/WFOv9rihbISY +Jnz8/DVRZZgeO2x28JkPxLkJ1YXYJKd/KsLak0tkuHB8VCnTglTVz6WUwzOeTTRn +4Dh2ZgCN0C/GqwmqcvrOLzWJ/MDtBgO334wlV/H77yiI2YIowAQPlIFpI+CRKMVe +1QzX1CA778n4wI+nQc1XRG5sZ2L+hN/nYNjvv9QiHg3n +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag4-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag4-chain.pem new file mode 100644 index 00000000000..e649abf28b3 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag4-chain.pem @@ -0,0 +1,92 @@ +Root Certificate: + Version: 3 (0x2) + Serial Number: + d9:b5:43:7f:af:a9:39:0f:00:00:00:00:55:65:ad:58 + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, O=Entrust, Inc., OU=See www.entrust.net/legal-terms, OU=(c) 2015 Entrust, Inc. - for authorized use only, CN=Entrust Root Certification Authority - G4 + Validity + Not Before: May 27 11:11:16 2015 GMT + Not After : Dec 27 11:41:16 2037 GMT + +-----BEGIN CERTIFICATE----- +MIIIIzCCBwugAwIBAgIQDD4I8FgD7+DVcBLMBwa39jANBgkqhkiG9w0BAQsFADCB +ujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsT +H1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAy +MDE0IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwG +A1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxTjAeFw0y +NDA2MjgyMTQzNTRaFw0yNTA3MjgyMTQzNTNaMIHLMQswCQYDVQQGEwJDQTEQMA4G +A1UECBMHT250YXJpbzEPMA0GA1UEBxMGT3R0YXdhMRMwEQYLKwYBBAGCNzwCAQMT +AkNBMRgwFgYLKwYBBAGCNzwCAQITB09udGFyaW8xGDAWBgNVBAoTD0VudHJ1c3Qg +TGltaXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEzARBgNVBAUT +CjEwMDA0OTI4NzkxHDAaBgNVBAMTE3ZhbGlkZzQuZW50cnVzdC5uZXQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCfUHGdeme0jraIiUzPYtuX1G9rlCU1 +eKDqDbsgp7VIS7rI/VgbsS7oKnE6KHP+qGrXRhYdvFLFDa+REY6fVOWkLuTXhVLb +5C7ym2pi0OUMKvrGtDLUxlHiEAmkmjPDl6TLMTDrLgWOLFMRzyeTcxnZtMrxUnAf +yzSPlqm1bkN/oRp2EOiXvuSbci8UA0QswV6g8EUbRB0qyv6OophoaQYo/+KRwTJT +k6S8YDsEJnlDb8tjEhfIUjp2Md5ThBxf5Ib29aXebZ5HFh2x5VPrzOwDUPk0fVNM +pWFfiX79RW6w5Vei5qtretLohbw6b5aJmaJ1LweAEkIlhy5eUuuG6v8Efm8JSAle +eKMtflTigmayaWMVCd2GeB6LajcflAw7BUU2brRMJwMpaeXXhL/mVpjbev/5TtVD ++H9IlW3PMyQnUJc0YuUVmdi1eOM5qoQaQE4BDPHz2G41eDgT8J9Gb1FX5mT+9l2I +iJD47pwcBIw5tHCn2nuz1+8CDuYpfH2+t2LPFHVI15h1scGotZvzUJ5TzPdQqZI7 +K2LTL49Zs2HsObrGr07Vj28WyzkjIfTrVSV/29hgz1zVjUa0uyTeOzrc3VIg7NTv +RoMTTYuUeUoMSmFQ8z9CSGh7cxFlrhGjFO+66++JFNwakAEp7kS5c2qTLaapY9dM +8UMIr5951z994QIDAQABo4IDEDCCAwwwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU +/EjjpmMa/SepMqPlglXS5AbGcScwHwYDVR0jBBgwFoAU7kfRhXHx/S23P7s+Y1h3 +F0lADpUwaAYIKwYBBQUHAQEEXDBaMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5l +bnRydXN0Lm5ldDAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5lbnRydXN0Lm5ldC9s +MW4tY2hhaW4yNTYuY2VyMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50 +cnVzdC5uZXQvbGV2ZWwxbi5jcmwwHgYDVR0RBBcwFYITdmFsaWRnNC5lbnRydXN0 +Lm5ldDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF +BwMBMEsGA1UdIAREMEIwBwYFZ4EMAQEwNwYKYIZIAYb6bAoBAjApMCcGCCsGAQUF +BwIBFhtodHRwczovL3d3dy5lbnRydXN0Lm5ldC9ycGEwggF/BgorBgEEAdZ5AgQC +BIIBbwSCAWsBaQB2ABLxTjS9U3JMhAYZw48/ehP457Vih4icbTAFhOvlhiY6AAAB +kGDOC9YAAAQDAEcwRQIgWhFWhf2sBQ3ufMH0yubwLDt+3f/b5rScs09o1YEjg6MC +IQDpkgEMWBAM+NV2aCnC8QH+RH6xBqhPPt6JZTm3W+vHkwB3ABoE/0nQVB1Ar/ag +w7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDODBQAAAQDAEgwRgIhAOgp+oas+jBr +9wOBo0QDdVQGmP8KJupfRf/MDKO+kSRjAiEA9JnEHTbFHre2TS9habVJA/3jM/t5 +CKtixwQqdpLXQUAAdgAN4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAA +AZBgzgwVAAAEAwBHMEUCIBOYI8rl87VepcPQlaGh6AbKhKw1UlbxIf7etR/d2M47 +AiEAkFXOVvzkP6kX/z1yRneYn0mlPbDvAFLsSDghl/gkdtYwDQYJKoZIhvcNAQEL +BQADggEBAJovgoheNHFBUpnodfOiKtpRo8AE6dLuOX1H2uRHiDg0Gza0/w95KkEE +BqjKmJIbJrs2TQJnkM0LjaubHn1TP4XC40qieMXB4ylJzC5FWDZBqMHZmLTvVY01 +irBMyub0On8d1BlEquD2r3KHQFnyUvi/uxzbNJOVbNJYglKhTI+UfcXk7zpHmNG+ ++SbBkpJkuqQ9ujG1K25FRa/01j1p4ZlDrJ3KCT7fDEf10TN0u5VX6moVT9cRVR2U +gX16BV8m/hoJVTD0fBCKIKjtklS//b+Jr49uxWFulrDwlRKyDWmBXLnqsZvpCobi +deDsWiUkcvd+DjNgpDTEHCTrXXjd8tU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGMjCCBBqgAwIBAgIRAKvsd/8bQQwHAAAAAFVl2AUwDQYJKoZIhvcNAQELBQAw +gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw +BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 +MB4XDTE3MTEyMjIwMDQyMFoXDTMwMTIyMjIwMzQyMFowgboxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 +c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNCBFbnRydXN0LCBJ +bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxLjAsBgNVBAMTJUVudHJ1c3Qg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBMMU4wggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDcSG+caYQ4xcvf+dt8bgCEHorO0g5j0H1NOtQzRXgUoG8y +QuRbJX9swyKqQZbsc18YvTV8OKA/uSNE46Jvq47TFPojWWTVLbNDqpM07e4EFYKs +A9NFzAUngijnf3ivnXA6iNPAMXaEhXmhY/YFjk8NoM7Y1PFsA0oj5hamKQ06iO/j +gvBScLmnQ1ju9Qj9IGIg18UL5AJNw0frspLUQBYVrLGaqAy5Nl2BUJKaZ4vnSLvP +nk6YrB15mo1phHae10Ba4fx7R3z8IZ/hby4OXTy/KZpu107VEQPAwTuDK8ZXxB5y +0DSzi4vaw27aLrUsq4aFqUo03gEfC31vWW76TNkFAgMBAAGjggErMIIBJzAOBgNV +HQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEF +BQcDAQYIKwYBBQUHAwIwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEW +Gmh0dHA6Ly93d3cuZW50cnVzdC5uZXQvcnBhMDMGCCsGAQUFBwEBBCcwJTAjBggr +BgEFBQcwAYYXaHR0cDovL29jc3AuZW50cnVzdC5uZXQwMAYDVR0fBCkwJzAloCOg +IYYfaHR0cDovL2NybC5lbnRydXN0Lm5ldC9nNGNhLmNybDAdBgNVHQ4EFgQU7kfR +hXHx/S23P7s+Y1h3F0lADpUwHwYDVR0jBBgwFoAUnzjEViPDOeigcWzoVEzk6Dqx +v2cwDQYJKoZIhvcNAQELBQADggIBACMeFFgsWmC7h6D1v8DJUkOpm/m5UhVhO0hb +pQMQKMhKkl744Y9SWG4WNmpQy743TTciEJPZFhc7ke2R6VmK8ZJUqro2awOw1RWZ +OtHla59Btf1NQd41vOVdU+qFhs8lFfXg9sK7YHTrfxHtMXLoGnkkamK3xJgn7sXa +/zUvUDBTpDCXcpO9SyHoKIQswmkIPpRyIdPF4biRdR3N+9MYmlfqN/Nk3OEZ73xZ +AUZP6Gu+f9cEiHTA8NdYHCPLJWyFnIHWK+QuTFEnKYnOYxCeroLBNOO64e8JWZ39 +kZ22BBXhHzqOCCczS7JOJTRF+JgvWuxbFwRstj8qf3fE+JndWmq2FC4hTHtpuK5K +ENuiRm5gdkXfsXmB+qB6y5gaajiTIMscGIcZIKTe2YdKrLoicvEz8k+loM7favik +vzFioTNTDHYGx3mkfElBE7ycY8n+jZE3QBBv33k28MeQi7XNgEaMc4tYwoZIdE9A +xVccXTzEQzka82dOkRB1dU0XZId9XAWv+CtNc2TjF6Wgx2seA/c6H8S0IfgQBIV2 +8iN2wZns2QFdawkdy3hMUqPnA++kuGhLW3GemsIY5dP/WxY8rd+OfLb/Ks9T1pCd +28t7PQRcQsgkYmouzrOW9ASBvYqLLdhl4y+fFXff8RkPIKMNoYP06WJvRKmky9R/ +41/nXRas +-----END CERTIFICATE----- diff --git a/test/jdk/sun/security/tools/keytool/i18n.java b/test/jdk/sun/security/tools/keytool/i18n.java index 15895cb47aa..d7e01bab35e 100644 --- a/test/jdk/sun/security/tools/keytool/i18n.java +++ b/test/jdk/sun/security/tools/keytool/i18n.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, 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 @@ -23,17 +23,340 @@ /* * @test - * @bug 4348369 8076069 - * @summary keytool not i18n compliant + * @bug 4348369 8076069 8294994 + * @summary keytool i18n compliant * @author charlie lai - * @run main/manual i18n + * @modules java.base/sun.security.tools.keytool + * @library /test/lib + * @run main/manual/othervm -Duser.language=en i18n */ -import java.nio.file.Path; +/* + * @test + * @bug 4348369 8076069 8294994 + * @summary keytool i18n compliant + * @author charlie lai + * @modules java.base/sun.security.tools.keytool + * @library /test/lib + * @run main/manual/othervm -Duser.language=de i18n + */ + +/* + * @test + * @bug 4348369 8076069 8294994 + * @summary keytool i18n compliant + * @author charlie lai + * @modules java.base/sun.security.tools.keytool + * @library /test/lib + * @run main/manual/othervm -Duser.language=ja i18n + */ + +/* + * @test + * @bug 4348369 8076069 8294994 + * @summary keytool i18n compliant + * @author charlie lai + * @modules java.base/sun.security.tools.keytool + * @library /test/lib + * @run main/manual/othervm -Duser.language=zh -Duser.country=CN i18n + */ + +import jdk.test.lib.UIBuilder; + +import javax.swing.*; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.Locale; + +public class i18n { + private static final String[][] TABLE = new String[][]{ + {"-help", "All the output in this test should be in ${LANG}. " + + "Otherwise, the test failed."}, + + {"-genkeypair -keyalg DSA -v -keysize 512 " + + "-dname cn=Name,ou=Java,o=Oracle,l=City,s=State,c=Country " + + "-storepass a " + + "-keypass a " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: java.lang.Exception: " + + "Keystore password must be at least 6 characters."}, + + {"-genkeypair -keyalg DSA -v -keysize 512 " + + "-dname cn=Name,ou=Java,o=Oracle,l=City,s=State,c=Country " + + "-storepass password " + + "-keypass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check: generated a 512 bit DSA key pair " + + "for CN=Name, OU=Java, O=Oracle, L=City, ST=State " + + "C=Country."}, + + {"-list -v -storepass password -keystore ./i18n.keystore", + "Output in ${LANG}. Check: contains 1 keystore entry with " + + "512-bit DSA key algorithm for CN=Name, OU=Java, " + + "O=Oracle, L=City, ST=State C=Country."}, + + {"-list -v -storepass a -keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error:java.io.IOException: " + + "keystore password was incorrect."}, + + {"-genkey -keyalg DSA -v -keysize 512 " + + "-storepass password " + + "-keypass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: java.lang.Exception: " + + "alias 'mykey' already exists."}, + + {"-genkeypair -keyalg DSA -v -keysize 512 " + + "-dname cn=Name,ou=Java,o=Oracle,l=City,s=State,c=Country " + + "-alias mykey2 " + + "-storepass password " + + "-keypass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check: generated a 512 bit DSA key pair " + + "for CN=Name, OU=Java, O=Oracle, L=City, ST=State " + + "C=Country."}, + + {"-list -v -storepass password -keystore ./i18n.keystore", + "Output in ${LANG}. Check: contains 2 keystore entries " + + "(alias name mykey & mykey2), both with 512-bit DSA" + + " key algorithm for CN=Name, OU=Java, O=Oracle, " + + "L=City, ST=State C=Country."}, + + {"-keypasswd -v " + + "-alias mykey2 " + + "-storepass password " + + "-keypass password " + + "-new a " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: java.lang.Exception: " + + "New password must be at least 6 characters."}, + + {"-keypasswd -v " + + "-alias mykey2 " + + "-storepass password " + + "-keypass password " + + "-new aaaaaa " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: -keypasswd " + + "commands not supported if -storetype is PKCS12."}, + + {"-genkeypair -keyalg DSA -v -keysize 512 " + + "-dname cn=Name,ou=Java,o=Oracle,l=City,s=State,c=Country " + + "-storepass password " + + "-keypass password " + + "-keystore ./i18n.jks " + + "-storetype JKS", + "Output in ${LANG}. Check: generated a 512 bit DSA key pair " + + "with a JKS warning."}, + + {"-keypasswd -v " + + "-storepass password " + + "-keypass password " + + "-new aaaaaa " + + "-keystore ./i18n.jks", + "Output in ${LANG}. Check: storing i18n.jks with a JKS warning."}, + + {"-selfcert -v -alias mykey " + + "-storepass password " + + "-keypass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check: generated a new certificate " + + "(self-signed)."}, + + {"-list -v -storepass password -keystore ./i18n.keystore", + "Output in ${LANG}. Check: contains 2 keystore entries " + + "(alias name mykey & mykey2), both with 512-bit DSA" + + " key algorithm for CN=Name, OU=Java, O=Oracle, " + + "L=City, ST=State C=Country."}, + + {"-export -v -alias mykey " + + "-file backup.keystore " + + "-storepass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check: certificate stored in file ."}, + + {"-import -v " + + "-file backup.keystore " + + "-storepass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: reply and certificate " + + "in keystore are identical."}, + + {"-printcert -file backup.keystore", + "Output in ${LANG}. Check: 512 bit DSA key pair for CN=Name," + + " OU=Java, O=Oracle, L=City, ST=State C=Country."}, + + {"-list -storepass password -keystore ./i18n.keystore " + + "-addprovider SUN", + "Output in ${LANG}. Check: contains 2 keystore entries " + + "(alias name mykey & mykey2)."}, + + {"-storepasswd " + + "-storepass password " + + "-new a " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: java.lang.Exception: " + + "New password must be at least 6 characters."}, + + {"-storepasswd " + + "-storetype PKCS11 " + + "-keystore NONE", + "Output in ${LANG}. Check keytool error: java.lang" + + ".UnsupportedOperationException: -storepasswd and " + + "-keypasswd commands not supported if -storetype is" + + " PKCS11."}, + + {"-keypasswd " + + "-storetype PKCS11 " + + "-keystore NONE", + "Output in ${LANG}. Check keytool error: java.lang" + + ".UnsupportedOperationException: -storepasswd and " + + "-keypasswd commands not supported if -storetype is" + + " PKCS11."}, + + {"-list -protected " + + "-storepass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: java.lang" + + ".IllegalArgumentException: if -protected is " + + "specified, then -storepass, -keypass, and -new " + + "must not be specified."}, + + {"-keypasswd -protected " + + "-storepass password " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: java.lang" + + ".IllegalArgumentException: if -protected is " + + "specified, then -storepass, -keypass, and -new " + + "must not be specified."}, + + {"-keypasswd -protected " + + "-storepass password " + + "-new aaaaaa " + + "-keystore ./i18n.keystore", + "Output in ${LANG}. Check keytool error: java.lang" + + ".IllegalArgumentException: if -protected is " + + "specified, then -storepass, -keypass, and -new " + + "must not be specified."}, + }; + private static String TEST_SRC = System.getProperty("test.src"); + private static int TIMEOUT_MS = 120000; + private volatile boolean failed = false; + private volatile boolean aborted = false; + private Thread currentThread = null; + + public static void executeKeytool(String command) throws Exception { + sun.security.tools.keytool.Main.main(command.split("\\s+")); + } + + public static void main(String[] args) { + final String lang = System.getProperty("user.language"); + final String country = System.getProperty("user.country"); + + if (lang != null) { + if (country != null) { + Locale.setDefault(new Locale(lang, country)); + } else { + Locale.setDefault(new Locale(lang)); + } + } + + final String displayName = Locale.getDefault().getDisplayName(); + + boolean testFailed = false; + i18n i18nTest = new i18n(); + + for (String[] entry : TABLE) { + String command = entry[0].replaceAll("\\$\\{TEST_SRC\\}", TEST_SRC); + String instruction = entry[1].replaceAll("\\$\\{LANG\\}", displayName); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + doKeytool(command, new PrintStream(buffer, true)); + + testFailed |= i18nTest.validate(command, instruction, buffer.toString()); + } + + if (testFailed) { + throw new RuntimeException("One or more tests failed."); + } + } + + public static void doKeytool(String command, PrintStream dest) { + // Backups stdout and stderr. + PrintStream origStdOut = System.out; + PrintStream origErrOut = System.err; + + // Redirects the system output to a custom one. + System.setOut(dest); + System.setErr(dest); + + try { + executeKeytool("-debug " + command); + } catch (Exception e) { + // Do nothing. + } finally { + System.setOut(origStdOut); + System.setErr(origErrOut); + } + } + + public boolean validate(String command, String instruction, String message) { + failed = false; + currentThread = Thread.currentThread(); + JDialog dialog = new UIBuilder.DialogBuilder() + .setTitle("keytool " + command) + .setInstruction(instruction) + .setMessage(message) + .setPassAction(e -> pass()) + .setFailAction(e -> fail()) + .setCloseAction(() -> abort()) + .build(); + + SwingUtilities.invokeLater(() -> { + try { + dialog.setVisible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + try { + Thread.sleep(TIMEOUT_MS); + //Timed out, so fail the test + throw new RuntimeException( + "Timed out after " + TIMEOUT_MS / 1000 + " seconds"); + } catch (InterruptedException e) { + if (aborted) { + throw new RuntimeException("TEST ABORTED"); + } + + if (failed) { + System.out.println(command + ": TEST FAILED"); + System.out.println(message); + } else { + System.out.println(command + ": TEST PASSED"); + } + } finally { + dialog.dispose(); + } + + return failed; + } + + public void pass() { + failed = false; + currentThread.interrupt(); + } + + public void fail() { + failed = true; + currentThread.interrupt(); + } -public class i18n{ - public static void main(String[] args) throws Exception { - System.out.println("see i18n.html"); - System.out.println(Path.of(System.getProperty("test.jdk"), "bin", "keytool")); + public void abort() { + aborted = true; + currentThread.interrupt(); } } diff --git a/test/jdk/sun/security/util/Debug/DebugOptions.java b/test/jdk/sun/security/util/Debug/DebugOptions.java new file mode 100644 index 00000000000..a52566e7aeb --- /dev/null +++ b/test/jdk/sun/security/util/Debug/DebugOptions.java @@ -0,0 +1,138 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8051959 + * @summary Option to print extra information in java.security.debug output + * @library /test/lib + * @run junit DebugOptions + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.security.KeyStore; +import java.security.Security; +import java.util.stream.Stream; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class DebugOptions { + + static final String DATE_REGEX = "\\d{4}-\\d{2}-\\d{2}"; + + private static Stream patternMatches() { + return Stream.of( + // no extra info present + Arguments.of("properties", + "properties: Initial", + "properties\\["), + // thread info only + Arguments.of("properties+thread", + "properties\\[.*\\|main\\|.*java.*]:", + "properties\\[" + DATE_REGEX), + // timestamp info only + Arguments.of("properties+timestamp", + "properties\\[" + DATE_REGEX + ".*\\]", + "\\|main\\]:"), + // both thread and timestamp + Arguments.of("properties+timestamp+thread", + "properties\\[.*\\|main|" + DATE_REGEX + ".*\\]:", + "properties:"), + // flip the arguments of previous test + Arguments.of("properties+thread+timestamp", + "properties\\[.*\\|main|" + DATE_REGEX + ".*\\]:", + "properties:"), + // comma not valid separator, ignore extra info printing request + Arguments.of("properties,thread,timestamp", + "properties:", + "properties\\[.*\\|main|" + DATE_REGEX + ".*\\]:"), + // no extra info for keystore debug prints + Arguments.of("properties+thread+timestamp,keystore", + "properties\\[.*\\|main|" + DATE_REGEX + ".*\\]:", + "keystore\\["), + // flip arguments around in last test - same outcome expected + Arguments.of("keystore,properties+thread+timestamp", + "properties\\[.*\\|main|" + DATE_REGEX + ".*\\]:", + "keystore\\["), + // turn on thread info for both keystore and properties components + Arguments.of("keystore+thread,properties+thread", + "properties\\[.*\\|main|.*\\Rkeystore\\[.*\\|main|.*\\]:", + "\\|" + DATE_REGEX + ".*\\]:"), + // same as above with erroneous comma at end of string. same output expected + Arguments.of("keystore+thread,properties+thread,", + "properties\\[.*\\|main|.*\\Rkeystore\\[.*\\|main|.*\\]:", + "\\|" + DATE_REGEX + ".*\\]:"), + // turn on thread info for properties and timestamp for keystore + Arguments.of("keystore+timestamp,properties+thread", + "properties\\[.*\\|main|.*\\Rkeystore\\[" + DATE_REGEX + ".*\\]:", + "properties\\[.*\\|" + DATE_REGEX + ".*\\]:"), + // turn on thread info for all components + Arguments.of("all+thread", + "properties\\[.*\\|main.*((.*\\R)*)keystore\\[.*\\|main.*java.*\\]:", + "properties\\[" + DATE_REGEX + ".*\\]:"), + // turn on thread info and timestamp for all components + Arguments.of("all+thread+timestamp", + "properties\\[.*\\|main.*\\|" + DATE_REGEX + + ".*\\]((.*\\R)*)keystore\\[.*\\|main.*\\|" + DATE_REGEX + ".*\\]:", + "properties:"), + // all decorator option should override other component options + Arguments.of("all+thread+timestamp,properties", + "properties\\[.*\\|main.*\\|" + DATE_REGEX + + ".*\\]((.*\\R)*)keystore\\[.*\\|main.*\\|" + DATE_REGEX + ".*\\]:", + "properties:"), + // thread details should only be printed for properties option + Arguments.of("properties+thread,all", + "properties\\[.*\\|main\\|.*\\]:", + "keystore\\[.*\\|main\\|.*\\]:"), + // thread details should be printed for all statements + Arguments.of("properties,all+thread", + "properties\\[.*\\|main.*java" + + ".*\\]((.*\\R)*)keystore\\[.*\\|main.*java.*\\]:", + "properties:") + ); + } + + @ParameterizedTest + @MethodSource("patternMatches") + public void shouldContain(String params, String expected, String notExpected) throws Exception { + OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava( + "-Djava.security.debug=" + params, + "DebugOptions" + ); + outputAnalyzer.shouldHaveExitValue(0) + .shouldMatch(expected) + .shouldNotMatch(notExpected); + } + + public static void main(String[] args) throws Exception { + // something to trigger "properties" debug output + Security.getProperty("test"); + // trigger "keystore" debug output + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + } +} diff --git a/test/jdk/sun/security/util/math/TestIntegerModuloP.java b/test/jdk/sun/security/util/math/TestIntegerModuloP.java index 75a3d2dbc22..847262b47a0 100644 --- a/test/jdk/sun/security/util/math/TestIntegerModuloP.java +++ b/test/jdk/sun/security/util/math/TestIntegerModuloP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, 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 @@ -22,21 +22,81 @@ */ /* - * @test + * @test id=IntegerPolynomial25519 * @bug 8181594 8208648 * @summary Test proper operation of integer field arithmetic * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial25519 32 0 + */ + + /* + * @test id=IntegerPolynomial448 + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial448 56 1 + */ + + /* + * @test id=IntegerPolynomial1305 + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomial1305 16 2 + */ + + /* + * @test id=IntegerPolynomialP256 + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomialP256 32 5 + */ + + /* + * @test id=IntegerPolynomialP384 + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomialP384 48 6 + */ + + /* + * @test id=IntegerPolynomialP521 + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.IntegerPolynomialP521 66 7 + */ + + /* + * @test id=P256OrderField + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.P256OrderField 32 8 + */ + + /* + * @test id=P384OrderField + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.P384OrderField 48 9 + */ + + /* + * @test id=P521OrderField + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.P521OrderField 66 10 + */ + + /* + * @test id=Curve25519OrderField + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.Curve25519OrderField 32 11 + */ + + /* + * @test id=Curve448OrderField + * @modules java.base/sun.security.util java.base/sun.security.util.math java.base/sun.security.util.math.intpoly + * @build BigIntegerModuloP * @run main TestIntegerModuloP sun.security.util.math.intpoly.Curve448OrderField 56 12 */ diff --git a/test/jdk/sun/tools/jstatd/JstatdTest.java b/test/jdk/sun/tools/jstatd/JstatdTest.java index c603ed1096c..3b7b8739412 100644 --- a/test/jdk/sun/tools/jstatd/JstatdTest.java +++ b/test/jdk/sun/tools/jstatd/JstatdTest.java @@ -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 @@ -65,6 +65,8 @@ public final class JstatdTest { private static final int JSTAT_GCUTIL_INTERVAL_MS = 250; private static final String JPS_OUTPUT_REGEX = "^\\d+\\s*.*"; + private static final int MAX_JSTATD_TRIES = 10; + private boolean useDefaultPort = true; private boolean useDefaultRmiPort = true; private String port; @@ -288,7 +290,7 @@ private void addToolArg(JDKToolLauncher launcher, String name, String value) { private ProcessThread tryToSetupJstatdProcess() throws Throwable { portInUse = false; ProcessThread jstatdThread = new ProcessThread("Jstatd-Thread", - JstatdTest::isJstadReady, getJstatdCmd()); + JstatdTest::isJstatdReady, getJstatdCmd()); try { jstatdThread.start(); // Make sure jstatd is up and running @@ -308,8 +310,8 @@ private ProcessThread tryToSetupJstatdProcess() throws Throwable { return jstatdThread; } - private static boolean isJstadReady(String line) { - if (line.contains("Port already in use")) { + private static boolean isJstatdReady(String line) { + if (line.contains("Port already in use") || line.contains("Could not bind")) { portInUse = true; return true; } @@ -328,8 +330,9 @@ private void runTest(boolean useShortSyntax) throws Throwable { } ProcessThread jstatdThread = null; + int tries = 0; try { - while (jstatdThread == null) { + while (jstatdThread == null && ++tries <= MAX_JSTATD_TRIES) { if (!useDefaultPort) { port = String.valueOf(Utils.getFreePort()); } @@ -345,10 +348,11 @@ private void runTest(boolean useShortSyntax) throws Throwable { continue; } } - jstatdThread = tryToSetupJstatdProcess(); } - + if (jstatdThread == null) { + throw new RuntimeException("Cannot start jstatd."); + } runToolsAndVerify(); } finally { cleanUpThread(jstatdThread); @@ -356,7 +360,13 @@ private void runTest(boolean useShortSyntax) throws Throwable { // Verify output from jstatd OutputAnalyzer output = jstatdThread.getOutput(); - output.shouldBeEmptyIgnoreVMWarnings(); + List stdout = output.asLinesWithoutVMWarnings(); + output.reportDiagnosticSummary(); + // These asserts are disabled until JDK-8272317 is backported: + // otherwise there are SM deprecation notices that fail them. + // assertEquals(stdout.size(), 1, "Output should contain one line"); + // assertTrue(stdout.get(0).startsWith("jstatd started"), "List should start with 'jstatd started'"); + output.shouldContain("jstatd started"); assertNotEquals(output.getExitValue(), 0, "jstatd process exited with unexpected exit code"); } diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java index d7c581b6e99..2a14a4ea115 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java +++ b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.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 @@ -55,7 +55,7 @@ * @build jdk.jpackage.test.* * @modules jdk.jpackage/jdk.jpackage.internal * @compile MainClassTest.java - * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=jdk.jpackage.tests.MainClassTest */ diff --git a/test/jdk/tools/launcher/Settings.java b/test/jdk/tools/launcher/Settings.java index 29af50d5c02..36ae06640a1 100644 --- a/test/jdk/tools/launcher/Settings.java +++ b/test/jdk/tools/launcher/Settings.java @@ -25,7 +25,7 @@ /* * @test - * @bug 6994753 7123582 8305950 8281658 + * @bug 6994753 7123582 8305950 8281658 8310201 * @summary tests -XshowSettings options * @modules jdk.compiler * jdk.zipfs @@ -67,6 +67,9 @@ static void checkNotContains(TestResult tr, String str) { private static final String VM_SETTINGS = "VM settings:"; private static final String PROP_SETTINGS = "Property settings:"; private static final String LOCALE_SETTINGS = "Locale settings:"; + private static final String LOCALE_SUMMARY_SETTINGS = + "Locale settings summary:"; + private static final String AVAILABLE_LOCALES = "available locales"; private static final String SEC_PROPS_SETTINGS = "Security properties:"; private static final String SEC_SUMMARY_PROPS_SETTINGS = "Security settings summary:"; @@ -81,7 +84,9 @@ static void checkNotContains(TestResult tr, String str) { static void containsAllOptions(TestResult tr) { checkContains(tr, VM_SETTINGS); checkContains(tr, PROP_SETTINGS); - checkContains(tr, LOCALE_SETTINGS); + checkNotContains(tr, LOCALE_SETTINGS); + checkNotContains(tr, AVAILABLE_LOCALES); + checkContains(tr, LOCALE_SUMMARY_SETTINGS); // no verbose security settings unless "security" used checkNotContains(tr, SEC_PROPS_SETTINGS); checkContains(tr, SEC_SUMMARY_PROPS_SETTINGS); @@ -153,6 +158,8 @@ static void runTestOptionLocale() throws IOException { checkNotContains(tr, VM_SETTINGS); checkNotContains(tr, PROP_SETTINGS); checkContains(tr, LOCALE_SETTINGS); + checkContains(tr, AVAILABLE_LOCALES); + checkNotContains(tr, LOCALE_SUMMARY_SETTINGS); checkContains(tr, TZDATA_SETTINGS); } diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 452f3c9dce2..59cc2079939 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -46,6 +46,8 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.test.whitebox.code.Compiler; import jdk.test.whitebox.cpuinfo.CPUInfo; @@ -80,6 +82,10 @@ public void put(String key, Supplier s) { } map.put(key, value); } + + public void putAll(Map map) { + map.entrySet().forEach(e -> put(e.getKey(), () -> e.getValue())); + } } /** @@ -99,6 +105,7 @@ public Map call() { map.put("vm.simpleArch", this::vmArch); map.put("vm.debug", this::vmDebug); map.put("vm.jvmci", this::vmJvmci); + map.put("vm.jvmci.enabled", this::vmJvmciEnabled); map.put("vm.emulatedClient", this::vmEmulatedClient); // vm.hasSA is "true" if the VM contains the serviceability agent // and jhsdb. @@ -125,6 +132,7 @@ public Map call() { map.put("release.implementor", this::implementor); map.put("jdk.containerized", this::jdkContainerized); map.put("vm.flagless", this::isFlagless); + map.putAll(xOptFlags()); // -Xmx4g -> @requires vm.opt.x.Xmx == "4g" ) vmGC(map); // vm.gc.X = true/false vmOptFinalFlags(map); @@ -260,6 +268,20 @@ protected String vmJvmci() { return "true"; } + + /** + * @return true if JVMCI is enabled + */ + protected String vmJvmciEnabled() { + // builds with jvmci have this flag + if ("false".equals(vmJvmci())) { + return "false"; + } + + return "" + Compiler.isJVMCIEnabled(); + } + + /** * @return true if VM runs in emulated-client mode and false otherwise. */ @@ -568,9 +590,7 @@ private String isFlagless() { return "" + "true".equalsIgnoreCase(flagless); } - List allFlags = new ArrayList(); - Collections.addAll(allFlags, System.getProperty("test.vm.opts", "").trim().split("\\s+")); - Collections.addAll(allFlags, System.getProperty("test.java.opts", "").trim().split("\\s+")); + List allFlags = allFlags().toList(); // check -XX flags var ignoredXXFlags = Set.of( @@ -617,6 +637,35 @@ private String isFlagless() { return "" + result; } + private Stream allFlags() { + return Stream.of((System.getProperty("test.vm.opts", "") + " " + System.getProperty("test.java.opts", "")).trim().split("\\s+")); + } + + /** + * Parses extra options, options that start with -X excluding the + * bare -X option (as it is not considered an extra option). + * Ignores extra options not starting with -X + * + * This could be improved to handle extra options not starting + * with -X as well as "standard" options. + */ + private Map xOptFlags() { + return allFlags() + .filter(s -> s.startsWith("-X") && !s.startsWith("-XX:") && !s.equals("-X")) + .map(s -> s.replaceFirst("-", "")) + .map(flag -> { + String[] split = flag.split("[:0123456789]", 2); + return split.length == 2 ? new String[] {split[0], flag.substring(split[0].length(), flag.length() - split[1].length()), split[1]} + : split; + }) + .collect(Collectors.toMap(a -> "vm.opt.x." + a[0], + a -> (a.length == 1) + ? "true" // -Xnoclassgc + : (a[1].equals(":") + ? a[2] // ["-XshowSettings", ":", "system"] + : a[1] + a[2]))); // ["-Xmx", "4", "g"] + } + /** * Dumps the map to the file if the file name is given as the property. * This functionality could be helpful to know context in the real diff --git a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java index 48106face35..d8539b8c656 100644 --- a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java +++ b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java @@ -30,7 +30,7 @@ * jdk.compiler/com.sun.tools.javac.main * jdk.compiler/jdk.internal.shellsupport.doc * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask - * @run testng/timeout=900/othervm JavadocHelperTest + * @run testng/timeout=900/othervm -Xmx1024m JavadocHelperTest */ import java.io.IOException; diff --git a/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java b/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java index 72b1336dc59..31f1d3a66da 100644 --- a/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java +++ b/test/langtools/jdk/javadoc/doclet/testBreakIterator/TestBreakIterator.java @@ -23,7 +23,7 @@ /* * @test - * @bug 4165985 + * @bug 4165985 8326332 * @summary Determine the end of the first sentence using BreakIterator. * If the first sentence of "method" is parsed correctly, the test passes. * Correct Answer: "This is a class (i.e. it is indeed a class)." @@ -76,5 +76,10 @@ public void test() { """

        A constant indicating that the keyLocation is indeterminate or not relevant.
        """); + + checkOutput("pkg/BreakIteratorTest.html", true, + """ + """); } } diff --git a/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java b/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java index 9370d162808..3ea965377f6 100644 --- a/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java +++ b/test/langtools/jdk/javadoc/doclet/testBreakIterator/pkg/BreakIteratorTest.java @@ -56,4 +56,9 @@ public void foobar(){} */ public void fe(){} + /** + * Inline tags extending + * beyond the first sentence. Tags are closed here. + */ + public void meh(){} } diff --git a/test/langtools/jdk/javadoc/doclet/testIOException/TestIOException.java b/test/langtools/jdk/javadoc/doclet/testIOException/TestIOException.java index 9127cdf86bb..5dd81b62d33 100644 --- a/test/langtools/jdk/javadoc/doclet/testIOException/TestIOException.java +++ b/test/langtools/jdk/javadoc/doclet/testIOException/TestIOException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, 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 @@ -23,9 +23,9 @@ /* * @test - * @bug 8164130 + * @bug 8164130 8334332 * @summary test IOException handling - * @library ../../lib + * @library ../../lib /test/lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build javadoc.tester.* * @run main TestIOException @@ -39,6 +39,7 @@ import java.util.Map; import javadoc.tester.JavadocTester; +import jtreg.SkippedException; /** * Tests IO Exception handling. @@ -61,16 +62,13 @@ public static void main(String... args) throws Exception { public void testReadOnlyDirectory() { File outDir = new File("out1"); if (!outDir.mkdir()) { - throw error(outDir, "Cannot create directory"); + throw skip(outDir, "Cannot create directory"); } if (!outDir.setReadOnly()) { - if (skip(outDir)) { - return; - } - throw error(outDir, "could not set directory read-only"); + throw skip(outDir, "could not set directory read-only"); } if (outDir.canWrite()) { - throw error(outDir, "directory is writable"); + throw skip(outDir, "directory is writable"); } try { @@ -93,15 +91,15 @@ public void testReadOnlyDirectory() { public void testReadOnlyFile() throws Exception { File outDir = new File("out2"); if (!outDir.mkdir()) { - throw error(outDir, "Cannot create directory"); + throw skip(outDir, "Cannot create directory"); } File index = new File(outDir, "index.html"); try (FileWriter fw = new FileWriter(index)) { } if (!index.setReadOnly()) { - throw error(index, "could not set index read-only"); + throw skip(index, "could not set index read-only"); } if (index.canWrite()) { - throw error(index, "index is writable"); + throw skip(index, "index is writable"); } try { @@ -139,16 +137,13 @@ public void testReadOnlySubdirectory() throws Exception { File outDir = new File("out3"); File pkgOutDir = new File(outDir, "p"); if (!pkgOutDir.mkdirs()) { - throw error(pkgOutDir, "Cannot create directory"); + throw skip(pkgOutDir, "Cannot create directory"); } if (!pkgOutDir.setReadOnly()) { - if (skip(pkgOutDir)) { - return; - } - throw error(pkgOutDir, "could not set directory read-only"); + throw skip(pkgOutDir, "could not set directory read-only"); } if (pkgOutDir.canWrite()) { - throw error(pkgOutDir, "directory is writable"); + throw skip(pkgOutDir, "directory is writable"); } // run javadoc and check results @@ -192,16 +187,13 @@ public void testReadOnlyDocFilesDir() throws Exception { File pkgOutDir = new File(outDir, "p"); File docFilesOutDir = new File(pkgOutDir, "doc-files"); if (!docFilesOutDir.mkdirs()) { - throw error(docFilesOutDir, "Cannot create directory"); + throw skip(docFilesOutDir, "Cannot create directory"); } if (!docFilesOutDir.setReadOnly()) { - if (skip(docFilesOutDir)) { - return; - } - throw error(docFilesOutDir, "could not set directory read-only"); + throw skip(docFilesOutDir, "could not set directory read-only"); } if (docFilesOutDir.canWrite()) { - throw error(docFilesOutDir, "directory is writable"); + throw skip(docFilesOutDir, "directory is writable"); } try { @@ -219,10 +211,11 @@ public void testReadOnlyDocFilesDir() throws Exception { } } - private Error error(File f, String message) { + private Error skip(File f, String message) { + out.print(System.getProperty("user.name")); out.println(f + ": " + message); showAllAttributes(f.toPath()); - throw new Error(f + ": " + message); + throw new SkippedException(f + ": " + message); } private void showAllAttributes(Path p) { @@ -242,20 +235,5 @@ private void showAttributes(Path p, String attributes) { out.println("Error accessing attributes " + attributes + ": " + t); } } - - private boolean skip(File dir) { - if (isWindows()) { - showAllAttributes(dir.toPath()); - out.println("Windows: cannot set directory read only:" + dir); - out.println("TEST CASE SKIPPED"); - return true; - } else { - return false; - } - } - - private boolean isWindows() { - return System.getProperty("os.name").toLowerCase(Locale.US).startsWith("windows"); - } } diff --git a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java index 38dd919e7c5..6cad75053f1 100644 --- a/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java +++ b/test/langtools/jdk/javadoc/doclet/testSearch/TestSearch.java @@ -411,7 +411,7 @@ void checkSearchOutput(String fileName, boolean expectedOutput, boolean moduleDi """, """ - + """, """ """, @@ -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 52c1b136035..660dc2b1e1b 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 fb46f7b2e9e..af2da0969f1 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/tool/api/basic/APITest.java b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java index 0e2641ca04e..f7a5ba4fb80 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/jshell/AnalyzeSnippetTest.java b/test/langtools/jdk/jshell/AnalyzeSnippetTest.java index b566a023caf..3e2e1a839e2 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 3b3d5616a94..523981b3d91 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/ExecutionControlTestBase.java b/test/langtools/jdk/jshell/ExecutionControlTestBase.java index 9035d84d4a5..20336c902cf 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 da838798f8e..a094ed4a86f 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 f3218fab7c7..31011960880 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 778d004915c..9958b7a3284 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 f22dd821f40..4f29bfe9c7a 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 0843351815f..80dc56d72c4 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 23727aef643..e8a38dfe7f0 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 369b2ed1f44..d74f3484f4b 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 00000000000..b9a93c967dc --- /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 6e0192e3aa7..1e9f9942f42 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 df445d49750..aa8d9be03a9 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 13d583e51f5..4709584cd12 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 848c0d52797..477e540e716 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/patterns/T8312229.java b/test/langtools/tools/javac/patterns/T8312229.java new file mode 100644 index 00000000000..20c3eb3e023 --- /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/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java b/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java index f8ce856bddd..e78e200ac24 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/ProcessToolsStartProcessTest.java b/test/lib-test/jdk/test/lib/process/ProcessToolsStartProcessTest.java new file mode 100644 index 00000000000..02c10c61066 --- /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/hotspot/jtreg/vmTestbase/nsk/share/NativeUtils.java b/test/lib-test/jdk/test/lib/process/proc/A.java similarity index 80% rename from test/hotspot/jtreg/vmTestbase/nsk/share/NativeUtils.java rename to test/lib-test/jdk/test/lib/process/proc/A.java index 4fb0db7675d..8f80202b697 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/NativeUtils.java +++ b/test/lib-test/jdk/test/lib/process/proc/A.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -20,12 +20,8 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package nsk.share; - -public class NativeUtils { - static { - System.loadLibrary("native_utils"); +public class A { + public static void main(String[] args) throws Exception { + B.go(); } - - public static native long getCurrentPID(); } 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 00000000000..ef92b3f77a2 --- /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/hotspot/jtreg/vmTestbase/nsk/share/native/native_utils.cpp b/test/lib-test/jdk/test/lib/process/proc/Launcher.java similarity index 68% rename from test/hotspot/jtreg/vmTestbase/nsk/share/native/native_utils.cpp rename to test/lib-test/jdk/test/lib/process/proc/Launcher.java index 4331eb37286..857586586c6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/native/native_utils.cpp +++ b/test/lib-test/jdk/test/lib/process/proc/Launcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -20,24 +20,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -#include +import jdk.test.lib.process.Proc; -#if (defined(WIN32) || defined (_WIN32)) -#include -#define getpid _getpid -#define pidType int -#else -#include -#define pidType pid_t -#endif - -#include -#include - -extern "C" { - -JNIEXPORT jlong -JNICALL Java_nsk_share_NativeUtils_getCurrentPID(JNIEnv * jni, jobject jobj) { - return (jlong) getpid(); -} +/* + * @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/jdk/test/lib/NetworkConfiguration.java b/test/lib/jdk/test/lib/NetworkConfiguration.java index 386a2bf0a6f..8ea10ede6a4 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 00e922bb15a..2e2e16e6593 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 @@ -451,4 +451,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 d4daf5ef145..2f98cf99357 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/UIBuilder.java b/test/lib/jdk/test/lib/UIBuilder.java new file mode 100644 index 00000000000..c58f563ff52 --- /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/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 4ebe2491584..18c1aaea924 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 @@ -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/net/IPSupport.java b/test/lib/jdk/test/lib/net/IPSupport.java index 05f3966dd5e..d2e0becc0dd 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 5660ed048aa..e8611fb007f 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/Proc.java b/test/lib/jdk/test/lib/process/Proc.java index 3541f34144f..802ca11da7b 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 673d24066cd..fdfb81d834d 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); } /** @@ -615,14 +702,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 +724,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/util/ForceGC.java b/test/lib/jdk/test/lib/util/ForceGC.java index 3c9ebc4d35e..19bbef1f7e4 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, 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 @@ -38,14 +38,43 @@ 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); @@ -53,7 +82,7 @@ public static boolean wait(BooleanSupplier booleanSupplier) { Reference.reachabilityFence(obj); Reference.reachabilityFence(ref); - int retries = (int)(Math.round(1000L * TIMEOUT_FACTOR) / 200); + int retries = (int)(timeout / 200); for (; retries >= 0; retries--) { if (booleanSupplier.getAsBoolean()) { return true; diff --git a/test/micro/org/openjdk/bench/java/security/AlgorithmConstraintsPermits.java b/test/micro/org/openjdk/bench/java/security/AlgorithmConstraintsPermits.java index 3cb9567b92b..46e68ea627f 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 a7fe33432f7..9b7c39a3cf2 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 8703e85fef0..e37cec895ca 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 f8d16082f8b..583da306fa0 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 e72813d2811..1e36165d3f0 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 17434394ba1..5c7b31fa133 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 2969baaa22e..a3ab483c39c 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 9c8f378fb9b..f68b4503ef5 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 22bccdcb859..7018cc73c37 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 714fc9de31c..67e51e9532d 100644 --- a/test/micro/org/openjdk/bench/java/security/SSLHandshake.java +++ b/test/micro/org/openjdk/bench/java/security/SSLHandshake.java @@ -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; @@ -109,9 +112,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/src/hotspot/share/metaprogramming/removeExtent.hpp b/test/micro/org/openjdk/bench/java/security/SecureRandomBench.java similarity index 65% rename from src/hotspot/share/metaprogramming/removeExtent.hpp rename to test/micro/org/openjdk/bench/java/security/SecureRandomBench.java index f188e0db264..b9196b25c62 100644 --- a/src/hotspot/share/metaprogramming/removeExtent.hpp +++ b/test/micro/org/openjdk/bench/java/security/SecureRandomBench.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 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 @@ -22,14 +22,22 @@ * */ -#ifndef SHARE_METAPROGRAMMING_REMOVEEXTENT_HPP -#define SHARE_METAPROGRAMMING_REMOVEEXTENT_HPP +package org.openjdk.bench.java.security; -#include "memory/allocation.hpp" +import org.openjdk.jmh.annotations.*; -template struct RemoveExtent: AllStatic { typedef T type; }; +import java.security.SecureRandom; +import java.util.concurrent.TimeUnit; -template struct RemoveExtent: AllStatic { typedef T type; }; -template struct RemoveExtent: AllStatic { typedef T type; }; +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class SecureRandomBench { -#endif // SHARE_METAPROGRAMMING_REMOVEEXTENT_HPP + @Benchmark + public SecureRandom create() throws Exception { + return new SecureRandom(); + } +} diff --git a/test/micro/org/openjdk/bench/vm/compiler/InterfaceCalls.java b/test/micro/org/openjdk/bench/vm/compiler/InterfaceCalls.java index bbecd3d3b23..57badf8af93 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(); } + }