diff --git a/.github/scripts/gen-build-failure-report.sh b/.github/scripts/gen-build-failure-report.sh index fd3215fc7fe2d..c8336a5f7a1f9 100644 --- a/.github/scripts/gen-build-failure-report.sh +++ b/.github/scripts/gen-build-failure-report.sh @@ -24,12 +24,19 @@ # questions. # +# Import common utils +. report-utils.sh + GITHUB_STEP_SUMMARY="$1" BUILD_DIR="$(ls -d build/*)" # Send signal to the do-build action that we failed touch "$BUILD_DIR/build-failure" +# Collect hs_errs for build-time crashes, e.g. javac, jmod, jlink, CDS. +# These usually land in make/ +hs_err_files=$(ls make/hs_err*.log 2> /dev/null || true) + ( echo '### :boom: Build failure summary' echo '' @@ -46,6 +53,20 @@ touch "$BUILD_DIR/build-failure" echo '' echo '' + for hs_err in $hs_err_files; do + echo "
View HotSpot error log: "$hs_err"" + echo '' + echo '```' + echo "$hs_err:" + echo '' + cat "$hs_err" + echo '```' + echo '
' + echo '' + done + echo '' echo ':arrow_right: To see the entire test log, click the job in the list to the left. To download logs, see the `failure-logs` [artifact above](#artifacts).' ) >> $GITHUB_STEP_SUMMARY + +truncate_summary diff --git a/.github/scripts/gen-test-results.sh b/.github/scripts/gen-test-results.sh index 9e85eef4dc08d..6c6cbaa3740f6 100644 --- a/.github/scripts/gen-test-results.sh +++ b/.github/scripts/gen-test-results.sh @@ -24,6 +24,9 @@ # questions. # +# Import common utils +. report-utils.sh + GITHUB_STEP_SUMMARY="$1" test_suite_name=$(cat build/run-test-prebuilt/test-support/test-last-ids.txt) @@ -89,18 +92,6 @@ for test in $failures $errors; do fi done >> $GITHUB_STEP_SUMMARY -# With many failures, the summary can easily exceed 1024 kB, the limit set by Github -# Trim it down if so. -summary_size=$(wc -c < $GITHUB_STEP_SUMMARY) -if [[ $summary_size -gt 1000000 ]]; then - # Trim to below 1024 kB, and cut off after the last detail group - head -c 1000000 $GITHUB_STEP_SUMMARY | tac | sed -n -e '/<\/details>/,$ p' | tac > $GITHUB_STEP_SUMMARY.tmp - mv $GITHUB_STEP_SUMMARY.tmp $GITHUB_STEP_SUMMARY - ( - echo '' - echo ':x: **WARNING: Summary is too large and has been truncated.**' - echo '' - ) >> $GITHUB_STEP_SUMMARY -fi - echo ':arrow_right: To see the entire test log, click the job in the list to the left.' >> $GITHUB_STEP_SUMMARY + +truncate_summary diff --git a/.github/scripts/report-utils.sh b/.github/scripts/report-utils.sh new file mode 100644 index 0000000000000..da5b6c04b3cbe --- /dev/null +++ b/.github/scripts/report-utils.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +function truncate_summary() { + # With large hs_errs, the summary can easily exceed 1024 kB, the limit set by Github + # Trim it down if so. + summary_size=$(wc -c < $GITHUB_STEP_SUMMARY) + if [[ $summary_size -gt 1000000 ]]; then + # Trim to below 1024 kB, and cut off after the last detail group + head -c 1000000 $GITHUB_STEP_SUMMARY | tac | sed -n -e '/<\/details>/,$ p' | tac > $GITHUB_STEP_SUMMARY.tmp + mv $GITHUB_STEP_SUMMARY.tmp $GITHUB_STEP_SUMMARY + ( + echo '' + echo ':x: **WARNING: Summary is too large and has been truncated.**' + echo '' + ) >> $GITHUB_STEP_SUMMARY + fi +} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e4c05acb684b7..d5958853701ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -384,6 +384,7 @@ jobs: - build-windows-aarch64 - test-linux-x64 - test-macos-x64 + - test-macos-aarch64 - test-windows-x64 steps: diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..f4c5e7e67cb46 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# JDK Vulnerabilities + +Please follow the process outlined in the [OpenJDK Vulnerability Policy](https://openjdk.org/groups/vulnerability/report) to disclose vulnerabilities in the JDK. diff --git a/doc/building.html b/doc/building.html index 707531553124b..c91d876246cde 100644 --- a/doc/building.html +++ b/doc/building.html @@ -614,10 +614,9 @@

clang

--with-toolchain-type=clang.

Apple Xcode

The oldest supported version of Xcode is 13.0.

-

You will need the Xcode command line developer tools to be able to -build the JDK. (Actually, only the command line tools are -needed, not the IDE.) The simplest way to install these is to run:

-
xcode-select --install
+

You will need to download Xcode either from the App Store or specific +versions can be easily located via the Xcode Releases website.

When updating Xcode, it is advisable to keep an older version for building the JDK. To use a specific version of Xcode you have multiple options:

diff --git a/doc/building.md b/doc/building.md index 51ac0cad7d98b..47ad9e7c72b4c 100644 --- a/doc/building.md +++ b/doc/building.md @@ -422,13 +422,9 @@ To use clang instead of gcc on Linux, use `--with-toolchain-type=clang`. The oldest supported version of Xcode is 13.0. -You will need the Xcode command line developer tools to be able to build the -JDK. (Actually, *only* the command line tools are needed, not the IDE.) The -simplest way to install these is to run: - -``` -xcode-select --install -``` +You will need to download Xcode either from the App Store or specific versions +can be easily located via the [Xcode Releases](https://xcodereleases.com) +website. When updating Xcode, it is advisable to keep an older version for building the JDK. To use a specific version of Xcode you have multiple options: diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4 index bd33315090fce..9695644bafe63 100644 --- a/make/autoconf/jvm-features.m4 +++ b/make/autoconf/jvm-features.m4 @@ -479,6 +479,22 @@ AC_DEFUN([JVM_FEATURES_CALCULATE_ACTIVE], $JVM_FEATURES_ENABLED, $JVM_FEATURES_DISABLED) ]) +################################################################################ +# Filter the unsupported feature combinations. +# This is called after JVM_FEATURES_ACTIVE are fully populated. +# +AC_DEFUN([JVM_FEATURES_FILTER_UNSUPPORTED], +[ + # G1 late barrier expansion in C2 is not implemented for some platforms. + # Choose not to support G1 in this configuration. + if JVM_FEATURES_IS_ACTIVE(compiler2); then + if test "x$OPENJDK_TARGET_CPU" = "xx86"; then + AC_MSG_NOTICE([G1 cannot be used with C2 on this platform, disabling G1]) + UTIL_GET_NON_MATCHING_VALUES(JVM_FEATURES_ACTIVE, $JVM_FEATURES_ACTIVE, "g1gc") + fi + fi +]) + ################################################################################ # Helper function for JVM_FEATURES_VERIFY. Check if the specified JVM # feature is active. To be used in shell if constructs, like this: @@ -554,6 +570,9 @@ AC_DEFUN_ONCE([JVM_FEATURES_SETUP], # The result is stored in JVM_FEATURES_ACTIVE. JVM_FEATURES_CALCULATE_ACTIVE($variant) + # Filter unsupported feature combinations from JVM_FEATURES_ACTIVE. + JVM_FEATURES_FILTER_UNSUPPORTED + # Verify consistency for JVM_FEATURES_ACTIVE. JVM_FEATURES_VERIFY($variant) diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4 index 75c8d2b61d084..d84ae447e541a 100644 --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 @@ -307,7 +307,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_POST_DETECTION], [ # Restore old path, except for the microsoft toolchain, which requires the # toolchain path to remain in place. Otherwise the compiler will not work in - # some siutations in later configure checks. + # some situations in later configure checks. if test "x$TOOLCHAIN_TYPE" != "xmicrosoft"; then PATH="$OLD_PATH" fi @@ -316,10 +316,6 @@ AC_DEFUN_ONCE([TOOLCHAIN_POST_DETECTION], # This is necessary since AC_PROG_CC defaults CFLAGS to "-g -O2" CFLAGS="$ORG_CFLAGS" CXXFLAGS="$ORG_CXXFLAGS" - - # filter out some unwanted additions autoconf may add to CXX; we saw this on macOS with autoconf 2.72 - UTIL_GET_NON_MATCHING_VALUES(cxx_filtered, $CXX, -std=c++11 -std=gnu++11) - CXX="$cxx_filtered" ]) # Check if a compiler is of the toolchain type we expect, and save the version @@ -358,6 +354,11 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION], # Copyright (C) 2013 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + # or look like + # gcc (GCC) 10.2.1 20200825 (Alibaba 10.2.1-3.8 2.32) + # Copyright (C) 2020 Free Software Foundation, Inc. + # This is free software; see the source for copying conditions. There is NO + # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. COMPILER_VERSION_OUTPUT=`$COMPILER --version 2>&1` # Check that this is likely to be GCC. $ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "Free Software Foundation" > /dev/null @@ -371,7 +372,8 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_COMPILER_VERSION], COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \ $SED -e 's/ *Copyright .*//'` COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \ - $SED -e 's/^.* \(@<:@1-9@:>@<:@0-9@:>@*\.@<:@0-9.@:>@*\)@<:@^0-9.@:>@.*$/\1/'` + $AWK -F ')' '{print [$]2}' | \ + $AWK '{print [$]1}'` elif test "x$TOOLCHAIN_TYPE" = xclang; then # clang --version output typically looks like # Apple clang version 15.0.0 (clang-1500.3.9.4) diff --git a/make/autoconf/util.m4 b/make/autoconf/util.m4 index 6beadb4c942c7..5a6142d509202 100644 --- a/make/autoconf/util.m4 +++ b/make/autoconf/util.m4 @@ -25,6 +25,70 @@ m4_include([util_paths.m4]) +############################################################################### +# Overwrite the existing version of AC_PROG_CC with our own custom variant. +# Unlike the regular AC_PROG_CC, the compiler list must always be passed. +AC_DEFUN([AC_PROG_CC], +[ + AC_LANG_PUSH(C) + AC_ARG_VAR([CC], [C compiler command]) + AC_ARG_VAR([CFLAGS], [C compiler flags]) + + _AC_ARG_VAR_LDFLAGS() + _AC_ARG_VAR_LIBS() + _AC_ARG_VAR_CPPFLAGS() + + AC_CHECK_TOOLS(CC, [$1]) + + test -z "$CC" && AC_MSG_FAILURE([no acceptable C compiler found in \$PATH]) + + # Provide some information about the compiler. + _AS_ECHO_LOG([checking for _AC_LANG compiler version]) + set X $ac_compile + ac_compiler=$[2] + for ac_option in --version -v -V -qversion -version; do + _AC_DO_LIMIT([$ac_compiler $ac_option >&AS_MESSAGE_LOG_FD]) + done + + m4_expand_once([_AC_COMPILER_EXEEXT]) + m4_expand_once([_AC_COMPILER_OBJEXT]) + + _AC_PROG_CC_G + + AC_LANG_POP(C) +]) + +############################################################################### +# Overwrite the existing version of AC_PROG_CXX with our own custom variant. +# Unlike the regular AC_PROG_CXX, the compiler list must always be passed. +AC_DEFUN([AC_PROG_CXX], +[ + AC_LANG_PUSH(C++) + AC_ARG_VAR([CXX], [C++ compiler command]) + AC_ARG_VAR([CXXFLAGS], [C++ compiler flags]) + + _AC_ARG_VAR_LDFLAGS() + _AC_ARG_VAR_LIBS() + _AC_ARG_VAR_CPPFLAGS() + + AC_CHECK_TOOLS(CXX, [$1]) + + # Provide some information about the compiler. + _AS_ECHO_LOG([checking for _AC_LANG compiler version]) + set X $ac_compile + ac_compiler=$[2] + for ac_option in --version -v -V -qversion; do + _AC_DO_LIMIT([$ac_compiler $ac_option >&AS_MESSAGE_LOG_FD]) + done + + m4_expand_once([_AC_COMPILER_EXEEXT]) + m4_expand_once([_AC_COMPILER_OBJEXT]) + + _AC_PROG_CXX_G + + AC_LANG_POP(C++) +]) + ################################################################################ # Create a function/macro that takes a series of named arguments. The call is # similar to AC_DEFUN, but the setup of the function looks like this: diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index eca6c05033d88..a6b383daa8fd4 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -29,21 +29,21 @@ GTEST_VERSION=1.14.0 JTREG_VERSION=7.4+1 LINUX_X64_BOOT_JDK_EXT=tar.gz -LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_SHA256=41536f115668308ecf4eba92aaf6acaeb0936225828b741efd83b6173ba82963 +LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk23/3c5b90190c68498b986a97f276efd28a/37/GPL/openjdk-23_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_SHA256=08fea92724127c6fa0f2e5ea0b07ff4951ccb1e2f22db3c21eebbd7347152a67 ALPINE_LINUX_X64_BOOT_JDK_EXT=tar.gz -ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin22-binaries/releases/download/jdk-22.0.2%2B9/OpenJDK22U-jdk_x64_alpine-linux_hotspot_22.0.2_9.tar.gz -ALPINE_LINUX_X64_BOOT_JDK_SHA256=49f73414824b1a7c268a611225fa4d7ce5e25600201e0f1cd59f94d1040b5264 +ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin23-binaries/releases/download/jdk-23%2B37/OpenJDK23U-jdk_x64_alpine-linux_hotspot_23_37.tar.gz +ALPINE_LINUX_X64_BOOT_JDK_SHA256=bff4c78f30d8d173e622bf2f40c36113df47337fc6d1ee5105ed2459841165aa MACOS_AARCH64_BOOT_JDK_EXT=tar.gz -MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_macos-aarch64_bin.tar.gz -MACOS_AARCH64_BOOT_JDK_SHA256=3dab98730234e1a87aec14bcb8171d2cae101e96ff4eed1dab96abbb08e843fd +MACOS_AARCH64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk23/3c5b90190c68498b986a97f276efd28a/37/GPL/openjdk-23_macos-aarch64_bin.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=9527bf080a74ae6dca51df413aa826f0c011c6048885e4c8ad112172be8815f3 MACOS_X64_BOOT_JDK_EXT=tar.gz -MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_macos-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_SHA256=e8b3ec7a7077711223d31156e771f11723cd7af31c2017f1bd2eda20855940fb +MACOS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk23/3c5b90190c68498b986a97f276efd28a/37/GPL/openjdk-23_macos-x64_bin.tar.gz +MACOS_X64_BOOT_JDK_SHA256=5c3a909fd2079d0e376dd43c85c4f7d02d08914866f196480bd47784b2a0121e WINDOWS_X64_BOOT_JDK_EXT=zip -WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk22.0.2/c9ecb94cd31b495da20a27d4581645e8/9/GPL/openjdk-22.0.2_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_SHA256=f2a9b9ab944e71a64637fcdc6b13a1188cf02d4eb9ecf71dc927e98b3e45f5dc +WINDOWS_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk23/3c5b90190c68498b986a97f276efd28a/37/GPL/openjdk-23_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_SHA256=cba5013874ba50cae543c86fe6423453816c77281e2751a8a9a633d966f1dc04 diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 30c45d4cde161..a85c20b2098ea 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -390,8 +390,8 @@ var getJibProfilesCommon = function (input, data) { }; }; - common.boot_jdk_version = "22"; - common.boot_jdk_build_number = "36"; + common.boot_jdk_version = "23"; + common.boot_jdk_build_number = "37"; common.boot_jdk_home = input.get("boot_jdk", "install_path") + "/jdk-" + common.boot_jdk_version + (input.build_os == "macosx" ? ".jdk/Contents/Home" : ""); diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 1d47c2cddd001..055f9ca886618 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -37,6 +37,6 @@ DEFAULT_VERSION_DATE=2025-03-18 DEFAULT_VERSION_CLASSFILE_MAJOR=68 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 -DEFAULT_ACCEPTABLE_BOOT_VERSIONS="22 23 24" +DEFAULT_ACCEPTABLE_BOOT_VERSIONS="23 24" DEFAULT_JDK_SOURCE_TARGET_VERSION=24 DEFAULT_PROMOTED_VERSION_PRE=ea diff --git a/make/hotspot/gensrc/GensrcAdlc.gmk b/make/hotspot/gensrc/GensrcAdlc.gmk index 8dada3cec0a1d..ddb2c3e33e513 100644 --- a/make/hotspot/gensrc/GensrcAdlc.gmk +++ b/make/hotspot/gensrc/GensrcAdlc.gmk @@ -200,6 +200,13 @@ ifeq ($(call check-jvm-feature, compiler2), true) ))) endif + ifeq ($(call check-jvm-feature, g1gc), true) + AD_SRC_FILES += $(call uniq, $(wildcard $(foreach d, $(AD_SRC_ROOTS), \ + $d/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/gc/g1/g1_$(HOTSPOT_TARGET_CPU).ad \ + $d/cpu/$(HOTSPOT_TARGET_CPU_ARCH)/gc/g1/g1_$(HOTSPOT_TARGET_CPU_ARCH).ad \ + ))) + endif + SINGLE_AD_SRCFILE := $(ADLC_SUPPORT_DIR)/all-ad-src.ad INSERT_FILENAME_AWK_SCRIPT := \ diff --git a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java index 630d3a390d18a..426d0bb10ede1 100644 --- a/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java +++ b/make/jdk/src/classes/build/tools/tzdb/TzdbZoneRulesCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -273,7 +273,7 @@ private void outputFile(Path dstFile, String version, // link version-region-rules out.writeShort(builtZones.size()); for (Map.Entry entry : builtZones.entrySet()) { - int regionIndex = Arrays.binarySearch(regionArray, entry.getKey()); + int regionIndex = findRegionIndex(regionArray, entry.getKey()); int rulesIndex = rulesList.indexOf(entry.getValue()); out.writeShort(regionIndex); out.writeShort(rulesIndex); @@ -281,8 +281,8 @@ private void outputFile(Path dstFile, String version, // alias-region out.writeShort(links.size()); for (Map.Entry entry : links.entrySet()) { - int aliasIndex = Arrays.binarySearch(regionArray, entry.getKey()); - int regionIndex = Arrays.binarySearch(regionArray, entry.getValue()); + int aliasIndex = findRegionIndex(regionArray, entry.getKey()); + int regionIndex = findRegionIndex(regionArray, entry.getValue()); out.writeShort(aliasIndex); out.writeShort(regionIndex); } @@ -294,6 +294,14 @@ private void outputFile(Path dstFile, String version, } } + private static int findRegionIndex(String[] regionArray, String region) { + int index = Arrays.binarySearch(regionArray, region); + if (index < 0) { + throw new IllegalArgumentException("Unknown region: " + region); + } + return index; + } + /** Whether to output verbose messages. */ private boolean verbose; diff --git a/make/modules/jdk.hotspot.agent/Lib.gmk b/make/modules/jdk.hotspot.agent/Lib.gmk index f0ede594d0ce8..12f1c1f2a9077 100644 --- a/make/modules/jdk.hotspot.agent/Lib.gmk +++ b/make/modules/jdk.hotspot.agent/Lib.gmk @@ -59,9 +59,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBSAPROC, \ OPTIMIZATION := HIGH, \ EXTRA_HEADER_DIRS := java.base:libjvm, \ DISABLED_WARNINGS_gcc := sign-compare, \ - DISABLED_WARNINGS_gcc_LinuxDebuggerLocal.cpp := unused-variable, \ DISABLED_WARNINGS_gcc_ps_core.c := pointer-arith, \ - DISABLED_WARNINGS_gcc_symtab.c := unused-but-set-variable, \ DISABLED_WARNINGS_clang := sign-compare, \ DISABLED_WARNINGS_clang_libproc_impl.c := format-nonliteral, \ DISABLED_WARNINGS_clang_MacosxDebuggerLocal.m := unused-variable, \ diff --git a/make/modules/jdk.incubator.vector/Lib.gmk b/make/modules/jdk.incubator.vector/Lib.gmk index 0620549f05cd7..bf6ace6f97f7c 100644 --- a/make/modules/jdk.incubator.vector/Lib.gmk +++ b/make/modules/jdk.incubator.vector/Lib.gmk @@ -37,3 +37,21 @@ ifeq ($(call isTargetOs, linux windows)+$(call isTargetCpu, x86_64)+$(INCLUDE_CO TARGETS += $(BUILD_LIBJSVML) endif + +################################################################################ +## Build libsleef +################################################################################ + +ifeq ($(call isTargetOs, linux)+$(call isTargetCpu, riscv64)+$(INCLUDE_COMPILER2), true+true+true) + $(eval $(call SetupJdkLibrary, BUILD_LIBSLEEF, \ + NAME := sleef, \ + OPTIMIZATION := HIGH, \ + SRC := libsleef/lib, \ + EXTRA_SRC := libsleef/generated, \ + DISABLED_WARNINGS_gcc := unused-function sign-compare tautological-compare ignored-qualifiers, \ + DISABLED_WARNINGS_clang := unused-function sign-compare tautological-compare ignored-qualifiers, \ + CFLAGS := -march=rv64gcv, \ + )) + + TARGETS += $(BUILD_LIBSLEEF) +endif diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index d9f1e334a5cf8..90055cb5c0114 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -115,6 +115,8 @@ ifeq ($(call isTargetOs, linux), true) # stripping during the test libraries' build. BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libFib := -g BUILD_JDK_JTREG_LIBRARIES_STRIP_SYMBOLS_libFib := false + # nio tests' libCreationTimeHelper native needs -ldl linker flag + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libCreationTimeHelper := -ldl endif ifeq ($(ASAN_ENABLED), true) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index fced9cfc35e57..d9c77a2f52926 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1244,7 +1244,7 @@ source %{ // r27 is not allocatable when compressed oops is on and heapbase is not // zero, compressed klass pointers doesn't use r27 after JDK-8234794 - if (UseCompressedOops && (CompressedOops::ptrs_base() != nullptr)) { + if (UseCompressedOops && (CompressedOops::base() != nullptr)) { _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg())); _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg())); _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(r27->as_VMReg())); @@ -2307,10 +2307,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return &_PR_REG_mask; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return new TypeVectMask(elemTy, length); -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; @@ -2620,7 +2616,8 @@ static bool is_vector_bitwise_not_pattern(Node* n, Node* m) { bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { if (is_vshift_con_pattern(n, m) || is_vector_bitwise_not_pattern(n, m) || - is_valid_sve_arith_imm_pattern(n, m)) { + is_valid_sve_arith_imm_pattern(n, m) || + is_encode_and_store_pattern(n, m)) { mstack.push(m, Visit); return true; } @@ -2720,7 +2717,7 @@ typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, { Address addr = mem2address(opcode, base, index, scale, disp); if (addr.getMode() == Address::base_plus_offset) { - // Fix up any out-of-range offsets. + /* Fix up any out-of-range offsets. */ assert_different_registers(rscratch1, base); assert_different_registers(rscratch1, reg); addr = __ legitimize_address(addr, size_in_memory, rscratch1); @@ -2761,11 +2758,7 @@ typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, int opcode, Register base, int index, int size, int disp) { if (index == -1) { - // Fix up any out-of-range offsets. - assert_different_registers(rscratch1, base); - Address addr = Address(base, disp); - addr = __ legitimize_address(addr, (1 << T), rscratch1); - (masm->*insn)(reg, T, addr); + (masm->*insn)(reg, T, Address(base, disp)); } else { assert(disp == 0, "unsupported address mode"); (masm->*insn)(reg, T, Address(base, as_Register(index), Address::lsl(size))); @@ -2820,7 +2813,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsbw(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrsbw(iRegI dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsbw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2828,7 +2821,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsb(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrsb(iRegI dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsb, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2836,7 +2829,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrb(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrb(iRegI dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrb, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2844,7 +2837,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrb(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrb(iRegL dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrb, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2852,7 +2845,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrshw(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrshw(iRegI dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrshw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2860,7 +2853,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsh(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrsh(iRegI dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsh, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2868,7 +2861,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrh(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrh(iRegI dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrh, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2876,7 +2869,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrh(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrh(iRegL dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrh, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2884,7 +2877,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrw(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrw(iRegI dst, memory4 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2892,7 +2885,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrw(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrw(iRegL dst, memory4 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2900,7 +2893,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsw(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrsw(iRegL dst, memory4 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2908,7 +2901,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldr(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldr(iRegL dst, memory8 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldr, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); @@ -2916,7 +2909,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrs(vRegF dst, memory mem) %{ + enc_class aarch64_enc_ldrs(vRegF dst, memory4 mem) %{ FloatRegister dst_reg = as_FloatRegister($dst$$reg); loadStore(masm, &MacroAssembler::ldrs, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2924,7 +2917,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrd(vRegD dst, memory mem) %{ + enc_class aarch64_enc_ldrd(vRegD dst, memory8 mem) %{ FloatRegister dst_reg = as_FloatRegister($dst$$reg); loadStore(masm, &MacroAssembler::ldrd, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); @@ -2932,7 +2925,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb(iRegI src, memory mem) %{ + enc_class aarch64_enc_strb(iRegI src, memory1 mem) %{ Register src_reg = as_Register($src$$reg); loadStore(masm, &MacroAssembler::strb, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2940,14 +2933,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb0(memory mem) %{ + enc_class aarch64_enc_strb0(memory1 mem) %{ loadStore(masm, &MacroAssembler::strb, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strh(iRegI src, memory mem) %{ + enc_class aarch64_enc_strh(iRegI src, memory2 mem) %{ Register src_reg = as_Register($src$$reg); loadStore(masm, &MacroAssembler::strh, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2955,14 +2948,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strh0(memory mem) %{ + enc_class aarch64_enc_strh0(memory2 mem) %{ loadStore(masm, &MacroAssembler::strh, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strw(iRegI src, memory mem) %{ + enc_class aarch64_enc_strw(iRegI src, memory4 mem) %{ Register src_reg = as_Register($src$$reg); loadStore(masm, &MacroAssembler::strw, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2970,14 +2963,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strw0(memory mem) %{ + enc_class aarch64_enc_strw0(memory4 mem) %{ loadStore(masm, &MacroAssembler::strw, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_str(iRegL src, memory mem) %{ + enc_class aarch64_enc_str(iRegL src, memory8 mem) %{ Register src_reg = as_Register($src$$reg); // we sometimes get asked to store the stack pointer into the // current thread -- we cannot do that directly on AArch64 @@ -2992,14 +2985,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_str0(memory mem) %{ + enc_class aarch64_enc_str0(memory8 mem) %{ loadStore(masm, &MacroAssembler::str, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strs(vRegF src, memory mem) %{ + enc_class aarch64_enc_strs(vRegF src, memory4 mem) %{ FloatRegister src_reg = as_FloatRegister($src$$reg); loadStore(masm, &MacroAssembler::strs, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -3007,7 +3000,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strd(vRegD src, memory mem) %{ + enc_class aarch64_enc_strd(vRegD src, memory8 mem) %{ FloatRegister src_reg = as_FloatRegister($src$$reg); loadStore(masm, &MacroAssembler::strd, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); @@ -3015,7 +3008,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb0_ordered(memory mem) %{ + enc_class aarch64_enc_strb0_ordered(memory4 mem) %{ __ membar(Assembler::StoreStore); loadStore(masm, &MacroAssembler::strb, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -3217,7 +3210,7 @@ encode %{ // synchronized read/update encodings - enc_class aarch64_enc_ldaxr(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldaxr(iRegL dst, memory8 mem) %{ Register dst_reg = as_Register($dst$$reg); Register base = as_Register($mem$$base); int index = $mem$$index; @@ -3245,7 +3238,7 @@ encode %{ } %} - enc_class aarch64_enc_stlxr(iRegLNoSp src, memory mem) %{ + enc_class aarch64_enc_stlxr(iRegLNoSp src, memory8 mem) %{ Register src_reg = as_Register($src$$reg); Register base = as_Register($mem$$base); int index = $mem$$index; @@ -4173,10 +4166,60 @@ operand immIU7() interface(CONST_INTER); %} -// Offset for immediate loads and stores +// Offset for scaled or unscaled immediate loads and stores operand immIOffset() %{ - predicate(n->get_int() >= -256 && n->get_int() <= 65520); + predicate(Address::offset_ok_for_immed(n->get_int(), 0)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset1() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 0)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset2() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 1)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset4() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 2)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset8() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 3)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset16() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 4)); match(ConI); op_cost(0); @@ -4194,6 +4237,56 @@ operand immLOffset() interface(CONST_INTER); %} +operand immLoffset1() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 0)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset2() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 1)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset4() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 2)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset8() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 3)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset16() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 4)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // 5 bit signed long integer operand immL5() %{ @@ -5106,7 +5199,105 @@ operand indIndex(iRegP reg, iRegL lreg) %} %} -operand indOffI(iRegP reg, immIOffset off) +operand indOffI1(iRegP reg, immIOffset1 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffI2(iRegP reg, immIOffset2 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffI4(iRegP reg, immIOffset4 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffI8(iRegP reg, immIOffset8 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffI16(iRegP reg, immIOffset16 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL1(iRegP reg, immLoffset1 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL2(iRegP reg, immLoffset2 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL4(iRegP reg, immLoffset4 off) %{ constraint(ALLOC_IN_RC(ptr_reg)); match(AddP reg off); @@ -5120,7 +5311,21 @@ operand indOffI(iRegP reg, immIOffset off) %} %} -operand indOffL(iRegP reg, immLOffset off) +operand indOffL8(iRegP reg, immLoffset8 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL16(iRegP reg, immLoffset16 off) %{ constraint(ALLOC_IN_RC(ptr_reg)); match(AddP reg off); @@ -5496,7 +5701,10 @@ operand iRegL2P(iRegL reg) %{ interface(REG_INTER) %} -opclass vmem(indirect, indIndex, indOffI, indOffL, indOffIN, indOffLN); +opclass vmem2(indirect, indIndex, indOffI2, indOffL2); +opclass vmem4(indirect, indIndex, indOffI4, indOffL4); +opclass vmem8(indirect, indIndex, indOffI8, indOffL8); +opclass vmem16(indirect, indIndex, indOffI16, indOffL16); //----------OPERAND CLASSES---------------------------------------------------- // Operand Classes are groups of operands that are used as to simplify @@ -5508,9 +5716,23 @@ opclass vmem(indirect, indIndex, indOffI, indOffL, indOffIN, indOffLN); // memory is used to define read/write location for load/store // instruction defs. we can turn a memory op into an Address -opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI, indOffL, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, - indOffLN, indirectX2P, indOffX2P); +opclass memory1(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI1, indOffL1, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); + +opclass memory2(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI2, indOffL2, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); + +opclass memory4(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI4, indOffL4, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); + +opclass memory8(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI8, indOffL8, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); + +// All of the memory operands. For the pipeline description. +opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, + indOffI1, indOffL1, indOffI2, indOffL2, indOffI4, indOffL4, indOffI8, indOffL8, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); + // iRegIorL2I is used for src inputs in rules for 32 bit int (I) // operations. it allows the src to be either an iRegI or a (ConvL2I @@ -6212,7 +6434,7 @@ define %{ // Load Instructions // Load Byte (8 bit signed) -instruct loadB(iRegINoSp dst, memory mem) +instruct loadB(iRegINoSp dst, memory1 mem) %{ match(Set dst (LoadB mem)); predicate(!needs_acquiring_load(n)); @@ -6226,7 +6448,7 @@ instruct loadB(iRegINoSp dst, memory mem) %} // Load Byte (8 bit signed) into long -instruct loadB2L(iRegLNoSp dst, memory mem) +instruct loadB2L(iRegLNoSp dst, memory1 mem) %{ match(Set dst (ConvI2L (LoadB mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6240,7 +6462,7 @@ instruct loadB2L(iRegLNoSp dst, memory mem) %} // Load Byte (8 bit unsigned) -instruct loadUB(iRegINoSp dst, memory mem) +instruct loadUB(iRegINoSp dst, memory1 mem) %{ match(Set dst (LoadUB mem)); predicate(!needs_acquiring_load(n)); @@ -6254,7 +6476,7 @@ instruct loadUB(iRegINoSp dst, memory mem) %} // Load Byte (8 bit unsigned) into long -instruct loadUB2L(iRegLNoSp dst, memory mem) +instruct loadUB2L(iRegLNoSp dst, memory1 mem) %{ match(Set dst (ConvI2L (LoadUB mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6268,7 +6490,7 @@ instruct loadUB2L(iRegLNoSp dst, memory mem) %} // Load Short (16 bit signed) -instruct loadS(iRegINoSp dst, memory mem) +instruct loadS(iRegINoSp dst, memory2 mem) %{ match(Set dst (LoadS mem)); predicate(!needs_acquiring_load(n)); @@ -6282,7 +6504,7 @@ instruct loadS(iRegINoSp dst, memory mem) %} // Load Short (16 bit signed) into long -instruct loadS2L(iRegLNoSp dst, memory mem) +instruct loadS2L(iRegLNoSp dst, memory2 mem) %{ match(Set dst (ConvI2L (LoadS mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6296,7 +6518,7 @@ instruct loadS2L(iRegLNoSp dst, memory mem) %} // Load Char (16 bit unsigned) -instruct loadUS(iRegINoSp dst, memory mem) +instruct loadUS(iRegINoSp dst, memory2 mem) %{ match(Set dst (LoadUS mem)); predicate(!needs_acquiring_load(n)); @@ -6310,7 +6532,7 @@ instruct loadUS(iRegINoSp dst, memory mem) %} // Load Short/Char (16 bit unsigned) into long -instruct loadUS2L(iRegLNoSp dst, memory mem) +instruct loadUS2L(iRegLNoSp dst, memory2 mem) %{ match(Set dst (ConvI2L (LoadUS mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6324,7 +6546,7 @@ instruct loadUS2L(iRegLNoSp dst, memory mem) %} // Load Integer (32 bit signed) -instruct loadI(iRegINoSp dst, memory mem) +instruct loadI(iRegINoSp dst, memory4 mem) %{ match(Set dst (LoadI mem)); predicate(!needs_acquiring_load(n)); @@ -6338,7 +6560,7 @@ instruct loadI(iRegINoSp dst, memory mem) %} // Load Integer (32 bit signed) into long -instruct loadI2L(iRegLNoSp dst, memory mem) +instruct loadI2L(iRegLNoSp dst, memory4 mem) %{ match(Set dst (ConvI2L (LoadI mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6352,7 +6574,7 @@ instruct loadI2L(iRegLNoSp dst, memory mem) %} // Load Integer (32 bit unsigned) into long -instruct loadUI2L(iRegLNoSp dst, memory mem, immL_32bits mask) +instruct loadUI2L(iRegLNoSp dst, memory4 mem, immL_32bits mask) %{ match(Set dst (AndL (ConvI2L (LoadI mem)) mask)); predicate(!needs_acquiring_load(n->in(1)->in(1)->as_Load())); @@ -6366,7 +6588,7 @@ instruct loadUI2L(iRegLNoSp dst, memory mem, immL_32bits mask) %} // Load Long (64 bit signed) -instruct loadL(iRegLNoSp dst, memory mem) +instruct loadL(iRegLNoSp dst, memory8 mem) %{ match(Set dst (LoadL mem)); predicate(!needs_acquiring_load(n)); @@ -6380,7 +6602,7 @@ instruct loadL(iRegLNoSp dst, memory mem) %} // Load Range -instruct loadRange(iRegINoSp dst, memory mem) +instruct loadRange(iRegINoSp dst, memory4 mem) %{ match(Set dst (LoadRange mem)); @@ -6393,7 +6615,7 @@ instruct loadRange(iRegINoSp dst, memory mem) %} // Load Pointer -instruct loadP(iRegPNoSp dst, memory mem) +instruct loadP(iRegPNoSp dst, memory8 mem) %{ match(Set dst (LoadP mem)); predicate(!needs_acquiring_load(n) && (n->as_Load()->barrier_data() == 0)); @@ -6407,10 +6629,10 @@ instruct loadP(iRegPNoSp dst, memory mem) %} // Load Compressed Pointer -instruct loadN(iRegNNoSp dst, memory mem) +instruct loadN(iRegNNoSp dst, memory4 mem) %{ match(Set dst (LoadN mem)); - predicate(!needs_acquiring_load(n)); + predicate(!needs_acquiring_load(n) && n->as_Load()->barrier_data() == 0); ins_cost(4 * INSN_COST); format %{ "ldrw $dst, $mem\t# compressed ptr" %} @@ -6421,7 +6643,7 @@ instruct loadN(iRegNNoSp dst, memory mem) %} // Load Klass Pointer -instruct loadKlass(iRegPNoSp dst, memory mem) +instruct loadKlass(iRegPNoSp dst, memory8 mem) %{ match(Set dst (LoadKlass mem)); predicate(!needs_acquiring_load(n)); @@ -6435,7 +6657,7 @@ instruct loadKlass(iRegPNoSp dst, memory mem) %} // Load Narrow Klass Pointer -instruct loadNKlass(iRegNNoSp dst, memory mem) +instruct loadNKlass(iRegNNoSp dst, memory4 mem) %{ match(Set dst (LoadNKlass mem)); predicate(!needs_acquiring_load(n)); @@ -6449,7 +6671,7 @@ instruct loadNKlass(iRegNNoSp dst, memory mem) %} // Load Float -instruct loadF(vRegF dst, memory mem) +instruct loadF(vRegF dst, memory4 mem) %{ match(Set dst (LoadF mem)); predicate(!needs_acquiring_load(n)); @@ -6463,7 +6685,7 @@ instruct loadF(vRegF dst, memory mem) %} // Load Double -instruct loadD(vRegD dst, memory mem) +instruct loadD(vRegD dst, memory8 mem) %{ match(Set dst (LoadD mem)); predicate(!needs_acquiring_load(n)); @@ -6666,38 +6888,8 @@ instruct loadConD(vRegD dst, immD con) %{ // Store Instructions -// Store CMS card-mark Immediate -instruct storeimmCM0(immI0 zero, memory mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(INSN_COST); - format %{ "storestore (elided)\n\t" - "strb zr, $mem\t# byte" %} - - ins_encode(aarch64_enc_strb0(mem)); - - ins_pipe(istore_mem); -%} - -// Store CMS card-mark Immediate with intervening StoreStore -// needed when using CMS with no conditional card marking -instruct storeimmCM0_ordered(immI0 zero, memory mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(INSN_COST * 2); - format %{ "storestore\n\t" - "dmb ishst" - "\n\tstrb zr, $mem\t# byte" %} - - ins_encode(aarch64_enc_strb0_ordered(mem)); - - ins_pipe(istore_mem); -%} - // Store Byte -instruct storeB(iRegIorL2I src, memory mem) +instruct storeB(iRegIorL2I src, memory1 mem) %{ match(Set mem (StoreB mem src)); predicate(!needs_releasing_store(n)); @@ -6711,7 +6903,7 @@ instruct storeB(iRegIorL2I src, memory mem) %} -instruct storeimmB0(immI0 zero, memory mem) +instruct storeimmB0(immI0 zero, memory1 mem) %{ match(Set mem (StoreB mem zero)); predicate(!needs_releasing_store(n)); @@ -6725,7 +6917,7 @@ instruct storeimmB0(immI0 zero, memory mem) %} // Store Char/Short -instruct storeC(iRegIorL2I src, memory mem) +instruct storeC(iRegIorL2I src, memory2 mem) %{ match(Set mem (StoreC mem src)); predicate(!needs_releasing_store(n)); @@ -6738,7 +6930,7 @@ instruct storeC(iRegIorL2I src, memory mem) ins_pipe(istore_reg_mem); %} -instruct storeimmC0(immI0 zero, memory mem) +instruct storeimmC0(immI0 zero, memory2 mem) %{ match(Set mem (StoreC mem zero)); predicate(!needs_releasing_store(n)); @@ -6753,7 +6945,7 @@ instruct storeimmC0(immI0 zero, memory mem) // Store Integer -instruct storeI(iRegIorL2I src, memory mem) +instruct storeI(iRegIorL2I src, memory4 mem) %{ match(Set mem(StoreI mem src)); predicate(!needs_releasing_store(n)); @@ -6766,7 +6958,7 @@ instruct storeI(iRegIorL2I src, memory mem) ins_pipe(istore_reg_mem); %} -instruct storeimmI0(immI0 zero, memory mem) +instruct storeimmI0(immI0 zero, memory4 mem) %{ match(Set mem(StoreI mem zero)); predicate(!needs_releasing_store(n)); @@ -6780,7 +6972,7 @@ instruct storeimmI0(immI0 zero, memory mem) %} // Store Long (64 bit signed) -instruct storeL(iRegL src, memory mem) +instruct storeL(iRegL src, memory8 mem) %{ match(Set mem (StoreL mem src)); predicate(!needs_releasing_store(n)); @@ -6794,7 +6986,7 @@ instruct storeL(iRegL src, memory mem) %} // Store Long (64 bit signed) -instruct storeimmL0(immL0 zero, memory mem) +instruct storeimmL0(immL0 zero, memory8 mem) %{ match(Set mem (StoreL mem zero)); predicate(!needs_releasing_store(n)); @@ -6808,7 +7000,7 @@ instruct storeimmL0(immL0 zero, memory mem) %} // Store Pointer -instruct storeP(iRegP src, memory mem) +instruct storeP(iRegP src, memory8 mem) %{ match(Set mem (StoreP mem src)); predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); @@ -6822,7 +7014,7 @@ instruct storeP(iRegP src, memory mem) %} // Store Pointer -instruct storeimmP0(immP0 zero, memory mem) +instruct storeimmP0(immP0 zero, memory8 mem) %{ match(Set mem (StoreP mem zero)); predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); @@ -6836,10 +7028,10 @@ instruct storeimmP0(immP0 zero, memory mem) %} // Store Compressed Pointer -instruct storeN(iRegN src, memory mem) +instruct storeN(iRegN src, memory4 mem) %{ match(Set mem (StoreN mem src)); - predicate(!needs_releasing_store(n)); + predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); ins_cost(INSN_COST); format %{ "strw $src, $mem\t# compressed ptr" %} @@ -6849,10 +7041,10 @@ instruct storeN(iRegN src, memory mem) ins_pipe(istore_reg_mem); %} -instruct storeImmN0(immN0 zero, memory mem) +instruct storeImmN0(immN0 zero, memory4 mem) %{ match(Set mem (StoreN mem zero)); - predicate(!needs_releasing_store(n)); + predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); ins_cost(INSN_COST); format %{ "strw zr, $mem\t# compressed ptr" %} @@ -6863,7 +7055,7 @@ instruct storeImmN0(immN0 zero, memory mem) %} // Store Float -instruct storeF(vRegF src, memory mem) +instruct storeF(vRegF src, memory4 mem) %{ match(Set mem (StoreF mem src)); predicate(!needs_releasing_store(n)); @@ -6880,7 +7072,7 @@ instruct storeF(vRegF src, memory mem) // implement storeImmF0 and storeFImmPacked // Store Double -instruct storeD(vRegD src, memory mem) +instruct storeD(vRegD src, memory8 mem) %{ match(Set mem (StoreD mem src)); predicate(!needs_releasing_store(n)); @@ -6894,7 +7086,7 @@ instruct storeD(vRegD src, memory mem) %} // Store Compressed Klass Pointer -instruct storeNKlass(iRegN src, memory mem) +instruct storeNKlass(iRegN src, memory4 mem) %{ predicate(!needs_releasing_store(n)); match(Set mem (StoreNKlass mem src)); @@ -6913,7 +7105,7 @@ instruct storeNKlass(iRegN src, memory mem) // prefetch instructions // Must be safe to execute with invalid address (cannot fault). -instruct prefetchalloc( memory mem ) %{ +instruct prefetchalloc( memory8 mem ) %{ match(PrefetchAllocation mem); ins_cost(INSN_COST); @@ -7086,6 +7278,7 @@ instruct loadP_volatile(iRegPNoSp dst, /* sync_memory*/indirect mem) instruct loadN_volatile(iRegNNoSp dst, /* sync_memory*/indirect mem) %{ match(Set dst (LoadN mem)); + predicate(n->as_Load()->barrier_data() == 0); ins_cost(VOLATILE_REF_COST); format %{ "ldarw $dst, $mem\t# compressed ptr" %} @@ -7253,6 +7446,7 @@ instruct storeimmP0_volatile(immP0 zero, /* sync_memory*/indirect mem) instruct storeN_volatile(iRegN src, /* sync_memory*/indirect mem) %{ match(Set mem (StoreN mem src)); + predicate(n->as_Store()->barrier_data() == 0); ins_cost(VOLATILE_REF_COST); format %{ "stlrw $src, $mem\t# compressed ptr" %} @@ -7265,6 +7459,7 @@ instruct storeN_volatile(iRegN src, /* sync_memory*/indirect mem) instruct storeimmN0_volatile(immN0 zero, /* sync_memory*/indirect mem) %{ match(Set mem (StoreN mem zero)); + predicate(n->as_Store()->barrier_data() == 0); ins_cost(VOLATILE_REF_COST); format %{ "stlrw zr, $mem\t# compressed ptr" %} @@ -7482,7 +7677,7 @@ instruct popCountI(iRegINoSp dst, iRegIorL2I src, vRegF tmp) %{ ins_pipe(pipe_class_default); %} -instruct popCountI_mem(iRegINoSp dst, memory mem, vRegF tmp) %{ +instruct popCountI_mem(iRegINoSp dst, memory4 mem, vRegF tmp) %{ match(Set dst (PopCountI (LoadI mem))); effect(TEMP tmp); ins_cost(INSN_COST * 13); @@ -7523,7 +7718,7 @@ instruct popCountL(iRegINoSp dst, iRegL src, vRegD tmp) %{ ins_pipe(pipe_class_default); %} -instruct popCountL_mem(iRegINoSp dst, memory mem, vRegD tmp) %{ +instruct popCountL_mem(iRegINoSp dst, memory8 mem, vRegD tmp) %{ match(Set dst (PopCountL (LoadL mem))); effect(TEMP tmp); ins_cost(INSN_COST * 13); @@ -8061,6 +8256,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{ match(Set res (CompareAndSwapN mem (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); ins_cost(2 * VOLATILE_REF_COST); effect(KILL cr); @@ -8175,7 +8371,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{ - predicate(needs_acquiring_load_exclusive(n)); + predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapN mem (Binary oldval newval))); ins_cost(VOLATILE_REF_COST); @@ -8280,6 +8476,7 @@ instruct compareAndExchangeL(iRegLNoSp res, indirect mem, iRegL oldval, iRegL ne // This pattern is generated automatically from cas.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct compareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndExchangeN mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); effect(TEMP_DEF res, KILL cr); @@ -8389,7 +8586,7 @@ instruct compareAndExchangeLAcq(iRegLNoSp res, indirect mem, iRegL oldval, iRegL // This pattern is generated automatically from cas.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct compareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ - predicate(needs_acquiring_load_exclusive(n)); + predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndExchangeN mem (Binary oldval newval))); ins_cost(VOLATILE_REF_COST); effect(TEMP_DEF res, KILL cr); @@ -8501,6 +8698,7 @@ instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL ne // This pattern is generated automatically from cas.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); ins_cost(2 * VOLATILE_REF_COST); effect(KILL cr); @@ -8620,7 +8818,7 @@ instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL // This pattern is generated automatically from cas.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{ - predicate(needs_acquiring_load_exclusive(n)); + predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); ins_cost(VOLATILE_REF_COST); effect(KILL cr); @@ -8681,6 +8879,7 @@ instruct get_and_setL(indirect mem, iRegL newv, iRegLNoSp prev) %{ %} instruct get_and_setN(indirect mem, iRegN newv, iRegINoSp prev) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set prev (GetAndSetN mem newv)); ins_cost(2 * VOLATILE_REF_COST); format %{ "atomic_xchgw $prev, $newv, [$mem]" %} @@ -8724,7 +8923,7 @@ instruct get_and_setLAcq(indirect mem, iRegL newv, iRegLNoSp prev) %{ %} instruct get_and_setNAcq(indirect mem, iRegN newv, iRegINoSp prev) %{ - predicate(needs_acquiring_load_exclusive(n)); + predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0); match(Set prev (GetAndSetN mem newv)); ins_cost(VOLATILE_REF_COST); format %{ "atomic_xchgw_acq $prev, $newv, [$mem]" %} @@ -16672,7 +16871,7 @@ instruct compressBitsI_reg(iRegINoSp dst, iRegIorL2I src, iRegIorL2I mask, ins_pipe(pipe_slow); %} -instruct compressBitsI_memcon(iRegINoSp dst, memory mem, immI mask, +instruct compressBitsI_memcon(iRegINoSp dst, memory4 mem, immI mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (CompressBits (LoadI mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); @@ -16709,7 +16908,7 @@ instruct compressBitsL_reg(iRegLNoSp dst, iRegL src, iRegL mask, ins_pipe(pipe_slow); %} -instruct compressBitsL_memcon(iRegLNoSp dst, memory mem, immL mask, +instruct compressBitsL_memcon(iRegLNoSp dst, memory8 mem, immL mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (CompressBits (LoadL mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); @@ -16746,7 +16945,7 @@ instruct expandBitsI_reg(iRegINoSp dst, iRegIorL2I src, iRegIorL2I mask, ins_pipe(pipe_slow); %} -instruct expandBitsI_memcon(iRegINoSp dst, memory mem, immI mask, +instruct expandBitsI_memcon(iRegINoSp dst, memory4 mem, immI mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (ExpandBits (LoadI mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); @@ -16784,7 +16983,7 @@ instruct expandBitsL_reg(iRegLNoSp dst, iRegL src, iRegL mask, %} -instruct expandBitsL_memcon(iRegINoSp dst, memory mem, immL mask, +instruct expandBitsL_memcon(iRegINoSp dst, memory8 mem, immL mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (ExpandBits (LoadL mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index cdbc4103df89a..0d3a240cecfd3 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -345,7 +345,7 @@ source %{ // ------------------------------ Vector load/store ---------------------------- // Load Vector (16 bits) -instruct loadV2(vReg dst, vmem mem) %{ +instruct loadV2(vReg dst, vmem2 mem) %{ predicate(n->as_LoadVector()->memory_size() == 2); match(Set dst (LoadVector mem)); format %{ "loadV2 $dst, $mem\t# vector (16 bits)" %} @@ -354,7 +354,7 @@ instruct loadV2(vReg dst, vmem mem) %{ %} // Store Vector (16 bits) -instruct storeV2(vReg src, vmem mem) %{ +instruct storeV2(vReg src, vmem2 mem) %{ predicate(n->as_StoreVector()->memory_size() == 2); match(Set mem (StoreVector mem src)); format %{ "storeV2 $mem, $src\t# vector (16 bits)" %} @@ -363,7 +363,7 @@ instruct storeV2(vReg src, vmem mem) %{ %} // Load Vector (32 bits) -instruct loadV4(vReg dst, vmem mem) %{ +instruct loadV4(vReg dst, vmem4 mem) %{ predicate(n->as_LoadVector()->memory_size() == 4); match(Set dst (LoadVector mem)); format %{ "loadV4 $dst, $mem\t# vector (32 bits)" %} @@ -372,7 +372,7 @@ instruct loadV4(vReg dst, vmem mem) %{ %} // Store Vector (32 bits) -instruct storeV4(vReg src, vmem mem) %{ +instruct storeV4(vReg src, vmem4 mem) %{ predicate(n->as_StoreVector()->memory_size() == 4); match(Set mem (StoreVector mem src)); format %{ "storeV4 $mem, $src\t# vector (32 bits)" %} @@ -381,7 +381,7 @@ instruct storeV4(vReg src, vmem mem) %{ %} // Load Vector (64 bits) -instruct loadV8(vReg dst, vmem mem) %{ +instruct loadV8(vReg dst, vmem8 mem) %{ predicate(n->as_LoadVector()->memory_size() == 8); match(Set dst (LoadVector mem)); format %{ "loadV8 $dst, $mem\t# vector (64 bits)" %} @@ -390,7 +390,7 @@ instruct loadV8(vReg dst, vmem mem) %{ %} // Store Vector (64 bits) -instruct storeV8(vReg src, vmem mem) %{ +instruct storeV8(vReg src, vmem8 mem) %{ predicate(n->as_StoreVector()->memory_size() == 8); match(Set mem (StoreVector mem src)); format %{ "storeV8 $mem, $src\t# vector (64 bits)" %} @@ -399,7 +399,7 @@ instruct storeV8(vReg src, vmem mem) %{ %} // Load Vector (128 bits) -instruct loadV16(vReg dst, vmem mem) %{ +instruct loadV16(vReg dst, vmem16 mem) %{ predicate(n->as_LoadVector()->memory_size() == 16); match(Set dst (LoadVector mem)); format %{ "loadV16 $dst, $mem\t# vector (128 bits)" %} @@ -408,7 +408,7 @@ instruct loadV16(vReg dst, vmem mem) %{ %} // Store Vector (128 bits) -instruct storeV16(vReg src, vmem mem) %{ +instruct storeV16(vReg src, vmem16 mem) %{ predicate(n->as_StoreVector()->memory_size() == 16); match(Set mem (StoreVector mem src)); format %{ "storeV16 $mem, $src\t# vector (128 bits)" %} diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 020a75b51fa8f..99708e9ef317d 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -338,7 +338,7 @@ dnl VECTOR_LOAD_STORE($1, $2, $3, $4, $5 ) dnl VECTOR_LOAD_STORE(type, nbytes, arg_name, nbits, size) define(`VECTOR_LOAD_STORE', ` // ifelse(load, $1, Load, Store) Vector ($4 bits) -instruct $1V$2(vReg $3, vmem mem) %{ +instruct $1V$2(vReg $3, vmem$2 mem) %{ predicate(`n->as_'ifelse(load, $1, Load, Store)Vector()->memory_size() == $2); match(Set ifelse(load, $1, dst (LoadVector mem), mem (StoreVector mem src))); format %{ "$1V$2 ifelse(load, $1, `$dst, $mem', `$mem, $src')\t# vector ($4 bits)" %} diff --git a/src/hotspot/cpu/aarch64/ad_encode.m4 b/src/hotspot/cpu/aarch64/ad_encode.m4 index e3d8ea661b60a..008dbd2c9369c 100644 --- a/src/hotspot/cpu/aarch64/ad_encode.m4 +++ b/src/hotspot/cpu/aarch64/ad_encode.m4 @@ -34,7 +34,7 @@ define(access, ` define(load,` // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_$2($1 dst, memory mem) %{dnl + enc_class aarch64_enc_$2($1 dst, memory$5 mem) %{dnl access(dst,$2,$3,$4,$5)')dnl load(iRegI,ldrsbw,,,1) load(iRegI,ldrsb,,,1) @@ -53,12 +53,12 @@ load(vRegD,ldrd,Float,,8) define(STORE,` // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_$2($1 src, memory mem) %{dnl + enc_class aarch64_enc_$2($1 src, memory$5 mem) %{dnl access(src,$2,$3,$4,$5)')dnl define(STORE0,` // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_$2`'0(memory mem) %{ + enc_class aarch64_enc_$2`'0(memory$4 mem) %{ choose(masm,zr,$2,$mem->opcode(), as_$3Register($mem$$base),$mem$$index,$mem$$scale,$mem$$disp,$4)')dnl STORE(iRegI,strb,,,1) @@ -82,7 +82,7 @@ STORE(vRegD,strd,Float,,8) // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb0_ordered(memory mem) %{ + enc_class aarch64_enc_strb0_ordered(memory4 mem) %{ __ membar(Assembler::StoreStore); loadStore(masm, &MacroAssembler::strb, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 5e116d82761ac..1385366d8793b 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1168,8 +1168,8 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { - __ ldrb(rscratch1, Address(op->klass()->as_register(), - InstanceKlass::init_state_offset())); + __ lea(rscratch1, Address(op->klass()->as_register(), InstanceKlass::init_state_offset())); + __ ldarb(rscratch1, rscratch1); __ cmpw(rscratch1, InstanceKlass::fully_initialized); add_debug_info_for_null_check_here(op->stub()->info()); __ br(Assembler::NE, *op->stub()->entry()); diff --git a/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp index dabafb9288b83..4bd509880f29c 100644 --- a/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp @@ -64,31 +64,4 @@ void C2EntryBarrierStub::emit(C2_MacroAssembler& masm) { __ emit_int32(0); // nmethod guard value } -int C2HandleAnonOMOwnerStub::max_size() const { - // Max size of stub has been determined by testing with 0, in which case - // C2CodeStubList::emit() will throw an assertion and report the actual size that - // is needed. - return 24; -} - -void C2HandleAnonOMOwnerStub::emit(C2_MacroAssembler& masm) { - __ bind(entry()); - Register mon = monitor(); - Register t = tmp(); - assert(t != noreg, "need tmp register"); - - // Fix owner to be the current thread. - __ str(rthread, Address(mon, ObjectMonitor::owner_offset())); - - // Pop owner object from lock-stack. - __ ldrw(t, Address(rthread, JavaThread::lock_stack_top_offset())); - __ subw(t, t, oopSize); -#ifdef ASSERT - __ str(zr, Address(rthread, t)); -#endif - __ strw(t, Address(rthread, JavaThread::lock_stack_top_offset())); - - __ b(continuation()); -} - #undef __ diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index b4c12ecd4a849..62831ee72ba05 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -150,10 +150,12 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe Register oop = objectReg; Register box = boxReg; Register disp_hdr = tmpReg; + Register owner_addr = tmpReg; Register tmp = tmp2Reg; Label cont; Label object_has_monitor; Label count, no_count; + Label unlocked; assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight"); assert_different_registers(oop, box, tmp, disp_hdr); @@ -204,14 +206,40 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe b(cont); bind(notRecursive); + + // Compute owner address. + lea(owner_addr, Address(tmp, ObjectMonitor::owner_offset())); + + // Set owner to null. + // Release to satisfy the JMM + stlr(zr, owner_addr); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); + + // Check if the entry lists are empty. ldr(rscratch1, Address(tmp, ObjectMonitor::EntryList_offset())); - ldr(disp_hdr, Address(tmp, ObjectMonitor::cxq_offset())); - orr(rscratch1, rscratch1, disp_hdr); // Will be 0 if both are 0. - cmp(rscratch1, zr); // Sets flags for result - cbnz(rscratch1, cont); - // need a release store here - lea(tmp, Address(tmp, ObjectMonitor::owner_offset())); - stlr(zr, tmp); // set unowned + ldr(tmpReg, Address(tmp, ObjectMonitor::cxq_offset())); + orr(rscratch1, rscratch1, tmpReg); + cmp(rscratch1, zr); + br(Assembler::EQ, cont); // If so we are done. + + // Check if there is a successor. + ldr(rscratch1, Address(tmp, ObjectMonitor::succ_offset())); + cmp(rscratch1, zr); + br(Assembler::NE, unlocked); // If so we are done. + + // Save the monitor pointer in the current thread, so we can try to + // reacquire the lock in SharedRuntime::monitor_exit_helper(). + str(tmp, Address(rthread, JavaThread::unlocked_inflated_monitor_offset())); + + cmp(zr, rthread); // Set Flag to NE => slow path + b(cont); + + bind(unlocked); + cmp(zr, zr); // Set Flag to EQ => fast path + + // Intentional fall-through bind(cont); // flag == EQ indicates success @@ -498,33 +526,41 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Regi bind(not_recursive); - Label release; const Register t2_owner_addr = t2; // Compute owner address. lea(t2_owner_addr, Address(t1_monitor, ObjectMonitor::owner_offset())); + // Set owner to null. + // Release to satisfy the JMM + stlr(zr, t2_owner_addr); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); + // Check if the entry lists are empty. ldr(rscratch1, Address(t1_monitor, ObjectMonitor::EntryList_offset())); ldr(t3_t, Address(t1_monitor, ObjectMonitor::cxq_offset())); orr(rscratch1, rscratch1, t3_t); cmp(rscratch1, zr); - br(Assembler::EQ, release); + br(Assembler::EQ, unlocked); // If so we are done. - // The owner may be anonymous and we removed the last obj entry in - // the lock-stack. This loses the information about the owner. - // Write the thread to the owner field so the runtime knows the owner. - str(rthread, Address(t2_owner_addr)); - b(slow_path); + // Check if there is a successor. + ldr(rscratch1, Address(t1_monitor, ObjectMonitor::succ_offset())); + cmp(rscratch1, zr); + br(Assembler::NE, unlocked); // If so we are done. - bind(release); - // Set owner to null. - // Release to satisfy the JMM - stlr(zr, t2_owner_addr); + // Save the monitor pointer in the current thread, so we can try to + // reacquire the lock in SharedRuntime::monitor_exit_helper(). + str(t1_monitor, Address(rthread, JavaThread::unlocked_inflated_monitor_offset())); + + cmp(zr, rthread); // Set Flag to NE => slow path + b(slow_path); } bind(unlocked); decrement(Address(rthread, JavaThread::held_monitor_count_offset())); + cmp(zr, zr); // Set Flags to EQ => fast path #ifdef ASSERT // Check that unlocked label is reached with Flags == EQ. diff --git a/src/hotspot/cpu/aarch64/cas.m4 b/src/hotspot/cpu/aarch64/cas.m4 index f8aac0c4939fa..7e13e153db18a 100644 --- a/src/hotspot/cpu/aarch64/cas.m4 +++ b/src/hotspot/cpu/aarch64/cas.m4 @@ -45,7 +45,9 @@ define(`CAS_INSN', // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct compareAndExchange$1$6(iReg$2NoSp res, indirect mem, iReg$2 oldval, iReg$2 newval, rFlagsReg cr) %{ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == 0));), + $1$6,NAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0);), $1,P,INDENT(predicate(n->as_LoadStore()->barrier_data() == 0);), + $1,N,INDENT(predicate(n->as_LoadStore()->barrier_data() == 0);), $6,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));), `dnl') match(Set res (CompareAndExchange$1 mem (Binary oldval newval))); @@ -122,7 +124,9 @@ define(`CAS_INSN3', // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct weakCompareAndSwap$1$6(iRegINoSp res, indirect mem, iReg$2 oldval, iReg$2 newval, rFlagsReg cr) %{ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_LoadStore()->barrier_data() == 0));), + $1$6,NAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0);), $1,P,INDENT(predicate(n->as_LoadStore()->barrier_data() == 0);), + $1,N,INDENT(predicate(n->as_LoadStore()->barrier_data() == 0);), $6,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));), `dnl') match(Set res (WeakCompareAndSwap$1 mem (Binary oldval newval))); diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp index d02038b6e9193..b978c350ce131 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp @@ -38,7 +38,10 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/g1/c1/g1BarrierSetC1.hpp" -#endif +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#endif // COMPILER2 #define __ masm-> @@ -95,6 +98,54 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas __ pop(saved_regs, sp); } +static void generate_queue_test_and_insertion(MacroAssembler* masm, ByteSize index_offset, ByteSize buffer_offset, Label& runtime, + const Register thread, const Register value, const Register temp1, const Register temp2) { + // Can we store a value in the given thread's buffer? + // (The index field is typed as size_t.) + __ ldr(temp1, Address(thread, in_bytes(index_offset))); // temp1 := *(index address) + __ cbz(temp1, runtime); // jump to runtime if index == 0 (full buffer) + // The buffer is not full, store value into it. + __ sub(temp1, temp1, wordSize); // temp1 := next index + __ str(temp1, Address(thread, in_bytes(index_offset))); // *(index address) := next index + __ ldr(temp2, Address(thread, in_bytes(buffer_offset))); // temp2 := buffer address + __ str(value, Address(temp2, temp1)); // *(buffer address + next index) := value +} + +static void generate_pre_barrier_fast_path(MacroAssembler* masm, + const Register thread, + const Register tmp1) { + Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); + // Is marking active? + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { + __ ldrw(tmp1, in_progress); + } else { + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); + __ ldrb(tmp1, in_progress); + } +} + +static void generate_pre_barrier_slow_path(MacroAssembler* masm, + const Register obj, + const Register pre_val, + const Register thread, + const Register tmp1, + const Register tmp2, + Label& done, + Label& runtime) { + // Do we need to load the previous value? + if (obj != noreg) { + __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); + } + // Is the previous value null? + __ cbz(pre_val, done); + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::satb_mark_queue_index_offset(), + G1ThreadLocalData::satb_mark_queue_buffer_offset(), + runtime, + thread, pre_val, tmp1, tmp2); + __ b(done); +} + void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -115,43 +166,10 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, assert_different_registers(obj, pre_val, tmp1, tmp2); assert(pre_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register"); - Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); - Address index(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset())); - - // Is marking active? - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { - __ ldrw(tmp1, in_progress); - } else { - assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ ldrb(tmp1, in_progress); - } + generate_pre_barrier_fast_path(masm, thread, tmp1); + // If marking is not active (*(mark queue active address) == 0), jump to done __ cbzw(tmp1, done); - - // Do we need to load the previous value? - if (obj != noreg) { - __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); - } - - // Is the previous value null? - __ cbz(pre_val, done); - - // Can we store original value in the thread's buffer? - // Is index == 0? - // (The index field is typed as size_t.) - - __ ldr(tmp1, index); // tmp := *index_adr - __ cbz(tmp1, runtime); // tmp == 0? - // If yes, goto runtime - - __ sub(tmp1, tmp1, wordSize); // tmp := tmp - wordSize - __ str(tmp1, index); // *index_adr := tmp - __ ldr(tmp2, buffer); - __ add(tmp1, tmp1, tmp2); // tmp := tmp + *buffer_adr - - // Record the previous value - __ str(pre_val, Address(tmp1, 0)); - __ b(done); + generate_pre_barrier_slow_path(masm, obj, pre_val, thread, tmp1, tmp2, done, runtime); __ bind(runtime); @@ -182,6 +200,50 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, } +static void generate_post_barrier_fast_path(MacroAssembler* masm, + const Register store_addr, + const Register new_val, + const Register tmp1, + const Register tmp2, + Label& done, + bool new_val_may_be_null) { + // Does store cross heap regions? + __ eor(tmp1, store_addr, new_val); // tmp1 := store address ^ new value + __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) + __ cbz(tmp1, done); + // Crosses regions, storing null? + if (new_val_may_be_null) { + __ cbz(new_val, done); + } + // Storing region crossing non-null, is card young? + __ lsr(tmp1, store_addr, CardTable::card_shift()); // tmp1 := card address relative to card table base + __ load_byte_map_base(tmp2); // tmp2 := card table base address + __ add(tmp1, tmp1, tmp2); // tmp1 := card address + __ ldrb(tmp2, Address(tmp1)); // tmp2 := card + __ cmpw(tmp2, (int)G1CardTable::g1_young_card_val()); // tmp2 := card == young_card_val? +} + +static void generate_post_barrier_slow_path(MacroAssembler* masm, + const Register thread, + const Register tmp1, + const Register tmp2, + Label& done, + Label& runtime) { + __ membar(Assembler::StoreLoad); // StoreLoad membar + __ ldrb(tmp2, Address(tmp1)); // tmp2 := card + __ cbzw(tmp2, done); + // Storing a region crossing, non-null oop, card is clean. + // Dirty card and log. + STATIC_ASSERT(CardTable::dirty_card_val() == 0); + __ strb(zr, Address(tmp1)); // *(card address) := dirty_card_val + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + runtime, + thread, tmp1, tmp2, rscratch1); + __ b(done); +} + void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Register store_addr, Register new_val, @@ -194,70 +256,116 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, assert(store_addr != noreg && new_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register"); - Address queue_index(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset())); - Address buffer(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset())); - - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - Label done; Label runtime; - // Does store cross heap regions? + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp1, tmp2, done, true /* new_val_may_be_null */); + // If card is young, jump to done + __ br(Assembler::EQ, done); + generate_post_barrier_slow_path(masm, thread, tmp1, tmp2, done, runtime); - __ eor(tmp1, store_addr, new_val); - __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); - __ cbz(tmp1, done); + __ bind(runtime); + // save the live input values + RegSet saved = RegSet::of(store_addr); + __ push(saved, sp); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), tmp1, thread); + __ pop(saved, sp); - // crosses regions, storing null? + __ bind(done); +} - __ cbz(new_val, done); +#if defined(COMPILER2) - // storing region crossing non-null, is card already dirty? +static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStubC2* stub, const Register arg, const address runtime_path) { + SaveLiveRegisters save_registers(masm, stub); + if (c_rarg0 != arg) { + __ mov(c_rarg0, arg); + } + __ mov(c_rarg1, rthread); + __ mov(rscratch1, runtime_path); + __ blr(rscratch1); +} - const Register card_addr = tmp1; +void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* stub) { + assert(thread == rthread, "must be"); + assert_different_registers(obj, pre_val, tmp1, tmp2); + assert(pre_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register"); - __ lsr(card_addr, store_addr, CardTable::card_shift()); + stub->initialize_registers(obj, pre_val, thread, tmp1, tmp2); - // get the address of the card - __ load_byte_map_base(tmp2); - __ add(card_addr, card_addr, tmp2); - __ ldrb(tmp2, Address(card_addr)); - __ cmpw(tmp2, (int)G1CardTable::g1_young_card_val()); - __ br(Assembler::EQ, done); + generate_pre_barrier_fast_path(masm, thread, tmp1); + // If marking is active (*(mark queue active address) != 0), jump to stub (slow path) + __ cbnzw(tmp1, *stub->entry()); - assert((int)CardTable::dirty_card_val() == 0, "must be 0"); + __ bind(*stub->continuation()); +} - __ membar(Assembler::StoreLoad); +void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register obj = stub->obj(); + Register pre_val = stub->pre_val(); + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); + Register tmp2 = stub->tmp2(); - __ ldrb(tmp2, Address(card_addr)); - __ cbzw(tmp2, done); + __ bind(*stub->entry()); + generate_pre_barrier_slow_path(masm, obj, pre_val, thread, tmp1, tmp2, *stub->continuation(), runtime); - // storing a region crossing, non-null oop, card is clean. - // dirty card and log. + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, pre_val, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry)); + __ b(*stub->continuation()); +} - __ strb(zr, Address(card_addr)); +void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* stub) { + assert(thread == rthread, "must be"); + assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, + rscratch1); + assert(store_addr != noreg && new_val != noreg && tmp1 != noreg + && tmp2 != noreg, "expecting a register"); - __ ldr(rscratch1, queue_index); - __ cbz(rscratch1, runtime); - __ sub(rscratch1, rscratch1, wordSize); - __ str(rscratch1, queue_index); + stub->initialize_registers(thread, tmp1, tmp2); - __ ldr(tmp2, buffer); - __ str(card_addr, Address(tmp2, rscratch1)); - __ b(done); + bool new_val_may_be_null = (stub->barrier_data() & G1C2BarrierPostNotNull) == 0; + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp1, tmp2, *stub->continuation(), new_val_may_be_null); + // If card is not young, jump to stub (slow path) + __ br(Assembler::NE, *stub->entry()); - __ bind(runtime); - // save the live input values - RegSet saved = RegSet::of(store_addr); - __ push(saved, sp); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, thread); - __ pop(saved, sp); + __ bind(*stub->continuation()); +} - __ bind(done); +void G1BarrierSetAssembler::generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); // tmp1 holds the card address. + Register tmp2 = stub->tmp2(); + assert(stub->tmp3() == noreg, "not needed in this platform"); + + __ bind(*stub->entry()); + generate_post_barrier_slow_path(masm, thread, tmp1, tmp2, *stub->continuation(), runtime); + + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, tmp1, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry)); + __ b(*stub->continuation()); } +#endif // COMPILER2 + void G1BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp2) { bool on_oop = is_reference_type(type); diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp index 7b4bc8cdc49de..4baa18cb94544 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.hpp @@ -33,6 +33,8 @@ class LIR_Assembler; class StubAssembler; class G1PreBarrierStub; class G1PostBarrierStub; +class G1PreBarrierStubC2; +class G1PostBarrierStubC2; class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { protected: @@ -69,6 +71,27 @@ class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { void generate_c1_post_barrier_runtime_stub(StubAssembler* sasm); #endif +#ifdef COMPILER2 + void g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* c2_stub); + void generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const; + void g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* c2_stub); + void generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const; +#endif + void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp2); }; diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad new file mode 100644 index 0000000000000..081a67d68807b --- /dev/null +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad @@ -0,0 +1,680 @@ +// +// 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. +// + +source_hpp %{ + +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#include "gc/shared/gc_globals.hpp" + +%} + +source %{ + +#include "gc/g1/g1BarrierSetAssembler_aarch64.hpp" +#include "gc/g1/g1BarrierSetRuntime.hpp" + +static void write_barrier_pre(MacroAssembler* masm, + const MachNode* node, + Register obj, + Register pre_val, + Register tmp1, + Register tmp2, + RegSet preserve = RegSet(), + RegSet no_preserve = RegSet()) { + if (!G1PreBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PreBarrierStubC2* const stub = G1PreBarrierStubC2::create(node); + for (RegSetIterator reg = preserve.begin(); *reg != noreg; ++reg) { + stub->preserve(*reg); + } + for (RegSetIterator reg = no_preserve.begin(); *reg != noreg; ++reg) { + stub->dont_preserve(*reg); + } + g1_asm->g1_write_barrier_pre_c2(masm, obj, pre_val, rthread, tmp1, tmp2, stub); +} + +static void write_barrier_post(MacroAssembler* masm, + const MachNode* node, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2) { + if (!G1PostBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PostBarrierStubC2* const stub = G1PostBarrierStubC2::create(node); + g1_asm->g1_write_barrier_post_c2(masm, store_addr, new_val, rthread, tmp1, tmp2, stub); +} + +%} + +// BEGIN This section of the file is automatically generated. Do not edit -------------- + +// This section is generated from g1_aarch64.m4 + + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1StoreP(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_releasing_store(n) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(INSN_COST); + format %{ "str $src, $mem\t# ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ str($src$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(istore_reg_mem); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1StorePVolatile(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_releasing_store(n) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "stlr $src, $mem\t# ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ stlr($src$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_class_memory); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1StoreN(indirect mem, iRegN src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_releasing_store(n) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(INSN_COST); + format %{ "strw $src, $mem\t# compressed ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ strw($src$$Register, $mem$$Register); + if ((barrier_data() & G1C2BarrierPost) != 0) { + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ decode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ decode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + } + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(istore_reg_mem); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1StoreNVolatile(indirect mem, iRegN src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_releasing_store(n) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "stlrw $src, $mem\t# compressed ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ stlrw($src$$Register, $mem$$Register); + if ((barrier_data() & G1C2BarrierPost) != 0) { + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ decode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ decode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + } + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_class_memory); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1EncodePAndStoreN(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_releasing_store(n) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem (EncodeP src))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(INSN_COST); + format %{ "encode_heap_oop $tmp1, $src\n\t" + "strw $tmp1, $mem\t# compressed ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ encode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ encode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + __ strw($tmp1$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(istore_reg_mem); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1EncodePAndStoreNVolatile(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_releasing_store(n) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem (EncodeP src))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "encode_heap_oop $tmp1, $src\n\t" + "stlrw $tmp1, $mem\t# compressed ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ encode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ encode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + __ stlrw($tmp1$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_class_memory); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $res = $mem, $oldval, $newval\t# ptr" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + // Pass $oldval to the pre-barrier (instead of loading from $mem), because + // $oldval is the only value that can be overwritten. + // The same holds for g1CompareAndSwapP and its Acq variant. + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + false /* acquire */, true /* release */, false /* weak */, $res$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $res = $mem, $oldval, $newval\t# ptr" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + // Pass $oldval to the pre-barrier (instead of loading from $mem), because + // $oldval is the only value that can be overwritten. + // The same holds for g1CompareAndSwapP and its Acq variant. + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + true /* acquire */, true /* release */, false /* weak */, $res$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $res = $mem, $oldval, $newval\t# narrow oop" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, + false /* acquire */, true /* release */, false /* weak */, $res$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $res = $mem, $oldval, $newval\t# narrow oop" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, + true /* acquire */, true /* release */, false /* weak */, $res$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $mem, $oldval, $newval\t# (ptr)\n\t" + "cset $res, EQ" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + false /* acquire */, true /* release */, false /* weak */, noreg); + __ cset($res$$Register, Assembler::EQ); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $mem, $oldval, $newval\t# (ptr)\n\t" + "cset $res, EQ" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + true /* acquire */, true /* release */, false /* weak */, noreg); + __ cset($res$$Register, Assembler::EQ); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $mem, $oldval, $newval\t# (narrow oop)\n\t" + "cset $res, EQ" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, + false /* acquire */, true /* release */, false /* weak */, noreg); + __ cset($res$$Register, Assembler::EQ); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $mem, $oldval, $newval\t# (narrow oop)\n\t" + "cset $res, EQ" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, + true /* acquire */, true /* release */, false /* weak */, noreg); + __ cset($res$$Register, Assembler::EQ); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1GetAndSetP(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetP mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "atomic_xchg $preval, $newval, [$mem]" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $preval$$Register /* pre_val (as a temporary register) */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchg($preval$$Register, $newval$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1GetAndSetPAcq(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetP mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "atomic_xchg_acq $preval, $newval, [$mem]" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $preval$$Register /* pre_val (as a temporary register) */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchgal($preval$$Register, $newval$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1GetAndSetN(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetN mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "atomic_xchgw $preval, $newval, [$mem]" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchgw($preval$$Register, $newval$$Register, $mem$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1GetAndSetNAcq(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetN mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "atomic_xchgw_acq $preval, $newval, [$mem]" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchgalw($preval$$Register, $newval$$Register, $mem$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1LoadP(iRegPNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + // This instruction does not need an acquiring counterpart because it is only + // used for reference loading (Reference::get()). The same holds for g1LoadN. + predicate(UseG1GC && !needs_acquiring_load(n) && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadP mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(4 * INSN_COST); + format %{ "ldr $dst, $mem\t# ptr" %} + ins_encode %{ + __ ldr($dst$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $dst$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(iload_reg_mem); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1LoadN(iRegNNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load(n) && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadN mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(4 * INSN_COST); + format %{ "ldrw $dst, $mem\t# compressed ptr" %} + ins_encode %{ + __ ldrw($dst$$Register, $mem$$Register); + if ((barrier_data() & G1C2BarrierPre) != 0) { + __ decode_heap_oop($tmp1$$Register, $dst$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + } + %} + ins_pipe(iload_reg_mem); +%} + +// END This section of the file is automatically generated. Do not edit -------------- diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 new file mode 100644 index 0000000000000..8fb1f7e8e428b --- /dev/null +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 @@ -0,0 +1,384 @@ +dnl Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +dnl DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +dnl +dnl This code is free software; you can redistribute it and/or modify it +dnl under the terms of the GNU General Public License version 2 only, as +dnl published by the Free Software Foundation. +dnl +dnl This code is distributed in the hope that it will be useful, but WITHOUT +dnl ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +dnl FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +dnl version 2 for more details (a copy is included in the LICENSE file that +dnl accompanied this code). +dnl +dnl You should have received a copy of the GNU General Public License version +dnl 2 along with this work; if not, write to the Free Software Foundation, +dnl Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +dnl +dnl Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +dnl or visit www.oracle.com if you need additional information or have any +dnl questions. +dnl +// BEGIN This section of the file is automatically generated. Do not edit -------------- + +// This section is generated from g1_aarch64.m4 + +define(`STOREP_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1StoreP$1(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Volatile,'needs_releasing_store(n)`,'!needs_releasing_store(n)`) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(ifelse($1,Volatile,VOLATILE_REF_COST,INSN_COST)); + format %{ "$2 $src, $mem\t# ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ $2($src$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(ifelse($1,Volatile,pipe_class_memory,istore_reg_mem)); +%}')dnl +STOREP_INSN(,str) +STOREP_INSN(Volatile,stlr) +dnl +define(`STOREN_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1StoreN$1(indirect mem, iRegN src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Volatile,'needs_releasing_store(n)`,'!needs_releasing_store(n)`) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(ifelse($1,Volatile,VOLATILE_REF_COST,INSN_COST)); + format %{ "$2 $src, $mem\t# compressed ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ $2($src$$Register, $mem$$Register); + if ((barrier_data() & G1C2BarrierPost) != 0) { + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ decode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ decode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + } + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(ifelse($1,Volatile,pipe_class_memory,istore_reg_mem)); +%}')dnl +STOREN_INSN(,strw) +STOREN_INSN(Volatile,stlrw) +dnl +define(`ENCODESTOREN_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1EncodePAndStoreN$1(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Volatile,'needs_releasing_store(n)`,'!needs_releasing_store(n)`) && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem (EncodeP src))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(ifelse($1,Volatile,VOLATILE_REF_COST,INSN_COST)); + format %{ "encode_heap_oop $tmp1, $src\n\t" + "$2 $tmp1, $mem\t# compressed ptr" %} + ins_encode %{ + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ encode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ encode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + __ $2($tmp1$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(ifelse($1,Volatile,pipe_class_memory,istore_reg_mem)); +%}')dnl +ENCODESTOREN_INSN(,strw) +ENCODESTOREN_INSN(Volatile,stlrw) +dnl +define(`CAEP_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndExchangeP$1(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Acq,'needs_acquiring_load_exclusive(n)`,'!needs_acquiring_load_exclusive(n)`) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(ifelse($1,Acq,VOLATILE_REF_COST,2 * VOLATILE_REF_COST)); + format %{ "cmpxchg$2 $res = $mem, $oldval, $newval\t# ptr" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + // Pass $oldval to the pre-barrier (instead of loading from $mem), because + // $oldval is the only value that can be overwritten. + // The same holds for g1CompareAndSwapP and its Acq variant. + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%}')dnl +CAEP_INSN(,,false) +CAEP_INSN(Acq,_acq,true) +dnl +define(`CAEN_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndExchangeN$1(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Acq,'needs_acquiring_load_exclusive(n)`,'!needs_acquiring_load_exclusive(n)`) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(ifelse($1,Acq,VOLATILE_REF_COST,2 * VOLATILE_REF_COST)); + format %{ "cmpxchg$2 $res = $mem, $oldval, $newval\t# narrow oop" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, + $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%}')dnl +CAEN_INSN(,,false) +CAEN_INSN(Acq,_acq,true) +dnl +define(`CASP_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndSwapP$1(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Acq,'needs_acquiring_load_exclusive(n)`,'!needs_acquiring_load_exclusive(n)`) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(ifelse($1,Acq,VOLATILE_REF_COST,2 * VOLATILE_REF_COST)); + format %{ "cmpxchg$2 $mem, $oldval, $newval\t# (ptr)\n\t" + "cset $res, EQ" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, + $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cset($res$$Register, Assembler::EQ); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%}')dnl +CASP_INSN(,,false) +CASP_INSN(Acq,_acq,true) +dnl +define(`CASN_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1CompareAndSwapN$1(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Acq,'needs_acquiring_load_exclusive(n)`,'!needs_acquiring_load_exclusive(n)`) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(ifelse($1,Acq,VOLATILE_REF_COST,2 * VOLATILE_REF_COST)); + format %{ "cmpxchg$2 $mem, $oldval, $newval\t# (narrow oop)\n\t" + "cset $res, EQ" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, + $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cset($res$$Register, Assembler::EQ); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%}')dnl +CASN_INSN(,,false) +CASN_INSN(Acq,_acq,true) +dnl +define(`XCHGP_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1GetAndSetP$1(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Acq,'needs_acquiring_load_exclusive(n)`,'!needs_acquiring_load_exclusive(n)`) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetP mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(ifelse($1,Acq,VOLATILE_REF_COST,2 * VOLATILE_REF_COST)); + format %{ "atomic_xchg$2 $preval, $newval, [$mem]" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $preval$$Register /* pre_val (as a temporary register) */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ $3($preval$$Register, $newval$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%}')dnl +XCHGP_INSN(,,atomic_xchg) +XCHGP_INSN(Acq,_acq,atomic_xchgal) +dnl +define(`XCHGN_INSN', +` +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1GetAndSetN$1(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && ifelse($1,Acq,'needs_acquiring_load_exclusive(n)`,'!needs_acquiring_load_exclusive(n)`) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetN mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(ifelse($1,Acq,VOLATILE_REF_COST,2 * VOLATILE_REF_COST)); + format %{ "$2 $preval, $newval, [$mem]" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ $3($preval$$Register, $newval$$Register, $mem$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%}')dnl +XCHGN_INSN(,atomic_xchgw,atomic_xchgw) +XCHGN_INSN(Acq,atomic_xchgw_acq,atomic_xchgalw) + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1LoadP(iRegPNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + // This instruction does not need an acquiring counterpart because it is only + // used for reference loading (Reference::get()). The same holds for g1LoadN. + predicate(UseG1GC && !needs_acquiring_load(n) && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadP mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(4 * INSN_COST); + format %{ "ldr $dst, $mem\t# ptr" %} + ins_encode %{ + __ ldr($dst$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $dst$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(iload_reg_mem); +%} + +// This pattern is generated automatically from g1_aarch64.m4. +// DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE +instruct g1LoadN(iRegNNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && !needs_acquiring_load(n) && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadN mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(4 * INSN_COST); + format %{ "ldrw $dst, $mem\t# compressed ptr" %} + ins_encode %{ + __ ldrw($dst$$Register, $mem$$Register); + if ((barrier_data() & G1C2BarrierPre) != 0) { + __ decode_heap_oop($tmp1$$Register, $dst$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + } + %} + ins_pipe(iload_reg_mem); +%} + +// END This section of the file is automatically generated. Do not edit -------------- diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 06f4382015603..84d06dbcc7bfd 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -67,9 +67,9 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec __ push(saved_regs, sp); if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop_entry), src, dst, count); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop), src, dst, count); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop_entry), src, dst, count); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop), src, dst, count); } __ pop(saved_regs, sp); __ bind(done); @@ -164,9 +164,9 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, if (expand_call) { assert(pre_val != c_rarg1, "smashed arg"); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); } __ pop(saved, sp); @@ -698,7 +698,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ bind(runtime); __ push_call_clobbered_registers(); __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); __ pop_call_clobbered_registers(); __ bind(done); diff --git a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad index 5e690a8e47b94..6e401724baa82 100644 --- a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad @@ -51,7 +51,7 @@ static void x_load_barrier_slow_path(MacroAssembler* masm, const MachNode* node, %} // Load Pointer -instruct xLoadP(iRegPNoSp dst, memory mem, rFlagsReg cr) +instruct xLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && !ZGenerational && !needs_acquiring_load(n) && (n->as_Load()->barrier_data() != 0)); diff --git a/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp index cd834969e1a4f..fcec3ae64fde8 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp @@ -93,7 +93,7 @@ static size_t probe_valid_max_address_bit() { } size_t ZPlatformAddressOffsetBits() { - const static size_t valid_max_address_offset_bits = probe_valid_max_address_bit() + 1; + static const size_t valid_max_address_offset_bits = probe_valid_max_address_bit() + 1; const size_t max_address_offset_bits = valid_max_address_offset_bits - 3; const size_t min_address_offset_bits = max_address_offset_bits - 2; const size_t address_offset = round_up_power_of_2(MaxHeapSize * ZVirtualToPhysicalRatio); diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 466e77a4460d0..3f1898b6742e1 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -1189,6 +1189,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } @@ -1307,11 +1309,11 @@ Label* ZLoadBarrierStubC2Aarch64::entry() { return ZBarrierStubC2::entry(); } -ZStoreBarrierStubC2Aarch64::ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) - : ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic), _deferred_emit(false) {} +ZStoreBarrierStubC2Aarch64::ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) + : ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive), _deferred_emit(false) {} -ZStoreBarrierStubC2Aarch64* ZStoreBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) { - ZStoreBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2Aarch64(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic); +ZStoreBarrierStubC2Aarch64* ZStoreBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) { + ZStoreBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2Aarch64(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive); register_stub(stub); return stub; } diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp index 2f716140ed19d..ad3a171c10370 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp @@ -280,10 +280,10 @@ class ZStoreBarrierStubC2Aarch64 : public ZStoreBarrierStubC2 { private: bool _deferred_emit; - ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); public: - static ZStoreBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + static ZStoreBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); virtual void emit_code(MacroAssembler& masm); }; diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 1510b42bfe97d..088f92a01573e 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -91,7 +91,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address z_color(masm, node, rnew_zpointer, rnew_zaddress); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2Aarch64* const stub = ZStoreBarrierStubC2Aarch64::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2Aarch64* const stub = ZStoreBarrierStubC2Aarch64::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, tmp, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } @@ -100,7 +101,7 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address %} // Load Pointer -instruct zLoadP(iRegPNoSp dst, memory mem, rFlagsReg cr) +instruct zLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && ZGenerational && !needs_acquiring_load(n) && n->as_Load()->barrier_data() != 0); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 08b69b34a9462..9835fb5aca159 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1838,7 +1838,8 @@ void MacroAssembler::clinit_barrier(Register klass, Register scratch, Label* L_f L_slow_path = &L_fallthrough; } // Fast path check: class is fully initialized - ldrb(scratch, Address(klass, InstanceKlass::init_state_offset())); + lea(scratch, Address(klass, InstanceKlass::init_state_offset())); + ldarb(scratch, scratch); subs(zr, scratch, InstanceKlass::fully_initialized); br(Assembler::EQ, *L_fast_path); @@ -2967,7 +2968,7 @@ void MacroAssembler::verify_heapbase(const char* msg) { if (CheckCompressedOops) { Label ok; push(1 << rscratch1->encoding(), sp); // cmpptr trashes rscratch1 - cmpptr(rheapbase, ExternalAddress(CompressedOops::ptrs_base_addr())); + cmpptr(rheapbase, ExternalAddress(CompressedOops::base_addr())); br(Assembler::EQ, ok); stop(msg); bind(ok); @@ -3133,9 +3134,9 @@ void MacroAssembler::reinit_heapbase() { if (UseCompressedOops) { if (Universe::is_fully_initialized()) { - mov(rheapbase, CompressedOops::ptrs_base()); + mov(rheapbase, CompressedOops::base()); } else { - lea(rheapbase, ExternalAddress(CompressedOops::ptrs_base_addr())); + lea(rheapbase, ExternalAddress(CompressedOops::base_addr())); ldr(rheapbase, Address(rheapbase)); } } @@ -5010,8 +5011,10 @@ void MacroAssembler::decode_heap_oop(Register d, Register s) { verify_heapbase("MacroAssembler::decode_heap_oop: heap base corrupted?"); #endif if (CompressedOops::base() == nullptr) { - if (CompressedOops::shift() != 0 || d != s) { + if (CompressedOops::shift() != 0) { lsl(d, s, CompressedOops::shift()); + } else if (d != s) { + mov(d, s); } } else { Label done; diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index 68800d04d69ba..aa6a9d14ff176 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #include "asm/macroAssembler.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "memory/allocation.inline.hpp" @@ -36,7 +37,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/stubRoutines.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 3117c75149854..52996f4c4a503 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -49,6 +49,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/timerTrace.hpp" #include "runtime/vframeArray.hpp" #include "utilities/align.hpp" #include "utilities/formatBuffer.hpp" @@ -2233,7 +2234,7 @@ void SharedRuntime::generate_deopt_blob() { int reexecute_offset = __ pc() - start; #if INCLUDE_JVMCI && !defined(COMPILER1) - if (EnableJVMCI && UseJVMCICompiler) { + if (UseJVMCICompiler) { // JVMCI does not use this kind of deoptimization __ should_not_reach_here(); } diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index b3513a586de35..31116e006f025 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -3417,15 +3417,15 @@ class StubGenerator: public StubCodeGenerator { Register rscratch3 = r10; Register rscratch4 = r11; - __ andw(rscratch3, r2, r4); - __ bicw(rscratch4, r3, r4); reg_cache.extract_u32(rscratch1, k); __ movw(rscratch2, t); - __ orrw(rscratch3, rscratch3, rscratch4); __ addw(rscratch4, r1, rscratch2); __ addw(rscratch4, rscratch4, rscratch1); - __ addw(rscratch3, rscratch3, rscratch4); - __ rorw(rscratch2, rscratch3, 32 - s); + __ bicw(rscratch2, r3, r4); + __ andw(rscratch3, r2, r4); + __ addw(rscratch2, rscratch2, rscratch4); + __ addw(rscratch2, rscratch2, rscratch3); + __ rorw(rscratch2, rscratch2, 32 - s); __ addw(r1, rscratch2, r2); } @@ -7320,6 +7320,28 @@ class StubGenerator: public StubCodeGenerator { return start; } + // load Method* target of MethodHandle + // j_rarg0 = jobject receiver + // rmethod = result + address generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, rscratch1, rscratch2); + // Load target method from receiver + __ load_heap_oop(rmethod, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), rscratch1, rscratch2); + __ load_heap_oop(rmethod, Address(rmethod, java_lang_invoke_LambdaForm::vmentry_offset()), rscratch1, rscratch2); + __ load_heap_oop(rmethod, Address(rmethod, java_lang_invoke_MemberName::method_offset()), rscratch1, rscratch2); + __ access_load_at(T_ADDRESS, IN_HEAP, rmethod, + Address(rmethod, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ str(rmethod, Address(rthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ ret(lr); + + return start; + } + #undef __ #define __ masm-> @@ -8241,6 +8263,7 @@ class StubGenerator: public StubCodeGenerator { #endif StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated } diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 38d48b86f23b0..9894841e933d8 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "compiler/compiler_globals.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/bytecodeHistogram.hpp" @@ -67,13 +68,7 @@ // Max size with JVMTI int TemplateInterpreter::InterpreterCodeSize = 200 * 1024; -#define __ _masm-> - -//----------------------------------------------------------------------------- - -extern "C" void entry(CodeBuffer*); - -//----------------------------------------------------------------------------- +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> address TemplateInterpreterGenerator::generate_slow_signature_handler() { address entry = __ pc(); @@ -2004,13 +1999,21 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& vep) { assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); Label L; - aep = __ pc(); __ push_ptr(); __ b(L); - fep = __ pc(); __ push_f(); __ b(L); - dep = __ pc(); __ push_d(); __ b(L); - lep = __ pc(); __ push_l(); __ b(L); - bep = cep = sep = - iep = __ pc(); __ push_i(); - vep = __ pc(); + aep = __ pc(); // atos entry point + __ push_ptr(); + __ b(L); + fep = __ pc(); // ftos entry point + __ push_f(); + __ b(L); + dep = __ pc(); // dtos entry point + __ push_d(); + __ b(L); + lep = __ pc(); // ltos entry point + __ push_l(); + __ b(L); + bep = cep = sep = iep = __ pc(); // [bcsi]tos entry point + __ push_i(); + vep = __ pc(); // vtos entry point __ bind(L); generate_and_dispatch(t); } diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 25eb339bfce71..48ff356f9a558 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "compiler/compilerDefinitions.inline.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.hpp" @@ -49,7 +50,7 @@ #include "runtime/synchronizer.hpp" #include "utilities/powerOfTwo.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // Address computation: local variables diff --git a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp index 28ec07815be5c..517fccb2d1aa5 100644 --- a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -117,7 +118,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -222,7 +223,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ lea(c_rarg0, Address(sp, frame_data_offset)); - __ movptr(c_rarg1, (intptr_t)receiver); __ movptr(rscratch1, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ blr(rscratch1); __ mov(rthread, r0); @@ -238,12 +238,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(shuffle_reg), abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, rthread); - __ block_comment("} receiver "); - - __ mov_metadata(rmethod, entry); - __ str(rmethod, Address(rthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (intptr_t)receiver); + __ far_call(RuntimeAddress(StubRoutines::upcall_stub_load_target()), rscratch1); // puts target Method* in rmethod + __ block_comment("} load target "); __ push_cont_fastpath(rthread); @@ -318,7 +316,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 2c7de0a58a204..bfca986f350cb 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1003,10 +1003,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return nullptr; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return nullptr; -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; @@ -3890,6 +3886,7 @@ instruct loadRange(iRegI dst, memoryI mem) %{ instruct loadP(iRegP dst, memoryP mem) %{ + predicate(!(UseG1GC && n->as_Load()->barrier_data() != 0)); match(Set dst (LoadP mem)); ins_cost(MEMORY_REF_COST); size(4); @@ -4225,18 +4222,6 @@ instruct storeB(memoryB mem, store_RegI src) %{ ins_pipe(istore_mem_reg); %} -instruct storeCM(memoryB mem, store_RegI src) %{ - match(Set mem (StoreCM mem src)); - ins_cost(MEMORY_REF_COST); - - size(4); - format %{ "STRB $src,$mem\t! CMS card-mark byte" %} - ins_encode %{ - __ strb($src$$Register, $mem$$Address); - %} - ins_pipe(istore_mem_reg); -%} - // Store Char/Short @@ -4356,6 +4341,7 @@ instruct movSP(store_ptr_RegP dst, SPRegP src) %{ instruct storeP(memoryP mem, store_ptr_RegP src) %{ + predicate(!(UseG1GC && n->as_Store()->barrier_data() != 0)); match(Set mem (StoreP mem src)); ins_cost(MEMORY_REF_COST); size(4); @@ -5390,6 +5376,7 @@ instruct compareAndSwapI_bool(memoryex mem, iRegI oldval, iRegI newval, iRegI re %} instruct compareAndSwapP_bool(memoryex mem, iRegP oldval, iRegP newval, iRegI res, iRegI tmp, flagsReg ccr ) %{ + predicate(!(UseG1GC && n->as_LoadStore()->barrier_data() != 0)); match(Set res (CompareAndSwapP mem (Binary oldval newval))); effect( KILL ccr, TEMP tmp); size(28); @@ -5659,6 +5646,7 @@ instruct xchgL(memoryex mem, iRegLd newval, iRegLd res, iRegI tmp, flagsReg ccr) %} instruct xchgP(memoryex mem, iRegP newval, iRegP res, iRegI tmp, flagsReg ccr) %{ + predicate(!(UseG1GC && n->as_LoadStore()->barrier_data() != 0)); match(Set res (GetAndSetP mem newval)); effect(KILL ccr, TEMP tmp, TEMP res); size(16); diff --git a/src/hotspot/cpu/arm/assembler_arm_32.hpp b/src/hotspot/cpu/arm/assembler_arm_32.hpp index dd04ad1ab3a3c..e53eefac097ef 100644 --- a/src/hotspot/cpu/arm/assembler_arm_32.hpp +++ b/src/hotspot/cpu/arm/assembler_arm_32.hpp @@ -119,8 +119,9 @@ class RegisterSet { } friend RegisterSet operator | (const RegisterSet set1, const RegisterSet set2) { - assert((set1._encoding & set2._encoding) == 0, - "encoding constraint"); +// why so strong constraint? +// assert((set1._encoding & set2._encoding) == 0, +// "encoding constraint"); return RegisterSet(set1._encoding | set2._encoding); } @@ -142,6 +143,11 @@ class RegisterSet { } return count; } + + static RegisterSet from(RegSet set) { + assert(set.size(), "RegSet must not be empty"); + return RegisterSet(set.bits()); + } }; #if R9_IS_SCRATCHED @@ -157,6 +163,10 @@ class FloatRegisterSet { public: + FloatRegisterSet() { + _encoding = 0; + } + FloatRegisterSet(FloatRegister reg) { if (reg->hi_bit() == 0) { _encoding = reg->hi_bits() << 12 | reg->lo_bit() << 22 | 1; @@ -185,6 +195,15 @@ class FloatRegisterSet { return (_encoding & 0xFFFFFF00) | ((_encoding & 0xFF) << 1); } + static FloatRegisterSet from(FloatRegSet set) { + assert(set.size(), "FloatRegSet must not be empty"); + // the vector load/store instructions operate on a set of consecutive registers. + // for the sake of simplicity, write all registers between the first and last in the set + size_t range = (*set.rbegin())->encoding() - (*set.begin())->encoding() + 1; + // push_float stores float regisgters by pairs + return FloatRegisterSet(*set.begin(), (range+1)/2); + } + }; diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index bb6a93e6f8da7..b14e6f0b4ca0c 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -948,6 +948,7 @@ void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { Register tmp = op->tmp1()->as_register(); __ ldrb(tmp, Address(op->klass()->as_register(), InstanceKlass::init_state_offset())); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); add_debug_info_for_null_check_here(op->stub()->info()); __ cmp(tmp, InstanceKlass::fully_initialized); __ b(*op->stub()->entry(), ne); diff --git a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp index 3c5e29aa8710f..56ae7707fbf38 100644 --- a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp @@ -39,8 +39,10 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/g1/c1/g1BarrierSetC1.hpp" -#endif - +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#endif // COMPILER2 #define __ masm-> #ifdef PRODUCT @@ -106,70 +108,87 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas #endif // !R9_IS_SCRATCHED } -// G1 pre-barrier. -// Blows all volatile registers R0-R3, Rtemp, LR). -// If store_addr != noreg, then previous value is loaded from [store_addr]; -// in such case store_addr and new_val registers are preserved; -// otherwise pre_val register is preserved. -void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, - Register store_addr, - Register new_val, - Register pre_val, - Register tmp1, - Register tmp2) { - Label done; - Label runtime; - - if (store_addr != noreg) { - assert_different_registers(store_addr, new_val, pre_val, tmp1, tmp2, noreg); - } else { - assert (new_val == noreg, "should be"); - assert_different_registers(pre_val, tmp1, tmp2, noreg); - } - - Address in_progress(Rthread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); - Address index(Rthread, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(Rthread, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset())); +static void generate_queue_test_and_insertion(MacroAssembler* masm, ByteSize index_offset, ByteSize buffer_offset, Label& runtime, + const Register thread, const Register value, const Register temp1, const Register temp2) { + assert_different_registers(value, temp1, temp2); + // Can we store original value in the thread's buffer? + // (The index field is typed as size_t.) + __ ldr(temp1, Address(thread, in_bytes(index_offset))); // temp1 := *(index address) + __ cbz(temp1, runtime); // jump to runtime if index == 0 (full buffer) + // The buffer is not full, store value into it. + __ sub(temp1, temp1, wordSize); // temp1 := next index + __ str(temp1, Address(thread, in_bytes(index_offset))); // *(index address) := next index + __ ldr(temp2, Address(thread, in_bytes(buffer_offset))); // temp2 := buffer address + // Record the previous value + __ str(value, Address(temp2, temp1)); // *(buffer address + next index) := value + } +static void generate_pre_barrier_fast_path(MacroAssembler* masm, + const Register thread, + const Register tmp1) { + Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); // Is marking active? assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "adjust this code"); __ ldrb(tmp1, in_progress); - __ cbz(tmp1, done); +} +static void generate_pre_barrier_slow_path(MacroAssembler* masm, + const Register obj, + const Register pre_val, + const Register thread, + const Register tmp1, + const Register tmp2, + Label& done, + Label& runtime) { // Do we need to load the previous value? - if (store_addr != noreg) { - __ load_heap_oop(pre_val, Address(store_addr, 0)); + if (obj != noreg) { + __ load_heap_oop(pre_val, Address(obj, 0)); } // Is the previous value null? __ cbz(pre_val, done); - // Can we store original value in the thread's buffer? - // Is index == 0? - // (The index field is typed as size_t.) + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::satb_mark_queue_index_offset(), + G1ThreadLocalData::satb_mark_queue_buffer_offset(), + runtime, + thread, pre_val, tmp1, tmp2); + __ b(done); +} - __ ldr(tmp1, index); // tmp1 := *index_adr - __ ldr(tmp2, buffer); +// G1 pre-barrier. +// Blows all volatile registers R0-R3, LR). +// If obj != noreg, then previous value is loaded from [obj]; +// in such case obj and pre_val registers is preserved; +// otherwise pre_val register is preserved. +void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp1, + Register tmp2) { + Label done; + Label runtime; - __ subs(tmp1, tmp1, wordSize); // tmp1 := tmp1 - wordSize - __ b(runtime, lt); // If negative, goto runtime + assert_different_registers(obj, pre_val, tmp1, tmp2, noreg); - __ str(tmp1, index); // *index_adr := tmp1 + generate_pre_barrier_fast_path(masm, Rthread, tmp1); + // If marking is not active (*(mark queue active address) == 0), jump to done + __ cbz(tmp1, done); - // Record the previous value - __ str(pre_val, Address(tmp2, tmp1)); - __ b(done); + generate_pre_barrier_slow_path(masm, obj, pre_val, Rthread, tmp1, tmp2, done, runtime); __ bind(runtime); // save the live input values - if (store_addr != noreg) { - // avoid raw_push to support any ordering of store_addr and new_val - __ push(RegisterSet(store_addr) | RegisterSet(new_val)); - } else { - __ push(pre_val); + RegisterSet set = RegisterSet(pre_val) | RegisterSet(R0, R3) | RegisterSet(R12); + // save the live input values + if (obj != noreg) { + // avoid raw_push to support any ordering of store_addr and pre_val + set = set | RegisterSet(obj); } + __ push(set); + if (pre_val != R0) { __ mov(R0, pre_val); } @@ -177,33 +196,17 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), R0, R1); - if (store_addr != noreg) { - __ pop(RegisterSet(store_addr) | RegisterSet(new_val)); - } else { - __ pop(pre_val); - } - + __ pop(set); __ bind(done); } -// G1 post-barrier. -// Blows all volatile registers R0-R3, Rtemp, LR). -void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, - Register store_addr, - Register new_val, - Register tmp1, - Register tmp2, - Register tmp3) { - - Address queue_index(Rthread, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset())); - Address buffer(Rthread, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset())); - - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - Label done; - Label runtime; - +static void generate_post_barrier_fast_path(MacroAssembler* masm, + const Register store_addr, + const Register new_val, + const Register tmp1, + const Register tmp2, + Label& done, + bool new_val_may_be_null) { // Does store cross heap regions? __ eor(tmp1, store_addr, new_val); @@ -211,22 +214,31 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, __ b(done, eq); // crosses regions, storing null? - - __ cbz(new_val, done); - + if (new_val_may_be_null) { + __ cbz(new_val, done); + } // storing region crossing non-null, is card already dirty? const Register card_addr = tmp1; - __ mov_address(tmp2, (address)ct->byte_map_base()); + CardTableBarrierSet* ct = barrier_set_cast(BarrierSet::barrier_set()); + __ mov_address(tmp2, (address)ct->card_table()->byte_map_base()); __ add(card_addr, tmp2, AsmOperand(store_addr, lsr, CardTable::card_shift())); __ ldrb(tmp2, Address(card_addr)); __ cmp(tmp2, (int)G1CardTable::g1_young_card_val()); - __ b(done, eq); +} +static void generate_post_barrier_slow_path(MacroAssembler* masm, + const Register thread, + const Register tmp1, + const Register tmp2, + const Register tmp3, + Label& done, + Label& runtime) { __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::StoreLoad), tmp2); - assert(CardTable::dirty_card_val() == 0, "adjust this code"); + // card_addr is loaded by generate_post_barrier_fast_path + const Register card_addr = tmp1; __ ldrb(tmp2, Address(card_addr)); __ cbz(tmp2, done); @@ -234,29 +246,139 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, // dirty card and log. __ strb(__ zero_register(tmp2), Address(card_addr)); + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + runtime, + thread, card_addr, tmp2, tmp3); + __ b(done); +} - __ ldr(tmp2, queue_index); - __ ldr(tmp3, buffer); - __ subs(tmp2, tmp2, wordSize); - __ b(runtime, lt); // go to runtime if now negative - - __ str(tmp2, queue_index); +// G1 post-barrier. +// Blows all volatile registers R0-R3, LR). +void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2, + Register tmp3) { + Label done; + Label runtime; - __ str(card_addr, Address(tmp3, tmp2)); - __ b(done); + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp1, tmp2, done, true /* new_val_may_be_null */); + // If card is young, jump to done + // card_addr and card are loaded by generate_post_barrier_fast_path + const Register card = tmp2; + const Register card_addr = tmp1; + __ b(done, eq); + generate_post_barrier_slow_path(masm, Rthread, card_addr, tmp2, tmp3, done, runtime); __ bind(runtime); + RegisterSet set = RegisterSet(store_addr) | RegisterSet(R0, R3) | RegisterSet(R12); + __ push(set); + if (card_addr != R0) { __ mov(R0, card_addr); } __ mov(R1, Rthread); __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), R0, R1); + __ pop(set); + __ bind(done); } +#if defined(COMPILER2) + +static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStubC2* stub, const Register arg, const address runtime_path, Register tmp1) { + SaveLiveRegisters save_registers(masm, stub); + if (c_rarg0 != arg) { + __ mov(c_rarg0, arg); + } + __ mov(c_rarg1, Rthread); + __ call_VM_leaf(runtime_path, R0, R1); +} + +void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* stub) { + assert(thread == Rthread, "must be"); + assert_different_registers(obj, pre_val, tmp1, tmp2); + assert(pre_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register"); + + stub->initialize_registers(obj, pre_val, thread, tmp1, tmp2); + + generate_pre_barrier_fast_path(masm, thread, tmp1); + // If marking is active (*(mark queue active address) != 0), jump to stub (slow path) + __ cbnz(tmp1, *stub->entry()); + + __ bind(*stub->continuation()); +} + +void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register obj = stub->obj(); + Register pre_val = stub->pre_val(); + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); + Register tmp2 = stub->tmp2(); + + __ bind(*stub->entry()); + generate_pre_barrier_slow_path(masm, obj, pre_val, thread, tmp1, tmp2, *stub->continuation(), runtime); + + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, pre_val, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), tmp1); + __ b(*stub->continuation()); +} + +void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + Register tmp3, + G1PostBarrierStubC2* stub) { + assert(thread == Rthread, "must be"); + assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg); + + stub->initialize_registers(thread, tmp1, tmp2, tmp3); + + bool new_val_may_be_null = (stub->barrier_data() & G1C2BarrierPostNotNull) == 0; + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp1, tmp2, *stub->continuation(), new_val_may_be_null); + // If card is not young, jump to stub (slow path) + __ b(*stub->entry(), ne); + + __ bind(*stub->continuation()); +} + +void G1BarrierSetAssembler::generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); // tmp1 holds the card address. + Register tmp2 = stub->tmp2(); + Register tmp3 = stub->tmp3(); + + __ bind(*stub->entry()); + generate_post_barrier_slow_path(masm, thread, tmp1, tmp2, tmp3, *stub->continuation(), runtime); + + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, tmp1, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), tmp2); + __ b(*stub->continuation()); +} + +#endif // COMPILER2 + void G1BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp2, Register tmp3) { bool on_oop = type == T_OBJECT || type == T_ARRAY; @@ -268,7 +390,7 @@ void G1BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorator if (on_oop && on_reference) { // Generate the G1 pre-barrier code to log the value of // the referent field in an SATB buffer. - g1_write_barrier_pre(masm, noreg, noreg, dst, tmp1, tmp2); + g1_write_barrier_pre(masm, noreg, dst, tmp1, tmp2); } } @@ -295,7 +417,7 @@ void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet deco } if (needs_pre_barrier) { - g1_write_barrier_pre(masm, store_addr, new_val, tmp1, tmp2, tmp3); + g1_write_barrier_pre(masm, store_addr, tmp3 /*pre_val*/, tmp1, tmp2); } if (is_null) { diff --git a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp index 52932faa3e4de..aefde19142e40 100644 --- a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp +++ b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.hpp @@ -33,6 +33,8 @@ class LIR_Assembler; class StubAssembler; class G1PreBarrierStub; class G1PostBarrierStub; +class G1PreBarrierStubC2; +class G1PostBarrierStubC2; class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { protected: @@ -43,7 +45,6 @@ class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { void g1_write_barrier_pre(MacroAssembler* masm, Register store_addr, - Register new_val, Register pre_val, Register tmp1, Register tmp2); @@ -70,6 +71,29 @@ class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); void generate_c1_post_barrier_runtime_stub(StubAssembler* sasm); #endif + +#ifdef COMPILER2 + void g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* c2_stub); + void generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const; + void g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + Register tmp3, + G1PostBarrierStubC2* c2_stub); + void generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const; +#endif + }; #endif // CPU_ARM_GC_G1_G1BARRIERSETASSEMBLER_ARM_HPP diff --git a/src/hotspot/cpu/arm/gc/g1/g1_arm.ad b/src/hotspot/cpu/arm/gc/g1/g1_arm.ad new file mode 100644 index 0000000000000..8a0a9e1aa531a --- /dev/null +++ b/src/hotspot/cpu/arm/gc/g1/g1_arm.ad @@ -0,0 +1,201 @@ +// +// 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. +// + +source_hpp %{ + +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#include "gc/shared/gc_globals.hpp" + +%} + +source %{ + +#include "gc/g1/g1BarrierSetAssembler_arm.hpp" +#include "gc/g1/g1BarrierSetRuntime.hpp" + +static void write_barrier_pre(MacroAssembler* masm, + const MachNode* node, + Register obj, + Register pre_val, + Register tmp1, + Register tmp2, + RegSet preserve = RegSet(), + RegSet no_preserve = RegSet()) { + if (!G1PreBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PreBarrierStubC2* const stub = G1PreBarrierStubC2::create(node); + for (RegSetIterator reg = preserve.begin(); *reg != noreg; ++reg) { + stub->preserve(*reg); + } + for (RegSetIterator reg = no_preserve.begin(); *reg != noreg; ++reg) { + stub->dont_preserve(*reg); + } + g1_asm->g1_write_barrier_pre_c2(masm, obj, pre_val, Rthread, tmp1, tmp2, stub); +} + +static void write_barrier_post(MacroAssembler* masm, + const MachNode* node, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2, + Register tmp3) { + if (!G1PostBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PostBarrierStubC2* const stub = G1PostBarrierStubC2::create(node); + g1_asm->g1_write_barrier_post_c2(masm, store_addr, new_val, Rthread, tmp1, tmp2, tmp3, stub); +} + +%} + +instruct g1StoreP(indirect mem, iRegP src, iRegP tmp1, iRegP tmp2, iRegP tmp3, flagsReg icc) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL icc); + ins_cost(2 * (MEMORY_REF_COST + BRANCH_COST)); + format %{ "sd $src, $mem\t# ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ str($src$$Register, Address($mem$$Register)); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + $tmp3$$Register /* tmp3 */); + %} + ins_pipe(istore_mem_reg); +%} + +instruct g1CompareAndSwapP(iRegI res, indirect mem, iRegP newval, iRegP tmp1, iRegP tmp2, iRegP tmp3, iRegP oldval, flagsReg ccr ) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + effect(KILL ccr, TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3); + ins_cost(4 * (MEMORY_REF_COST + BRANCH_COST)); + format %{ "loop: \n\t" + "LDREX $tmp1, $mem\t! If $oldval==[$mem] Then store $newval into [$mem]\n\t" + "CMP $tmp1, $oldval\n\t" + "STREX.eq $tmp1, $newval, $mem\n\t" + "MOV.ne $tmp1, 0 \n\t" + "EORS.eq $tmp1,$tmp1, 1 \n\t" + "B.eq loop \n\t" + "MOV $res, $tmp1" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + Label loop; + __ bind(loop); + __ ldrex($tmp1$$Register,$mem$$Address); + __ cmp($tmp1$$Register, $oldval$$Register); + __ strex($tmp1$$Register, $newval$$Register, $mem$$Address, eq); + __ mov($tmp1$$Register, 0, ne); + __ eors($tmp1$$Register, $tmp1$$Register, 1, eq); + __ b(loop, eq); + __ mov($res$$Register, $tmp1$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + $tmp3$$Register /* tmp3 */); + %} + ins_pipe(long_memory_op); +%} + + +instruct g1GetAndSetP(indirect mem, iRegP newval, iRegP tmp1, iRegP tmp2, iRegP tmp3, iRegP preval, flagsReg ccr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetP mem newval)); + effect(KILL ccr, TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3); + ins_cost(4 * (MEMORY_REF_COST + BRANCH_COST)); + format %{ "loop: \n\t" + "LDREX $preval, $mem\n\t" + "STREX $tmp1, $newval, $mem\n\t" + "CMP $tmp1, 0 \n\t" + "B.ne loop \n\t" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $preval$$Register /* pre_val (as a temporary register) */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + Label loop; + __ bind(loop); + __ ldrex($preval$$Register,$mem$$Address); + __ strex($tmp1$$Register, $newval$$Register, $mem$$Address); + __ cmp($tmp1$$Register, 0); + __ b(loop, ne); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + $tmp3$$Register /* tmp3 */); + %} + ins_pipe(long_memory_op); +%} + +instruct g1LoadP(iRegP dst, indirect mem, iRegP tmp1, iRegP tmp2, flagsReg icc) +%{ + predicate(UseG1GC && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadP mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, KILL icc); + ins_cost(MEMORY_REF_COST + BRANCH_COST); + format %{ "ld $dst, $mem\t# ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + __ ldr($dst$$Register, Address($mem$$Register)); + write_barrier_pre(masm, this, + noreg /* obj */, + $dst$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(iload_mem); +%} diff --git a/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp b/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp index ea19730673cb6..c13a259a1b960 100644 --- a/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.cpp @@ -31,6 +31,10 @@ #include "runtime/javaThread.hpp" #include "runtime/stubRoutines.hpp" +#ifdef COMPILER2 +#include "gc/shared/c2/barrierSetC2.hpp" +#endif // COMPILER2 + #define __ masm-> void BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, @@ -206,7 +210,57 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) { #ifdef COMPILER2 OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Name opto_reg) { - Unimplemented(); // This must be implemented to support late barrier expansion. + if (!OptoReg::is_reg(opto_reg)) { + return OptoReg::Bad; + } + + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if (!vm_reg->is_valid()){ + // skip APSR and FPSCR + return OptoReg::Bad; + } + + return opto_reg; } +void SaveLiveRegisters::initialize(BarrierStubC2* stub) { + // Record registers that needs to be saved/restored + RegMaskIterator rmi(stub->preserve_set()); + while (rmi.has_next()) { + const OptoReg::Name opto_reg = rmi.next(); + if (OptoReg::is_reg(opto_reg)) { + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if (vm_reg->is_Register()) { + gp_regs += RegSet::of(vm_reg->as_Register()); + } else if (vm_reg->is_FloatRegister()) { + fp_regs += FloatRegSet::of(vm_reg->as_FloatRegister()); + } else { + fatal("Unknown register type"); + } + } + } + // Remove C-ABI SOE registers that will be updated + gp_regs -= RegSet::range(R4, R11) + RegSet::of(R13, R15); + + // Remove C-ABI SOE fp registers + fp_regs -= FloatRegSet::range(S16, S31); +} + +SaveLiveRegisters::SaveLiveRegisters(MacroAssembler* masm, BarrierStubC2* stub) + : masm(masm), + gp_regs(), + fp_regs() { + // Figure out what registers to save/restore + initialize(stub); + + // Save registers + if (gp_regs.size() > 0) __ push(RegisterSet::from(gp_regs)); + if (fp_regs.size() > 0) __ fpush(FloatRegisterSet::from(fp_regs)); +} + +SaveLiveRegisters::~SaveLiveRegisters() { + // Restore registers + if (fp_regs.size() > 0) __ fpop(FloatRegisterSet::from(fp_regs)); + if (gp_regs.size() > 0) __ pop(RegisterSet::from(gp_regs)); +} #endif // COMPILER2 diff --git a/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.hpp b/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.hpp index 60021390ea26f..054d172f46340 100644 --- a/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.hpp +++ b/src/hotspot/cpu/arm/gc/shared/barrierSetAssembler_arm.hpp @@ -31,7 +31,9 @@ #ifdef COMPILER2 #include "code/vmreg.hpp" #include "opto/optoreg.hpp" +#include "opto/regmask.hpp" +class BarrierStubC2; class Node; #endif // COMPILER2 @@ -69,4 +71,26 @@ class BarrierSetAssembler: public CHeapObj { #endif // COMPILER2 }; +#ifdef COMPILER2 +// This class saves and restores the registers that need to be preserved across +// the runtime call represented by a given C2 barrier stub. Use as follows: +// { +// SaveLiveRegisters save(masm, stub); +// .. +// __ bl(...); +// .. +// } +class SaveLiveRegisters { +private: + MacroAssembler* const masm; + RegSet gp_regs; + FloatRegSet fp_regs; + +public: + void initialize(BarrierStubC2* stub); + SaveLiveRegisters(MacroAssembler* masm, BarrierStubC2* stub); + ~SaveLiveRegisters(); +}; + +#endif // COMPILER2 #endif // CPU_ARM_GC_SHARED_BARRIERSETASSEMBLER_ARM_HPP diff --git a/src/hotspot/cpu/arm/register_arm.hpp b/src/hotspot/cpu/arm/register_arm.hpp index 9f486d2a62586..d8961fd293578 100644 --- a/src/hotspot/cpu/arm/register_arm.hpp +++ b/src/hotspot/cpu/arm/register_arm.hpp @@ -303,6 +303,31 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl { static const int max_fpr; }; +typedef AbstractRegSet RegSet; +typedef AbstractRegSet FloatRegSet; + +template <> +inline Register AbstractRegSet::first() { + if (_bitset == 0) { return noreg; } + return as_Register(count_trailing_zeros(_bitset)); +} + + +template <> +inline FloatRegister AbstractRegSet::first() { + uint32_t first = _bitset & -_bitset; + return first ? as_FloatRegister(exact_log2(first)) : fnoreg; +} + +template <> +inline FloatRegister AbstractRegSet::last() { + if (_bitset == 0) { return fnoreg; } + int last = max_size() - 1 - count_leading_zeros(_bitset); + return as_FloatRegister(last); +} + + + class VFPSystemRegisterImpl; typedef VFPSystemRegisterImpl* VFPSystemRegister; class VFPSystemRegisterImpl : public AbstractRegisterImpl { diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index 7648e5c5d9260..7c1f3aafe7d52 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -38,6 +38,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/safepointMechanism.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/timerTrace.hpp" #include "runtime/vframeArray.hpp" #include "utilities/align.hpp" #include "utilities/powerOfTwo.hpp" diff --git a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp index 679f07a028e2c..ec9d237e50da0 100644 --- a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp @@ -175,6 +175,7 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M break; case Interpreter::java_lang_math_fmaD: case Interpreter::java_lang_math_fmaF: + case Interpreter::java_lang_math_tanh: // TODO: Implement intrinsic break; default: diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp index 80519fd89f426..0974ff1f9a9c3 100644 --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -3974,6 +3974,7 @@ void TemplateTable::_new() { // make sure klass is initialized // make sure klass is fully initialized __ ldrb(Rtemp, Address(Rklass, InstanceKlass::init_state_offset())); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); __ cmp(Rtemp, InstanceKlass::fully_initialized); __ b(slow_case, ne); diff --git a/src/hotspot/cpu/arm/upcallLinker_arm.cpp b/src/hotspot/cpu/arm/upcallLinker_arm.cpp index c7645f4a03351..696b2001e6b7b 100644 --- a/src/hotspot/cpu/arm/upcallLinker_arm.cpp +++ b/src/hotspot/cpu/arm/upcallLinker_arm.cpp @@ -25,7 +25,7 @@ #include "prims/upcallLinker.hpp" #include "utilities/debug.hpp" -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 42934dc7c3179..684c06614a97a 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -2274,6 +2274,7 @@ void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { } __ lbz(op->tmp1()->as_register(), in_bytes(InstanceKlass::init_state_offset()), op->klass()->as_register()); + // acquire barrier included in membar_storestore() which follows the allocation immediately. __ cmpwi(CCR0, op->tmp1()->as_register(), InstanceKlass::fully_initialized); __ bc_far_optimized(Assembler::bcondCRbiIs0, __ bi0(CCR0, Assembler::equal), *op->stub()->entry()); } diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp index c05e97a4e9aa3..83fad376d292a 100644 --- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp @@ -92,7 +92,7 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox } if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(Roop, Rmark, Rscratch, slow_int); + lightweight_lock(Rbox, Roop, Rmark, Rscratch, slow_int); } else if (LockingMode == LM_LEGACY) { // ... and mark it unlocked. ori(Rmark, Rmark, markWord::unlocked_value); diff --git a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp index cc69c0abe361f..1147c3b42b25f 100644 --- a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp @@ -39,12 +39,12 @@ void C2_MacroAssembler::fast_lock_lightweight(ConditionRegister flag, Register obj, Register box, Register tmp1, Register tmp2, Register tmp3) { - compiler_fast_lock_lightweight_object(flag, obj, tmp1, tmp2, tmp3); + compiler_fast_lock_lightweight_object(flag, obj, box, tmp1, tmp2, tmp3); } void C2_MacroAssembler::fast_unlock_lightweight(ConditionRegister flag, Register obj, Register box, Register tmp1, Register tmp2, Register tmp3) { - compiler_fast_unlock_lightweight_object(flag, obj, tmp1, tmp2, tmp3); + compiler_fast_unlock_lightweight_object(flag, obj, box, tmp1, tmp2, tmp3); } // Intrinsics for CompactStrings diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp index 4c1ffeb0d768e..eb16af5e9db1b 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.cpp +++ b/src/hotspot/cpu/ppc/frame_ppc.cpp @@ -117,9 +117,9 @@ bool frame::safe_for_sender(JavaThread *thread) { return false; } - common_abi* sender_abi = (common_abi*) fp; + volatile common_abi* sender_abi = (common_abi*) fp; // May get updated concurrently by deoptimization! intptr_t* sender_sp = (intptr_t*) fp; - address sender_pc = (address) sender_abi->lr;; + address sender_pc = (address) sender_abi->lr; if (Continuation::is_return_barrier_entry(sender_pc)) { // If our sender_pc is the return barrier, then our "real" sender is the continuation entry @@ -134,9 +134,18 @@ bool frame::safe_for_sender(JavaThread *thread) { return false; } + intptr_t* unextended_sender_sp = is_interpreted_frame() ? interpreter_frame_sender_sp() : sender_sp; + + // If the sender is a deoptimized nmethod we need to check if the original pc is valid. + nmethod* sender_nm = sender_blob->as_nmethod_or_null(); + if (sender_nm != nullptr && sender_nm->is_deopt_pc(sender_pc)) { + address orig_pc = *(address*)((address)unextended_sender_sp + sender_nm->orig_pc_offset()); + if (!sender_nm->insts_contains_inclusive(orig_pc)) return false; + } + // It should be safe to construct the sender though it might not be valid. - frame sender(sender_sp, sender_pc, nullptr /* unextended_sp */, nullptr /* fp */, sender_blob); + frame sender(sender_sp, sender_pc, unextended_sender_sp, nullptr /* fp */, sender_blob); // Do we have a valid fp? address sender_fp = (address) sender.fp(); diff --git a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp index 7d230d301c22b..39693bdf925bf 100644 --- a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.cpp @@ -41,10 +41,20 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/g1/c1/g1BarrierSetC1.hpp" -#endif +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#endif // COMPILER2 #define __ masm-> +static void generate_marking_inactive_test(MacroAssembler* masm) { + int active_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()); + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); + __ lbz(R0, active_offset, R16_thread); // tmp1 := *(mark queue active address) + __ cmpwi(CCR0, R0, 0); +} + void G1BarrierSetAssembler::gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators, Register from, Register to, Register count, Register preserve1, Register preserve2) { @@ -58,13 +68,7 @@ void G1BarrierSetAssembler::gen_write_ref_array_pre_barrier(MacroAssembler* masm Label filtered; // Is marking active? - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { - __ lwz(R0, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()), R16_thread); - } else { - guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ lbz(R0, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()), R16_thread); - } - __ cmpdi(CCR0, R0, 0); + generate_marking_inactive_test(masm); __ beq(CCR0, filtered); __ save_LR(R0); @@ -109,35 +113,48 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas __ restore_LR(R0); } +static void generate_queue_insertion(MacroAssembler* masm, ByteSize index_offset, ByteSize buffer_offset, Label& runtime, + const Register value, const Register temp) { + assert_different_registers(value, temp); + // Can we store a value in the given thread's buffer? + // (The index field is typed as size_t.) + __ ld(temp, in_bytes(index_offset), R16_thread); // temp := *(index address) + __ cmpdi(CCR0, temp, 0); // jump to runtime if index == 0 (full buffer) + __ beq(CCR0, runtime); + // The buffer is not full, store value into it. + __ ld(R0, in_bytes(buffer_offset), R16_thread); // R0 := buffer address + __ addi(temp, temp, -wordSize); // temp := next index + __ std(temp, in_bytes(index_offset), R16_thread); // *(index address) := next index + __ stdx(value, temp, R0); // *(buffer address + next index) := value +} + void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, DecoratorSet decorators, Register obj, RegisterOrConstant ind_or_offs, Register pre_val, Register tmp1, Register tmp2, MacroAssembler::PreservationLevel preservation_level) { + assert_different_registers(pre_val, tmp1, tmp2); + bool not_null = (decorators & IS_NOT_NULL) != 0, preloaded = obj == noreg; Register nv_save = noreg; - if (preloaded) { + // Determine necessary runtime invocation preservation measures + const bool needs_frame = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR; + const bool preserve_gp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS; + const bool preserve_fp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS; + int nbytes_save = 0; + + if (pre_val->is_volatile() && preloaded && !preserve_gp_registers) { // We are not loading the previous value so make // sure that we don't trash the value in pre_val // with the code below. - assert_different_registers(pre_val, tmp1, tmp2); - if (pre_val->is_volatile()) { - nv_save = !tmp1->is_volatile() ? tmp1 : tmp2; - assert(!nv_save->is_volatile(), "need one nv temp register if pre_val lives in volatile register"); - } + nv_save = !tmp1->is_volatile() ? tmp1 : tmp2; + assert(!nv_save->is_volatile(), "need one nv temp register if pre_val lives in volatile register"); } Label runtime, filtered; - // Is marking active? - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { - __ lwz(tmp1, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()), R16_thread); - } else { - guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ lbz(tmp1, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()), R16_thread); - } - __ cmpdi(CCR0, tmp1, 0); + generate_marking_inactive_test(masm); __ beq(CCR0, filtered); // Do we need to load the previous value? @@ -175,28 +192,12 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Decorator // Can we store original value in the thread's buffer? // Is index == 0? // (The index field is typed as size_t.) - const Register Rbuffer = tmp1, Rindex = tmp2; - - __ ld(Rindex, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset()), R16_thread); - __ cmpdi(CCR0, Rindex, 0); - __ beq(CCR0, runtime); // If index == 0, goto runtime. - __ ld(Rbuffer, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset()), R16_thread); - - __ addi(Rindex, Rindex, -wordSize); // Decrement index. - __ std(Rindex, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset()), R16_thread); - - // Record the previous value. - __ stdx(pre_val, Rbuffer, Rindex); + generate_queue_insertion(masm, G1ThreadLocalData::satb_mark_queue_index_offset(), G1ThreadLocalData::satb_mark_queue_buffer_offset(), + runtime, pre_val, tmp1); __ b(filtered); __ bind(runtime); - // Determine necessary runtime invocation preservation measures - const bool needs_frame = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR; - const bool preserve_gp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS; - const bool preserve_fp_registers = preservation_level >= MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS; - int nbytes_save = 0; - // May need to preserve LR. Also needed if current frame is not compatible with C calling convention. if (needs_frame) { if (preserve_gp_registers) { @@ -210,11 +211,11 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Decorator __ push_frame_reg_args(nbytes_save, tmp2); } - if (pre_val->is_volatile() && preloaded && !preserve_gp_registers) { + if (nv_save != noreg) { __ mr(nv_save, pre_val); // Save pre_val across C call if it was preloaded. } __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), pre_val, R16_thread); - if (pre_val->is_volatile() && preloaded && !preserve_gp_registers) { + if (nv_save != noreg) { __ mr(pre_val, nv_save); // restore } @@ -230,6 +231,26 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Decorator __ bind(filtered); } +static void generate_region_crossing_test(MacroAssembler* masm, const Register store_addr, const Register new_val) { + __ xorr(R0, store_addr, new_val); // tmp1 := store address ^ new value + __ srdi_(R0, R0, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) +} + +static Address generate_card_young_test(MacroAssembler* masm, const Register store_addr, const Register tmp1, const Register tmp2) { + CardTableBarrierSet* ct = barrier_set_cast(BarrierSet::barrier_set()); + __ load_const_optimized(tmp1, (address)(ct->card_table()->byte_map_base()), tmp2); + __ srdi(tmp2, store_addr, CardTable::card_shift()); // tmp1 := card address relative to card table base + __ lbzx(R0, tmp1, tmp2); // tmp1 := card address + __ cmpwi(CCR0, R0, (int)G1CardTable::g1_young_card_val()); + return Address(tmp1, tmp2); // return card address +} + +static void generate_card_dirty_test(MacroAssembler* masm, Address card_addr) { + __ membar(Assembler::StoreLoad); // Must reload after StoreLoad membar due to concurrent refinement + __ lbzx(R0, card_addr.base(), card_addr.index()); // tmp2 := card + __ cmpwi(CCR0, R0, (int)G1CardTable::dirty_card_val()); // tmp2 := card == dirty_card_val? +} + void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, DecoratorSet decorators, Register store_addr, Register new_val, Register tmp1, Register tmp2, Register tmp3, @@ -241,9 +262,7 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Decorato CardTableBarrierSet* ct = barrier_set_cast(BarrierSet::barrier_set()); - // Does store cross heap regions? - __ xorr(tmp1, store_addr, new_val); - __ srdi_(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); + generate_region_crossing_test(masm, store_addr, new_val); __ beq(CCR0, filtered); // Crosses regions, storing null? @@ -257,43 +276,22 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Decorato __ beq(CCR0, filtered); } - // Storing region crossing non-null, is card already dirty? - const Register Rcard_addr = tmp1; - Register Rbase = tmp2; - __ load_const_optimized(Rbase, (address)(ct->card_table()->byte_map_base()), /*temp*/ tmp3); - - __ srdi(Rcard_addr, store_addr, CardTable::card_shift()); - - // Get the address of the card. - __ lbzx(/*card value*/ tmp3, Rbase, Rcard_addr); - __ cmpwi(CCR0, tmp3, (int)G1CardTable::g1_young_card_val()); + Address card_addr = generate_card_young_test(masm, store_addr, tmp1, tmp2); __ beq(CCR0, filtered); - __ membar(Assembler::StoreLoad); - __ lbzx(/*card value*/ tmp3, Rbase, Rcard_addr); // Reload after membar. - __ cmpwi(CCR0, tmp3 /* card value */, (int)G1CardTable::dirty_card_val()); + generate_card_dirty_test(masm, card_addr); __ beq(CCR0, filtered); - // Storing a region crossing, non-null oop, card is clean. - // Dirty card and log. - __ li(tmp3, (int)G1CardTable::dirty_card_val()); - //release(); // G1: oops are allowed to get visible after dirty marking. - __ stbx(tmp3, Rbase, Rcard_addr); - - __ add(Rcard_addr, Rbase, Rcard_addr); // This is the address which needs to get enqueued. - Rbase = noreg; // end of lifetime + __ li(R0, (int)G1CardTable::dirty_card_val()); + __ stbx(R0, card_addr.base(), card_addr.index()); // *(card address) := dirty_card_val - const Register Rqueue_index = tmp2, - Rqueue_buf = tmp3; - __ ld(Rqueue_index, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset()), R16_thread); - __ cmpdi(CCR0, Rqueue_index, 0); - __ beq(CCR0, runtime); // index == 0 then jump to runtime - __ ld(Rqueue_buf, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset()), R16_thread); + Register Rcard_addr = tmp3; + __ add(Rcard_addr, card_addr.base(), card_addr.index()); // This is the address which needs to get enqueued. - __ addi(Rqueue_index, Rqueue_index, -wordSize); // decrement index - __ std(Rqueue_index, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset()), R16_thread); - - __ stdx(Rcard_addr, Rqueue_buf, Rqueue_index); // store card + generate_queue_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + runtime, Rcard_addr, tmp1); __ b(filtered); __ bind(runtime); @@ -392,6 +390,142 @@ void G1BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value __ bind(done); } +#ifdef COMPILER2 + +static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStubC2* stub, const Register arg, const address runtime_path) { + SaveLiveRegisters save_registers(masm, stub); + __ call_VM_leaf(runtime_path, arg, R16_thread); +} + +void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* stub) { + assert_different_registers(obj, tmp1, tmp2, R0); + assert_different_registers(pre_val, tmp1, R0); + assert(!UseCompressedOops || tmp2 != noreg, "tmp2 needed with CompressedOops"); + + stub->initialize_registers(obj, pre_val, R16_thread, tmp1, tmp2); + + generate_marking_inactive_test(masm); + __ bc_far_optimized(Assembler::bcondCRbiIs0, __ bi0(CCR0, Assembler::equal), *stub->entry()); + + __ bind(*stub->continuation()); +} + +void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register obj = stub->obj(); + Register pre_val = stub->pre_val(); + Register tmp1 = stub->tmp1(); + + __ bind(*stub->entry()); + + if (obj != noreg) { + // Note: C2 currently doesn't use implicit null checks with barriers. + // Otherwise, obj could be null and the following instruction would raise a SIGSEGV. + if (UseCompressedOops) { + __ lwz(pre_val, 0, obj); + } else { + __ ld(pre_val, 0, obj); + } + } + __ cmpdi(CCR0, pre_val, 0); + __ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CCR0, Assembler::equal), *stub->continuation()); + + Register pre_val_decoded = pre_val; + if (UseCompressedOops) { + pre_val_decoded = __ decode_heap_oop_not_null(stub->tmp2(), pre_val); + } + + generate_queue_insertion(masm, + G1ThreadLocalData::satb_mark_queue_index_offset(), + G1ThreadLocalData::satb_mark_queue_buffer_offset(), + runtime, pre_val_decoded, tmp1); + __ b(*stub->continuation()); + + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, pre_val_decoded, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry)); + __ b(*stub->continuation()); +} + +void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* stub, + bool decode_new_val) { + assert_different_registers(store_addr, new_val, tmp1, R0); + assert_different_registers(store_addr, tmp1, tmp2, R0); + + stub->initialize_registers(R16_thread, tmp1, tmp2); + + bool null_check_required = (stub->barrier_data() & G1C2BarrierPostNotNull) == 0; + Register new_val_decoded = new_val; + + if (decode_new_val) { + assert(UseCompressedOops, "or should not be here"); + if (null_check_required && CompressedOops::base() != nullptr) { + // We prefer doing the null check after the region crossing check. + // Only compressed oop modes with base != null require a null check here. + __ cmpwi(CCR0, new_val, 0); + __ beq(CCR0, *stub->continuation()); + null_check_required = false; + } + new_val_decoded = __ decode_heap_oop_not_null(tmp2, new_val); + } + + generate_region_crossing_test(masm, store_addr, new_val_decoded); + __ beq(CCR0, *stub->continuation()); + + // crosses regions, storing null? + if (null_check_required) { + __ cmpdi(CCR0, new_val_decoded, 0); + __ beq(CCR0, *stub->continuation()); + } + + Address card_addr = generate_card_young_test(masm, store_addr, tmp1, tmp2); + assert(card_addr.base() == tmp1 && card_addr.index() == tmp2, "needed by post barrier stub"); + __ bc_far_optimized(Assembler::bcondCRbiIs0, __ bi0(CCR0, Assembler::equal), *stub->entry()); + + __ bind(*stub->continuation()); +} + +void G1BarrierSetAssembler::generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Address card_addr(stub->tmp1(), stub->tmp2()); // See above. + + __ bind(*stub->entry()); + + generate_card_dirty_test(masm, card_addr); + __ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CCR0, Assembler::equal), *stub->continuation()); + + __ li(R0, (int)G1CardTable::dirty_card_val()); + __ stbx(R0, card_addr.base(), card_addr.index()); // *(card address) := dirty_card_val + + Register Rcard_addr = stub->tmp1(); + __ add(Rcard_addr, card_addr.base(), card_addr.index()); // This is the address which needs to get enqueued. + + generate_queue_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + runtime, Rcard_addr, stub->tmp2()); + __ b(*stub->continuation()); + + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, Rcard_addr, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry)); + __ b(*stub->continuation()); +} + +#endif // COMPILER2 + #ifdef COMPILER1 #undef __ @@ -470,13 +604,7 @@ void G1BarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* __ std(tmp2, -24, R1_SP); // Is marking still active? - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { - __ lwz(tmp, satb_q_active_byte_offset, R16_thread); - } else { - assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ lbz(tmp, satb_q_active_byte_offset, R16_thread); - } - __ cmpdi(CCR0, tmp, 0); + generate_marking_inactive_test(sasm); __ beq(CCR0, marking_not_active); __ bind(restart); diff --git a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp index d9a252ff6eaee..1c9fe8a5d106f 100644 --- a/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/g1/g1BarrierSetAssembler_ppc.hpp @@ -30,10 +30,16 @@ #include "gc/shared/modRefBarrierSetAssembler.hpp" #include "utilities/macros.hpp" +#ifdef COMPILER2 +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#endif + class LIR_Assembler; class StubAssembler; class G1PreBarrierStub; class G1PostBarrierStub; +class G1PreBarrierStubC2; +class G1PostBarrierStubC2; class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { protected: @@ -59,6 +65,25 @@ class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { MacroAssembler::PreservationLevel preservation_level); public: +#ifdef COMPILER2 + void g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* c2_stub); + void generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const; + void g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* c2_stub, + bool decode_new_val); + void generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const; +#endif #ifdef COMPILER1 void gen_pre_barrier_stub(LIR_Assembler* ce, G1PreBarrierStub* stub); void gen_post_barrier_stub(LIR_Assembler* ce, G1PostBarrierStub* stub); diff --git a/src/hotspot/cpu/ppc/gc/g1/g1_ppc.ad b/src/hotspot/cpu/ppc/gc/g1/g1_ppc.ad new file mode 100644 index 0000000000000..f4163242cad7b --- /dev/null +++ b/src/hotspot/cpu/ppc/gc/g1/g1_ppc.ad @@ -0,0 +1,684 @@ +// +// Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2024 SAP SE. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// 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. +// + +source_hpp %{ + +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#include "gc/shared/gc_globals.hpp" + +%} + +source %{ + +#include "gc/g1/g1BarrierSetAssembler_ppc.hpp" +#include "gc/g1/g1BarrierSetRuntime.hpp" + +static void pre_write_barrier(MacroAssembler* masm, + const MachNode* node, + Register obj, + Register pre_val, + Register tmp1, + Register tmp2 = noreg, // only needed with CompressedOops when pre_val needs to be preserved + RegSet preserve = RegSet(), + RegSet no_preserve = RegSet()) { + if (!G1PreBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PreBarrierStubC2* const stub = G1PreBarrierStubC2::create(node); + for (RegSetIterator reg = preserve.begin(); *reg != noreg; ++reg) { + stub->preserve(*reg); + } + for (RegSetIterator reg = no_preserve.begin(); *reg != noreg; ++reg) { + stub->dont_preserve(*reg); + } + g1_asm->g1_write_barrier_pre_c2(masm, obj, pre_val, tmp1, (tmp2 != noreg) ? tmp2 : pre_val, stub); +} + +static void post_write_barrier(MacroAssembler* masm, + const MachNode* node, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2, + bool decode_new_val = false) { + if (!G1PostBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PostBarrierStubC2* const stub = G1PostBarrierStubC2::create(node); + g1_asm->g1_write_barrier_post_c2(masm, store_addr, new_val, tmp1, tmp2, stub, decode_new_val); +} + +%} + +instruct g1StoreP(indirect mem, iRegPsrc src, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP tmp1, TEMP tmp2, KILL cr0); + ins_cost(2 * MEMORY_REF_COST); + format %{ "std $mem, $src\t# ptr" %} + ins_encode %{ + pre_write_barrier(masm, this, + $mem$$Register, + $tmp1$$Register, + $tmp2$$Register, + noreg, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ std($src$$Register, 0, $mem$$Register); + post_write_barrier(masm, this, + $mem$$Register, + $src$$Register /* new_val */, + $tmp1$$Register, + $tmp2$$Register); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1StoreN(indirect mem, iRegNsrc src, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem src)); + effect(TEMP tmp1, TEMP tmp2, KILL cr0); + ins_cost(2 * MEMORY_REF_COST); + format %{ "stw $mem, $src\t# ptr" %} + ins_encode %{ + pre_write_barrier(masm, this, + $mem$$Register, + $tmp1$$Register, + $tmp2$$Register, + noreg, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ stw($src$$Register, 0, $mem$$Register); + post_write_barrier(masm, this, + $mem$$Register, + $src$$Register /* new_val */, + $tmp1$$Register, + $tmp2$$Register, + true /* decode_new_val */); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1EncodePAndStoreN(indirect mem, iRegPsrc src, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem (EncodeP src))); + effect(TEMP tmp1, TEMP tmp2, KILL cr0); + ins_cost(2 * MEMORY_REF_COST); + format %{ "encode_heap_oop $src\n\t" + "stw $mem, $src\t# ptr" %} + ins_encode %{ + pre_write_barrier(masm, this, + $mem$$Register, + $tmp1$$Register, + $tmp2$$Register, + noreg, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + Register encoded_oop = noreg; + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + encoded_oop = __ encode_heap_oop($tmp2$$Register, $src$$Register); + } else { + encoded_oop = __ encode_heap_oop_not_null($tmp2$$Register, $src$$Register); + } + __ stw(encoded_oop, 0, $mem$$Register); + post_write_barrier(masm, this, + $mem$$Register, + $src$$Register /* new_val */, + $tmp1$$Register, + $tmp2$$Register); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndExchangeP(iRegPdst res, indirect mem, iRegPsrc oldval, iRegPsrc newval, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndExchangeNode*)n)->order() != MemNode::acquire && ((CompareAndExchangeNode*)n)->order() != MemNode::seqcst)); + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); + format %{ "cmpxchgd $newval, $mem" %} + ins_encode %{ + Label no_update; + __ cmpxchgd(CCR0, $res$$Register, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register, + $tmp1$$Register, + $tmp2$$Register, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp1$$Register, + $tmp2$$Register); + __ bind(no_update); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndExchangeP_acq(iRegPdst res, indirect mem, iRegPsrc oldval, iRegPsrc newval, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndExchangeNode*)n)->order() == MemNode::acquire || ((CompareAndExchangeNode*)n)->order() == MemNode::seqcst)); + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); + format %{ "cmpxchgd acq $newval, $mem" %} + ins_encode %{ + Label no_update; + __ cmpxchgd(CCR0, $res$$Register, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register, + $tmp1$$Register, + $tmp2$$Register, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp1$$Register, + $tmp2$$Register); + __ bind(no_update); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + // isync would be sufficient in case of CompareAndExchangeAcquire, but we currently don't optimize for that. + __ sync(); + } + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndExchangeN(iRegNdst res, indirect mem, iRegNsrc oldval, iRegNsrc newval, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndExchangeNode*)n)->order() != MemNode::acquire && ((CompareAndExchangeNode*)n)->order() != MemNode::seqcst)); + match(Set res (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); + format %{ "cmpxchgw $newval, $mem" %} + ins_encode %{ + Label no_update; + __ cmpxchgw(CCR0, $res$$Register, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register, + $tmp1$$Register, + $tmp2$$Register, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp1$$Register, + $tmp2$$Register, + true /* decode_new_val */); + __ bind(no_update); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndExchangeN_acq(iRegNdst res, indirect mem, iRegNsrc oldval, iRegNsrc newval, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndExchangeNode*)n)->order() == MemNode::acquire || ((CompareAndExchangeNode*)n)->order() == MemNode::seqcst)); + match(Set res (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); + format %{ "cmpxchgw acq $newval, $mem" %} + ins_encode %{ + Label no_update; + __ cmpxchgw(CCR0, $res$$Register, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register, + $tmp1$$Register, + $tmp2$$Register, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp1$$Register, + $tmp2$$Register, + true /* decode_new_val */); + __ bind(no_update); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + // isync would be sufficient in case of CompareAndExchangeAcquire, but we currently don't optimize for that. + __ sync(); + } + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndSwapP(iRegIdst res, indirect mem, iRegPsrc oldval, iRegPsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst)); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "CMPXCHGD $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgd(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */); + __ li($res$$Register, 1); + __ bind(no_update); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndSwapP_acq(iRegIdst res, indirect mem, iRegPsrc oldval, iRegPsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst)); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "CMPXCHGD acq $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgd(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */); + __ li($res$$Register, 1); + __ bind(no_update); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + // isync would be sufficient in case of CompareAndExchangeAcquire, but we currently don't optimize for that. + __ sync(); + } + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndSwapN(iRegIdst res, indirect mem, iRegNsrc oldval, iRegNsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst)); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "CMPXCHGW $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgw(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */, + true /* decode_new_val */); + __ li($res$$Register, 1); + __ bind(no_update); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1CompareAndSwapN_acq(iRegIdst res, indirect mem, iRegNsrc oldval, iRegNsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst)); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "CMPXCHGW acq $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgw(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */, + true /* decode_new_val */); + __ li($res$$Register, 1); + __ bind(no_update); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + // isync would be sufficient in case of CompareAndExchangeAcquire, but we currently don't optimize for that. + __ sync(); + } + %} + ins_pipe(pipe_class_default); +%} + +instruct weakG1CompareAndSwapP(iRegIdst res, indirect mem, iRegPsrc oldval, iRegPsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst)); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "weak CMPXCHGD $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgd(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */); + __ li($res$$Register, 1); + __ bind(no_update); + %} + ins_pipe(pipe_class_default); +%} + +instruct weakG1CompareAndSwapP_acq(iRegIdst res, indirect mem, iRegPsrc oldval, iRegPsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst)); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "weak CMPXCHGD acq $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgd(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */); + __ li($res$$Register, 1); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + // isync would be sufficient in case of CompareAndExchangeAcquire, but we currently don't optimize for that. + __ sync(); + } + __ bind(no_update); // weak version requires no memory barrier on failure + %} + ins_pipe(pipe_class_default); +%} + +instruct weakG1CompareAndSwapN(iRegIdst res, indirect mem, iRegNsrc oldval, iRegNsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst)); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "weak CMPXCHGW $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgw(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */, + true /* decode_new_val */); + __ li($res$$Register, 1); + __ bind(no_update); + %} + ins_pipe(pipe_class_default); +%} + +instruct weakG1CompareAndSwapN_acq(iRegIdst res, indirect mem, iRegNsrc oldval, iRegNsrc newval, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0 && + (((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst)); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP_DEF res, TEMP tmp, KILL cr0); + format %{ "weak CMPXCHGW acq $res, $mem, $oldval, $newval; as bool; ptr" %} + ins_encode %{ + Label no_update; + __ li($res$$Register, 0); + __ cmpxchgw(CCR0, R0, $oldval$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, &no_update, true, true); + // Pass oldval to SATB which is the only value which can get overwritten. + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg, + $oldval$$Register /* pre_val */, + $tmp$$Register, + $res$$Register /* temp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp$$Register, + $res$$Register /* temp */, + true /* decode_new_val */); + __ li($res$$Register, 1); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + // isync would be sufficient in case of CompareAndExchangeAcquire, but we currently don't optimize for that. + __ sync(); + } + __ bind(no_update); // weak version requires no memory barrier on failure + %} + ins_pipe(pipe_class_default); +%} + +instruct g1GetAndSetP(iRegPdst res, indirect mem, iRegPsrc newval, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (GetAndSetP mem newval)); + effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); + format %{ "GetAndSetP $newval, $mem" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + __ getandsetd($res$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::cmpxchgx_hint_atomic_update()); + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg /* obj */, + $res$$Register /* res */, + $tmp1$$Register, + $tmp2$$Register, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp1$$Register, + $tmp2$$Register); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + __ sync(); + } + %} + ins_pipe(pipe_class_default); +%} + +instruct g1GetAndSetN(iRegNdst res, indirect mem, iRegNsrc newval, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (GetAndSetN mem newval)); + effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); + format %{ "GetAndSetN $newval, $mem" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + __ getandsetw($res$$Register, $newval$$Register, $mem$$Register, + MacroAssembler::cmpxchgx_hint_atomic_update()); + // Can be done after cmpxchg because there's no safepoint here. + pre_write_barrier(masm, this, + noreg /* obj */, + $res$$Register /* res */, + $tmp1$$Register, + $tmp2$$Register, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + post_write_barrier(masm, this, + $mem$$Register, + $newval$$Register, + $tmp1$$Register, + $tmp2$$Register, + true /* decode_new_val */); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ isync(); + } else { + __ sync(); + } + %} + ins_pipe(pipe_class_default); +%} + +instruct g1LoadP(iRegPdst dst, memoryAlg4 mem, iRegPdst tmp, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_Load()->is_unordered() && n->as_Load()->barrier_data() != 0); + // This instruction does not need an acquiring counterpart because it is only + // used for reference loading (Reference::get()). + match(Set dst (LoadP mem)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr0); + ins_cost(2 * MEMORY_REF_COST); + format %{ "ld $dst, $mem\t# ptr" %} + ins_encode %{ + __ ld($dst$$Register, $mem$$disp, $mem$$base$$Register); + pre_write_barrier(masm, this, + noreg /* obj */, + $dst$$Register /* pre_val */, + $tmp$$Register); + %} + ins_pipe(pipe_class_default); +%} + +instruct g1LoadN(iRegNdst dst, memoryAlg4 mem, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR0 cr0) +%{ + predicate(UseG1GC && n->as_Load()->is_unordered() && n->as_Load()->barrier_data() != 0); + // This instruction does not need an acquiring counterpart because it is only + // used for reference loading (Reference::get()). + match(Set dst (LoadN mem)); + effect(TEMP_DEF dst, TEMP tmp1, TEMP tmp2, KILL cr0); + ins_cost(2 * MEMORY_REF_COST); + format %{ "lwz $dst, $mem\t# ptr" %} + ins_encode %{ + __ lwz($dst$$Register, $mem$$disp, $mem$$base$$Register); + pre_write_barrier(masm, this, + noreg /* obj */, + $dst$$Register, + $tmp1$$Register, + $tmp2$$Register); + %} + ins_pipe(pipe_class_default); +%} diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 3cb5c5a628f39..5315080721249 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -144,9 +144,9 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, Dec // Invoke runtime. address jrt_address = nullptr; if (UseCompressedOops) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop_entry); + jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop); } else { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop_entry); + jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop); } assert(jrt_address != nullptr, "jrt routine cannot be found"); @@ -302,7 +302,7 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_impl(MacroAssembler *masm } // Invoke runtime. - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, R16_thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, R16_thread); // Restore to-be-preserved registers. if (!preserve_gp_registers && preloaded_mode && pre_val->is_volatile()) { @@ -906,7 +906,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ push_frame_reg_args(nbytes_save, R11_tmp1); // Invoke runtime. - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), R0_pre_val, R16_thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), R0_pre_val, R16_thread); // Restore to-be-preserved registers. __ pop_frame(); diff --git a/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp index 136fd7a8ad1cd..ddeb9adf0a9ae 100644 --- a/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp @@ -90,7 +90,7 @@ static size_t probe_valid_max_address_bit() { } size_t ZPlatformAddressOffsetBits() { - const static size_t valid_max_address_offset_bits = probe_valid_max_address_bit() + 1; + static const size_t valid_max_address_offset_bits = probe_valid_max_address_bit() + 1; const size_t max_address_offset_bits = valid_max_address_offset_bits - 3; const size_t min_address_offset_bits = max_address_offset_bits - 2; const size_t address_offset = round_up_power_of_2(MaxHeapSize * ZVirtualToPhysicalRatio); diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp index 89ab1b1edeeb4..8a65022126e66 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -943,6 +943,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr(), R3_ARG1); } else if (stub->is_atomic()) { __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr(), R3_ARG1); + } else if (stub->is_nokeepalive()) { + __ call_VM_leaf(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr(), R3_ARG1); } else { __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), R3_ARG1); } diff --git a/src/hotspot/cpu/ppc/gc/z/z_ppc.ad b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad index 017574d40ff8b..bb696a4738f40 100644 --- a/src/hotspot/cpu/ppc/gc/z/z_ppc.ad +++ b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad @@ -83,7 +83,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Register z_color(masm, rnew_zpointer, rnew_zaddress); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, Address(ref_base, disp), rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, Address(ref_base, disp), rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_base, disp, rnew_zaddress, rnew_zpointer, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index a29e0810d52ca..aa77f0169ea1a 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -968,7 +968,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { } if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(object, header, tmp, slow_case); + lightweight_lock(monitor, object, header, tmp, slow_case); b(count_locking); } else if (LockingMode == LM_LEGACY) { // Load markWord from object into header. diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 8449d74d8a861..a194c030a6124 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -2410,7 +2410,7 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fast_path, Label* L_slow_path) { assert(L_fast_path != nullptr || L_slow_path != nullptr, "at least one is required"); - Label L_fallthrough; + Label L_check_thread, L_fallthrough; if (L_fast_path == nullptr) { L_fast_path = &L_fallthrough; } else if (L_slow_path == nullptr) { @@ -2419,10 +2419,14 @@ void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fa // Fast path check: class is fully initialized lbz(R0, in_bytes(InstanceKlass::init_state_offset()), klass); + // acquire by cmp-branch-isync if fully_initialized cmpwi(CCR0, R0, InstanceKlass::fully_initialized); - beq(CCR0, *L_fast_path); + bne(CCR0, L_check_thread); + isync(); + b(*L_fast_path); // Fast path check: current thread is initializer thread + bind(L_check_thread); ld(R0, in_bytes(InstanceKlass::init_thread_offset()), klass); cmpd(CCR0, thread, R0); if (L_slow_path == &L_fallthrough) { @@ -2715,13 +2719,34 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe b(success); bind(notRecursive); + + // Set owner to null. + // Release to satisfy the JMM + release(); + li(temp, 0); + std(temp, in_bytes(ObjectMonitor::owner_offset()), current_header); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); + + // Check if the entry lists are empty. ld(temp, in_bytes(ObjectMonitor::EntryList_offset()), current_header); ld(displaced_header, in_bytes(ObjectMonitor::cxq_offset()), current_header); orr(temp, temp, displaced_header); // Will be 0 if both are 0. cmpdi(flag, temp, 0); - bne(flag, failure); - release(); - std(temp, in_bytes(ObjectMonitor::owner_offset()), current_header); + beq(flag, success); // If so we are done. + + // Check if there is a successor. + ld(temp, in_bytes(ObjectMonitor::succ_offset()), current_header); + cmpdi(flag, temp, 0); + bne(flag, success); // If so we are done. + + // Save the monitor pointer in the current thread, so we can try + // to reacquire the lock in SharedRuntime::monitor_exit_helper(). + std(current_header, in_bytes(JavaThread::unlocked_inflated_monitor_offset()), R16_thread); + + crxor(flag, Assembler::equal, flag, Assembler::equal); // Set flag = NE => slow path + b(failure); // flag == EQ indicates success, decrement held monitor count // flag == NE indicates failure @@ -2730,9 +2755,9 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe bind(failure); } -void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister flag, Register obj, Register tmp1, - Register tmp2, Register tmp3) { - assert_different_registers(obj, tmp1, tmp2, tmp3); +void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister flag, Register obj, Register box, + Register tmp1, Register tmp2, Register tmp3) { + assert_different_registers(obj, box, tmp1, tmp2, tmp3); assert(flag == CCR0, "bad condition register"); // Handle inflated monitor. @@ -2742,11 +2767,17 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla // Finish fast lock unsuccessfully. MUST branch to with flag == EQ Label slow_path; + if (UseObjectMonitorTable) { + // Clear cache in case fast locking succeeds. + li(tmp1, 0); + std(tmp1, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box); + } + if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(tmp1, obj); lbz(tmp1, in_bytes(Klass::misc_flags_offset()), tmp1); - testbitdi(flag, R0, tmp1, exact_log2(KlassFlags::_misc_is_value_based_class)); - bne(flag, slow_path); + testbitdi(CCR0, R0, tmp1, exact_log2(KlassFlags::_misc_is_value_based_class)); + bne(CCR0, slow_path); } const Register mark = tmp1; @@ -2761,8 +2792,8 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla // Check if lock-stack is full. lwz(top, in_bytes(JavaThread::lock_stack_top_offset()), R16_thread); - cmplwi(flag, top, LockStack::end_offset() - 1); - bgt(flag, slow_path); + cmplwi(CCR0, top, LockStack::end_offset() - 1); + bgt(CCR0, slow_path); // The underflow check is elided. The recursive check will always fail // when the lock stack is empty because of the _bad_oop_sentinel field. @@ -2770,19 +2801,19 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla // Check if recursive. subi(t, top, oopSize); ldx(t, R16_thread, t); - cmpd(flag, obj, t); - beq(flag, push); + cmpd(CCR0, obj, t); + beq(CCR0, push); // Check for monitor (0b10) or locked (0b00). ld(mark, oopDesc::mark_offset_in_bytes(), obj); andi_(t, mark, markWord::lock_mask_in_place); - cmpldi(flag, t, markWord::unlocked_value); - bgt(flag, inflated); - bne(flag, slow_path); + cmpldi(CCR0, t, markWord::unlocked_value); + bgt(CCR0, inflated); + bne(CCR0, slow_path); // Not inflated. - // Try to lock. Transition lock bits 0b00 => 0b01 + // Try to lock. Transition lock bits 0b01 => 0b00 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid a lea"); atomically_flip_locked_state(/* is_unlock */ false, obj, mark, slow_path, MacroAssembler::MemBarAcq); @@ -2797,38 +2828,84 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla { // Handle inflated monitor. bind(inflated); + // mark contains the tagged ObjectMonitor*. + const uintptr_t monitor_tag = markWord::monitor_value; + const Register monitor = mark; + const Register owner_addr = tmp2; + Label monitor_locked; + if (!UseObjectMonitorTable) { - // mark contains the tagged ObjectMonitor*. - const Register tagged_monitor = mark; - const uintptr_t monitor_tag = markWord::monitor_value; - const Register owner_addr = tmp2; + // Compute owner address. + addi(owner_addr, mark, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag); + } else { + Label monitor_found; + Register cache_addr = tmp2; + + // Load cache address + addi(cache_addr, R16_thread, in_bytes(JavaThread::om_cache_oops_offset())); + + const int num_unrolled = 2; + for (int i = 0; i < num_unrolled; i++) { + ld(tmp3, 0, cache_addr); + cmpd(CCR0, tmp3, obj); + beq(CCR0, monitor_found); + addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference())); + } + + Label loop; + + // Search for obj in cache. + bind(loop); + + // Check for match. + ld(tmp3, 0, cache_addr); + cmpd(CCR0, tmp3, obj); + beq(CCR0, monitor_found); + + // Search until null encountered, guaranteed _null_sentinel at end. + addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference())); + cmpdi(CCR1, tmp3, 0); + bne(CCR1, loop); + // Cache Miss, CCR0.NE set from cmp above + b(slow_path); + + bind(monitor_found); + ld(monitor, in_bytes(OMCache::oop_to_monitor_difference()), cache_addr); // Compute owner address. - addi(owner_addr, tagged_monitor, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag); - - // CAS owner (null => current thread). - cmpxchgd(/*flag=*/flag, - /*current_value=*/t, - /*compare_value=*/(intptr_t)0, - /*exchange_value=*/R16_thread, - /*where=*/owner_addr, - MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq, - MacroAssembler::cmpxchgx_hint_acquire_lock()); - beq(flag, locked); - - // Check if recursive. - cmpd(flag, t, R16_thread); - bne(flag, slow_path); - - // Recursive. + addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset())); + } + + // CAS owner (null => current thread). + cmpxchgd(/*flag=*/CCR0, + /*current_value=*/t, + /*compare_value=*/(intptr_t)0, + /*exchange_value=*/R16_thread, + /*where=*/owner_addr, + MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq, + MacroAssembler::cmpxchgx_hint_acquire_lock()); + beq(CCR0, monitor_locked); + + // Check if recursive. + cmpd(CCR0, t, R16_thread); + bne(CCR0, slow_path); + + // Recursive. + if (!UseObjectMonitorTable) { + assert_different_registers(tmp1, owner_addr); ld(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr); addi(tmp1, tmp1, 1); std(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr); } else { - // OMCache lookup not supported yet. Take the slowpath. - // Set flag to NE - crxor(flag, Assembler::equal, flag, Assembler::equal); - b(slow_path); + assert_different_registers(tmp2, monitor); + ld(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor); + addi(tmp2, tmp2, 1); + std(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor); + } + + bind(monitor_locked); + if (UseObjectMonitorTable) { + std(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box); } } @@ -2838,21 +2915,21 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla #ifdef ASSERT // Check that locked label is reached with flag == EQ. Label flag_correct; - beq(flag, flag_correct); + beq(CCR0, flag_correct); stop("Fast Lock Flag != EQ"); #endif bind(slow_path); #ifdef ASSERT // Check that slow_path label is reached with flag == NE. - bne(flag, flag_correct); + bne(CCR0, flag_correct); stop("Fast Lock Flag != NE"); bind(flag_correct); #endif // C2 uses the value of flag (NE vs EQ) to determine the continuation. } -void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister flag, Register obj, Register tmp1, - Register tmp2, Register tmp3) { +void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister flag, Register obj, Register box, + Register tmp1, Register tmp2, Register tmp3) { assert_different_registers(obj, tmp1, tmp2, tmp3); assert(flag == CCR0, "bad condition register"); @@ -2874,9 +2951,9 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f lwz(top, in_bytes(JavaThread::lock_stack_top_offset()), R16_thread); subi(top, top, oopSize); ldx(t, R16_thread, top); - cmpd(flag, obj, t); + cmpd(CCR0, obj, t); // Top of lock stack was not obj. Must be monitor. - bne(flag, inflated_load_monitor); + bne(CCR0, inflated_load_monitor); // Pop lock-stack. DEBUG_ONLY(li(t, 0);) @@ -2889,8 +2966,8 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f // Check if recursive. subi(t, top, oopSize); ldx(t, R16_thread, t); - cmpd(flag, obj, t); - beq(flag, unlocked); + cmpd(CCR0, obj, t); + beq(CCR0, unlocked); // Not recursive. @@ -2941,62 +3018,74 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f cmplwi(CCR0, top, in_bytes(JavaThread::lock_stack_base_offset())); blt(CCR0, check_done); ldx(t, R16_thread, top); - cmpd(flag, obj, t); - bne(flag, inflated); + cmpd(CCR0, obj, t); + bne(CCR0, inflated); stop("Fast Unlock lock on stack"); bind(check_done); #endif - if (!UseObjectMonitorTable) { - // mark contains the tagged ObjectMonitor*. - const Register monitor = mark; - const uintptr_t monitor_tag = markWord::monitor_value; + // mark contains the tagged ObjectMonitor*. + const Register monitor = mark; + const uintptr_t monitor_tag = markWord::monitor_value; + if (!UseObjectMonitorTable) { // Untag the monitor. subi(monitor, mark, monitor_tag); + } else { + ld(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box); + // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*) + cmpldi(CCR0, monitor, checked_cast(alignof(ObjectMonitor*))); + blt(CCR0, slow_path); + } - const Register recursions = tmp2; - Label not_recursive; - - // Check if recursive. - ld(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor); - addic_(recursions, recursions, -1); - blt(CCR0, not_recursive); + const Register recursions = tmp2; + Label not_recursive; - // Recursive unlock. - std(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor); - crorc(CCR0, Assembler::equal, CCR0, Assembler::equal); - b(unlocked); + // Check if recursive. + ld(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor); + addic_(recursions, recursions, -1); + blt(CCR0, not_recursive); - bind(not_recursive); + // Recursive unlock. + std(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor); + crorc(CCR0, Assembler::equal, CCR0, Assembler::equal); + b(unlocked); - Label release_; - const Register t2 = tmp2; + bind(not_recursive); - // Check if the entry lists are empty. - ld(t, in_bytes(ObjectMonitor::EntryList_offset()), monitor); - ld(t2, in_bytes(ObjectMonitor::cxq_offset()), monitor); - orr(t, t, t2); - cmpdi(flag, t, 0); - beq(flag, release_); + Label set_eq_unlocked; + const Register t2 = tmp2; - // The owner may be anonymous and we removed the last obj entry in - // the lock-stack. This loses the information about the owner. - // Write the thread to the owner field so the runtime knows the owner. - std(R16_thread, in_bytes(ObjectMonitor::owner_offset()), monitor); - b(slow_path); + // Set owner to null. + // Release to satisfy the JMM + release(); + li(t, 0); + std(t, in_bytes(ObjectMonitor::owner_offset()), monitor); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); + + // Check if the entry lists are empty. + ld(t, in_bytes(ObjectMonitor::EntryList_offset()), monitor); + ld(t2, in_bytes(ObjectMonitor::cxq_offset()), monitor); + orr(t, t, t2); + cmpdi(CCR0, t, 0); + beq(CCR0, unlocked); // If so we are done. + + // Check if there is a successor. + ld(t, in_bytes(ObjectMonitor::succ_offset()), monitor); + cmpdi(CCR0, t, 0); + bne(CCR0, set_eq_unlocked); // If so we are done. + + // Save the monitor pointer in the current thread, so we can try + // to reacquire the lock in SharedRuntime::monitor_exit_helper(). + std(monitor, in_bytes(JavaThread::unlocked_inflated_monitor_offset()), R16_thread); + + crxor(CCR0, Assembler::equal, CCR0, Assembler::equal); // Set flag = NE => slow path + b(slow_path); - bind(release_); - // Set owner to null. - release(); - // t contains 0 - std(t, in_bytes(ObjectMonitor::owner_offset()), monitor); - } else { - // OMCache lookup not supported yet. Take the slowpath. - // Set flag to NE - crxor(flag, Assembler::equal, flag, Assembler::equal); - b(slow_path); - } + bind(set_eq_unlocked); + crorc(CCR0, Assembler::equal, CCR0, Assembler::equal); // Set flag = EQ => fast path } bind(unlocked); @@ -3005,13 +3094,13 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f #ifdef ASSERT // Check that unlocked label is reached with flag == EQ. Label flag_correct; - beq(flag, flag_correct); + beq(CCR0, flag_correct); stop("Fast Lock Flag != EQ"); #endif bind(slow_path); #ifdef ASSERT // Check that slow_path label is reached with flag == NE. - bne(flag, flag_correct); + bne(CCR0, flag_correct); stop("Fast Lock Flag != NE"); bind(flag_correct); #endif @@ -4640,15 +4729,21 @@ void MacroAssembler::atomically_flip_locked_state(bool is_unlock, Register obj, // // - obj: the object to be locked // - t1, t2: temporary register -void MacroAssembler::lightweight_lock(Register obj, Register t1, Register t2, Label& slow) { +void MacroAssembler::lightweight_lock(Register box, Register obj, Register t1, Register t2, Label& slow) { assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); - assert_different_registers(obj, t1, t2); + assert_different_registers(box, obj, t1, t2); Label push; const Register top = t1; const Register mark = t2; const Register t = R0; + if (UseObjectMonitorTable) { + // Clear cache in case fast locking succeeds. + li(t, 0); + std(t, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box); + } + // Check if the lock-stack is full. lwz(top, in_bytes(JavaThread::lock_stack_top_offset()), R16_thread); cmplwi(CCR0, top, LockStack::end_offset()); @@ -4669,7 +4764,7 @@ void MacroAssembler::lightweight_lock(Register obj, Register t1, Register t2, La andi_(t, t, markWord::lock_mask_in_place); bne(CCR0, slow); - // Try to lock. Transition lock bits 0b00 => 0b01 + // Try to lock. Transition lock bits 0b01 => 0b00 atomically_flip_locked_state(/* is_unlock */ false, obj, mark, slow, MacroAssembler::MemBarAcq); bind(push); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index 03ad37a4fb04a..224e7bff99541 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -654,7 +654,7 @@ class MacroAssembler: public Assembler { void inc_held_monitor_count(Register tmp); void dec_held_monitor_count(Register tmp); void atomically_flip_locked_state(bool is_unlock, Register obj, Register tmp, Label& failed, int semantics); - void lightweight_lock(Register obj, Register t1, Register t2, Label& slow); + void lightweight_lock(Register box, Register obj, Register t1, Register t2, Label& slow); void lightweight_unlock(Register obj, Register t1, Label& slow); // allocation (for C1) @@ -675,11 +675,11 @@ class MacroAssembler: public Assembler { void compiler_fast_unlock_object(ConditionRegister flag, Register oop, Register box, Register tmp1, Register tmp2, Register tmp3); - void compiler_fast_lock_lightweight_object(ConditionRegister flag, Register oop, Register tmp1, - Register tmp2, Register tmp3); + void compiler_fast_lock_lightweight_object(ConditionRegister flag, Register oop, Register box, + Register tmp1, Register tmp2, Register tmp3); - void compiler_fast_unlock_lightweight_object(ConditionRegister flag, Register oop, Register tmp1, - Register tmp2, Register tmp3); + void compiler_fast_unlock_lightweight_object(ConditionRegister flag, Register oop, Register box, + Register tmp1, Register tmp2, Register tmp3); // Check if safepoint requested and if so branch void safepoint_poll(Label& slow_path, Register temp, bool at_return, bool in_nmethod); diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index e7e066ebcc6d3..f74dde0f97e6e 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -1000,6 +1000,10 @@ int MachNode::compute_padding(int current_offset) const { // Should the matcher clone input 'm' of node 'n'? bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { + if (is_encode_and_store_pattern(n, m)) { + mstack.push(m, Visit); + return true; + } return false; } @@ -2150,10 +2154,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return nullptr; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return nullptr; -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; @@ -5407,7 +5407,7 @@ instruct loadRange(iRegIdst dst, memory mem) %{ // Load Compressed Pointer instruct loadN(iRegNdst dst, memory mem) %{ match(Set dst (LoadN mem)); - predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + predicate((n->as_Load()->is_unordered() || followed_by_acquire(n)) && n->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); format %{ "LWZ $dst, $mem \t// load compressed ptr" %} @@ -5419,6 +5419,7 @@ instruct loadN(iRegNdst dst, memory mem) %{ // Load Compressed Pointer acquire. instruct loadN_ac(iRegNdst dst, memory mem) %{ match(Set dst (LoadN mem)); + predicate(n->as_Load()->barrier_data() == 0); ins_cost(3*MEMORY_REF_COST); format %{ "LWZ $dst, $mem \t// load acquire compressed ptr\n\t" @@ -5432,7 +5433,7 @@ instruct loadN_ac(iRegNdst dst, memory mem) %{ // Load Compressed Pointer and decode it if narrow_oop_shift == 0. instruct loadN2P_unscaled(iRegPdst dst, memory mem) %{ match(Set dst (DecodeN (LoadN mem))); - predicate(_kids[0]->_leaf->as_Load()->is_unordered() && CompressedOops::shift() == 0); + predicate(_kids[0]->_leaf->as_Load()->is_unordered() && CompressedOops::shift() == 0 && _kids[0]->_leaf->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); format %{ "LWZ $dst, $mem \t// DecodeN (unscaled)" %} @@ -6423,6 +6424,7 @@ instruct reinterpretX(vecX dst) %{ // Store Compressed Oop instruct storeN(memory dst, iRegN_P2N src) %{ match(Set dst (StoreN dst src)); + predicate(n->as_Store()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); format %{ "STW $src, $dst \t// compressed oop" %} @@ -6476,23 +6478,6 @@ instruct storeD(memory mem, regD src) %{ ins_pipe(pipe_class_memory); %} -//----------Store Instructions With Zeros-------------------------------------- - -instruct storeCM(memory mem, immI_0 zero) %{ - match(Set mem (StoreCM mem zero)); - ins_cost(MEMORY_REF_COST); - - format %{ "STB #0, $mem \t// CMS card-mark byte store" %} - size(8); - ins_encode %{ - __ li(R0, 0); - // No release barrier: Oops are allowed to get visible after marking. - guarantee($mem$$base$$Register != R1_SP, "use frame_slots_bias"); - __ stb(R0, $mem$$disp, $mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - // Convert oop pointer into compressed form. // Nodes for postalloc expand. @@ -6598,7 +6583,7 @@ instruct encodeP_not_null_Ex(iRegNdst dst, iRegPsrc src) %{ instruct encodeP_not_null_base_null(iRegNdst dst, iRegPsrc src) %{ match(Set dst (EncodeP src)); predicate(CompressedOops::shift() != 0 && - CompressedOops::base() ==0); + CompressedOops::base() == nullptr); format %{ "SRDI $dst, $src, #3 \t// encodeP, $src != nullptr" %} size(4); @@ -6695,7 +6680,7 @@ instruct decodeN_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ predicate((n->bottom_type()->is_oopptr()->ptr() != TypePtr::NotNull && n->bottom_type()->is_oopptr()->ptr() != TypePtr::Constant) && CompressedOops::shift() != 0 && - CompressedOops::base() != 0); + CompressedOops::base() != nullptr); ins_cost(4 * DEFAULT_COST); // Should be more expensive than decodeN_Disjoint_isel_Ex. effect(TEMP crx); @@ -6707,7 +6692,7 @@ instruct decodeN_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ instruct decodeN_nullBase(iRegPdst dst, iRegNsrc src) %{ match(Set dst (DecodeN src)); predicate(CompressedOops::shift() != 0 && - CompressedOops::base() == 0); + CompressedOops::base() == nullptr); format %{ "SLDI $dst, $src, #3 \t// DecodeN (zerobased)" %} size(4); @@ -6825,7 +6810,7 @@ instruct decodeN_notNull_addBase_Ex(iRegPdst dst, iRegNsrc src) %{ predicate((n->bottom_type()->is_oopptr()->ptr() == TypePtr::NotNull || n->bottom_type()->is_oopptr()->ptr() == TypePtr::Constant) && CompressedOops::shift() != 0 && - CompressedOops::base() != 0); + CompressedOops::base() != nullptr); ins_cost(2 * DEFAULT_COST); format %{ "DecodeN $dst, $src \t// $src != nullptr, postalloc expanded" %} @@ -7477,6 +7462,7 @@ instruct compareAndSwapI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc instruct compareAndSwapN_regP_regN_regN(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndSwapN mem_ptr (Binary src1 src2))); + predicate(n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as bool" %} ins_encode %{ @@ -7676,7 +7662,7 @@ instruct weakCompareAndSwapI_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, instruct weakCompareAndSwapN_regP_regN_regN(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapN mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); + predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "weak CMPXCHGW $res, $mem_ptr, $src1, $src2; as bool" %} ins_encode %{ @@ -7690,7 +7676,7 @@ instruct weakCompareAndSwapN_regP_regN_regN(iRegIdst res, iRegPdst mem_ptr, iReg instruct weakCompareAndSwapN_acq_regP_regN_regN(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapN mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst); + predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst) && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "weak CMPXCHGW acq $res, $mem_ptr, $src1, $src2; as bool" %} ins_encode %{ @@ -7939,7 +7925,7 @@ instruct compareAndExchangeI_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, instruct compareAndExchangeN_regP_regN_regN(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeN mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); + predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as narrow oop" %} ins_encode %{ @@ -7953,7 +7939,7 @@ instruct compareAndExchangeN_regP_regN_regN(iRegNdst res, iRegPdst mem_ptr, iReg instruct compareAndExchangeN_acq_regP_regN_regN(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeN mem_ptr (Binary src1 src2))); - predicate(((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst); + predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst) && n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); format %{ "CMPXCHGW acq $res, $mem_ptr, $src1, $src2; as narrow oop" %} ins_encode %{ @@ -8262,6 +8248,7 @@ instruct getAndSetP(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src, flagsRegCR0 cr instruct getAndSetN(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src, flagsRegCR0 cr0) %{ match(Set res (GetAndSetN mem_ptr src)); + predicate(n->as_LoadStore()->barrier_data() == 0); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndSetN $res, $mem_ptr, $src" %} ins_encode %{ @@ -12106,10 +12093,10 @@ instruct cmpFastUnlock(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp ins_pipe(pipe_class_compare); %} -instruct cmpFastLockLightweight(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2) %{ +instruct cmpFastLockLightweight(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, flagsRegCR1 cr1) %{ predicate(LockingMode == LM_LIGHTWEIGHT); match(Set crx (FastLock oop box)); - effect(TEMP tmp1, TEMP tmp2); + effect(TEMP tmp1, TEMP tmp2, KILL cr1); format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2" %} ins_encode %{ diff --git a/src/hotspot/cpu/ppc/register_ppc.hpp b/src/hotspot/cpu/ppc/register_ppc.hpp index 302d49884fae3..b7ba4f053b5d6 100644 --- a/src/hotspot/cpu/ppc/register_ppc.hpp +++ b/src/hotspot/cpu/ppc/register_ppc.hpp @@ -27,6 +27,7 @@ #define CPU_PPC_REGISTER_PPC_HPP #include "asm/register.hpp" +#include "utilities/count_trailing_zeros.hpp" // forward declaration class VMRegImpl; @@ -555,4 +556,12 @@ constexpr Register R29_TOC = R29; constexpr Register R11_scratch1 = R11; constexpr Register R12_scratch2 = R12; +template <> +inline Register AbstractRegSet::first() { + if (_bitset == 0) { return noreg; } + return as_Register(count_trailing_zeros(_bitset)); +} + +typedef AbstractRegSet RegSet; + #endif // CPU_PPC_REGISTER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 5cf5f7cf73e03..aa8ae6070b6a6 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -2399,7 +2399,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Try fastpath for locking. if (LockingMode == LM_LIGHTWEIGHT) { // fast_lock kills r_temp_1, r_temp_2, r_temp_3. - __ compiler_fast_lock_lightweight_object(CCR0, r_oop, r_temp_1, r_temp_2, r_temp_3); + __ compiler_fast_lock_lightweight_object(CCR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); } else { // fast_lock kills r_temp_1, r_temp_2, r_temp_3. __ compiler_fast_lock_object(CCR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); @@ -2605,7 +2605,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Try fastpath for unlocking. if (LockingMode == LM_LIGHTWEIGHT) { - __ compiler_fast_unlock_lightweight_object(CCR0, r_oop, r_temp_1, r_temp_2, r_temp_3); + __ compiler_fast_unlock_lightweight_object(CCR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); } else { __ compiler_fast_unlock_object(CCR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); } diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index ee3f1911e2082..206c161287fa2 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -4587,6 +4587,30 @@ address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { return start; } + // load Method* target of MethodHandle + // R3_ARG1 = jobject receiver + // R19_method = result Method* + address generate_upcall_stub_load_target() { + + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(R3_ARG1, R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); + // Load target method from receiver + __ load_heap_oop(R19_method, java_lang_invoke_MethodHandle::form_offset(), R3_ARG1, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ load_heap_oop(R19_method, java_lang_invoke_LambdaForm::vmentry_offset(), R19_method, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ load_heap_oop(R19_method, java_lang_invoke_MemberName::method_offset(), R19_method, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ ld(R19_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset(), R19_method); + __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); // just in case callee is deoptimized + + __ blr(); + + return start; + } + // Initialization void generate_initial_stubs() { // Generates all stubs and initializes the entry points @@ -4651,6 +4675,7 @@ address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index 03dca2aeb9b7b..cf3dd4cbd34c0 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -1078,6 +1078,7 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M case Interpreter::java_lang_math_sin : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dsin); break; case Interpreter::java_lang_math_cos : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dcos); break; case Interpreter::java_lang_math_tan : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dtan); break; + case Interpreter::java_lang_math_tanh : /* run interpreted */ break; case Interpreter::java_lang_math_abs : /* run interpreted */ break; case Interpreter::java_lang_math_sqrt : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dsqrt); break; case Interpreter::java_lang_math_log : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dlog); break; diff --git a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp index b60fd4f16d163..635bab900d157 100644 --- a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -118,7 +119,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -221,7 +222,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry), R0); __ addi(R3_ARG1, R1_SP, frame_data_offset); - __ load_const_optimized(R4_ARG2, (intptr_t)receiver, R0); __ call_c(call_target_address); __ mr(R16_thread, R3_RET); __ block_comment("} on_entry"); @@ -236,12 +236,12 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(callerSP), frame::native_abi_minframe_size, frame::jit_out_preserve_size); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(R3_ARG1); - __ block_comment("} receiver "); - - __ load_const_optimized(R19_method, (intptr_t)entry); - __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); + __ block_comment("{ load target "); + __ load_const_optimized(call_target_address, StubRoutines::upcall_stub_load_target(), R0); + __ load_const_optimized(R3_ARG1, (intptr_t)receiver, R0); + __ mtctr(call_target_address); + __ bctrl(); // loads target Method* into R19_method + __ block_comment("} load target "); __ push_cont_fastpath(); @@ -326,7 +326,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 98ab86bf72eb6..ad3d18fa39268 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -46,8 +46,10 @@ class Argument { public: enum { - n_int_register_parameters_c = 8, // x10, x11, ... x17 (c_rarg0, c_rarg1, ...) - n_float_register_parameters_c = 8, // f10, f11, ... f17 (c_farg0, c_farg1, ... ) + // check more info at https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc + n_int_register_parameters_c = 8, // x10, x11, ... x17 (c_rarg0, c_rarg1, ...) + n_float_register_parameters_c = 8, // f10, f11, ... f17 (c_farg0, c_farg1, ... ) + n_vector_register_parameters_c = 16, // v8, v9, ... v23 n_int_register_parameters_j = 8, // x11, ... x17, x10 (j_rarg0, j_rarg1, ...) n_float_register_parameters_j = 8 // f10, f11, ... f17 (j_farg0, j_farg1, ...) @@ -143,6 +145,10 @@ constexpr Register x19_sender_sp = x19; // Sender's SP while in interpreter constexpr Register t0 = x5; constexpr Register t1 = x6; constexpr Register t2 = x7; +constexpr Register t3 = x28; +constexpr Register t4 = x29; +constexpr Register t5 = x30; +constexpr Register t6 = x31; const Register g_INTArgReg[Argument::n_int_register_parameters_c] = { c_rarg0, c_rarg1, c_rarg2, c_rarg3, c_rarg4, c_rarg5, c_rarg6, c_rarg7 @@ -705,6 +711,16 @@ class Assembler : public AbstractAssembler { emit(insn); } + void fencei() { + unsigned insn = 0; + patch((address)&insn, 6, 0, 0b0001111); // opcode + patch((address)&insn, 11, 7, 0b00000); // rd + patch((address)&insn, 14, 12, 0b001); // func + patch((address)&insn, 19, 15, 0b00000); // rs1 + patch((address)&insn, 31, 20, 0b000000000000); // fm + emit(insn); + } + #define INSN(NAME, op, funct3, funct7) \ void NAME() { \ unsigned insn = 0; \ diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 940706b0a7376..828f70e4decee 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -980,6 +980,7 @@ void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { __ lbu(t0, Address(op->klass()->as_register(), InstanceKlass::init_state_offset())); + __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ mv(t1, (u1)InstanceKlass::fully_initialized); add_debug_info_for_null_check_here(op->stub()->info()); __ bne(t0, t1, *op->stub()->entry(), /* is_far */ true); diff --git a/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp b/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp index 7995750aba96b..db18525b89c76 100644 --- a/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp @@ -71,32 +71,4 @@ void C2EntryBarrierStub::emit(C2_MacroAssembler& masm) { __ emit_int32(0); // nmethod guard value } -int C2HandleAnonOMOwnerStub::max_size() const { - // Max size of stub has been determined by testing with 0 without using RISC-V compressed - // instruction-set extension, in which case C2CodeStubList::emit() will throw an assertion - // and report the actual size that is needed. - return 20 DEBUG_ONLY(+8); -} - -void C2HandleAnonOMOwnerStub::emit(C2_MacroAssembler& masm) { - __ bind(entry()); - Register mon = monitor(); - Register t = tmp(); - assert(t != noreg, "need tmp register"); - - // Fix owner to be the current thread. - __ sd(xthread, Address(mon, ObjectMonitor::owner_offset())); - - // Pop owner object from lock-stack. - __ lwu(t, Address(xthread, JavaThread::lock_stack_top_offset())); - __ subw(t, t, oopSize); -#ifdef ASSERT - __ add(t0, xthread, t); - __ sd(zr, Address(t0, 0)); -#endif - __ sw(t, Address(xthread, JavaThread::lock_stack_top_offset())); - - __ j(continuation()); -} - #undef __ diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index e2c9b9dd609e0..75f87e35adf41 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -165,6 +165,7 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Register oop = objectReg; Register box = boxReg; Register disp_hdr = tmp1Reg; + Register owner_addr = tmp1Reg; Register tmp = tmp2Reg; Label object_has_monitor; // Finish fast lock successfully. MUST branch to with flag == 0 @@ -222,15 +223,33 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, j(unlocked); bind(notRecursive); - ld(t0, Address(tmp, ObjectMonitor::EntryList_offset())); - ld(disp_hdr, Address(tmp, ObjectMonitor::cxq_offset())); - orr(t0, t0, disp_hdr); // Will be 0 if both are 0. - bnez(t0, slow_path); + // Compute owner address. + la(owner_addr, Address(tmp, ObjectMonitor::owner_offset())); - // need a release store here - la(tmp, Address(tmp, ObjectMonitor::owner_offset())); + // Set owner to null. + // Release to satisfy the JMM membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - sd(zr, Address(tmp)); // set unowned + sd(zr, Address(owner_addr)); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); + + // Check if the entry lists are empty. + ld(t0, Address(tmp, ObjectMonitor::EntryList_offset())); + ld(tmp1Reg, Address(tmp, ObjectMonitor::cxq_offset())); + orr(t0, t0, tmp1Reg); + beqz(t0, unlocked); // If so we are done. + + // Check if there is a successor. + ld(t0, Address(tmp, ObjectMonitor::succ_offset())); + bnez(t0, unlocked); // If so we are done. + + // Save the monitor pointer in the current thread, so we can try to + // reacquire the lock in SharedRuntime::monitor_exit_helper(). + sd(tmp, Address(xthread, JavaThread::unlocked_inflated_monitor_offset())); + + mv(flag, 1); + j(slow_path); bind(unlocked); mv(flag, zr); @@ -534,28 +553,35 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, bind(not_recursive); - Label release; const Register tmp2_owner_addr = tmp2; // Compute owner address. la(tmp2_owner_addr, Address(tmp1_monitor, ObjectMonitor::owner_offset())); + // Set owner to null. + // Release to satisfy the JMM + membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); + sd(zr, Address(tmp2_owner_addr)); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); + // Check if the entry lists are empty. ld(t0, Address(tmp1_monitor, ObjectMonitor::EntryList_offset())); ld(tmp3_t, Address(tmp1_monitor, ObjectMonitor::cxq_offset())); orr(t0, t0, tmp3_t); - beqz(t0, release); + beqz(t0, unlocked); // If so we are done. - // The owner may be anonymous and we removed the last obj entry in - // the lock-stack. This loses the information about the owner. - // Write the thread to the owner field so the runtime knows the owner. - sd(xthread, Address(tmp2_owner_addr)); - j(slow_path); + // Check if there is a successor. + ld(tmp3_t, Address(tmp1_monitor, ObjectMonitor::succ_offset())); + bnez(tmp3_t, unlocked); // If so we are done. - bind(release); - // Set owner to null. - membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - sd(zr, Address(tmp2_owner_addr)); + // Save the monitor pointer in the current thread, so we can try + // to reacquire the lock in SharedRuntime::monitor_exit_helper(). + sd(tmp1_monitor, Address(xthread, JavaThread::unlocked_inflated_monitor_offset())); + + mv(flag, 1); + j(slow_path); } bind(unlocked); diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp index 062f80290626f..7036c44d99dc9 100644 --- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. * 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,10 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/g1/c1/g1BarrierSetC1.hpp" -#endif +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#endif // COMPILER2 #define __ masm-> @@ -96,6 +99,55 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas __ pop_reg(saved_regs, sp); } +static void generate_queue_test_and_insertion(MacroAssembler* masm, ByteSize index_offset, ByteSize buffer_offset, Label& runtime, + const Register thread, const Register value, const Register tmp1, const Register tmp2) { + // Can we store a value in the given thread's buffer? + // (The index field is typed as size_t.) + __ ld(tmp1, Address(thread, in_bytes(index_offset))); // tmp1 := *(index address) + __ beqz(tmp1, runtime); // jump to runtime if index == 0 (full buffer) + // The buffer is not full, store value into it. + __ sub(tmp1, tmp1, wordSize); // tmp1 := next index + __ sd(tmp1, Address(thread, in_bytes(index_offset))); // *(index address) := next index + __ ld(tmp2, Address(thread, in_bytes(buffer_offset))); // tmp2 := buffer address + __ add(tmp2, tmp2, tmp1); + __ sd(value, Address(tmp2)); // *(buffer address + next index) := value +} + +static void generate_pre_barrier_fast_path(MacroAssembler* masm, + const Register thread, + const Register tmp1) { + Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); + // Is marking active? + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { + __ lwu(tmp1, in_progress); + } else { + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); + __ lbu(tmp1, in_progress); + } +} + +static void generate_pre_barrier_slow_path(MacroAssembler* masm, + const Register obj, + const Register pre_val, + const Register thread, + const Register tmp1, + const Register tmp2, + Label& done, + Label& runtime) { + // Do we need to load the previous value? + if (obj != noreg) { + __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); + } + // Is the previous value null? + __ beqz(pre_val, done, true); + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::satb_mark_queue_index_offset(), + G1ThreadLocalData::satb_mark_queue_buffer_offset(), + runtime, + thread, pre_val, tmp1, tmp2); + __ j(done); +} + void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -116,43 +168,10 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, assert_different_registers(obj, pre_val, tmp1, tmp2); assert(pre_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register"); - Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); - Address index(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset())); - - // Is marking active? - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { // 4-byte width - __ lwu(tmp1, in_progress); - } else { - assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ lbu(tmp1, in_progress); - } + generate_pre_barrier_fast_path(masm, thread, tmp1); + // If marking is not active (*(mark queue active address) == 0), jump to done __ beqz(tmp1, done); - - // Do we need to load the previous value? - if (obj != noreg) { - __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); - } - - // Is the previous value null? - __ beqz(pre_val, done); - - // Can we store original value in the thread's buffer? - // Is index == 0? - // (The index field is typed as size_t.) - - __ ld(tmp1, index); // tmp := *index_adr - __ beqz(tmp1, runtime); // tmp == 0? - // If yes, goto runtime - - __ sub(tmp1, tmp1, wordSize); // tmp := tmp - wordSize - __ sd(tmp1, index); // *index_adr := tmp - __ ld(tmp2, buffer); - __ add(tmp1, tmp1, tmp2); // tmp := tmp + *buffer_adr - - // Record the previous value - __ sd(pre_val, Address(tmp1, 0)); - __ j(done); + generate_pre_barrier_slow_path(masm, obj, pre_val, thread, tmp1, tmp2, done, runtime); __ bind(runtime); @@ -171,6 +190,49 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, } +static void generate_post_barrier_fast_path(MacroAssembler* masm, + const Register store_addr, + const Register new_val, + const Register tmp1, + const Register tmp2, + Label& done, + bool new_val_may_be_null) { + // Does store cross heap regions? + __ xorr(tmp1, store_addr, new_val); // tmp1 := store address ^ new value + __ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) + __ beqz(tmp1, done); + // Crosses regions, storing null? + if (new_val_may_be_null) { + __ beqz(new_val, done); + } + // Storing region crossing non-null, is card young? + __ srli(tmp1, store_addr, CardTable::card_shift()); // tmp1 := card address relative to card table base + __ load_byte_map_base(tmp2); // tmp2 := card table base address + __ add(tmp1, tmp1, tmp2); // tmp1 := card address + __ lbu(tmp2, Address(tmp1)); // tmp2 := card +} + +static void generate_post_barrier_slow_path(MacroAssembler* masm, + const Register thread, + const Register tmp1, + const Register tmp2, + Label& done, + Label& runtime) { + __ membar(MacroAssembler::StoreLoad); // StoreLoad membar + __ lbu(tmp2, Address(tmp1)); // tmp2 := card + __ beqz(tmp2, done, true); + // Storing a region crossing, non-null oop, card is clean. + // Dirty card and log. + STATIC_ASSERT(CardTable::dirty_card_val() == 0); + __ sb(zr, Address(tmp1)); // *(card address) := dirty_card_val + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + runtime, + thread, tmp1, tmp2, t0); + __ j(done); +} + void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Register store_addr, Register new_val, @@ -179,73 +241,119 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Register tmp2) { assert(thread == xthread, "must be"); assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, t0); - assert(store_addr != noreg && new_val != noreg && tmp1 != noreg && - tmp2 != noreg, "expecting a register"); - - Address queue_index(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset())); - Address buffer(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset())); - - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); + assert(store_addr != noreg && new_val != noreg && tmp1 != noreg && tmp2 != noreg, + "expecting a register"); Label done; Label runtime; - // Does store cross heap regions? + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp1, tmp2, done, true /* new_val_may_be_null */); + // If card is young, jump to done (tmp2 holds the card value) + __ mv(t0, (int)G1CardTable::g1_young_card_val()); + __ beq(tmp2, t0, done); // card == young_card_val? + generate_post_barrier_slow_path(masm, thread, tmp1, tmp2, done, runtime); - __ xorr(tmp1, store_addr, new_val); - __ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); - __ beqz(tmp1, done); + __ bind(runtime); + // save the live input values + RegSet saved = RegSet::of(store_addr); + __ push_reg(saved, sp); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), tmp1, thread); + __ pop_reg(saved, sp); - // crosses regions, storing null? + __ bind(done); +} - __ beqz(new_val, done); +#if defined(COMPILER2) - // storing region crossing non-null, is card already dirty? +static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStubC2* stub, const Register arg, const address runtime_path) { + SaveLiveRegisters save_registers(masm, stub); + if (c_rarg0 != arg) { + __ mv(c_rarg0, arg); + } + __ mv(c_rarg1, xthread); + __ mv(t0, runtime_path); + __ jalr(t0); +} - const Register card_addr = tmp1; +void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* stub) { + assert(thread == xthread, "must be"); + assert_different_registers(obj, pre_val, tmp1, tmp2); + assert(pre_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register"); - __ srli(card_addr, store_addr, CardTable::card_shift()); + stub->initialize_registers(obj, pre_val, thread, tmp1, tmp2); - // get the address of the card - __ load_byte_map_base(tmp2); - __ add(card_addr, card_addr, tmp2); - __ lbu(tmp2, Address(card_addr)); - __ mv(t0, (int)G1CardTable::g1_young_card_val()); - __ beq(tmp2, t0, done); + generate_pre_barrier_fast_path(masm, thread, tmp1); + // If marking is active (*(mark queue active address) != 0), jump to stub (slow path) + __ bnez(tmp1, *stub->entry(), true); - assert((int)CardTable::dirty_card_val() == 0, "must be 0"); + __ bind(*stub->continuation()); +} - __ membar(MacroAssembler::StoreLoad); +void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register obj = stub->obj(); + Register pre_val = stub->pre_val(); + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); + Register tmp2 = stub->tmp2(); - __ lbu(tmp2, Address(card_addr)); - __ beqz(tmp2, done); + __ bind(*stub->entry()); + generate_pre_barrier_slow_path(masm, obj, pre_val, thread, tmp1, tmp2, *stub->continuation(), runtime); - // storing a region crossing, non-null oop, card is clean. - // dirty card and log. + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, pre_val, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry)); + __ j(*stub->continuation()); +} - __ sb(zr, Address(card_addr)); +void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* stub) { + assert(thread == xthread, "must be"); + assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, t0); + assert(store_addr != noreg && new_val != noreg && tmp1 != noreg && tmp2 != noreg, + "expecting a register"); - __ ld(t0, queue_index); - __ beqz(t0, runtime); - __ sub(t0, t0, wordSize); - __ sd(t0, queue_index); + stub->initialize_registers(thread, tmp1, tmp2); - __ ld(tmp2, buffer); - __ add(t0, tmp2, t0); - __ sd(card_addr, Address(t0, 0)); - __ j(done); + bool new_val_may_be_null = (stub->barrier_data() & G1C2BarrierPostNotNull) == 0; + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp1, tmp2, *stub->continuation(), new_val_may_be_null); + // If card is not young, jump to stub (slow path) (tmp2 holds the card value) + __ mv(t0, (int)G1CardTable::g1_young_card_val()); + __ bne(tmp2, t0, *stub->entry(), true); - __ bind(runtime); - // save the live input values - RegSet saved = RegSet::of(store_addr); - __ push_reg(saved, sp); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, thread); - __ pop_reg(saved, sp); + __ bind(*stub->continuation()); +} - __ bind(done); +void G1BarrierSetAssembler::generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); // tmp1 holds the card address. + Register tmp2 = stub->tmp2(); + + __ bind(*stub->entry()); + generate_post_barrier_slow_path(masm, thread, tmp1, tmp2, *stub->continuation(), runtime); + + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, tmp1, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry)); + __ j(*stub->continuation()); } +#endif // COMPILER2 + void G1BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp2) { bool on_oop = is_reference_type(type); diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp index 96568994079dd..c7bee2ef6f3a8 100644 --- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. * 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,8 @@ class LIR_Assembler; class StubAssembler; class G1PreBarrierStub; class G1PostBarrierStub; +class G1PreBarrierStubC2; +class G1PostBarrierStubC2; class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { protected: @@ -72,6 +74,27 @@ class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { void generate_c1_post_barrier_runtime_stub(StubAssembler* sasm); #endif +#ifdef COMPILER2 + void g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + G1PreBarrierStubC2* c2_stub); + void generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const; + void g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* c2_stub); + void generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const; +#endif + void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp2); }; diff --git a/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad b/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad new file mode 100644 index 0000000000000..7a525323021dd --- /dev/null +++ b/src/hotspot/cpu/riscv/gc/g1/g1_riscv.ad @@ -0,0 +1,564 @@ +// +// Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2024, Huawei Technologies Co., Ltd. All rights reserved. +// 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. +// + +source_hpp %{ + +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#include "gc/shared/gc_globals.hpp" + +%} + +source %{ + +#include "gc/g1/g1BarrierSetAssembler_riscv.hpp" +#include "gc/g1/g1BarrierSetRuntime.hpp" + +static void write_barrier_pre(MacroAssembler* masm, + const MachNode* node, + Register obj, + Register pre_val, + Register tmp1, + Register tmp2, + RegSet preserve = RegSet(), + RegSet no_preserve = RegSet()) { + if (!G1PreBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PreBarrierStubC2* const stub = G1PreBarrierStubC2::create(node); + for (RegSetIterator reg = preserve.begin(); *reg != noreg; ++reg) { + stub->preserve(*reg); + } + for (RegSetIterator reg = no_preserve.begin(); *reg != noreg; ++reg) { + stub->dont_preserve(*reg); + } + g1_asm->g1_write_barrier_pre_c2(masm, obj, pre_val, xthread, tmp1, tmp2, stub); +} + +static void write_barrier_post(MacroAssembler* masm, + const MachNode* node, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2) { + if (!G1PostBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PostBarrierStubC2* const stub = G1PostBarrierStubC2::create(node); + g1_asm->g1_write_barrier_post_c2(masm, store_addr, new_val, xthread, tmp1, tmp2, stub); +} + +%} + +instruct g1StoreP(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(STORE_COST); + format %{ "sd $src, $mem\t# ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ sd($src$$Register, Address($mem$$Register)); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(istore_reg_mem); +%} + +instruct g1StoreN(indirect mem, iRegN src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(STORE_COST); + format %{ "sw $src, $mem\t# compressed ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + __ sw($src$$Register, Address($mem$$Register)); + if ((barrier_data() & G1C2BarrierPost) != 0) { + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ decode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ decode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + } + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(istore_reg_mem); +%} + +instruct g1EncodePAndStoreN(indirect mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem (EncodeP src))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(STORE_COST); + format %{ "encode_heap_oop $tmp1, $src\n\t" + "sw $tmp1, $mem\t# compressed ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ encode_heap_oop($tmp1$$Register, $src$$Register); + } else { + __ encode_heap_oop_not_null($tmp1$$Register, $src$$Register); + } + __ sw($tmp1$$Register, Address($mem$$Register)); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(istore_reg_mem); +%} + +instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $res = $mem, $oldval, $newval\t# ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + // Pass $oldval to the pre-barrier (instead of loading from $mem), because + // $oldval is the only value that can be overwritten. + // The same holds for g1CompareAndSwapP and its Acq variant. + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::int64, + /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $res = $mem, $oldval, $newval\t# ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + // Pass $oldval to the pre-barrier (instead of loading from $mem), because + // $oldval is the only value that can be overwritten. + // The same holds for g1CompareAndSwapP and its Acq variant. + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::int64, + /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $res = $mem, $oldval, $newval\t# narrow oop" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::uint32, + /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $res = $mem, $oldval, $newval\t# narrow oop" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::uint32, + /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $mem, $oldval, $newval\t# (ptr)\n\t" + "mv $res, $res == $oldval" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::int64, + /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, + /*result as bool*/ true); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegP oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $mem, $oldval, $newval\t# (ptr)\n\t" + "mv $res, $res == $oldval" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::int64, + /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, + /*result as bool*/ true); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "cmpxchg $mem, $oldval, $newval\t# (narrow oop)\n\t" + "mv $res, $res == $oldval" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::uint32, + /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, + /*result as bool*/ true); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegN oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "cmpxchg_acq $mem, $oldval, $newval\t# (narrow oop)\n\t" + "mv $res, $res == $oldval" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($oldval$$Register, $mem$$Register); + assert_different_registers($newval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::uint32, + /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, + /*result as bool*/ true); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_slow); +%} + +instruct g1GetAndSetP(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetP mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "atomic_xchg $preval, $newval, [$mem]" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $preval$$Register /* pre_val (as a temporary register) */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchg($preval$$Register, $newval$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +instruct g1GetAndSetPAcq(indirect mem, iRegP newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetP mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "atomic_xchg_acq $preval, $newval, [$mem]" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $preval$$Register /* pre_val (as a temporary register) */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchgal($preval$$Register, $newval$$Register, $mem$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +instruct g1GetAndSetN(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetN mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(2 * VOLATILE_REF_COST); + format %{ "atomic_xchgwu $preval, $newval, [$mem]" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchgwu($preval$$Register, $newval$$Register, $mem$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +instruct g1GetAndSetNAcq(indirect mem, iRegN newval, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegNNoSp preval, rFlagsReg cr) +%{ + predicate(UseG1GC && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); + match(Set preval (GetAndSetN mem newval)); + effect(TEMP preval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(VOLATILE_REF_COST); + format %{ "atomic_xchgwu_acq $preval, $newval, [$mem]" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */, + RegSet::of($mem$$Register, $preval$$Register, $newval$$Register) /* preserve */); + __ atomic_xchgalwu($preval$$Register, $newval$$Register, $mem$$Register); + __ decode_heap_oop($tmp1$$Register, $newval$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_serial); +%} + +instruct g1LoadP(iRegPNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadP mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(LOAD_COST + BRANCH_COST); + format %{ "ld $dst, $mem\t# ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + __ ld($dst$$Register, Address($mem$$Register)); + write_barrier_pre(masm, this, + noreg /* obj */, + $dst$$Register /* pre_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(iload_reg_mem); +%} + +instruct g1LoadN(iRegNNoSp dst, indirect mem, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadN mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(LOAD_COST + BRANCH_COST); + format %{ "lwu $dst, $mem\t# compressed ptr" %} + ins_encode %{ + guarantee($mem$$disp == 0, "impossible encoding"); + __ lwu($dst$$Register, Address($mem$$Register)); + if ((barrier_data() & G1C2BarrierPre) != 0) { + __ decode_heap_oop($tmp1$$Register, $dst$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + } + %} + ins_pipe(iload_reg_mem); +%} diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 9a79a92327723..cc73d14a756f2 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -70,10 +70,10 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec __ push_reg(saved_regs, sp); if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop_entry), + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop), src, dst, count); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop_entry), src, dst, count); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop), src, dst, count); } __ pop_reg(saved_regs, sp); __ bind(done); @@ -165,9 +165,9 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, // expand_call should be passed true. if (expand_call) { assert(pre_val != c_rarg1, "smashed arg"); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); } __ pop_reg(saved, sp); @@ -645,7 +645,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ bind(runtime); __ push_call_clobbered_registers(); __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); __ pop_call_clobbered_registers(); __ bind(done); diff --git a/src/hotspot/cpu/riscv/gc/x/x_riscv.ad b/src/hotspot/cpu/riscv/gc/x/x_riscv.ad index ef02f301c6aeb..b93b7066425b9 100644 --- a/src/hotspot/cpu/riscv/gc/x/x_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/x/x_riscv.ad @@ -52,11 +52,11 @@ static void x_load_barrier_slow_path(MacroAssembler* masm, const MachNode* node, %} // Load Pointer -instruct xLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) +instruct xLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && !ZGenerational && (n->as_Load()->barrier_data() != 0)); - effect(TEMP dst, TEMP tmp); + effect(TEMP dst, TEMP tmp, KILL cr); ins_cost(4 * DEFAULT_COST); @@ -71,11 +71,11 @@ instruct xLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) ins_pipe(iload_reg_mem); %} -instruct xCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == XLoadBarrierStrong); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -105,11 +105,11 @@ instruct xCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newva ins_pipe(pipe_slow); %} -instruct xCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == XLoadBarrierStrong)); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -139,10 +139,10 @@ instruct xCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne ins_pipe(pipe_slow); %} -instruct xCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == XLoadBarrierStrong); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -167,10 +167,10 @@ instruct xCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP n ins_pipe(pipe_slow); %} -instruct xCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp) %{ +instruct xCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && !ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == XLoadBarrierStrong); - effect(TEMP_DEF res, TEMP tmp); + effect(TEMP_DEF res, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -195,10 +195,10 @@ instruct xCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg ins_pipe(pipe_slow); %} -instruct xGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct xGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && !ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -212,10 +212,10 @@ instruct xGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ ins_pipe(pipe_serial); %} -instruct xGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct xGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && !ZGenerational && needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() != 0)); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(VOLATILE_REF_COST); diff --git a/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp index ef13676b02ed8..df111723d56b6 100644 --- a/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp @@ -92,7 +92,7 @@ static size_t probe_valid_max_address_bit() { } size_t ZPlatformAddressOffsetBits() { - const static size_t valid_max_address_offset_bits = probe_valid_max_address_bit() + 1; + static const size_t valid_max_address_offset_bits = probe_valid_max_address_bit() + 1; const size_t max_address_offset_bits = valid_max_address_offset_bits - 3; const size_t min_address_offset_bits = max_address_offset_bits - 2; const size_t address_offset = round_up_power_of_2(MaxHeapSize * ZVirtualToPhysicalRatio); diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index 8fbeaa45371d1..4a82bd9c2d09a 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -636,8 +636,20 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { ShouldNotReachHere(); } - // A full fence is generated before icache_flush by default in invalidate_word - ICache::invalidate_range(addr, bytes); + // If we are using UseCtxFencei no ICache invalidation is needed here. + // Instead every hart will preform an fence.i either by a Java thread + // (due to patching epoch will take it to slow path), + // or by the kernel when a Java thread is moved to a hart. + // The instruction streams changes must only happen before the disarm of + // the nmethod barrier. Where the disarm have a leading full two way fence. + // If this is performed during a safepoint, all Java threads will emit a fence.i + // before transitioning to 'Java', e.g. leaving native or the safepoint wait barrier. + if (!UseCtxFencei) { + // ICache invalidation is a serialization point. + // The above patching of instructions happens before the invalidation. + // Hence it have a leading full two way fence (wr, wr). + ICache::invalidate_range(addr, bytes); + } } #ifdef COMPILER2 @@ -749,6 +761,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ la(t0, RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } diff --git a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad index 4c94e504475ee..24669f45eb4d2 100644 --- a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad @@ -82,7 +82,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address z_color(masm, node, rnew_zpointer, rnew_zaddress, tmp); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, tmp, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } @@ -90,11 +91,11 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address %} // Load Pointer -instruct zLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) +instruct zLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && ZGenerational && n->as_Load()->barrier_data() != 0); - effect(TEMP dst, TEMP tmp); + effect(TEMP dst, TEMP tmp, KILL cr); ins_cost(4 * DEFAULT_COST); @@ -110,11 +111,11 @@ instruct zLoadP(iRegPNoSp dst, memory mem, iRegPNoSp tmp) %} // Store Pointer -instruct zStoreP(memory mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2) +instruct zStoreP(memory mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2, rFlagsReg cr) %{ predicate(UseZGC && ZGenerational && n->as_Store()->barrier_data() != 0); match(Set mem (StoreP mem src)); - effect(TEMP tmp1, TEMP tmp2); + effect(TEMP tmp1, TEMP tmp2, KILL cr); ins_cost(125); // XXX format %{ "sd $mem, $src\t# ptr" %} @@ -127,11 +128,11 @@ instruct zStoreP(memory mem, iRegP src, iRegPNoSp tmp1, iRegPNoSp tmp2) %} instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -150,11 +151,11 @@ instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newva %} instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndSwapP mem (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -173,10 +174,10 @@ instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne %} instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -195,10 +196,10 @@ instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP n %} instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, - iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1) %{ + iRegPNoSp oldval_tmp, iRegPNoSp newval_tmp, iRegPNoSp tmp1, rFlagsReg cr) %{ match(Set res (CompareAndExchangeP mem (Binary oldval newval))); predicate(UseZGC && ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res); + effect(TEMP oldval_tmp, TEMP newval_tmp, TEMP tmp1, TEMP_DEF res, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -216,10 +217,10 @@ instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg ins_pipe(pipe_slow); %} -instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && ZGenerational && !needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); @@ -234,10 +235,10 @@ instruct zGetAndSetP(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ ins_pipe(pipe_serial); %} -instruct zGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp) %{ +instruct zGetAndSetPAcq(indirect mem, iRegP newv, iRegPNoSp prev, iRegPNoSp tmp, rFlagsReg cr) %{ match(Set prev (GetAndSetP mem newv)); predicate(UseZGC && ZGenerational && needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() != 0); - effect(TEMP_DEF prev, TEMP tmp); + effect(TEMP_DEF prev, TEMP tmp, KILL cr); ins_cost(2 * VOLATILE_REF_COST); diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index c2585f2d1618d..dd31de14704ab 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -122,6 +122,8 @@ define_pd_global(intx, InlineSmallCode, 1000); product(bool, UseRVVForBigIntegerShiftIntrinsics, true, \ "Use RVV instructions for left/right shift of BigInteger") \ product(bool, UseTrampolines, false, EXPERIMENTAL, \ - "Far calls uses jal to trampoline.") + "Far calls uses jal to trampoline.") \ + product(bool, UseCtxFencei, false, EXPERIMENTAL, \ + "Use PR_RISCV_CTX_SW_FENCEI_ON to avoid explicit icache flush") #endif // CPU_RISCV_GLOBALS_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index cbca980288984..46701b6ede387 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -493,6 +493,7 @@ void MacroAssembler::clinit_barrier(Register klass, Register tmp, Label* L_fast_ // Fast path check: class is fully initialized lbu(tmp, Address(klass, InstanceKlass::init_state_offset())); + membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); sub(tmp, tmp, InstanceKlass::fully_initialized); beqz(tmp, *L_fast_path); @@ -1454,6 +1455,105 @@ void MacroAssembler::update_word_crc32(Register crc, Register v, Register tmp1, xorr(crc, crc, tmp2); } + +#ifdef COMPILER2 +// This improvement (vectorization) is based on java.base/share/native/libzip/zlib/zcrc32.c. +// To make it, following steps are taken: +// 1. in zcrc32.c, modify N to 16 and related code, +// 2. re-generate the tables needed, we use tables of (N == 16, W == 4) +// 3. finally vectorize the code (original implementation in zcrc32.c is just scalar code). +// New tables for vector version is after table3. +void MacroAssembler::vector_update_crc32(Register crc, Register buf, Register len, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, + Register table0, Register table3) { + assert_different_registers(t1, crc, buf, len, tmp1, tmp2, tmp3, tmp4, tmp5, table0, table3); + const int N = 16, W = 4; + const int64_t single_table_size = 256; + const Register blks = tmp2; + const Register tmpTable = tmp3, tableN16 = tmp4; + const VectorRegister vcrc = v4, vword = v8, vtmp = v12; + Label VectorLoop; + Label LastBlock; + + add(tableN16, table3, 1*single_table_size*sizeof(juint), tmp1); + mv(tmp5, 0xff); + + if (MaxVectorSize == 16) { + vsetivli(zr, N, Assembler::e32, Assembler::m4, Assembler::ma, Assembler::ta); + } else if (MaxVectorSize == 32) { + vsetivli(zr, N, Assembler::e32, Assembler::m2, Assembler::ma, Assembler::ta); + } else { + assert(MaxVectorSize > 32, "sanity"); + vsetivli(zr, N, Assembler::e32, Assembler::m1, Assembler::ma, Assembler::ta); + } + + vmv_v_x(vcrc, zr); + vmv_s_x(vcrc, crc); + + // multiple of 64 + srli(blks, len, 6); + slli(t1, blks, 6); + sub(len, len, t1); + sub(blks, blks, 1); + blez(blks, LastBlock); + + bind(VectorLoop); + { + mv(tmpTable, tableN16); + + vle32_v(vword, buf); + vxor_vv(vword, vword, vcrc); + + addi(buf, buf, N*4); + + vand_vx(vtmp, vword, tmp5); + vsll_vi(vtmp, vtmp, 2); + vluxei32_v(vcrc, tmpTable, vtmp); + + mv(tmp1, 1); + for (int k = 1; k < W; k++) { + addi(tmpTable, tmpTable, single_table_size*4); + + slli(t1, tmp1, 3); + vsrl_vx(vtmp, vword, t1); + + vand_vx(vtmp, vtmp, tmp5); + vsll_vi(vtmp, vtmp, 2); + vluxei32_v(vtmp, tmpTable, vtmp); + + vxor_vv(vcrc, vcrc, vtmp); + + addi(tmp1, tmp1, 1); + } + + sub(blks, blks, 1); + bgtz(blks, VectorLoop); + } + + bind(LastBlock); + { + vle32_v(vtmp, buf); + vxor_vv(vcrc, vcrc, vtmp); + mv(crc, zr); + for (int i = 0; i < N; i++) { + vmv_x_s(tmp2, vcrc); + // in vmv_x_s, the value is sign-extended to SEW bits, but we need zero-extended here. + zext_w(tmp2, tmp2); + vslidedown_vi(vcrc, vcrc, 1); + xorr(crc, crc, tmp2); + for (int j = 0; j < W; j++) { + andr(t1, crc, tmp5); + shadd(t1, t1, table0, tmp1, 2); + lwu(t1, Address(t1, 0)); + srli(tmp2, crc, 8); + xorr(crc, tmp2, t1); + } + } + addi(buf, buf, N*4); + } +} +#endif // COMPILER2 + /** * @param crc register containing existing CRC (32-bit) * @param buf register pointing to input byte buffer (byte*) @@ -1465,33 +1565,41 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, Register table0, Register table1, Register table2, Register table3, Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, Register tmp6) { assert_different_registers(crc, buf, len, table0, table1, table2, table3, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6); - Label L_by16_loop, L_unroll_loop, L_unroll_loop_entry, L_by4, L_by4_loop, L_by1, L_by1_loop, L_exit; + Label L_vector_entry, + L_unroll_loop, + L_by4_loop_entry, L_by4_loop, + L_by1_loop, L_exit; + const int64_t single_table_size = 256; const int64_t unroll = 16; const int64_t unroll_words = unroll*wordSize; mv(tmp5, right_32_bits); - subw(len, len, unroll_words); andn(crc, tmp5, crc); const ExternalAddress table_addr = StubRoutines::crc_table_addr(); la(table0, table_addr); - add(table1, table0, 1*256*sizeof(juint), tmp1); - add(table2, table0, 2*256*sizeof(juint), tmp1); - add(table3, table2, 1*256*sizeof(juint), tmp1); + add(table1, table0, 1*single_table_size*sizeof(juint), tmp1); + add(table2, table0, 2*single_table_size*sizeof(juint), tmp1); + add(table3, table2, 1*single_table_size*sizeof(juint), tmp1); - bge(len, zr, L_unroll_loop_entry); - addiw(len, len, unroll_words-4); - bge(len, zr, L_by4_loop); - addiw(len, len, 4); - bgt(len, zr, L_by1_loop); - j(L_exit); +#ifdef COMPILER2 + if (UseRVV) { + const int64_t tmp_limit = MaxVectorSize >= 32 ? unroll_words*3 : unroll_words*5; + mv(tmp1, tmp_limit); + bge(len, tmp1, L_vector_entry); + } +#endif // COMPILER2 + + mv(tmp1, unroll_words); + blt(len, tmp1, L_by4_loop_entry); + + const Register loop_buf_end = tmp3; align(CodeEntryAlignment); - bind(L_unroll_loop_entry); - const Register buf_end = tmp3; - add(buf_end, buf, len); // buf_end will be used as endpoint for loop below + // Entry for L_unroll_loop + add(loop_buf_end, buf, len); // loop_buf_end will be used as endpoint for loop below andi(len, len, unroll_words-1); // len = (len % unroll_words) - sub(len, len, unroll_words); // Length after all iterations + sub(loop_buf_end, loop_buf_end, len); bind(L_unroll_loop); for (int i = 0; i < unroll; i++) { ld(tmp1, Address(buf, i*wordSize)); @@ -1500,44 +1608,52 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, } addi(buf, buf, unroll_words); - ble(buf, buf_end, L_unroll_loop); - addiw(len, len, unroll_words-4); - bge(len, zr, L_by4_loop); - addiw(len, len, 4); - bgt(len, zr, L_by1_loop); - j(L_exit); - + blt(buf, loop_buf_end, L_unroll_loop); + + bind(L_by4_loop_entry); + mv(tmp1, 4); + blt(len, tmp1, L_by1_loop); + add(loop_buf_end, buf, len); // loop_buf_end will be used as endpoint for loop below + andi(len, len, 3); + sub(loop_buf_end, loop_buf_end, len); bind(L_by4_loop); lwu(tmp1, Address(buf)); update_word_crc32(crc, tmp1, tmp2, tmp4, tmp6, table0, table1, table2, table3, false); - subw(len, len, 4); addi(buf, buf, 4); - bge(len, zr, L_by4_loop); - addiw(len, len, 4); - ble(len, zr, L_exit); + blt(buf, loop_buf_end, L_by4_loop); bind(L_by1_loop); + beqz(len, L_exit); + subw(len, len, 1); lwu(tmp1, Address(buf)); andi(tmp2, tmp1, right_8_bits); update_byte_crc32(crc, tmp2, table0); - ble(len, zr, L_exit); + beqz(len, L_exit); subw(len, len, 1); srli(tmp2, tmp1, 8); andi(tmp2, tmp2, right_8_bits); update_byte_crc32(crc, tmp2, table0); - ble(len, zr, L_exit); + beqz(len, L_exit); subw(len, len, 1); srli(tmp2, tmp1, 16); andi(tmp2, tmp2, right_8_bits); update_byte_crc32(crc, tmp2, table0); - ble(len, zr, L_exit); - srli(tmp2, tmp1, 24); - andi(tmp2, tmp2, right_8_bits); - update_byte_crc32(crc, tmp2, table0); +#ifdef COMPILER2 + // put vector code here, otherwise "offset is too large" error occurs. + if (UseRVV) { + // only need to jump exit when UseRVV == true, it's a jump from end of block `L_by1_loop`. + j(L_exit); + + bind(L_vector_entry); + vector_update_crc32(crc, buf, len, tmp1, tmp2, tmp3, tmp4, tmp6, table0, table3); + + bgtz(len, L_by4_loop_entry); + } +#endif // COMPILER2 bind(L_exit); andn(crc, tmp5, crc); @@ -1853,9 +1969,9 @@ int MacroAssembler::patch_oop(address insn_addr, address o) { void MacroAssembler::reinit_heapbase() { if (UseCompressedOops) { if (Universe::is_fully_initialized()) { - mv(xheapbase, CompressedOops::ptrs_base()); + mv(xheapbase, CompressedOops::base()); } else { - ExternalAddress target(CompressedOops::ptrs_base_addr()); + ExternalAddress target(CompressedOops::base_addr()); relocate(target.rspec(), [&] { int32_t offset; la(xheapbase, target.target(), offset); @@ -1968,23 +2084,11 @@ void MacroAssembler::addw(Register Rd, Register Rn, int32_t increment, Register } void MacroAssembler::sub(Register Rd, Register Rn, int64_t decrement, Register temp) { - if (is_simm12(-decrement)) { - addi(Rd, Rn, -decrement); - } else { - assert_different_registers(Rn, temp); - li(temp, decrement); - sub(Rd, Rn, temp); - } + add(Rd, Rn, -decrement, temp); } void MacroAssembler::subw(Register Rd, Register Rn, int32_t decrement, Register temp) { - if (is_simm12(-decrement)) { - addiw(Rd, Rn, -decrement); - } else { - assert_different_registers(Rn, temp); - li(temp, decrement); - subw(Rd, Rn, temp); - } + addw(Rd, Rn, -decrement, temp); } void MacroAssembler::andrw(Register Rd, Register Rs1, Register Rs2) { @@ -2844,7 +2948,7 @@ int MacroAssembler::corrected_idivq(Register result, Register rs1, Register rs2, return idivq_offset; } -// Look up the method for a megamorpic invkkeinterface call. +// Look up the method for a megamorphic invokeinterface call. // 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. @@ -2859,9 +2963,9 @@ void MacroAssembler::lookup_interface_method(Register recv_klass, assert_different_registers(recv_klass, intf_klass, scan_tmp); assert_different_registers(method_result, intf_klass, scan_tmp); assert(recv_klass != method_result || !return_method, - "recv_klass can be destroyed when mehtid isn't needed"); + "recv_klass can be destroyed when method isn't needed"); assert(itable_index.is_constant() || itable_index.as_register() == method_result, - "caller must be same register for non-constant itable index as for method"); + "caller must use same register for non-constant itable index as for method"); // Compute start of first itableOffsetEntry (which is at the end of the vtable). int vtable_base = in_bytes(Klass::vtable_start_offset()); @@ -3054,6 +3158,13 @@ void MacroAssembler::membar(uint32_t order_constraint) { } } +void MacroAssembler::cmodx_fence() { + BLOCK_COMMENT("cmodx fence"); + if (VM_Version::supports_fencei_barrier()) { + Assembler::fencei(); + } +} + // Form an address from base + offset in Rd. Rd my or may not // actually be used: you must use the Address that is returned. It // is up to you to ensure that the shift provided matches the size diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index c3161beea117d..fd174f241eb0b 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -431,6 +431,8 @@ class MacroAssembler: public Assembler { } } + void cmodx_fence(); + void pause() { Assembler::fence(w, 0); } @@ -1321,6 +1323,10 @@ class MacroAssembler: public Assembler { void update_byte_crc32(Register crc, Register val, Register table); #ifdef COMPILER2 + void vector_update_crc32(Register crc, Register buf, Register len, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, + Register table0, Register table3); + void mul_add(Register out, Register in, Register offset, Register len, Register k, Register tmp); void wide_mul(Register prod_lo, Register prod_hi, Register n, Register m); @@ -1350,7 +1356,7 @@ class MacroAssembler: public Assembler { Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, Register tmp6, Register product_hi); -#endif +#endif // COMPILER2 void inflate_lo32(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); void inflate_hi32(Register Rd, Register Rs, Register tmp1 = t0, Register tmp2 = t1); diff --git a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp index deeb771d83bb8..f638db9f0bfe4 100644 --- a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp +++ b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -28,6 +28,7 @@ #include "asm/macroAssembler.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "memory/allocation.inline.hpp" @@ -37,7 +38,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/stubRoutines.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ @@ -444,7 +445,6 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, __ far_jump(RuntimeAddress(SharedRuntime::throw_IncompatibleClassChangeError_entry())); } } - } #ifndef PRODUCT diff --git a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp index d0903c96e2271..18b4302c7e68e 100644 --- a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp +++ b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp @@ -55,7 +55,21 @@ void Relocation::pd_set_data_value(address x, bool verify_only) { bytes = MacroAssembler::pd_patch_instruction_size(addr(), x); break; } - ICache::invalidate_range(addr(), bytes); + + // If we are using UseCtxFencei no ICache invalidation is needed here. + // Instead every hart will preform an fence.i either by a Java thread + // (due to patching epoch will take it to slow path), + // or by the kernel when a Java thread is moved to a hart. + // The instruction streams changes must only happen before the disarm of + // the nmethod barrier. Where the disarm have a leading full two way fence. + // If this is performed during a safepoint, all Java threads will emit a fence.i + // before transitioning to 'Java', e.g. leaving native or the safepoint wait barrier. + if (!UseCtxFencei) { + // ICache invalidation is a serialization point. + // The above patching of instructions happens before the invalidation. + // Hence it have a leading full two way fence (wr, wr). + ICache::invalidate_range(addr(), bytes); + } } address Relocation::pd_call_destination(address orig_addr) { diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 05f55fd0da7af..54d1f1c05736d 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1966,18 +1966,18 @@ const RegMask* Matcher::predicate_reg_mask(void) { return &_VMASK_REG_mask; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return new TypeVectMask(elemTy, length); -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { - return false; + return EnableVectorSupport && UseVectorStubs; } OptoRegPair Matcher::vector_return_value(uint ideal_reg) { - Unimplemented(); - return OptoRegPair(0, 0); + assert(EnableVectorSupport && UseVectorStubs, "sanity"); + assert(ideal_reg == Op_VecA, "sanity"); + // check more info at https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc + int lo = V8_num; + int hi = V8_K_num; + return OptoRegPair(hi, lo); } // Is this branch offset short enough that a short branch can be used? @@ -2224,7 +2224,8 @@ bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { assert_cond(m != nullptr); if (is_vshift_con_pattern(n, m) || // ShiftV src (ShiftCntV con) is_vector_bitwise_not_pattern(n, m) || - is_vector_scalar_bitwise_pattern(n, m)) { + is_vector_scalar_bitwise_pattern(n, m) || + is_encode_and_store_pattern(n, m)) { mstack.push(m, Visit); return true; } @@ -4785,6 +4786,7 @@ instruct loadP(iRegPNoSp dst, memory mem) // Load Compressed Pointer instruct loadN(iRegNNoSp dst, memory mem) %{ + predicate(n->as_Load()->barrier_data() == 0); match(Set dst (LoadN mem)); ins_cost(LOAD_COST); @@ -5033,41 +5035,6 @@ instruct loadConD0(fRegD dst, immD0 con) %{ ins_pipe(fp_load_constant_d); %} -// Store Instructions -// Store CMS card-mark Immediate -instruct storeimmCM0(immI0 zero, memory mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(STORE_COST); - format %{ "storestore (elided)\n\t" - "sb zr, $mem\t# byte, #@storeimmCM0" %} - - ins_encode %{ - __ sb(zr, Address(as_Register($mem$$base), $mem$$disp)); - %} - - ins_pipe(istore_mem); -%} - -// Store CMS card-mark Immediate with intervening StoreStore -// needed when using CMS with no conditional card marking -instruct storeimmCM0_ordered(immI0 zero, memory mem) -%{ - match(Set mem (StoreCM mem zero)); - - ins_cost(ALU_COST + STORE_COST); - format %{ "membar(StoreStore)\n\t" - "sb zr, $mem\t# byte, #@storeimmCM0_ordered" %} - - ins_encode %{ - __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - __ sb(zr, Address(as_Register($mem$$base), $mem$$disp)); - %} - - ins_pipe(istore_mem); -%} - // Store Byte instruct storeB(iRegIorL2I src, memory mem) %{ @@ -5220,6 +5187,7 @@ instruct storeimmP0(immP0 zero, memory mem) // Store Compressed Pointer instruct storeN(iRegN src, memory mem) %{ + predicate(n->as_Store()->barrier_data() == 0); match(Set mem (StoreN mem src)); ins_cost(STORE_COST); @@ -5234,6 +5202,7 @@ instruct storeN(iRegN src, memory mem) instruct storeImmN0(immN0 zero, memory mem) %{ + predicate(n->as_Store()->barrier_data() == 0); match(Set mem (StoreN mem zero)); ins_cost(STORE_COST); @@ -5424,6 +5393,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapN mem (Binary oldval newval))); ins_cost(LOAD_COST + STORE_COST + ALU_COST * 8 + BRANCH_COST * 4); @@ -5545,7 +5515,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) %{ - predicate(needs_acquiring_load_reserved(n)); + predicate(needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapN mem (Binary oldval newval))); @@ -5653,6 +5623,7 @@ instruct compareAndExchangeL(iRegLNoSp res, indirect mem, iRegL oldval, iRegL ne instruct compareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndExchangeN mem (Binary oldval newval))); ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 3); @@ -5786,7 +5757,7 @@ instruct compareAndExchangeLAcq(iRegLNoSp res, indirect mem, iRegL oldval, iRegL instruct compareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval) %{ - predicate(needs_acquiring_load_reserved(n)); + predicate(needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndExchangeN mem (Binary oldval newval))); @@ -5914,6 +5885,7 @@ instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL ne instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 4); @@ -6045,7 +6017,7 @@ instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) %{ - predicate(needs_acquiring_load_reserved(n)); + predicate(needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == 0); match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); @@ -6117,6 +6089,8 @@ instruct get_and_setL(indirect mem, iRegL newv, iRegLNoSp prev) instruct get_and_setN(indirect mem, iRegN newv, iRegINoSp prev) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); + match(Set prev (GetAndSetN mem newv)); ins_cost(ALU_COST); @@ -6182,7 +6156,7 @@ instruct get_and_setLAcq(indirect mem, iRegL newv, iRegLNoSp prev) instruct get_and_setNAcq(indirect mem, iRegN newv, iRegINoSp prev) %{ - predicate(needs_acquiring_load_reserved(n)); + predicate(needs_acquiring_load_reserved(n) && n->as_LoadStore()->barrier_data() == 0); match(Set prev (GetAndSetN mem newv)); @@ -10066,6 +10040,23 @@ instruct CallLeafDirect(method meth, rFlagsReg cr) ins_pipe(pipe_class_call); %} +// Call Runtime Instruction without safepoint and with vector arguments + +instruct CallLeafDirectVector(method meth, rFlagsReg cr) +%{ + match(CallLeafVector); + + effect(USE meth, KILL cr); + + ins_cost(BRANCH_COST); + + format %{ "CALL, runtime leaf vector $meth" %} + + ins_encode(riscv_enc_java_to_runtime(meth)); + + ins_pipe(pipe_class_call); +%} + // Call Runtime Instruction instruct CallLeafNoFPDirect(method meth, rFlagsReg cr) diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index 54947f6bf9a19..510c0ff5d4646 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -4895,11 +4895,10 @@ instruct gather_loadS(vReg dst, indirect mem, vReg idx) %{ effect(TEMP_DEF dst); format %{ "gather_loadS $dst, $mem, $idx" %} ins_encode %{ - __ vmv1r_v(as_VectorRegister($dst$$reg), as_VectorRegister($idx$$reg)); BasicType bt = Matcher::vector_element_basic_type(this); Assembler::SEW sew = Assembler::elemtype_to_sew(bt); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vsll_vi(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), (int)sew); + __ vsll_vi(as_VectorRegister($dst$$reg), as_VectorRegister($idx$$reg), (int)sew); __ vluxei32_v(as_VectorRegister($dst$$reg), as_Register($mem$$base), as_VectorRegister($dst$$reg)); %} @@ -4929,11 +4928,10 @@ instruct gather_loadS_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, v effect(TEMP_DEF dst, TEMP tmp); format %{ "gather_loadS_masked $dst, $mem, $idx, $v0\t# KILL $tmp" %} ins_encode %{ - __ vmv1r_v(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg)); BasicType bt = Matcher::vector_element_basic_type(this); Assembler::SEW sew = Assembler::elemtype_to_sew(bt); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew); + __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg), (int)sew); __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg)); __ vluxei32_v(as_VectorRegister($dst$$reg), as_Register($mem$$base), @@ -4969,11 +4967,10 @@ instruct scatter_storeS(indirect mem, vReg src, vReg idx, vReg tmp) %{ effect(TEMP tmp); format %{ "scatter_storeS $mem, $idx, $src\t# KILL $tmp" %} ins_encode %{ - __ vmv1r_v(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg)); BasicType bt = Matcher::vector_element_basic_type(this, $src); Assembler::SEW sew = Assembler::elemtype_to_sew(bt); __ vsetvli_helper(bt, Matcher::vector_length(this, $src)); - __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew); + __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg), (int)sew); __ vsuxei32_v(as_VectorRegister($src$$reg), as_Register($mem$$base), as_VectorRegister($tmp$$reg)); %} @@ -5003,11 +5000,10 @@ instruct scatter_storeS_masked(indirect mem, vReg src, vReg idx, vRegMask_V0 v0, effect(TEMP tmp); format %{ "scatter_storeS_masked $mem, $idx, $src, $v0\t# KILL $tmp" %} ins_encode %{ - __ vmv1r_v(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg)); BasicType bt = Matcher::vector_element_basic_type(this, $src); Assembler::SEW sew = Assembler::elemtype_to_sew(bt); __ vsetvli_helper(bt, Matcher::vector_length(this, $src)); - __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew); + __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg), (int)sew); __ vsuxei32_v(as_VectorRegister($src$$reg), as_Register($mem$$base), as_VectorRegister($tmp$$reg), Assembler::v0_t); %} diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 879fd92272279..2b629fcfcb293 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -666,7 +666,20 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm int SharedRuntime::vector_calling_convention(VMRegPair *regs, uint num_bits, uint total_args_passed) { - Unimplemented(); + assert(total_args_passed <= Argument::n_vector_register_parameters_c, "unsupported"); + assert(num_bits >= 64 && num_bits <= 2048 && is_power_of_2(num_bits), "unsupported"); + + // check more info at https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc + static const VectorRegister VEC_ArgReg[Argument::n_vector_register_parameters_c] = { + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23 + }; + + const int next_reg_val = 3; + for (uint i = 0; i < total_args_passed; i++) { + VMReg vmreg = VEC_ArgReg[i]->as_VMReg(); + regs[i].set_pair(vmreg->next(next_reg_val), vmreg); + } return 0; } @@ -2110,7 +2123,7 @@ void SharedRuntime::generate_deopt_blob() { int reexecute_offset = __ pc() - start; #if INCLUDE_JVMCI && !defined(COMPILER1) - if (EnableJVMCI && UseJVMCICompiler) { + if (UseJVMCICompiler) { // JVMCI does not use this kind of deoptimization __ should_not_reach_here(); } diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 8792dea7de5eb..bdb92e0b835f4 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -2428,6 +2428,14 @@ class StubGenerator: public StubCodeGenerator { __ la(t1, ExternalAddress(bs_asm->patching_epoch_addr())); __ lwu(t1, t1); __ sw(t1, thread_epoch_addr); + // There are two ways this can work: + // - The writer did system icache shootdown after the instruction stream update. + // Hence do nothing. + // - The writer trust us to make sure our icache is in sync before entering. + // Hence use cmodx fence (fence.i, may change). + if (UseCtxFencei) { + __ cmodx_fence(); + } __ membar(__ LoadLoad); } @@ -4474,7 +4482,7 @@ class StubGenerator: public StubCodeGenerator { RegSet reg_cache_saved_regs = RegSet::of(x24, x25, x26, x27); // s8, s9, s10, s11 RegSet reg_cache_regs; reg_cache_regs += reg_cache_saved_regs; - reg_cache_regs += RegSet::of(x28, x29, x30, x31); // t3, t4, t5, t6 + reg_cache_regs += RegSet::of(t3, t4, t5, t6); BufRegCache reg_cache(_masm, reg_cache_regs); RegSet saved_regs; @@ -5331,7 +5339,7 @@ class StubGenerator: public StubCodeGenerator { * NOTE: each field will occupy a single vector register group */ void base64_vector_decode_round(Register src, Register dst, Register codec, - Register size, Register stepSrc, Register stepDst, Register failedIdx, Register minusOne, + Register size, Register stepSrc, Register stepDst, Register failedIdx, VectorRegister inputV1, VectorRegister inputV2, VectorRegister inputV3, VectorRegister inputV4, VectorRegister idxV1, VectorRegister idxV2, VectorRegister idxV3, VectorRegister idxV4, VectorRegister outputV1, VectorRegister outputV2, VectorRegister outputV3, @@ -5358,8 +5366,11 @@ class StubGenerator: public StubCodeGenerator { __ vor_vv(outputV1, outputV1, outputV2); __ vmseq_vi(v0, outputV1, -1); __ vfirst_m(failedIdx, v0); - Label NoFailure; - __ beq(failedIdx, minusOne, NoFailure); + Label NoFailure, FailureAtIdx0; + // valid value can only be -1 when < 0 + __ bltz(failedIdx, NoFailure); + // when the first data (at index 0) fails, no need to process data anymore + __ beqz(failedIdx, FailureAtIdx0); __ vsetvli(x0, failedIdx, Assembler::e8, lmul, Assembler::mu, Assembler::tu); __ slli(stepDst, failedIdx, 1); __ add(stepDst, failedIdx, stepDst); @@ -5382,6 +5393,7 @@ class StubGenerator: public StubCodeGenerator { // dst = dst + register_group_len_bytes * 3 __ add(dst, dst, stepDst); + __ BIND(FailureAtIdx0); } /** @@ -5450,8 +5462,8 @@ class StubGenerator: public StubCodeGenerator { Register isMIME = c_rarg6; Register codec = c_rarg7; - Register dstBackup = x31; - Register length = x28; // t3, total length of src data in bytes + Register dstBackup = t6; + Register length = t3; // total length of src data in bytes Label ProcessData, Exit; Label ProcessScalar, ScalarLoop; @@ -5486,10 +5498,8 @@ class StubGenerator: public StubCodeGenerator { Register stepSrcM1 = send; Register stepSrcM2 = doff; Register stepDst = isURL; - Register size = x29; // t4 - Register minusOne = x30; // t5 + Register size = t4; - __ mv(minusOne, -1); __ mv(size, MaxVectorSize * 2); __ mv(stepSrcM1, MaxVectorSize * 4); __ slli(stepSrcM2, stepSrcM1, 1); @@ -5501,7 +5511,7 @@ class StubGenerator: public StubCodeGenerator { // Assembler::m2 __ BIND(ProcessM2); base64_vector_decode_round(src, dst, codec, - size, stepSrcM2, stepDst, failedIdx, minusOne, + size, stepSrcM2, stepDst, failedIdx, v2, v4, v6, v8, // inputs v10, v12, v14, v16, // indexes v18, v20, v22, // outputs @@ -5509,7 +5519,8 @@ class StubGenerator: public StubCodeGenerator { __ sub(length, length, stepSrcM2); // error check - __ bne(failedIdx, minusOne, Exit); + // valid value of failedIdx can only be -1 when < 0 + __ bgez(failedIdx, Exit); __ bge(length, stepSrcM2, ProcessM2); @@ -5521,7 +5532,7 @@ class StubGenerator: public StubCodeGenerator { __ srli(size, size, 1); __ srli(stepDst, stepDst, 1); base64_vector_decode_round(src, dst, codec, - size, stepSrcM1, stepDst, failedIdx, minusOne, + size, stepSrcM1, stepDst, failedIdx, v1, v2, v3, v4, // inputs v5, v6, v7, v8, // indexes v9, v10, v11, // outputs @@ -5529,7 +5540,8 @@ class StubGenerator: public StubCodeGenerator { __ sub(length, length, stepSrcM1); // error check - __ bne(failedIdx, minusOne, Exit); + // valid value of failedIdx can only be -1 when < 0 + __ bgez(failedIdx, Exit); __ BIND(ProcessScalar); __ beqz(length, Exit); @@ -5538,7 +5550,7 @@ class StubGenerator: public StubCodeGenerator { // scalar version { Register byte0 = soff, byte1 = send, byte2 = doff, byte3 = isURL; - Register combined32Bits = x29; // t5 + Register combined32Bits = t4; // encoded: [byte0[5:0] : byte1[5:0] : byte2[5:0]] : byte3[5:0]] => // plain: [byte0[5:0]+byte1[5:4] : byte1[3:0]+byte2[5:2] : byte2[1:0]+byte3[5:0]] @@ -5696,10 +5708,10 @@ class StubGenerator: public StubCodeGenerator { Register nmax = c_rarg4; Register base = c_rarg5; Register count = c_rarg6; - Register temp0 = x28; // t3 - Register temp1 = x29; // t4 - Register temp2 = x30; // t5 - Register temp3 = x31; // t6 + Register temp0 = t3; + Register temp1 = t4; + Register temp2 = t5; + Register temp3 = t6; VectorRegister vzero = v31; VectorRegister vbytes = v8; // group: v8, v9, v10, v11 @@ -6059,6 +6071,58 @@ static const int64_t right_3_bits = right_n_bits(3); return start; } + void generate_vector_math_stubs() { + if (!UseRVV) { + log_info(library)("vector is not supported, skip loading vector math (sleef) library!"); + return; + } + + // Get native vector math stub routine addresses + void* libsleef = nullptr; + char ebuf[1024]; + char dll_name[JVM_MAXPATHLEN]; + if (os::dll_locate_lib(dll_name, sizeof(dll_name), Arguments::get_dll_dir(), "sleef")) { + libsleef = os::dll_load(dll_name, ebuf, sizeof ebuf); + } + if (libsleef == nullptr) { + log_info(library)("Failed to load native vector math (sleef) library, %s!", ebuf); + return; + } + + // Method naming convention + // All the methods are named as _ + // + // Where: + // is the operation name, e.g. sin, cos + // is to indicate float/double + // "fx/dx" for vector float/double operation + // is the precision level + // "u10/u05" represents 1.0/0.5 ULP error bounds + // We use "u10" for all operations by default + // But for those functions do not have u10 support, we use "u05" instead + // rvv, indicates riscv vector extension + // + // e.g. sinfx_u10rvv is the method for computing vector float sin using rvv instructions + // + log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, JNI_LIB_PREFIX "sleef" JNI_LIB_SUFFIX, p2i(libsleef)); + + for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { + int vop = VectorSupport::VECTOR_OP_MATH_START + op; + if (vop == VectorSupport::VECTOR_OP_TANH) { // skip tanh because of performance regression + continue; + } + + // The native library does not support u10 level of "hypot". + const char* ulf = (vop == VectorSupport::VECTOR_OP_HYPOT) ? "u05" : "u10"; + + snprintf(ebuf, sizeof(ebuf), "%sfx_%srvv", VectorSupport::mathname[op], ulf); + StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); + + snprintf(ebuf, sizeof(ebuf), "%sdx_%srvv", VectorSupport::mathname[op], ulf); + StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); + } + } + #endif // COMPILER2 /** @@ -6080,26 +6144,17 @@ static const int64_t right_3_bits = right_n_bits(3); address start = __ pc(); + // input parameters const Register crc = c_rarg0; // crc const Register buf = c_rarg1; // source java byte array address const Register len = c_rarg2; // length - const Register table0 = c_rarg3; // crc_table address - const Register table1 = c_rarg4; - const Register table2 = c_rarg5; - const Register table3 = c_rarg6; - - const Register tmp1 = c_rarg7; - const Register tmp2 = t2; - const Register tmp3 = x28; // t3 - const Register tmp4 = x29; // t4 - const Register tmp5 = x30; // t5 - const Register tmp6 = x31; // t6 BLOCK_COMMENT("Entry:"); __ enter(); // required for proper stackwalking of RuntimeStub frame - __ kernel_crc32(crc, buf, len, table0, table1, table2, - table3, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6); + __ kernel_crc32(crc, buf, len, + c_rarg3, c_rarg4, c_rarg5, c_rarg6, // tmp's for tables + c_rarg7, t2, t3, t4, t5, t6); // misc tmps __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(); @@ -6121,6 +6176,29 @@ static const int64_t right_3_bits = right_n_bits(3); return start; } + // load Method* target of MethodHandle + // j_rarg0 = jobject receiver + // xmethod = Method* result + address generate_upcall_stub_load_target() { + + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, t0, t1); + // Load target method from receiver + __ load_heap_oop(xmethod, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), t0, t1); + __ load_heap_oop(xmethod, Address(xmethod, java_lang_invoke_LambdaForm::vmentry_offset()), t0, t1); + __ load_heap_oop(xmethod, Address(xmethod, java_lang_invoke_MemberName::method_offset()), t0, t1); + __ access_load_at(T_ADDRESS, IN_HEAP, xmethod, + Address(xmethod, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ sd(xmethod, Address(xthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ ret(); + + return start; + } + #undef __ // Initialization @@ -6186,6 +6264,7 @@ static const int64_t right_3_bits = right_n_bits(3); #endif // COMPILER2 StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); StubRoutines::riscv::set_completed(); } @@ -6264,6 +6343,8 @@ static const int64_t right_3_bits = right_n_bits(3); generate_string_indexof_stubs(); + generate_vector_math_stubs(); + #endif // COMPILER2 } diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp index 05bdeaf757078..6d5492b86b3c3 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp @@ -276,4 +276,219 @@ ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] = 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, 0xde0506f1UL, + + // Tables for vector version + // This improvement (vectorization) is based on java.base/share/native/libzip/zlib/zcrc32.c. + // To make it, following steps are taken: + // 1. in zcrc32.c, modify N to 16 and related code, + // 2. re-generate the tables needed, we use tables of (N == 16, W == 4) + // 3. finally vectorize the code (original implementation in zcrc32.c is just scalar code). + 0x00000000, 0x8f352d95, 0xc51b5d6b, 0x4a2e70fe, 0x5147bc97, + 0xde729102, 0x945ce1fc, 0x1b69cc69, 0xa28f792e, 0x2dba54bb, + 0x67942445, 0xe8a109d0, 0xf3c8c5b9, 0x7cfde82c, 0x36d398d2, + 0xb9e6b547, 0x9e6ff41d, 0x115ad988, 0x5b74a976, 0xd44184e3, + 0xcf28488a, 0x401d651f, 0x0a3315e1, 0x85063874, 0x3ce08d33, + 0xb3d5a0a6, 0xf9fbd058, 0x76cefdcd, 0x6da731a4, 0xe2921c31, + 0xa8bc6ccf, 0x2789415a, 0xe7aeee7b, 0x689bc3ee, 0x22b5b310, + 0xad809e85, 0xb6e952ec, 0x39dc7f79, 0x73f20f87, 0xfcc72212, + 0x45219755, 0xca14bac0, 0x803aca3e, 0x0f0fe7ab, 0x14662bc2, + 0x9b530657, 0xd17d76a9, 0x5e485b3c, 0x79c11a66, 0xf6f437f3, + 0xbcda470d, 0x33ef6a98, 0x2886a6f1, 0xa7b38b64, 0xed9dfb9a, + 0x62a8d60f, 0xdb4e6348, 0x547b4edd, 0x1e553e23, 0x916013b6, + 0x8a09dfdf, 0x053cf24a, 0x4f1282b4, 0xc027af21, 0x142cdab7, + 0x9b19f722, 0xd13787dc, 0x5e02aa49, 0x456b6620, 0xca5e4bb5, + 0x80703b4b, 0x0f4516de, 0xb6a3a399, 0x39968e0c, 0x73b8fef2, + 0xfc8dd367, 0xe7e41f0e, 0x68d1329b, 0x22ff4265, 0xadca6ff0, + 0x8a432eaa, 0x0576033f, 0x4f5873c1, 0xc06d5e54, 0xdb04923d, + 0x5431bfa8, 0x1e1fcf56, 0x912ae2c3, 0x28cc5784, 0xa7f97a11, + 0xedd70aef, 0x62e2277a, 0x798beb13, 0xf6bec686, 0xbc90b678, + 0x33a59bed, 0xf38234cc, 0x7cb71959, 0x369969a7, 0xb9ac4432, + 0xa2c5885b, 0x2df0a5ce, 0x67ded530, 0xe8ebf8a5, 0x510d4de2, + 0xde386077, 0x94161089, 0x1b233d1c, 0x004af175, 0x8f7fdce0, + 0xc551ac1e, 0x4a64818b, 0x6dedc0d1, 0xe2d8ed44, 0xa8f69dba, + 0x27c3b02f, 0x3caa7c46, 0xb39f51d3, 0xf9b1212d, 0x76840cb8, + 0xcf62b9ff, 0x4057946a, 0x0a79e494, 0x854cc901, 0x9e250568, + 0x111028fd, 0x5b3e5803, 0xd40b7596, 0x2859b56e, 0xa76c98fb, + 0xed42e805, 0x6277c590, 0x791e09f9, 0xf62b246c, 0xbc055492, + 0x33307907, 0x8ad6cc40, 0x05e3e1d5, 0x4fcd912b, 0xc0f8bcbe, + 0xdb9170d7, 0x54a45d42, 0x1e8a2dbc, 0x91bf0029, 0xb6364173, + 0x39036ce6, 0x732d1c18, 0xfc18318d, 0xe771fde4, 0x6844d071, + 0x226aa08f, 0xad5f8d1a, 0x14b9385d, 0x9b8c15c8, 0xd1a26536, + 0x5e9748a3, 0x45fe84ca, 0xcacba95f, 0x80e5d9a1, 0x0fd0f434, + 0xcff75b15, 0x40c27680, 0x0aec067e, 0x85d92beb, 0x9eb0e782, + 0x1185ca17, 0x5babbae9, 0xd49e977c, 0x6d78223b, 0xe24d0fae, + 0xa8637f50, 0x275652c5, 0x3c3f9eac, 0xb30ab339, 0xf924c3c7, + 0x7611ee52, 0x5198af08, 0xdead829d, 0x9483f263, 0x1bb6dff6, + 0x00df139f, 0x8fea3e0a, 0xc5c44ef4, 0x4af16361, 0xf317d626, + 0x7c22fbb3, 0x360c8b4d, 0xb939a6d8, 0xa2506ab1, 0x2d654724, + 0x674b37da, 0xe87e1a4f, 0x3c756fd9, 0xb340424c, 0xf96e32b2, + 0x765b1f27, 0x6d32d34e, 0xe207fedb, 0xa8298e25, 0x271ca3b0, + 0x9efa16f7, 0x11cf3b62, 0x5be14b9c, 0xd4d46609, 0xcfbdaa60, + 0x408887f5, 0x0aa6f70b, 0x8593da9e, 0xa21a9bc4, 0x2d2fb651, + 0x6701c6af, 0xe834eb3a, 0xf35d2753, 0x7c680ac6, 0x36467a38, + 0xb97357ad, 0x0095e2ea, 0x8fa0cf7f, 0xc58ebf81, 0x4abb9214, + 0x51d25e7d, 0xdee773e8, 0x94c90316, 0x1bfc2e83, 0xdbdb81a2, + 0x54eeac37, 0x1ec0dcc9, 0x91f5f15c, 0x8a9c3d35, 0x05a910a0, + 0x4f87605e, 0xc0b24dcb, 0x7954f88c, 0xf661d519, 0xbc4fa5e7, + 0x337a8872, 0x2813441b, 0xa726698e, 0xed081970, 0x623d34e5, + 0x45b475bf, 0xca81582a, 0x80af28d4, 0x0f9a0541, 0x14f3c928, + 0x9bc6e4bd, 0xd1e89443, 0x5eddb9d6, 0xe73b0c91, 0x680e2104, + 0x222051fa, 0xad157c6f, 0xb67cb006, 0x39499d93, 0x7367ed6d, + 0xfc52c0f8, + 0x00000000, 0x50b36adc, 0xa166d5b8, 0xf1d5bf64, 0x99bcad31, + 0xc90fc7ed, 0x38da7889, 0x68691255, 0xe8085c23, 0xb8bb36ff, + 0x496e899b, 0x19dde347, 0x71b4f112, 0x21079bce, 0xd0d224aa, + 0x80614e76, 0x0b61be07, 0x5bd2d4db, 0xaa076bbf, 0xfab40163, + 0x92dd1336, 0xc26e79ea, 0x33bbc68e, 0x6308ac52, 0xe369e224, + 0xb3da88f8, 0x420f379c, 0x12bc5d40, 0x7ad54f15, 0x2a6625c9, + 0xdbb39aad, 0x8b00f071, 0x16c37c0e, 0x467016d2, 0xb7a5a9b6, + 0xe716c36a, 0x8f7fd13f, 0xdfccbbe3, 0x2e190487, 0x7eaa6e5b, + 0xfecb202d, 0xae784af1, 0x5fadf595, 0x0f1e9f49, 0x67778d1c, + 0x37c4e7c0, 0xc61158a4, 0x96a23278, 0x1da2c209, 0x4d11a8d5, + 0xbcc417b1, 0xec777d6d, 0x841e6f38, 0xd4ad05e4, 0x2578ba80, + 0x75cbd05c, 0xf5aa9e2a, 0xa519f4f6, 0x54cc4b92, 0x047f214e, + 0x6c16331b, 0x3ca559c7, 0xcd70e6a3, 0x9dc38c7f, 0x2d86f81c, + 0x7d3592c0, 0x8ce02da4, 0xdc534778, 0xb43a552d, 0xe4893ff1, + 0x155c8095, 0x45efea49, 0xc58ea43f, 0x953dcee3, 0x64e87187, + 0x345b1b5b, 0x5c32090e, 0x0c8163d2, 0xfd54dcb6, 0xade7b66a, + 0x26e7461b, 0x76542cc7, 0x878193a3, 0xd732f97f, 0xbf5beb2a, + 0xefe881f6, 0x1e3d3e92, 0x4e8e544e, 0xceef1a38, 0x9e5c70e4, + 0x6f89cf80, 0x3f3aa55c, 0x5753b709, 0x07e0ddd5, 0xf63562b1, + 0xa686086d, 0x3b458412, 0x6bf6eece, 0x9a2351aa, 0xca903b76, + 0xa2f92923, 0xf24a43ff, 0x039ffc9b, 0x532c9647, 0xd34dd831, + 0x83feb2ed, 0x722b0d89, 0x22986755, 0x4af17500, 0x1a421fdc, + 0xeb97a0b8, 0xbb24ca64, 0x30243a15, 0x609750c9, 0x9142efad, + 0xc1f18571, 0xa9989724, 0xf92bfdf8, 0x08fe429c, 0x584d2840, + 0xd82c6636, 0x889f0cea, 0x794ab38e, 0x29f9d952, 0x4190cb07, + 0x1123a1db, 0xe0f61ebf, 0xb0457463, 0x5b0df038, 0x0bbe9ae4, + 0xfa6b2580, 0xaad84f5c, 0xc2b15d09, 0x920237d5, 0x63d788b1, + 0x3364e26d, 0xb305ac1b, 0xe3b6c6c7, 0x126379a3, 0x42d0137f, + 0x2ab9012a, 0x7a0a6bf6, 0x8bdfd492, 0xdb6cbe4e, 0x506c4e3f, + 0x00df24e3, 0xf10a9b87, 0xa1b9f15b, 0xc9d0e30e, 0x996389d2, + 0x68b636b6, 0x38055c6a, 0xb864121c, 0xe8d778c0, 0x1902c7a4, + 0x49b1ad78, 0x21d8bf2d, 0x716bd5f1, 0x80be6a95, 0xd00d0049, + 0x4dce8c36, 0x1d7de6ea, 0xeca8598e, 0xbc1b3352, 0xd4722107, + 0x84c14bdb, 0x7514f4bf, 0x25a79e63, 0xa5c6d015, 0xf575bac9, + 0x04a005ad, 0x54136f71, 0x3c7a7d24, 0x6cc917f8, 0x9d1ca89c, + 0xcdafc240, 0x46af3231, 0x161c58ed, 0xe7c9e789, 0xb77a8d55, + 0xdf139f00, 0x8fa0f5dc, 0x7e754ab8, 0x2ec62064, 0xaea76e12, + 0xfe1404ce, 0x0fc1bbaa, 0x5f72d176, 0x371bc323, 0x67a8a9ff, + 0x967d169b, 0xc6ce7c47, 0x768b0824, 0x263862f8, 0xd7eddd9c, + 0x875eb740, 0xef37a515, 0xbf84cfc9, 0x4e5170ad, 0x1ee21a71, + 0x9e835407, 0xce303edb, 0x3fe581bf, 0x6f56eb63, 0x073ff936, + 0x578c93ea, 0xa6592c8e, 0xf6ea4652, 0x7deab623, 0x2d59dcff, + 0xdc8c639b, 0x8c3f0947, 0xe4561b12, 0xb4e571ce, 0x4530ceaa, + 0x1583a476, 0x95e2ea00, 0xc55180dc, 0x34843fb8, 0x64375564, + 0x0c5e4731, 0x5ced2ded, 0xad389289, 0xfd8bf855, 0x6048742a, + 0x30fb1ef6, 0xc12ea192, 0x919dcb4e, 0xf9f4d91b, 0xa947b3c7, + 0x58920ca3, 0x0821667f, 0x88402809, 0xd8f342d5, 0x2926fdb1, + 0x7995976d, 0x11fc8538, 0x414fefe4, 0xb09a5080, 0xe0293a5c, + 0x6b29ca2d, 0x3b9aa0f1, 0xca4f1f95, 0x9afc7549, 0xf295671c, + 0xa2260dc0, 0x53f3b2a4, 0x0340d878, 0x8321960e, 0xd392fcd2, + 0x224743b6, 0x72f4296a, 0x1a9d3b3f, 0x4a2e51e3, 0xbbfbee87, + 0xeb48845b, + 0x00000000, 0xb61be070, 0xb746c6a1, 0x015d26d1, 0xb5fc8b03, + 0x03e76b73, 0x02ba4da2, 0xb4a1add2, 0xb0881047, 0x0693f037, + 0x07ced6e6, 0xb1d53696, 0x05749b44, 0xb36f7b34, 0xb2325de5, + 0x0429bd95, 0xba6126cf, 0x0c7ac6bf, 0x0d27e06e, 0xbb3c001e, + 0x0f9dadcc, 0xb9864dbc, 0xb8db6b6d, 0x0ec08b1d, 0x0ae93688, + 0xbcf2d6f8, 0xbdaff029, 0x0bb41059, 0xbf15bd8b, 0x090e5dfb, + 0x08537b2a, 0xbe489b5a, 0xafb34bdf, 0x19a8abaf, 0x18f58d7e, + 0xaeee6d0e, 0x1a4fc0dc, 0xac5420ac, 0xad09067d, 0x1b12e60d, + 0x1f3b5b98, 0xa920bbe8, 0xa87d9d39, 0x1e667d49, 0xaac7d09b, + 0x1cdc30eb, 0x1d81163a, 0xab9af64a, 0x15d26d10, 0xa3c98d60, + 0xa294abb1, 0x148f4bc1, 0xa02ee613, 0x16350663, 0x176820b2, + 0xa173c0c2, 0xa55a7d57, 0x13419d27, 0x121cbbf6, 0xa4075b86, + 0x10a6f654, 0xa6bd1624, 0xa7e030f5, 0x11fbd085, 0x841791ff, + 0x320c718f, 0x3351575e, 0x854ab72e, 0x31eb1afc, 0x87f0fa8c, + 0x86addc5d, 0x30b63c2d, 0x349f81b8, 0x828461c8, 0x83d94719, + 0x35c2a769, 0x81630abb, 0x3778eacb, 0x3625cc1a, 0x803e2c6a, + 0x3e76b730, 0x886d5740, 0x89307191, 0x3f2b91e1, 0x8b8a3c33, + 0x3d91dc43, 0x3cccfa92, 0x8ad71ae2, 0x8efea777, 0x38e54707, + 0x39b861d6, 0x8fa381a6, 0x3b022c74, 0x8d19cc04, 0x8c44ead5, + 0x3a5f0aa5, 0x2ba4da20, 0x9dbf3a50, 0x9ce21c81, 0x2af9fcf1, + 0x9e585123, 0x2843b153, 0x291e9782, 0x9f0577f2, 0x9b2cca67, + 0x2d372a17, 0x2c6a0cc6, 0x9a71ecb6, 0x2ed04164, 0x98cba114, + 0x999687c5, 0x2f8d67b5, 0x91c5fcef, 0x27de1c9f, 0x26833a4e, + 0x9098da3e, 0x243977ec, 0x9222979c, 0x937fb14d, 0x2564513d, + 0x214deca8, 0x97560cd8, 0x960b2a09, 0x2010ca79, 0x94b167ab, + 0x22aa87db, 0x23f7a10a, 0x95ec417a, 0xd35e25bf, 0x6545c5cf, + 0x6418e31e, 0xd203036e, 0x66a2aebc, 0xd0b94ecc, 0xd1e4681d, + 0x67ff886d, 0x63d635f8, 0xd5cdd588, 0xd490f359, 0x628b1329, + 0xd62abefb, 0x60315e8b, 0x616c785a, 0xd777982a, 0x693f0370, + 0xdf24e300, 0xde79c5d1, 0x686225a1, 0xdcc38873, 0x6ad86803, + 0x6b854ed2, 0xdd9eaea2, 0xd9b71337, 0x6facf347, 0x6ef1d596, + 0xd8ea35e6, 0x6c4b9834, 0xda507844, 0xdb0d5e95, 0x6d16bee5, + 0x7ced6e60, 0xcaf68e10, 0xcbaba8c1, 0x7db048b1, 0xc911e563, + 0x7f0a0513, 0x7e5723c2, 0xc84cc3b2, 0xcc657e27, 0x7a7e9e57, + 0x7b23b886, 0xcd3858f6, 0x7999f524, 0xcf821554, 0xcedf3385, + 0x78c4d3f5, 0xc68c48af, 0x7097a8df, 0x71ca8e0e, 0xc7d16e7e, + 0x7370c3ac, 0xc56b23dc, 0xc436050d, 0x722de57d, 0x760458e8, + 0xc01fb898, 0xc1429e49, 0x77597e39, 0xc3f8d3eb, 0x75e3339b, + 0x74be154a, 0xc2a5f53a, 0x5749b440, 0xe1525430, 0xe00f72e1, + 0x56149291, 0xe2b53f43, 0x54aedf33, 0x55f3f9e2, 0xe3e81992, + 0xe7c1a407, 0x51da4477, 0x508762a6, 0xe69c82d6, 0x523d2f04, + 0xe426cf74, 0xe57be9a5, 0x536009d5, 0xed28928f, 0x5b3372ff, + 0x5a6e542e, 0xec75b45e, 0x58d4198c, 0xeecff9fc, 0xef92df2d, + 0x59893f5d, 0x5da082c8, 0xebbb62b8, 0xeae64469, 0x5cfda419, + 0xe85c09cb, 0x5e47e9bb, 0x5f1acf6a, 0xe9012f1a, 0xf8faff9f, + 0x4ee11fef, 0x4fbc393e, 0xf9a7d94e, 0x4d06749c, 0xfb1d94ec, + 0xfa40b23d, 0x4c5b524d, 0x4872efd8, 0xfe690fa8, 0xff342979, + 0x492fc909, 0xfd8e64db, 0x4b9584ab, 0x4ac8a27a, 0xfcd3420a, + 0x429bd950, 0xf4803920, 0xf5dd1ff1, 0x43c6ff81, 0xf7675253, + 0x417cb223, 0x402194f2, 0xf63a7482, 0xf213c917, 0x44082967, + 0x45550fb6, 0xf34eefc6, 0x47ef4214, 0xf1f4a264, 0xf0a984b5, + 0x46b264c5, + 0x00000000, 0x7dcd4d3f, 0xfb9a9a7e, 0x8657d741, 0x2c4432bd, + 0x51897f82, 0xd7dea8c3, 0xaa13e5fc, 0x5888657a, 0x25452845, + 0xa312ff04, 0xdedfb23b, 0x74cc57c7, 0x09011af8, 0x8f56cdb9, + 0xf29b8086, 0xb110caf4, 0xccdd87cb, 0x4a8a508a, 0x37471db5, + 0x9d54f849, 0xe099b576, 0x66ce6237, 0x1b032f08, 0xe998af8e, + 0x9455e2b1, 0x120235f0, 0x6fcf78cf, 0xc5dc9d33, 0xb811d00c, + 0x3e46074d, 0x438b4a72, 0xb95093a9, 0xc49dde96, 0x42ca09d7, + 0x3f0744e8, 0x9514a114, 0xe8d9ec2b, 0x6e8e3b6a, 0x13437655, + 0xe1d8f6d3, 0x9c15bbec, 0x1a426cad, 0x678f2192, 0xcd9cc46e, + 0xb0518951, 0x36065e10, 0x4bcb132f, 0x0840595d, 0x758d1462, + 0xf3dac323, 0x8e178e1c, 0x24046be0, 0x59c926df, 0xdf9ef19e, + 0xa253bca1, 0x50c83c27, 0x2d057118, 0xab52a659, 0xd69feb66, + 0x7c8c0e9a, 0x014143a5, 0x871694e4, 0xfadbd9db, 0xa9d02113, + 0xd41d6c2c, 0x524abb6d, 0x2f87f652, 0x859413ae, 0xf8595e91, + 0x7e0e89d0, 0x03c3c4ef, 0xf1584469, 0x8c950956, 0x0ac2de17, + 0x770f9328, 0xdd1c76d4, 0xa0d13beb, 0x2686ecaa, 0x5b4ba195, + 0x18c0ebe7, 0x650da6d8, 0xe35a7199, 0x9e973ca6, 0x3484d95a, + 0x49499465, 0xcf1e4324, 0xb2d30e1b, 0x40488e9d, 0x3d85c3a2, + 0xbbd214e3, 0xc61f59dc, 0x6c0cbc20, 0x11c1f11f, 0x9796265e, + 0xea5b6b61, 0x1080b2ba, 0x6d4dff85, 0xeb1a28c4, 0x96d765fb, + 0x3cc48007, 0x4109cd38, 0xc75e1a79, 0xba935746, 0x4808d7c0, + 0x35c59aff, 0xb3924dbe, 0xce5f0081, 0x644ce57d, 0x1981a842, + 0x9fd67f03, 0xe21b323c, 0xa190784e, 0xdc5d3571, 0x5a0ae230, + 0x27c7af0f, 0x8dd44af3, 0xf01907cc, 0x764ed08d, 0x0b839db2, + 0xf9181d34, 0x84d5500b, 0x0282874a, 0x7f4fca75, 0xd55c2f89, + 0xa89162b6, 0x2ec6b5f7, 0x530bf8c8, 0x88d14467, 0xf51c0958, + 0x734bde19, 0x0e869326, 0xa49576da, 0xd9583be5, 0x5f0feca4, + 0x22c2a19b, 0xd059211d, 0xad946c22, 0x2bc3bb63, 0x560ef65c, + 0xfc1d13a0, 0x81d05e9f, 0x078789de, 0x7a4ac4e1, 0x39c18e93, + 0x440cc3ac, 0xc25b14ed, 0xbf9659d2, 0x1585bc2e, 0x6848f111, + 0xee1f2650, 0x93d26b6f, 0x6149ebe9, 0x1c84a6d6, 0x9ad37197, + 0xe71e3ca8, 0x4d0dd954, 0x30c0946b, 0xb697432a, 0xcb5a0e15, + 0x3181d7ce, 0x4c4c9af1, 0xca1b4db0, 0xb7d6008f, 0x1dc5e573, + 0x6008a84c, 0xe65f7f0d, 0x9b923232, 0x6909b2b4, 0x14c4ff8b, + 0x929328ca, 0xef5e65f5, 0x454d8009, 0x3880cd36, 0xbed71a77, + 0xc31a5748, 0x80911d3a, 0xfd5c5005, 0x7b0b8744, 0x06c6ca7b, + 0xacd52f87, 0xd11862b8, 0x574fb5f9, 0x2a82f8c6, 0xd8197840, + 0xa5d4357f, 0x2383e23e, 0x5e4eaf01, 0xf45d4afd, 0x899007c2, + 0x0fc7d083, 0x720a9dbc, 0x21016574, 0x5ccc284b, 0xda9bff0a, + 0xa756b235, 0x0d4557c9, 0x70881af6, 0xf6dfcdb7, 0x8b128088, + 0x7989000e, 0x04444d31, 0x82139a70, 0xffded74f, 0x55cd32b3, + 0x28007f8c, 0xae57a8cd, 0xd39ae5f2, 0x9011af80, 0xeddce2bf, + 0x6b8b35fe, 0x164678c1, 0xbc559d3d, 0xc198d002, 0x47cf0743, + 0x3a024a7c, 0xc899cafa, 0xb55487c5, 0x33035084, 0x4ece1dbb, + 0xe4ddf847, 0x9910b578, 0x1f476239, 0x628a2f06, 0x9851f6dd, + 0xe59cbbe2, 0x63cb6ca3, 0x1e06219c, 0xb415c460, 0xc9d8895f, + 0x4f8f5e1e, 0x32421321, 0xc0d993a7, 0xbd14de98, 0x3b4309d9, + 0x468e44e6, 0xec9da11a, 0x9150ec25, 0x17073b64, 0x6aca765b, + 0x29413c29, 0x548c7116, 0xd2dba657, 0xaf16eb68, 0x05050e94, + 0x78c843ab, 0xfe9f94ea, 0x8352d9d5, 0x71c95953, 0x0c04146c, + 0x8a53c32d, 0xf79e8e12, 0x5d8d6bee, 0x204026d1, 0xa617f190, + 0xdbdabcaf }; diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 1f32488777d57..7c811aa3a0c26 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -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) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -27,6 +27,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/bytecodeTracer.hpp" @@ -70,7 +71,7 @@ // Max size with JVMTI int TemplateInterpreter::InterpreterCodeSize = 256 * 1024; -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> //----------------------------------------------------------------------------- @@ -1748,13 +1749,21 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& vep) { assert(t != nullptr && t->is_valid() && t->tos_in() == vtos, "illegal template"); Label L; - aep = __ pc(); __ push_ptr(); __ j(L); - fep = __ pc(); __ push_f(); __ j(L); - dep = __ pc(); __ push_d(); __ j(L); - lep = __ pc(); __ push_l(); __ j(L); - bep = cep = sep = - iep = __ pc(); __ push_i(); - vep = __ pc(); + aep = __ pc(); // atos entry point + __ push_ptr(); + __ j(L); + fep = __ pc(); // ftos entry point + __ push_f(); + __ j(L); + dep = __ pc(); // dtos entry point + __ push_d(); + __ j(L); + lep = __ pc(); // ltos entry point + __ push_l(); + __ j(L); + bep = cep = sep = iep = __ pc(); // [bcsi]tos entry point + __ push_i(); + vep = __ pc(); // vtos entry point __ bind(L); generate_and_dispatch(t); } diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 078f54adc3682..2fede262057ce 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/tlab_globals.hpp" @@ -49,7 +50,7 @@ #include "runtime/synchronizer.hpp" #include "utilities/powerOfTwo.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // Address computation: local variables @@ -178,7 +179,6 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg, __ la(temp_reg, Address(temp_reg, in_bytes(ResolvedFieldEntry::put_code_offset()))); } // Load-acquire the bytecode to match store-release in ResolvedFieldEntry::fill_in() - __ membar(MacroAssembler::AnyAny); __ lbu(temp_reg, Address(temp_reg, 0)); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ mv(bc_reg, bc); @@ -320,7 +320,6 @@ void TemplateTable::ldc(LdcType type) { // get type __ addi(x13, x11, tags_offset); __ add(x13, x10, x13); - __ membar(MacroAssembler::AnyAny); __ lbu(x13, Address(x13, 0)); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); @@ -2189,7 +2188,6 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, break; } // Load-acquire the bytecode to match store-release in InterpreterRuntime - __ membar(MacroAssembler::AnyAny); __ lbu(temp, Address(temp, 0)); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); @@ -2241,7 +2239,6 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ la(temp, Address(Rcache, in_bytes(ResolvedFieldEntry::put_code_offset()))); } // Load-acquire the bytecode to match store-release in ResolvedFieldEntry::fill_in() - __ membar(MacroAssembler::AnyAny); __ lbu(temp, Address(temp, 0)); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ mv(t0, (int) code); // have we resolved this bytecode? @@ -2403,7 +2400,6 @@ void TemplateTable::load_invokedynamic_entry(Register method) { Label resolved; __ load_resolved_indy_entry(cache, index); - __ membar(MacroAssembler::AnyAny); __ ld(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); @@ -2418,7 +2414,6 @@ void TemplateTable::load_invokedynamic_entry(Register method) { __ call_VM(noreg, entry, method); // Update registers with resolved info __ load_resolved_indy_entry(cache, index); - __ membar(MacroAssembler::AnyAny); __ ld(method, Address(cache, in_bytes(ResolvedIndyEntry::method_offset()))); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); @@ -3533,7 +3528,6 @@ void TemplateTable::_new() { const int tags_offset = Array::base_offset_in_bytes(); __ add(t0, x10, x13); __ la(t0, Address(t0, tags_offset)); - __ membar(MacroAssembler::AnyAny); __ lbu(t0, t0); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ sub(t1, t0, (u1)JVM_CONSTANT_Class); @@ -3651,7 +3645,6 @@ void TemplateTable::checkcast() { // See if bytecode has already been quicked __ add(t0, x13, Array::base_offset_in_bytes()); __ add(x11, t0, x9); - __ membar(MacroAssembler::AnyAny); __ lbu(x11, x11); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ sub(t0, x11, (u1)JVM_CONSTANT_Class); @@ -3707,7 +3700,6 @@ void TemplateTable::instanceof() { // See if bytecode has already been quicked __ add(t0, x13, Array::base_offset_in_bytes()); __ add(x11, t0, x9); - __ membar(MacroAssembler::AnyAny); __ lbu(x11, x11); __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ sub(t0, x11, (u1)JVM_CONSTANT_Class); diff --git a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp index 383f332f8fd94..55160be99d0d8 100644 --- a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -117,7 +118,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -223,7 +224,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ la(c_rarg0, Address(sp, frame_data_offset)); - __ movptr(c_rarg1, (address) receiver); __ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry)); __ mv(xthread, x10); __ reinit_heapbase(); @@ -260,12 +260,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(shuffle_reg), abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, xthread); - __ block_comment("} receiver "); - - __ mov_metadata(xmethod, entry); - __ sd(xmethod, Address(xthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (address) receiver); + __ far_call(RuntimeAddress(StubRoutines::upcall_stub_load_target())); // loads Method* into xmethod + __ block_comment("} load target "); __ push_cont_fastpath(xthread); @@ -338,7 +336,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char *name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index bd4bfe86d9bf7..8fdde0094f40d 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -285,6 +285,7 @@ class VM_Version : public Abstract_VM_Version { // RISCV64 supports fast class initialization checks static bool supports_fast_class_init_checks() { return true; } + static bool supports_fencei_barrier() { return ext_Zifencei.enabled(); } }; #endif // CPU_RISCV_VM_VERSION_RISCV_HPP diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index d288f4a893d0a..8990cf1663dd5 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -2350,6 +2350,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { // Make sure klass is initialized & doesn't have finalizer. + // init_state needs acquire, but S390 is TSO, and so we are already good. const int state_offset = in_bytes(InstanceKlass::init_state_offset()); Register iklass = op->klass()->as_register(); add_debug_info_for_null_check_here(op->stub()->info()); diff --git a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp index f3fa19ddb31e0..f6dd20db3d67f 100644 --- a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp @@ -79,7 +79,7 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox assert(LockingMode != LM_MONITOR, "LM_MONITOR is already handled, by emit_lock()"); if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(Roop, Rmark, tmp, slow_case); + lightweight_lock(Rbox, Roop, Rmark, tmp, slow_case); } else if (LockingMode == LM_LEGACY) { NearLabel done; diff --git a/src/hotspot/cpu/s390/c2_MacroAssembler_s390.cpp b/src/hotspot/cpu/s390/c2_MacroAssembler_s390.cpp index 3641d82dabea9..025ef4c8915cd 100644 --- a/src/hotspot/cpu/s390/c2_MacroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c2_MacroAssembler_s390.cpp @@ -34,12 +34,12 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register temp1, Register temp2) { - compiler_fast_lock_lightweight_object(obj, temp1, temp2); + compiler_fast_lock_lightweight_object(obj, box, temp1, temp2); } void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Register temp1, Register temp2) { - compiler_fast_unlock_lightweight_object(obj, temp1, temp2); + compiler_fast_unlock_lightweight_object(obj, box, temp1, temp2); } //------------------------------------------------------ diff --git a/src/hotspot/cpu/s390/downcallLinker_s390.cpp b/src/hotspot/cpu/s390/downcallLinker_s390.cpp index 383a32448745c..85ddc5bf18548 100644 --- a/src/hotspot/cpu/s390/downcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/downcallLinker_s390.cpp @@ -36,8 +36,8 @@ #define __ _masm-> -static const int native_invoker_code_base_size = 512; -static const int native_invoker_size_per_args = 8; +static const int native_invoker_code_base_size = 384; +static const int native_invoker_size_per_args = 12; RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, int num_args, diff --git a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp index 37631298920ca..544c82d34a769 100644 --- a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2023 SAP SE. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -42,11 +42,47 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/g1/c1/g1BarrierSetC1.hpp" -#endif +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#endif // COMPILER2 #define __ masm-> -#define BLOCK_COMMENT(str) if (PrintAssembly) __ block_comment(str) +#define BLOCK_COMMENT(str) __ block_comment(str) + +static void generate_pre_barrier_fast_path(MacroAssembler* masm, + const Register thread, + const Register tmp1) { + Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); + // Is marking active? + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { + __ load_and_test_int(tmp1, in_progress); + } else { + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); + __ load_and_test_byte(tmp1, in_progress); + } +} + +static void generate_queue_test_and_insertion(MacroAssembler* masm, ByteSize index_offset, ByteSize buffer_offset, Label& runtime, + const Register Z_thread, const Register value, const Register temp) { + BLOCK_COMMENT("generate_queue_test_and_insertion {"); + + assert_different_registers(temp, value); + // Can we store a value in the given thread's buffer? + // (The index field is typed as size_t.) + + __ load_and_test_long(temp, Address(Z_thread, in_bytes(index_offset))); // temp := *(index address) + __ branch_optimized(Assembler::bcondEqual, runtime); // jump to runtime if index == 0 (full buffer) + + // The buffer is not full, store value into it. + __ add2reg(temp, -wordSize); // temp := next index + __ z_stg(temp, in_bytes(index_offset), Z_thread); // *(index address) := next index + + __ z_ag(temp, Address(Z_thread, in_bytes(buffer_offset))); // temp := buffer address + next index + __ z_stg(value, 0, temp); // *(buffer address + next index) := value + BLOCK_COMMENT("} generate_queue_test_and_insertion"); +} void G1BarrierSetAssembler::gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count) { @@ -59,13 +95,8 @@ void G1BarrierSetAssembler::gen_write_ref_array_pre_barrier(MacroAssembler* masm assert_different_registers(addr, Z_R0_scratch); // would be destroyed by push_frame() assert_different_registers(count, Z_R0_scratch); // would be destroyed by push_frame() Register Rtmp1 = Z_R0_scratch; - const int active_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()); - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { - __ load_and_test_int(Rtmp1, Address(Z_thread, active_offset)); - } else { - guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ load_and_test_byte(Rtmp1, Address(Z_thread, active_offset)); - } + + generate_pre_barrier_fast_path(masm, Z_thread, Rtmp1); __ z_bre(filtered); // Activity indicator is zero, so there is no marking going on currently. RegisterSaver::save_live_registers(masm, RegisterSaver::arg_registers); // Creates frame. @@ -100,6 +131,181 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas } } +#if defined(COMPILER2) + +#undef __ +#define __ masm-> + +static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStubC2* stub, const Register pre_val, const address runtime_path) { + BLOCK_COMMENT("generate_c2_barrier_runtime_call {"); + SaveLiveRegisters save_registers(masm, stub); + __ call_VM_leaf(runtime_path, pre_val, Z_thread); + BLOCK_COMMENT("} generate_c2_barrier_runtime_call"); +} + +void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + G1PreBarrierStubC2* stub) { + + BLOCK_COMMENT("g1_write_barrier_pre_c2 {"); + + assert(thread == Z_thread, "must be"); + assert_different_registers(obj, pre_val, tmp1); + assert(pre_val != noreg && tmp1 != noreg, "expecting a register"); + + stub->initialize_registers(obj, pre_val, thread, tmp1, noreg); + + generate_pre_barrier_fast_path(masm, thread, tmp1); + __ branch_optimized(Assembler::bcondNotEqual, *stub->entry()); // Activity indicator is zero, so there is no marking going on currently. + + __ bind(*stub->continuation()); + + BLOCK_COMMENT("} g1_write_barrier_pre_c2"); +} + +void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const { + + BLOCK_COMMENT("generate_c2_pre_barrier_stub {"); + + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + + Label runtime; + Register obj = stub->obj(); + Register pre_val = stub->pre_val(); + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); + + __ bind(*stub->entry()); + + BLOCK_COMMENT("generate_pre_val_not_null_test {"); + if (obj != noreg) { + __ load_heap_oop(pre_val, Address(obj), noreg, noreg, AS_RAW); + } + __ z_ltgr(pre_val, pre_val); + __ branch_optimized(Assembler::bcondEqual, *stub->continuation()); + BLOCK_COMMENT("} generate_pre_val_not_null_test"); + + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::satb_mark_queue_index_offset(), + G1ThreadLocalData::satb_mark_queue_buffer_offset(), + runtime, + Z_thread, pre_val, tmp1); + + __ branch_optimized(Assembler::bcondAlways, *stub->continuation()); + + __ bind(runtime); + + generate_c2_barrier_runtime_call(masm, stub, pre_val, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry)); + + __ branch_optimized(Assembler::bcondAlways, *stub->continuation()); + + BLOCK_COMMENT("} generate_c2_pre_barrier_stub"); +} + +void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* stub) { + BLOCK_COMMENT("g1_write_barrier_post_c2 {"); + + assert(thread == Z_thread, "must be"); + assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, Z_R1_scratch); + + assert(store_addr != noreg && new_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register"); + + stub->initialize_registers(thread, tmp1, tmp2); + + BLOCK_COMMENT("generate_region_crossing_test {"); + if (VM_Version::has_DistinctOpnds()) { + __ z_xgrk(tmp1, store_addr, new_val); + } else { + __ z_lgr(tmp1, store_addr); + __ z_xgr(tmp1, new_val); + } + __ z_srag(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); + __ branch_optimized(Assembler::bcondEqual, *stub->continuation()); + BLOCK_COMMENT("} generate_region_crossing_test"); + + // crosses regions, storing null? + if ((stub->barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ z_ltgr(new_val, new_val); + __ branch_optimized(Assembler::bcondEqual, *stub->continuation()); + } + + BLOCK_COMMENT("generate_card_young_test {"); + CardTableBarrierSet* ct = barrier_set_cast(BarrierSet::barrier_set()); + // calculate address of card + __ load_const_optimized(tmp2, (address)ct->card_table()->byte_map_base()); // Card table base. + __ z_srlg(tmp1, store_addr, CardTable::card_shift()); // Index into card table. + __ z_algr(tmp1, tmp2); // Explicit calculation needed for cli. + + // Filter young. + __ z_cli(0, tmp1, G1CardTable::g1_young_card_val()); + + BLOCK_COMMENT("} generate_card_young_test"); + + // From here on, tmp1 holds the card address. + __ branch_optimized(Assembler::bcondNotEqual, *stub->entry()); + + __ bind(*stub->continuation()); + + BLOCK_COMMENT("} g1_write_barrier_post_c2"); +} + +void G1BarrierSetAssembler::generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const { + + BLOCK_COMMENT("generate_c2_post_barrier_stub {"); + + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + + Register thread = stub->thread(); + Register tmp1 = stub->tmp1(); // tmp1 holds the card address. + Register tmp2 = stub->tmp2(); + Register Rcard_addr = tmp1; + + __ bind(*stub->entry()); + + BLOCK_COMMENT("generate_card_clean_test {"); + __ z_sync(); // Required to support concurrent cleaning. + __ z_cli(0, Rcard_addr, 0); // Reload after membar. + __ branch_optimized(Assembler::bcondEqual, *stub->continuation()); + BLOCK_COMMENT("} generate_card_clean_test"); + + BLOCK_COMMENT("generate_dirty_card {"); + // Storing a region crossing, non-null oop, card is clean. + // Dirty card and log. + STATIC_ASSERT(CardTable::dirty_card_val() == 0); + __ z_mvi(0, Rcard_addr, CardTable::dirty_card_val()); + BLOCK_COMMENT("} generate_dirty_card"); + + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + runtime, + Z_thread, tmp1, tmp2); + + __ branch_optimized(Assembler::bcondAlways, *stub->continuation()); + + __ bind(runtime); + + generate_c2_barrier_runtime_call(masm, stub, tmp1, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry)); + + __ branch_optimized(Assembler::bcondAlways, *stub->continuation()); + + BLOCK_COMMENT("} generate_c2_post_barrier_stub"); +} + +#endif //COMPILER2 + void G1BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, const Address& src, Register dst, Register tmp1, Register tmp2, Label *L_handle_null) { bool on_oop = is_reference_type(type); @@ -136,9 +342,6 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Decorator const Register Robj = obj ? obj->base() : noreg, Roff = obj ? obj->index() : noreg; - const int active_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()); - const int buffer_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset()); - const int index_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset()); assert_different_registers(Rtmp1, Rtmp2, Z_R0_scratch); // None of the Rtmp must be Z_R0!! assert_different_registers(Robj, Z_R0_scratch); // Used for addressing. Furthermore, push_frame destroys Z_R0!! assert_different_registers(Rval, Z_R0_scratch); // push_frame destroys Z_R0!! @@ -147,14 +350,7 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Decorator BLOCK_COMMENT("g1_write_barrier_pre {"); - // Is marking active? - // Note: value is loaded for test purposes only. No further use here. - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { - __ load_and_test_int(Rtmp1, Address(Z_thread, active_offset)); - } else { - guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ load_and_test_byte(Rtmp1, Address(Z_thread, active_offset)); - } + generate_pre_barrier_fast_path(masm, Z_thread, Rtmp1); __ z_bre(filtered); // Activity indicator is zero, so there is no marking going on currently. assert(Rpre_val != noreg, "must have a real register"); @@ -194,24 +390,14 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Decorator // We can store the original value in the thread's buffer // only if index > 0. Otherwise, we need runtime to handle. // (The index field is typed as size_t.) - Register Rbuffer = Rtmp1, Rindex = Rtmp2; - assert_different_registers(Rbuffer, Rindex, Rpre_val); - - __ z_lg(Rbuffer, buffer_offset, Z_thread); - __ load_and_test_long(Rindex, Address(Z_thread, index_offset)); - __ z_bre(callRuntime); // If index == 0, goto runtime. - - __ add2reg(Rindex, -wordSize); // Decrement index. - __ z_stg(Rindex, index_offset, Z_thread); - - // Record the previous value. - __ z_stg(Rpre_val, 0, Rbuffer, Rindex); + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::satb_mark_queue_index_offset(), + G1ThreadLocalData::satb_mark_queue_buffer_offset(), + callRuntime, + Z_thread, Rpre_val, Rtmp2); __ z_bru(filtered); // We are done. - Rbuffer = noreg; // end of life - Rindex = noreg; // end of life - __ bind(callRuntime); // Save some registers (inputs and result) over runtime call @@ -326,23 +512,16 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Decorato Register Rcard_addr_x = Rcard_addr; Register Rqueue_index = (Rtmp2 != Z_R0_scratch) ? Rtmp2 : Rtmp1; - Register Rqueue_buf = (Rtmp3 != Z_R0_scratch) ? Rtmp3 : Rtmp1; - const int qidx_off = in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset()); - const int qbuf_off = in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset()); - if ((Rcard_addr == Rqueue_buf) || (Rcard_addr == Rqueue_index)) { + if (Rcard_addr == Rqueue_index) { Rcard_addr_x = Z_R0_scratch; // Register shortage. We have to use Z_R0. } __ lgr_if_needed(Rcard_addr_x, Rcard_addr); - __ load_and_test_long(Rqueue_index, Address(Z_thread, qidx_off)); - __ z_bre(callRuntime); // Index == 0 then jump to runtime. - - __ z_lg(Rqueue_buf, qbuf_off, Z_thread); - - __ add2reg(Rqueue_index, -wordSize); // Decrement index. - __ z_stg(Rqueue_index, qidx_off, Z_thread); - - __ z_stg(Rcard_addr_x, 0, Rqueue_index, Rqueue_buf); // Store card. + generate_queue_test_and_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + callRuntime, + Z_thread, Rcard_addr_x, Rqueue_index); __ z_bru(filtered); __ bind(callRuntime); diff --git a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp index cc1d51d2fa13e..0f0bdd8b83cfd 100644 --- a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018 SAP SE. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -34,6 +34,8 @@ class LIR_Assembler; class StubAssembler; class G1PreBarrierStub; class G1PostBarrierStub; +class G1PreBarrierStubC2; +class G1PostBarrierStubC2; class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { protected: @@ -62,7 +64,27 @@ class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); void generate_c1_post_barrier_runtime_stub(StubAssembler* sasm); -#endif +#endif // COMPILER1 + +#ifdef COMPILER2 + void g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + G1PreBarrierStubC2* c2_stub); + void generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const; + void g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp1, + Register tmp2, + G1PostBarrierStubC2* c2_stub); + void generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const; +#endif // COMPILER2 virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, const Address& src, Register dst, Register tmp1, Register tmp2, Label *L_handle_null = nullptr); diff --git a/src/hotspot/cpu/s390/gc/g1/g1_s390.ad b/src/hotspot/cpu/s390/gc/g1/g1_s390.ad new file mode 100644 index 0000000000000..31f60c4aeff0b --- /dev/null +++ b/src/hotspot/cpu/s390/gc/g1/g1_s390.ad @@ -0,0 +1,457 @@ +// +// Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright 2024 IBM Corporation. All rights reserved. +// 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. +// + +source_hpp %{ + +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#include "gc/shared/gc_globals.hpp" + +%} + +source %{ + +#include "gc/g1/g1BarrierSetAssembler_s390.hpp" +#include "gc/g1/g1BarrierSetRuntime.hpp" + +static void write_barrier_pre(MacroAssembler* masm, + const MachNode* node, + Register obj, + Register pre_val, + Register tmp1, + RegSet preserve = RegSet(), + RegSet no_preserve = RegSet()) { + if (!G1PreBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PreBarrierStubC2* const stub = G1PreBarrierStubC2::create(node); + for (RegSetIterator reg = preserve.begin(); *reg != noreg; ++reg) { + stub->preserve(*reg); + } + for (RegSetIterator reg = no_preserve.begin(); *reg != noreg; ++reg) { + stub->dont_preserve(*reg); + } + g1_asm->g1_write_barrier_pre_c2(masm, obj, pre_val, Z_thread, tmp1, stub); +} + +static void write_barrier_post(MacroAssembler* masm, + const MachNode* node, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2) { + if (!G1PostBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PostBarrierStubC2* const stub = G1PostBarrierStubC2::create(node); + g1_asm->g1_write_barrier_post_c2(masm, store_addr, new_val, Z_thread, tmp1, tmp2, stub); +} + +%} // source + +// store pointer +instruct g1StoreP(indirect dst, memoryRegP src, iRegL tmp1, iRegL tmp2, flagsReg cr) %{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set dst (StoreP dst src)); + effect(TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(MEMORY_REF_COST); + format %{ "STG $src,$dst\t # ptr" %} + ins_encode %{ + __ block_comment("g1StoreP {"); + write_barrier_pre(masm, this, + $dst$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + RegSet::of($dst$$Register, $src$$Register) /* preserve */); + + __ z_stg($src$$Register, Address($dst$$Register)); + + write_barrier_post(masm, this, + $dst$$Register, /* store_addr */ + $src$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + __ block_comment("} g1StoreP"); + %} + ins_pipe(pipe_class_dummy); +%} + +// Store Compressed Pointer +instruct g1StoreN(indirect mem, iRegN_P2N src, iRegL tmp1, iRegL tmp2, iRegL tmp3, flagsReg cr) %{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(MEMORY_REF_COST); + format %{ "STY $src,$mem\t # (cOop)" %} + ins_encode %{ + __ block_comment("g1StoreN {"); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + + __ z_sty($src$$Register, Address($mem$$Register)); + + if ((barrier_data() & G1C2BarrierPost) != 0) { + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ oop_decoder($tmp1$$Register, $src$$Register, true /* maybe_null */); + } else { + __ oop_decoder($tmp1$$Register, $src$$Register, false /* maybe_null */); + } + } + + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + __ block_comment("} g1StoreN"); + %} + + ins_pipe(pipe_class_dummy); +%} + +instruct g1CompareAndSwapN(indirect mem_ptr, rarg5RegN oldval, iRegN_P2N newval, iRegI res, iRegL tmp1, iRegL tmp2, iRegL tmp3, flagsReg cr) %{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapN mem_ptr (Binary oldval newval))); + match(Set res (WeakCompareAndSwapN mem_ptr (Binary oldval newval))); + effect(USE mem_ptr, TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndSwapN $oldval,$newval,$mem_ptr" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem_ptr$$Register); + assert_different_registers($newval$$Register, $mem_ptr$$Register); + __ block_comment("g1compareAndSwapN {"); + + Register Rcomp = reg_to_register_object($oldval$$reg); + Register Rnew = reg_to_register_object($newval$$reg); + Register Raddr = reg_to_register_object($mem_ptr$$reg); + Register Rres = reg_to_register_object($res$$reg); + + write_barrier_pre(masm, this, + Raddr /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + RegSet::of(Raddr, Rcomp, Rnew) /* preserve */, + RegSet::of(Rres) /* no_preserve */); + + __ z_cs(Rcomp, Rnew, 0, Raddr); + + assert_different_registers(Rres, Raddr); + if (VM_Version::has_LoadStoreConditional()) { + __ load_const_optimized(Z_R0_scratch, 0L); // false (failed) + __ load_const_optimized(Rres, 1L); // true (succeed) + __ z_locgr(Rres, Z_R0_scratch, Assembler::bcondNotEqual); + } else { + Label done; + __ load_const_optimized(Rres, 0L); // false (failed) + __ z_brne(done); // Assume true to be the common case. + __ load_const_optimized(Rres, 1L); // true (succeed) + __ bind(done); + } + + __ oop_decoder($tmp3$$Register, Rnew, true /* maybe_null */); + + write_barrier_post(masm, this, + Raddr /* store_addr */, + $tmp3$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + __ block_comment("} g1compareAndSwapN"); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct g1CompareAndExchangeN(iRegP mem_ptr, rarg5RegN oldval, iRegN_P2N newval, iRegN res, iRegL tmp1, iRegL tmp2, iRegL tmp3, flagsReg cr) %{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeN mem_ptr (Binary oldval newval))); + effect(USE mem_ptr, TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndExchangeN $oldval,$newval,$mem_ptr" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem_ptr$$Register); + assert_different_registers($newval$$Register, $mem_ptr$$Register); + __ block_comment("g1CompareAndExchangeN {"); + write_barrier_pre(masm, this, + $mem_ptr$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + RegSet::of($mem_ptr$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + + Register Rcomp = reg_to_register_object($oldval$$reg); + Register Rnew = reg_to_register_object($newval$$reg); + Register Raddr = reg_to_register_object($mem_ptr$$reg); + + Register Rres = reg_to_register_object($res$$reg); + assert_different_registers(Rres, Raddr); + + __ z_lgr(Rres, Rcomp); // previous contents + __ z_csy(Rres, Rnew, 0, Raddr); // Try to store new value. + + __ oop_decoder($tmp1$$Register, Rnew, true /* maybe_null */); + + write_barrier_post(masm, this, + Raddr /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + __ block_comment("} g1CompareAndExchangeN"); + %} + ins_pipe(pipe_class_dummy); +%} + +// Load narrow oop +instruct g1LoadN(iRegN dst, indirect mem, iRegP tmp1, iRegP tmp2, flagsReg cr) %{ + predicate(UseG1GC && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadN mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(MEMORY_REF_COST); + format %{ "LoadN $dst,$mem\t # (cOop)" %} + ins_encode %{ + __ block_comment("g1LoadN {"); + __ z_llgf($dst$$Register, Address($mem$$Register)); + if ((barrier_data() & G1C2BarrierPre) != 0) { + __ oop_decoder($tmp1$$Register, $dst$$Register, true); + write_barrier_pre(masm, this, + noreg /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register ); + } + __ block_comment("} g1LoadN"); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct g1GetAndSetN(indirect mem, iRegN dst, iRegI tmp, iRegL tmp1, iRegL tmp2, iRegL tmp3, flagsReg cr) %{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set dst (GetAndSetN mem dst)); + effect(KILL cr, TEMP tmp, TEMP tmp1, TEMP tmp2, TEMP tmp3); // USE_DEF dst by match rule. + format %{ "XCHGN $dst,[$mem]\t # EXCHANGE (coop, atomic), temp $tmp" %} + ins_encode %{ + __ block_comment("g1GetAndSetN {"); + assert_different_registers($mem$$Register, $dst$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + RegSet::of($mem$$Register, $dst$$Register) /* preserve */); + + Register Rdst = reg_to_register_object($dst$$reg); + Register Rtmp = reg_to_register_object($tmp$$reg); + guarantee(Rdst != Rtmp, "Fix match rule to use TEMP_DEF"); + Label retry; + + // Iterate until swap succeeds. + __ z_llgf(Rtmp, Address($mem$$Register)); // current contents + __ bind(retry); + // Calculate incremented value. + __ z_csy(Rtmp, Rdst, Address($mem$$Register)); // Try to store new value. + __ z_brne(retry); // Yikes, concurrent update, need to retry. + + __ oop_decoder($tmp1$$Register, $dst$$Register, true /* maybe_null */); + + __ z_lgr(Rdst, Rtmp); // Exchanged value from memory is return value. + + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + + __ block_comment("} g1GetAndSetN"); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct g1CompareAndSwapP(iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval, iRegI res, iRegL tmp1, iRegL tmp2, flagsReg cr) %{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem_ptr (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, USE mem_ptr, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndSwapP $oldval,$newval,$mem_ptr" %} + ins_encode %{ + __ block_comment("g1CompareAndSwapP {"); + assert_different_registers($oldval$$Register, $mem_ptr$$Register); + assert_different_registers($newval$$Register, $mem_ptr$$Register); + + Register Rcomp = reg_to_register_object($oldval$$reg); + Register Rnew = reg_to_register_object($newval$$reg); + Register Raddr = reg_to_register_object($mem_ptr$$reg); + Register Rres = reg_to_register_object($res$$reg); + + write_barrier_pre(masm, this, + noreg /* obj */, + Rcomp /* pre_val */, + $tmp1$$Register /* tmp1 */, + RegSet::of(Raddr, Rcomp, Rnew) /* preserve */, + RegSet::of(Rres) /* no_preserve */); + + __ z_csg(Rcomp, Rnew, 0, Raddr); + + if (VM_Version::has_LoadStoreConditional()) { + __ load_const_optimized(Z_R0_scratch, 0L); // false (failed) + __ load_const_optimized(Rres, 1L); // true (succeed) + __ z_locgr(Rres, Z_R0_scratch, Assembler::bcondNotEqual); + } else { + Label done; + __ load_const_optimized(Rres, 0L); // false (failed) + __ z_brne(done); // Assume true to be the common case. + __ load_const_optimized(Rres, 1L); // true (succeed) + __ bind(done); + } + + write_barrier_post(masm, this, + Raddr /* store_addr */, + Rnew /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + __ block_comment("} g1CompareAndSwapP"); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct g1CompareAndExchangeP(iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval, iRegP res, iRegL tmp1, iRegL tmp2, flagsReg cr) %{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndExchangeP mem_ptr (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, USE mem_ptr, USE_KILL oldval, KILL cr); + format %{ "$res = CompareAndExchangeP $oldval,$newval,$mem_ptr" %} + ins_encode %{ + __ block_comment("g1CompareAndExchangeP {"); + assert_different_registers($oldval$$Register, $mem_ptr$$Register); + assert_different_registers($newval$$Register, $mem_ptr$$Register); + + // Pass $oldval to the pre-barrier (instead of loading from $mem), because + // $oldval is the only value that can be overwritten. + // The same holds for g1CompareAndSwapP. + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + RegSet::of($mem_ptr$$Register, $oldval$$Register, $newval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + + __ z_lgr($res$$Register, $oldval$$Register); // previous content + + __ z_csg($oldval$$Register, $newval$$Register, 0, $mem_ptr$$reg); + + write_barrier_post(masm, this, + $mem_ptr$$Register /* store_addr */, + $newval$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + __ block_comment("} g1CompareAndExchangeP"); + %} + ins_pipe(pipe_class_dummy); +%} + +// Load Pointer +instruct g1LoadP(iRegP dst, memory mem, iRegL tmp1, flagsReg cr) %{ + predicate(UseG1GC && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadP mem)); + effect(TEMP dst, TEMP tmp1, KILL cr); + ins_cost(MEMORY_REF_COST); + format %{ "LG $dst,$mem\t # ptr" %} + ins_encode %{ + __ block_comment("g1LoadP {"); + __ z_lg($dst$$Register, $mem$$Address); + write_barrier_pre(masm, this, + noreg /* obj */, + $dst$$Register /* pre_val */, + $tmp1$$Register ); + __ block_comment("} g1LoadP"); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct g1GetAndSetP(indirect mem, iRegP dst, iRegL tmp, iRegL tmp1, iRegL tmp2, flagsReg cr) %{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set dst (GetAndSetP mem dst)); + effect(KILL cr, TEMP tmp, TEMP tmp1, TEMP tmp2); // USE_DEF dst by match rule. + format %{ "XCHGP $dst,[$mem]\t # EXCHANGE (oop, atomic), temp $tmp" %} + ins_encode %{ + __ block_comment("g1GetAndSetP {"); + + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp$$Register /* pre_val (as a temporary register) */, + $tmp1$$Register /* tmp1 */, + RegSet::of($mem$$Register, $dst$$Register) /* preserve */); + + __ z_lgr($tmp1$$Register, $dst$$Register); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rtmp = reg_to_register_object($tmp$$reg); + guarantee(Rdst != Rtmp, "Fix match rule to use TEMP_DEF"); + Label retry; + + // Iterate until swap succeeds. + __ z_lg(Rtmp, Address($mem$$Register)); // current contents + __ bind(retry); + // Calculate incremented value. + __ z_csg(Rtmp, Rdst, Address($mem$$Register)); // Try to store new value. + __ z_brne(retry); // Yikes, concurrent update, need to retry. + __ z_lgr(Rdst, Rtmp); // Exchanged value from memory is return value. + + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp$$Register /* tmp2 */); + __ block_comment("} g1GetAndSetP"); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct g1EncodePAndStoreN(indirect mem, iRegP src, iRegL tmp1, iRegL tmp2, flagsReg cr) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem (EncodeP src))); + effect(TEMP tmp1, TEMP tmp2, KILL cr); + // ins_cost(INSN_COST); + format %{ "encode_heap_oop $tmp1, $src\n\t" + "st $tmp1, $mem\t# compressed ptr" %} + ins_encode %{ + __ block_comment("g1EncodePAndStoreN {"); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp1 */, + RegSet::of($mem$$Register, $src$$Register) /* preserve */); + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ oop_encoder($tmp1$$Register, $src$$Register, true /* maybe_null */); + } else { + __ oop_encoder($tmp1$$Register, $src$$Register, false /* maybe_null */); + } + __ z_st($tmp1$$Register, Address($mem$$Register)); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp1$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + __ block_comment("} g1EncodePAndStoreN"); + %} + ins_pipe(pipe_class_dummy); +%} diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp index d3457916bc9d5..d826b4a06f336 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp @@ -33,6 +33,9 @@ #include "runtime/jniHandles.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/macros.hpp" +#ifdef COMPILER2 +#include "gc/shared/c2/barrierSetC2.hpp" +#endif // COMPILER2 #define __ masm-> @@ -105,16 +108,60 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators } } +// Generic implementation. GCs can provide an optimized one. void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2) { - NearLabel Ldone; - __ z_ltgr(tmp1, value); - __ z_bre(Ldone); // Use null result as-is. - __ z_nill(value, ~JNIHandles::tag_mask); - __ z_lg(value, 0, value); // Resolve (untagged) jobject. + assert_different_registers(value, tmp1, tmp2); + NearLabel done, weak_tag, verify, tagged; + __ z_ltgr(value, value); + __ z_bre(done); // Use null result as-is. + + __ z_tmll(value, JNIHandles::tag_mask); + __ z_btrue(tagged); // not zero + + // Resolve Local handle + __ access_load_at(T_OBJECT, IN_NATIVE | AS_RAW, Address(value, 0), value, tmp1, tmp2); + __ z_bru(verify); + + __ bind(tagged); + __ testbit(value, exact_log2(JNIHandles::TypeTag::weak_global)); // test for weak tag + __ z_btrue(weak_tag); + + // resolve global handle + __ access_load_at(T_OBJECT, IN_NATIVE, Address(value, -JNIHandles::TypeTag::global), value, tmp1, tmp2); + __ z_bru(verify); + + __ bind(weak_tag); + // resolve jweak. + __ access_load_at(T_OBJECT, IN_NATIVE | ON_PHANTOM_OOP_REF, + Address(value, -JNIHandles::TypeTag::weak_global), value, tmp1, tmp2); + __ bind(verify); + __ verify_oop(value, FILE_AND_LINE); + __ bind(done); +} + +// Generic implementation. GCs can provide an optimized one. +void BarrierSetAssembler::resolve_global_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2) { + assert_different_registers(value, tmp1, tmp2); + NearLabel done; + + __ z_ltgr(value, value); + __ z_bre(done); // use null as-is. +#ifdef ASSERT + { + NearLabel valid_global_tag; + __ testbit(value, exact_log2(JNIHandles::TypeTag::global)); // test for global tag + __ z_btrue(valid_global_tag); + __ stop("non global jobject using resolve_global_jobject"); + __ bind(valid_global_tag); + } +#endif // ASSERT + + // Resolve global handle + __ access_load_at(T_OBJECT, IN_NATIVE, Address(value, -JNIHandles::TypeTag::global), value, tmp1, tmp2); __ verify_oop(value, FILE_AND_LINE); - __ bind(Ldone); + __ bind(done); } void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, @@ -150,8 +197,93 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) { #ifdef COMPILER2 -OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Name opto_reg) { - Unimplemented(); // This must be implemented to support late barrier expansion. +OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Name opto_reg) const { + if (!OptoReg::is_reg(opto_reg)) { + return OptoReg::Bad; + } + + VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if ((vm_reg->is_Register() || vm_reg ->is_FloatRegister()) && (opto_reg & 1) != 0) { + return OptoReg::Bad; + } + + return opto_reg; +} + +#undef __ +#define __ _masm-> + +SaveLiveRegisters::SaveLiveRegisters(MacroAssembler *masm, BarrierStubC2 *stub) + : _masm(masm), _reg_mask(stub->preserve_set()) { + + const int register_save_size = iterate_over_register_mask(ACTION_COUNT_ONLY) * BytesPerWord; + + _frame_size = align_up(register_save_size, frame::alignment_in_bytes) + frame::z_abi_160_size; // FIXME: this could be restricted to argument only + + __ save_return_pc(); + __ push_frame(_frame_size, Z_R14); // FIXME: check if Z_R1_scaratch can do a job here; + + __ z_lg(Z_R14, _z_common_abi(return_pc) + _frame_size, Z_SP); + + iterate_over_register_mask(ACTION_SAVE, _frame_size); +} + +SaveLiveRegisters::~SaveLiveRegisters() { + iterate_over_register_mask(ACTION_RESTORE, _frame_size); + + __ pop_frame(); + + __ restore_return_pc(); +} + +int SaveLiveRegisters::iterate_over_register_mask(IterationAction action, int offset) { + int reg_save_index = 0; + RegMaskIterator live_regs_iterator(_reg_mask); + + while(live_regs_iterator.has_next()) { + const OptoReg::Name opto_reg = live_regs_iterator.next(); + + // Filter out stack slots (spilled registers, i.e., stack-allocated registers). + if (!OptoReg::is_reg(opto_reg)) { + continue; + } + + const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); + if (vm_reg->is_Register()) { + Register std_reg = vm_reg->as_Register(); + + if (std_reg->encoding() >= Z_R2->encoding() && std_reg->encoding() <= Z_R15->encoding()) { + reg_save_index++; + + if (action == ACTION_SAVE) { + __ z_stg(std_reg, offset - reg_save_index * BytesPerWord, Z_SP); + } else if (action == ACTION_RESTORE) { + __ z_lg(std_reg, offset - reg_save_index * BytesPerWord, Z_SP); + } else { + assert(action == ACTION_COUNT_ONLY, "Sanity"); + } + } + } else if (vm_reg->is_FloatRegister()) { + FloatRegister fp_reg = vm_reg->as_FloatRegister(); + if (fp_reg->encoding() >= Z_F0->encoding() && fp_reg->encoding() <= Z_F15->encoding() + && fp_reg->encoding() != Z_F1->encoding()) { + reg_save_index++; + + if (action == ACTION_SAVE) { + __ z_std(fp_reg, offset - reg_save_index * BytesPerWord, Z_SP); + } else if (action == ACTION_RESTORE) { + __ z_ld(fp_reg, offset - reg_save_index * BytesPerWord, Z_SP); + } else { + assert(action == ACTION_COUNT_ONLY, "Sanity"); + } + } + } else if (false /* vm_reg->is_VectorRegister() */){ + fatal("Vector register support is not there yet!"); + } else { + fatal("Register type is not known"); + } + } + return reg_save_index; } #endif // COMPILER2 diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp index f83bbb864ea47..fb61adc55b500 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp @@ -32,7 +32,9 @@ #ifdef COMPILER2 #include "code/vmreg.hpp" #include "opto/optoreg.hpp" +#include "opto/regmask.hpp" +class BarrierStubC2; class Node; #endif // COMPILER2 @@ -51,6 +53,7 @@ class BarrierSetAssembler: public CHeapObj { const Address& addr, Register val, Register tmp1, Register tmp2, Register tmp3); virtual void resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2); + virtual void resolve_global_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2); virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); @@ -61,8 +64,42 @@ class BarrierSetAssembler: public CHeapObj { #ifdef COMPILER2 OptoReg::Name refine_register(const Node* node, - OptoReg::Name opto_reg); + OptoReg::Name opto_reg) const; #endif // COMPILER2 }; +#ifdef COMPILER2 + +// This class saves and restores the registers that need to be preserved across +// the runtime call represented by a given C2 barrier stub. Use as follows: +// { +// SaveLiveRegisters save(masm, stub); +// .. +// __ call_VM_leaf(...); +// .. +// } + +class SaveLiveRegisters { + MacroAssembler* _masm; + RegMask _reg_mask; + Register _result_reg; + int _frame_size; + + public: + SaveLiveRegisters(MacroAssembler *masm, BarrierStubC2 *stub); + + ~SaveLiveRegisters(); + + private: + enum IterationAction : int { + ACTION_SAVE, + ACTION_RESTORE, + ACTION_COUNT_ONLY + }; + + int iterate_over_register_mask(IterationAction action, int offset = 0); +}; + +#endif // COMPILER2 + #endif // CPU_S390_GC_SHARED_BARRIERSETASSEMBLER_S390_HPP diff --git a/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.cpp index fd21dd85e1195..f44a72c27abc1 100644 --- a/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2019 SAP SE. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "gc/shared/modRefBarrierSetAssembler.hpp" +#include "runtime/jniHandles.hpp" #define __ masm-> @@ -58,3 +59,16 @@ void ModRefBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet deco BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); } } + +void ModRefBarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2) { + NearLabel done; + + __ z_ltgr(value, value); + __ z_bre(done); // use null as-is. + + __ z_nill(value, ~JNIHandles::tag_mask); + __ z_lg(value, 0, value); // Resolve (untagged) jobject. + + __ verify_oop(value, FILE_AND_LINE); + __ bind(done); +} diff --git a/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.hpp index 865638477cd7a..7f53d033780c1 100644 --- a/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/shared/modRefBarrierSetAssembler_s390.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018 SAP SE. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -48,6 +48,8 @@ class ModRefBarrierSetAssembler: public BarrierSetAssembler { virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, const Address& dst, Register val, Register tmp1, Register tmp2, Register tmp3); + + virtual void resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2); }; #endif // CPU_S390_GC_SHARED_MODREFBARRIERSETASSEMBLER_S390_HPP diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index e56beaa9f569c..d00b6c3e2cc2e 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -1012,7 +1012,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { } if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(object, header, tmp, slow_case); + lightweight_lock(monitor, object, header, tmp, slow_case); } else if (LockingMode == LM_LEGACY) { // Load markWord from object into header. diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 50de705cd9f0c..6bfe5125959ad 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -2127,8 +2127,9 @@ unsigned int MacroAssembler::push_frame_abi160(unsigned int bytes) { // Pop current C frame. void MacroAssembler::pop_frame() { - BLOCK_COMMENT("pop_frame:"); + BLOCK_COMMENT("pop_frame {"); Assembler::z_lg(Z_SP, _z_abi(callers_sp), Z_SP); + BLOCK_COMMENT("} pop_frame"); } // Pop current C frame and restore return PC register (Z_R14). @@ -3458,7 +3459,8 @@ void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fa L_slow_path = &L_fallthrough; } - // Fast path check: class is fully initialized + // Fast path check: class is fully initialized. + // init_state needs acquire, but S390 is TSO, and so we are already good. z_cli(Address(klass, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); z_bre(*L_fast_path); @@ -3655,12 +3657,38 @@ void MacroAssembler::compiler_fast_unlock_object(Register oop, Register box, Reg bind(not_recursive); + NearLabel check_succ, set_eq_unlocked; + + // Set owner to null. + // Release to satisfy the JMM + z_release(); + z_lghi(temp, 0); + z_stg(temp, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), currentHeader); + // We need a full fence after clearing owner to avoid stranding. + z_fence(); + + // Check if the entry lists are empty. load_and_test_long(temp, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList))); - z_brne(done); + z_brne(check_succ); load_and_test_long(temp, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq))); - z_brne(done); - z_release(); - z_stg(temp/*=0*/, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), currentHeader); + z_bre(done); // If so we are done. + + bind(check_succ); + + // Check if there is a successor. + load_and_test_long(temp, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ))); + z_brne(set_eq_unlocked); // If so we are done. + + // Save the monitor pointer in the current thread, so we can try to + // reacquire the lock in SharedRuntime::monitor_exit_helper(). + z_xilf(currentHeader, markWord::monitor_value); + z_stg(currentHeader, Address(Z_thread, JavaThread::unlocked_inflated_monitor_offset())); + + z_ltgr(oop, oop); // Set flag = NE + z_bru(done); + + bind(set_eq_unlocked); + z_cr(temp, temp); // Set flag = EQ bind(done); @@ -3674,6 +3702,11 @@ void MacroAssembler::resolve_jobject(Register value, Register tmp1, Register tmp bs->resolve_jobject(this, value, tmp1, tmp2); } +void MacroAssembler::resolve_global_jobject(Register value, Register tmp1, Register tmp2) { + BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); + bs->resolve_global_jobject(this, value, tmp1, tmp2); +} + // Last_Java_sp must comply to the rules in frame_s390.hpp. void MacroAssembler::set_last_Java_frame(Register last_Java_sp, Register last_Java_pc, bool allow_relocation) { BLOCK_COMMENT("set_last_Java_frame {"); @@ -6002,10 +6035,10 @@ SkipIfEqual::~SkipIfEqual() { // - obj: the object to be locked, contents preserved. // - temp1, temp2: temporary registers, contents destroyed. // Note: make sure Z_R1 is not manipulated here when C2 compiler is in play -void MacroAssembler::lightweight_lock(Register obj, Register temp1, Register temp2, Label& slow) { +void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register temp1, Register temp2, Label& slow) { assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); - assert_different_registers(obj, temp1, temp2); + assert_different_registers(basic_lock, obj, temp1, temp2); Label push; const Register top = temp1; @@ -6017,6 +6050,11 @@ void MacroAssembler::lightweight_lock(Register obj, Register temp1, Register tem // instruction emitted as it is part of C1's null check semantics. z_lg(mark, Address(obj, mark_offset)); + if (UseObjectMonitorTable) { + // Clear cache in case fast locking succeeds. + const Address om_cache_addr = Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))); + z_mvghi(om_cache_addr, 0); + } // First we need to check if the lock-stack has room for pushing the object reference. z_lgf(top, Address(Z_thread, ls_top_offset)); @@ -6140,8 +6178,8 @@ void MacroAssembler::lightweight_unlock(Register obj, Register temp1, Register t bind(unlocked); } -void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Register tmp1, Register tmp2) { - assert_different_registers(obj, tmp1, tmp2); +void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Register box, Register tmp1, Register tmp2) { + assert_different_registers(obj, box, tmp1, tmp2); // Handle inflated monitor. NearLabel inflated; @@ -6150,6 +6188,11 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Registe // Finish fast lock unsuccessfully. MUST branch to with flag == EQ NearLabel slow_path; + if (UseObjectMonitorTable) { + // Clear cache in case fast locking succeeds. + z_mvghi(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0); + } + if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(tmp1, obj); z_tm(Address(tmp1, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); @@ -6214,33 +6257,77 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Registe { // Handle inflated monitor. bind(inflated); + const Register tmp1_monitor = tmp1; if (!UseObjectMonitorTable) { - // mark contains the tagged ObjectMonitor*. - const Register tagged_monitor = mark; - const Register zero = tmp2; - - // Try to CAS m->owner from null to current thread. - // If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ. - // Otherwise, register zero is filled with the current owner. - z_lghi(zero, 0); - z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), tagged_monitor); - z_bre(locked); - - // Check if recursive. - z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction - z_brne(slow_path); - - // Recursive - z_agsi(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 1ll); - z_cgr(zero, zero); - // z_bru(locked); - // Uncomment above line in the future, for now jump address is right next to us. + assert(tmp1_monitor == mark, "should be the same here"); } else { - // OMCache lookup not supported yet. Take the slowpath. - // Set flag to NE - z_ltgr(obj, obj); + NearLabel monitor_found; + + // load cache address + z_la(tmp1, Address(Z_thread, JavaThread::om_cache_oops_offset())); + + const int num_unrolled = 2; + for (int i = 0; i < num_unrolled; i++) { + z_cg(obj, Address(tmp1)); + z_bre(monitor_found); + add2reg(tmp1, in_bytes(OMCache::oop_to_oop_difference())); + } + + NearLabel loop; + // Search for obj in cache + + bind(loop); + + // check for match. + z_cg(obj, Address(tmp1)); + z_bre(monitor_found); + + // search until null encountered, guaranteed _null_sentinel at end. + add2reg(tmp1, in_bytes(OMCache::oop_to_oop_difference())); + z_cghsi(0, tmp1, 0); + z_brne(loop); // if not EQ to 0, go for another loop + + // we reached to the end, cache miss + z_ltgr(obj, obj); // set CC to NE z_bru(slow_path); + + // cache hit + bind(monitor_found); + z_lg(tmp1_monitor, Address(tmp1, OMCache::oop_to_monitor_difference())); } + NearLabel monitor_locked; + // lock the monitor + + // mark contains the tagged ObjectMonitor*. + const Register tagged_monitor = mark; + const Register zero = tmp2; + + const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); + const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset() - monitor_tag); + const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset() - monitor_tag); + + + // Try to CAS m->owner from null to current thread. + // If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ. + // Otherwise, register zero is filled with the current owner. + z_lghi(zero, 0); + z_csg(zero, Z_thread, owner_address); + z_bre(monitor_locked); + + // Check if recursive. + z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction + z_brne(slow_path); + + // Recursive + z_agsi(recursions_address, 1ll); + + bind(monitor_locked); + if (UseObjectMonitorTable) { + // Cache the monitor for unlock + z_stg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); + } + // set the CC now + z_cgr(obj, obj); } BLOCK_COMMENT("} handle_inflated_monitor_lightweight_locking"); @@ -6265,11 +6352,11 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Registe // C2 uses the value of flag (NE vs EQ) to determine the continuation. } -void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Register tmp1, Register tmp2) { - assert_different_registers(obj, tmp1, tmp2); +void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Register box, Register tmp1, Register tmp2) { + assert_different_registers(obj, box, tmp1, tmp2); // Handle inflated monitor. - NearLabel inflated, inflated_load_monitor; + NearLabel inflated, inflated_load_mark; // Finish fast unlock successfully. MUST reach to with flag == EQ. NearLabel unlocked; // Finish fast unlock unsuccessfully. MUST branch to with flag == NE. @@ -6289,7 +6376,7 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Regis z_aghi(top, -oopSize); z_cg(obj, Address(Z_thread, top)); - branch_optimized(bcondNotEqual, inflated_load_monitor); + branch_optimized(bcondNotEqual, inflated_load_mark); // Pop lock-stack. #ifdef ASSERT @@ -6310,6 +6397,9 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Regis // Not recursive // Check for monitor (0b10). + // Because we got here by popping (meaning we pushed in locked) + // there will be no monitor in the box. So we need to push back the obj + // so that the runtime can fix any potential anonymous owner. z_lg(mark, Address(obj, mark_offset)); z_tmll(mark, markWord::monitor_value); if (!UseObjectMonitorTable) { @@ -6348,7 +6438,7 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Regis { // Handle inflated monitor. - bind(inflated_load_monitor); + bind(inflated_load_mark); z_lg(mark, Address(obj, mark_offset)); @@ -6373,49 +6463,77 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Regis bind(check_done); #endif // ASSERT + const Register tmp1_monitor = tmp1; + if (!UseObjectMonitorTable) { - // mark contains the tagged ObjectMonitor*. - const Register monitor = mark; + assert(tmp1_monitor == mark, "should be the same here"); + } else { + // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack. + z_lg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); + // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*) + z_cghi(tmp1_monitor, alignof(ObjectMonitor*)); - NearLabel not_recursive; - const Register recursions = tmp2; + z_brl(slow_path); + } - // Check if recursive. - load_and_test_long(recursions, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - z_bre(not_recursive); // if 0 then jump, it's not recursive locking + // mark contains the tagged ObjectMonitor*. + const Register monitor = mark; - // Recursive unlock - z_agsi(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), -1ll); - z_cgr(monitor, monitor); // set the CC to EQUAL - z_bru(unlocked); + const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); + const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag}; + const Address cxq_address{monitor, ObjectMonitor::cxq_offset() - monitor_tag}; + const Address succ_address{monitor, ObjectMonitor::succ_offset() - monitor_tag}; + const Address EntryList_address{monitor, ObjectMonitor::EntryList_offset() - monitor_tag}; + const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag}; - bind(not_recursive); + NearLabel not_recursive; + const Register recursions = tmp2; - NearLabel not_ok; - // Check if the entry lists are empty. - load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList))); - z_brne(not_ok); - load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq))); - z_brne(not_ok); + // Check if recursive. + load_and_test_long(recursions, recursions_address); + z_bre(not_recursive); // if 0 then jump, it's not recursive locking - z_release(); - z_stg(tmp2 /*=0*/, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor); + // Recursive unlock + z_agsi(recursions_address, -1ll); + z_cgr(monitor, monitor); // set the CC to EQUAL + z_bru(unlocked); - z_bru(unlocked); // CC = EQ here + bind(not_recursive); - bind(not_ok); + NearLabel check_succ, set_eq_unlocked; - // The owner may be anonymous, and we removed the last obj entry in - // the lock-stack. This loses the information about the owner. - // Write the thread to the owner field so the runtime knows the owner. - z_stg(Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor); - z_bru(slow_path); // CC = NE here - } else { - // OMCache lookup not supported yet. Take the slowpath. - // Set flag to NE - z_ltgr(obj, obj); - z_bru(slow_path); + // Set owner to null. + // Release to satisfy the JMM + z_release(); + z_lghi(tmp2, 0); + z_stg(tmp2 /*=0*/, owner_address); + // We need a full fence after clearing owner to avoid stranding. + z_fence(); + + // Check if the entry lists are empty. + load_and_test_long(tmp2, EntryList_address); + z_brne(check_succ); + load_and_test_long(tmp2, cxq_address); + z_bre(unlocked); // If so we are done. + + bind(check_succ); + + // Check if there is a successor. + load_and_test_long(tmp2, succ_address); + z_brne(set_eq_unlocked); // If so we are done. + + // Save the monitor pointer in the current thread, so we can try to + // reacquire the lock in SharedRuntime::monitor_exit_helper(). + if (!UseObjectMonitorTable) { + z_xilf(monitor, markWord::monitor_value); } + z_stg(monitor, Address(Z_thread, JavaThread::unlocked_inflated_monitor_offset())); + + z_ltgr(obj, obj); // Set flag = NE + z_bru(slow_path); + + bind(set_eq_unlocked); + z_cr(tmp2, tmp2); // Set flag = EQ } bind(unlocked); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 90210eb28c3ad..5d3a4c2994091 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -752,12 +752,13 @@ class MacroAssembler: public Assembler { void compiler_fast_lock_object(Register oop, Register box, Register temp1, Register temp2); void compiler_fast_unlock_object(Register oop, Register box, Register temp1, Register temp2); - void lightweight_lock(Register obj, Register tmp1, Register tmp2, Label& slow); + void lightweight_lock(Register basic_lock, Register obj, Register tmp1, Register tmp2, Label& slow); void lightweight_unlock(Register obj, Register tmp1, Register tmp2, Label& slow); - void compiler_fast_lock_lightweight_object(Register obj, Register tmp1, Register tmp2); - void compiler_fast_unlock_lightweight_object(Register obj, Register tmp1, Register tmp2); + void compiler_fast_lock_lightweight_object(Register obj, Register box, Register tmp1, Register tmp2); + void compiler_fast_unlock_lightweight_object(Register obj, Register box, Register tmp1, Register tmp2); void resolve_jobject(Register value, Register tmp1, Register tmp2); + void resolve_global_jobject(Register value, Register tmp1, Register tmp2); // Support for last Java frame (but use call_VM instead where possible). private: @@ -819,7 +820,6 @@ class MacroAssembler: public Assembler { void compare_klass_ptr(Register Rop1, int64_t disp, Register Rbase, bool maybenull); // Access heap oop, handle encoding and GC barriers. - private: void access_store_at(BasicType type, DecoratorSet decorators, const Address& addr, Register val, Register tmp1, Register tmp2, Register tmp3); diff --git a/src/hotspot/cpu/s390/matcher_s390.hpp b/src/hotspot/cpu/s390/matcher_s390.hpp index 6c6cae3c58fc3..d8b1ae68f6f50 100644 --- a/src/hotspot/cpu/s390/matcher_s390.hpp +++ b/src/hotspot/cpu/s390/matcher_s390.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017, 2022 SAP SE. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,11 +63,16 @@ return true; } - // Suppress CMOVL. Conditional move available on z/Architecture only from z196 onwards. Not exploited yet. - static int long_cmove_cost() { return ConditionalMoveLimit; } + // Use conditional move (CMOVL) + static int long_cmove_cost() { + // z196/z11 or later hardware support conditional moves + return VM_Version::has_LoadStoreConditional() ? 0 : ConditionalMoveLimit; + } - // Suppress CMOVF. Conditional move available on z/Architecture only from z196 onwards. Not exploited yet. - static int float_cmove_cost() { return ConditionalMoveLimit; } + static int float_cmove_cost() { + // z196/z11 or later hardware support conditional moves + return VM_Version::has_LoadStoreConditional() ? 0 : ConditionalMoveLimit; + } // Set this as clone_shift_expressions. static bool narrow_oop_use_complex_address() { diff --git a/src/hotspot/cpu/s390/register_s390.hpp b/src/hotspot/cpu/s390/register_s390.hpp index 931e899257e92..18af232e56970 100644 --- a/src/hotspot/cpu/s390/register_s390.hpp +++ b/src/hotspot/cpu/s390/register_s390.hpp @@ -448,4 +448,12 @@ constexpr Register Z_R0_scratch = Z_R0; constexpr Register Z_R1_scratch = Z_R1; constexpr FloatRegister Z_fscratch_1 = Z_F1; +typedef AbstractRegSet RegSet; + +template <> +inline Register AbstractRegSet::first() { + if (_bitset == 0) { return noreg; } + return as_Register(count_trailing_zeros(_bitset)); +} + #endif // CPU_S390_REGISTER_S390_HPP diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 4de1a4e7b7f35..8b897033aa55d 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1477,10 +1477,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return nullptr; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return nullptr; -} - // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { return false; @@ -1644,6 +1640,10 @@ const RegMask Matcher::method_handle_invoke_SP_save_mask() { // Should the matcher clone input 'm' of node 'n'? bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { + if (is_encode_and_store_pattern(n, m)) { + mstack.push(m, Visit); + return true; + } return false; } @@ -3913,6 +3913,7 @@ instruct loadL_unaligned(iRegL dst, memory mem) %{ // Load Pointer instruct loadP(iRegP dst, memory mem) %{ match(Set dst (LoadP mem)); + predicate(n->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); format %{ "LG $dst,$mem\t # ptr" %} @@ -3924,6 +3925,7 @@ instruct loadP(iRegP dst, memory mem) %{ // LoadP + CastP2L instruct castP2X_loadP(iRegL dst, memory mem) %{ match(Set dst (CastP2X (LoadP mem))); + predicate(n->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); format %{ "LG $dst,$mem\t # ptr + p2x" %} @@ -4220,28 +4222,6 @@ instruct storeB(memory mem, iRegI src) %{ ins_pipe(pipe_class_dummy); %} -instruct storeCM(memory mem, immI_0 src) %{ - match(Set mem (StoreCM mem src)); - ins_cost(MEMORY_REF_COST); - // TODO: s390 port size(VARIABLE_SIZE); - format %{ "STC(Y) $src,$mem\t # CMS card-mark byte (must be 0!)" %} - ins_encode %{ - guarantee($mem$$index$$Register != Z_R0, "content will not be used."); - if ($mem$$index$$Register != noreg) { - // Can't use clear_mem --> load const zero and store character. - __ load_const_optimized(Z_R0_scratch, (long)0); - if (Immediate::is_uimm12($mem$$disp)) { - __ z_stc(Z_R0_scratch, $mem$$Address); - } else { - __ z_stcy(Z_R0_scratch, $mem$$Address); - } - } else { - __ clear_mem(Address($mem$$Address), 1); - } - %} - ins_pipe(pipe_class_dummy); -%} - // CHAR/SHORT // Store Char/Short @@ -4286,6 +4266,7 @@ instruct storeL(memory mem, iRegL src) %{ // Store Pointer instruct storeP(memory dst, memoryRegP src) %{ match(Set dst (StoreP dst src)); + predicate(n->as_Store()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); format %{ "STG $src,$dst\t # ptr" %} @@ -4388,6 +4369,7 @@ instruct memInitL(memoryRS mem, immL16 src) %{ // Move Immediate to 8-byte memory. instruct memInitP(memoryRS mem, immP16 src) %{ match(Set mem (StoreP mem src)); + predicate(n->as_Store()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(6); format %{ "MVGHI $mem,$src\t # direct mem init 8" %} @@ -4417,6 +4399,7 @@ instruct negL_reg_reg(iRegL dst, immL_0 zero, iRegL src, flagsReg cr) %{ // Load narrow oop instruct loadN(iRegN dst, memory mem) %{ match(Set dst (LoadN mem)); + predicate(n->as_Load()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); format %{ "LoadN $dst,$mem\t # (cOop)" %} @@ -4480,7 +4463,7 @@ instruct loadConNKlass(iRegN dst, immNKlass src) %{ instruct decodeLoadN(iRegP dst, memory mem) %{ match(Set dst (DecodeN (LoadN mem))); - predicate(false && (CompressedOops::base()==nullptr)&&(CompressedOops::shift()==0)); + predicate(false && (CompressedOops::base()==nullptr) && (CompressedOops::shift()==0)); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); format %{ "DecodeLoadN $dst,$mem\t # (cOop Load+Decode)" %} @@ -4628,7 +4611,7 @@ instruct encodeP(iRegN dst, iRegP src, flagsReg cr) %{ match(Set dst (EncodeP src)); effect(KILL cr); predicate((n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull) && - (CompressedOops::base() == 0 || + (CompressedOops::base() == nullptr || CompressedOops::base_disjoint() || !ExpandLoadingBaseEncode)); ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST); @@ -4651,7 +4634,7 @@ instruct encodeP_NN(iRegN dst, iRegP src, flagsReg cr) %{ match(Set dst (EncodeP src)); effect(KILL cr); predicate((n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull) && - (CompressedOops::base() == 0 || + (CompressedOops::base() == nullptr || CompressedOops::base_disjoint() || !ExpandLoadingBaseEncode_NN)); ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST); @@ -4735,6 +4718,7 @@ instruct encodeP_NN_Ex(iRegN dst, iRegP src, flagsReg cr) %{ // Store Compressed Pointer instruct storeN(memory mem, iRegN_P2N src) %{ match(Set mem (StoreN mem src)); + predicate(n->as_Store()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(Z_DISP_SIZE); format %{ "ST $src,$mem\t # (cOop)" %} @@ -5146,6 +5130,7 @@ instruct compareAndSwapL_bool(iRegP mem_ptr, rarg5RegL oldval, iRegL newval, iRe instruct compareAndSwapP_bool(iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval, iRegI res, flagsReg cr) %{ match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); effect(USE mem_ptr, USE_KILL oldval, KILL cr); size(18); format %{ "$res = CompareAndSwapP $oldval,$newval,$mem_ptr" %} @@ -5156,6 +5141,7 @@ instruct compareAndSwapP_bool(iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval, instruct compareAndSwapN_bool(iRegP mem_ptr, rarg5RegN oldval, iRegN_P2N newval, iRegI res, flagsReg cr) %{ match(Set res (CompareAndSwapN mem_ptr (Binary oldval newval))); + predicate(n->as_LoadStore()->barrier_data() == 0); effect(USE mem_ptr, USE_KILL oldval, KILL cr); size(16); format %{ "$res = CompareAndSwapN $oldval,$newval,$mem_ptr" %} @@ -5443,6 +5429,7 @@ instruct xchgL_reg_mem(memoryRSY mem, iRegL dst, iRegL tmp, flagsReg cr) %{ %} instruct xchgN_reg_mem(memoryRSY mem, iRegN dst, iRegI tmp, flagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set dst (GetAndSetN mem dst)); effect(KILL cr, TEMP tmp); // USE_DEF dst by match rule. format %{ "XCHGN $dst,[$mem]\t # EXCHANGE (coop, atomic), temp $tmp" %} @@ -5452,6 +5439,7 @@ instruct xchgN_reg_mem(memoryRSY mem, iRegN dst, iRegI tmp, flagsReg cr) %{ instruct xchgP_reg_mem(memoryRSY mem, iRegP dst, iRegL tmp, flagsReg cr) %{ match(Set dst (GetAndSetP mem dst)); + predicate(n->as_LoadStore()->barrier_data() == 0); effect(KILL cr, TEMP tmp); // USE_DEF dst by match rule. format %{ "XCHGP $dst,[$mem]\t # EXCHANGE (oop, atomic), temp $tmp" %} ins_encode(z_enc_SwapL(mem, dst, tmp)); @@ -5926,7 +5914,7 @@ instruct addP_regN_reg_imm20(iRegP dst, iRegP_N2P src1, iRegL src2, immL20 con) instruct addP_mem_imm(memoryRSY mem, immL8 src, flagsReg cr) %{ match(Set mem (StoreP mem (AddP (LoadP mem) src))); effect(KILL cr); - predicate(VM_Version::has_MemWithImmALUOps()); + predicate(VM_Version::has_MemWithImmALUOps() && n->as_LoadStore()->barrier_data() == 0); ins_cost(MEMORY_REF_COST); size(6); format %{ "AGSI $mem,$src\t # direct mem add 8 (ptr)" %} diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 9954c78ce1efa..468610b588e91 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -43,6 +43,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/timerTrace.hpp" #include "runtime/vframeArray.hpp" #include "utilities/align.hpp" #include "utilities/macros.hpp" @@ -1713,7 +1714,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Try fastpath for locking. if (LockingMode == LM_LIGHTWEIGHT) { // Fast_lock kills r_temp_1, r_temp_2. - __ compiler_fast_lock_lightweight_object(r_oop, r_tmp1, r_tmp2); + __ compiler_fast_lock_lightweight_object(r_oop, r_box, r_tmp1, r_tmp2); } else { // Fast_lock kills r_temp_1, r_temp_2. __ compiler_fast_lock_object(r_oop, r_box, r_tmp1, r_tmp2); @@ -1917,7 +1918,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Try fastpath for unlocking. if (LockingMode == LM_LIGHTWEIGHT) { // Fast_unlock kills r_tmp1, r_tmp2. - __ compiler_fast_unlock_lightweight_object(r_oop, r_tmp1, r_tmp2); + __ compiler_fast_unlock_lightweight_object(r_oop, r_box, r_tmp1, r_tmp2); } else { // Fast_unlock kills r_tmp1, r_tmp2. __ compiler_fast_unlock_object(r_oop, r_box, r_tmp1, r_tmp2); diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index d878731cca51f..dd9ed4c95462b 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -3053,6 +3053,29 @@ class StubGenerator: public StubCodeGenerator { return start; } + // load Method* target of MethodHandle + // Z_ARG1 = jobject receiver + // Z_method = Method* result + address generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(Z_ARG1, Z_tmp_1, Z_tmp_2); + // Load target method from receiver + __ load_heap_oop(Z_method, Address(Z_ARG1, java_lang_invoke_MethodHandle::form_offset()), + noreg, noreg, IS_NOT_NULL); + __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_LambdaForm::vmentry_offset()), + noreg, noreg, IS_NOT_NULL); + __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_MemberName::method_offset()), + noreg, noreg, IS_NOT_NULL); + __ z_lg(Z_method, Address(Z_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset())); + __ z_stg(Z_method, Address(Z_thread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ z_br(Z_R14); + + return start; + } + void generate_initial_stubs() { // Generates all stubs and initializes the entry points. @@ -3110,6 +3133,7 @@ class StubGenerator: public StubCodeGenerator { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index c16e444904563..2c2e8ed9e3b3a 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -1224,6 +1224,7 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M case Interpreter::java_lang_math_sin : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dsin); break; case Interpreter::java_lang_math_cos : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dcos); break; case Interpreter::java_lang_math_tan : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dtan); break; + case Interpreter::java_lang_math_tanh : /* run interpreted */ break; case Interpreter::java_lang_math_abs : /* run interpreted */ break; case Interpreter::java_lang_math_sqrt : /* runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dsqrt); not available */ break; case Interpreter::java_lang_math_log : runtime_entry = CAST_FROM_FN_PTR(address, SharedRuntime::dlog); break; diff --git a/src/hotspot/cpu/s390/upcallLinker_s390.cpp b/src/hotspot/cpu/s390/upcallLinker_s390.cpp index 734b4e89c7cb2..8baad40a519a4 100644 --- a/src/hotspot/cpu/s390/upcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/upcallLinker_s390.cpp @@ -23,6 +23,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -116,7 +117,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -206,7 +207,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("on_entry {"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ z_aghik(Z_ARG1, Z_SP, frame_data_offset); - __ load_const_optimized(Z_ARG2, (intptr_t)receiver); __ call(call_target_address); __ z_lgr(Z_thread, Z_RET); __ block_comment("} on_entry"); @@ -216,12 +216,11 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, shuffle_reg, abi._shadow_space_bytes, frame::z_jit_out_preserve_size); __ block_comment("} argument_shuffle"); - __ block_comment("receiver {"); - __ get_vm_result(Z_ARG1); - __ block_comment("} receiver"); - - __ load_const_optimized(Z_method, (intptr_t)entry); - __ z_stg(Z_method, Address(Z_thread, in_bytes(JavaThread::callee_target_offset()))); + __ block_comment("load_target {"); + __ load_const_optimized(Z_ARG1, (intptr_t)receiver); + __ load_const_optimized(call_target_address, StubRoutines::upcall_stub_load_target()); + __ call(call_target_address); // load taget Method* into Z_method + __ block_comment("} load_target"); __ z_lg(call_target_address, Address(Z_method, in_bytes(Method::from_compiled_offset()))); __ call(call_target_address); @@ -274,7 +273,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 352cfc0018848..c1679cd111f5a 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -1919,6 +1919,11 @@ void Assembler::cmpb(Address dst, int imm8) { emit_int8(imm8); } +void Assembler::cmpb(Register dst, int imm8) { + prefix(dst); + emit_arith_b(0x80, 0xF8, dst, imm8); +} + void Assembler::cmpl(Address dst, int32_t imm32) { InstructionMark im(this); prefix(dst); @@ -8048,6 +8053,14 @@ void Assembler::andpd(XMMRegister dst, XMMRegister src) { emit_int16(0x54, (0xC0 | encode)); } +void Assembler::andnpd(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_rex_vex_w_reverted(); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16(0x55, (0xC0 | encode)); +} + void Assembler::andps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ true); @@ -9659,6 +9672,15 @@ void Assembler::vinserti64x4(XMMRegister dst, XMMRegister nds, XMMRegister src, emit_int24(0x3A, (0xC0 | encode), imm8 & 0x01); } +void Assembler::evinserti64x2(XMMRegister dst, XMMRegister nds, XMMRegister src, uint8_t imm8, int vector_len) { + assert(VM_Version::supports_avx512dq(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x38, (0xC0 | encode), imm8 & 0x03); +} + // vinsertf forms @@ -11723,6 +11745,21 @@ void Assembler::vbroadcastf128(XMMRegister dst, Address src, int vector_len) { emit_operand(dst, src, 0); } +void Assembler::evbroadcastf64x2(XMMRegister dst, Address src, int vector_len) { + assert(VM_Version::supports_avx512dq(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + assert(dst != xnoreg, "sanity"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T2, /* input_size_in_bits */ EVEX_64bit); + attributes.set_is_evex_instruction(); + // swap src<->dst for encoding + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8(0x1A); + emit_operand(dst, src, 0); +} + + // gpr source broadcast forms // duplicate 1-byte integer data from src into programmed locations in dest : requires AVX512BW and AVX512VL diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 7f4790e05665e..eace7bb9cc169 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1239,6 +1239,7 @@ class Assembler : public AbstractAssembler { void cmpb(Address dst, int imm8); void cmpb(Address dst, Register reg); void cmpb(Register reg, Address dst); + void cmpb(Register reg, int imm8); void cmpl(Address dst, int32_t imm32); void cmpl(Register dst, int32_t imm32); @@ -2631,6 +2632,7 @@ class Assembler : public AbstractAssembler { // Bitwise Logical AND of Packed Floating-Point Values void andpd(XMMRegister dst, XMMRegister src); + void andnpd(XMMRegister dst, XMMRegister src); void andps(XMMRegister dst, XMMRegister src); void vandpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void vandps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); @@ -2985,6 +2987,7 @@ class Assembler : public AbstractAssembler { void vinserti32x4(XMMRegister dst, XMMRegister nds, XMMRegister src, uint8_t imm8); void vinserti32x4(XMMRegister dst, XMMRegister nds, Address src, uint8_t imm8); void vinserti64x4(XMMRegister dst, XMMRegister nds, XMMRegister src, uint8_t imm8); + void evinserti64x2(XMMRegister dst, XMMRegister nds, XMMRegister src, uint8_t imm8, int vector_len); // vinsertf forms void vinsertf128(XMMRegister dst, XMMRegister nds, XMMRegister src, uint8_t imm8); @@ -3034,6 +3037,7 @@ class Assembler : public AbstractAssembler { void vbroadcastsd(XMMRegister dst, XMMRegister src, int vector_len); void vbroadcastsd(XMMRegister dst, Address src, int vector_len); void vbroadcastf128(XMMRegister dst, Address src, int vector_len); + void evbroadcastf64x2(XMMRegister dst, Address src, int vector_len); // gpr sourced byte/word/dword/qword replicate void evpbroadcastb(XMMRegister dst, Register src, int vector_len); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index c3444d5a5abce..6d9812c11ae6e 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1578,6 +1578,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { add_debug_info_for_null_check_here(op->stub()->info()); + // init_state needs acquire, but x86 is TSO, and so we are already good. __ cmpb(Address(op->klass()->as_register(), InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index ff237d16d2216..36e2021138f2e 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -807,7 +807,11 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) { if (x->id() == vmIntrinsics::_dexp || x->id() == vmIntrinsics::_dlog || x->id() == vmIntrinsics::_dpow || x->id() == vmIntrinsics::_dcos || x->id() == vmIntrinsics::_dsin || x->id() == vmIntrinsics::_dtan || - x->id() == vmIntrinsics::_dlog10) { + x->id() == vmIntrinsics::_dlog10 +#ifdef _LP64 + || x->id() == vmIntrinsics::_dtanh +#endif + ) { do_LibmIntrinsic(x); return; } @@ -989,11 +993,17 @@ void LIRGenerator::do_LibmIntrinsic(Intrinsic* x) { break; case vmIntrinsics::_dtan: if (StubRoutines::dtan() != nullptr) { - __ call_runtime_leaf(StubRoutines::dtan(), getThreadTemp(), result_reg, cc->args()); + __ call_runtime_leaf(StubRoutines::dtan(), getThreadTemp(), result_reg, cc->args()); } else { __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtan), getThreadTemp(), result_reg, cc->args()); } break; + case vmIntrinsics::_dtanh: + assert(StubRoutines::dtanh() != nullptr, "tanh intrinsic not found"); + if (StubRoutines::dtanh() != nullptr) { + __ call_runtime_leaf(StubRoutines::dtanh(), getThreadTemp(), result_reg, cc->args()); + } + break; default: ShouldNotReachHere(); } #endif // _LP64 diff --git a/src/hotspot/cpu/x86/c2_CodeStubs_x86.cpp b/src/hotspot/cpu/x86/c2_CodeStubs_x86.cpp index 1990488d8a0df..44f897529e7ce 100644 --- a/src/hotspot/cpu/x86/c2_CodeStubs_x86.cpp +++ b/src/hotspot/cpu/x86/c2_CodeStubs_x86.cpp @@ -80,8 +80,6 @@ int C2FastUnlockLightweightStub::max_size() const { void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) { assert(_t == rax, "must be"); - Label restore_held_monitor_count_and_slow_path; - { // Restore lock-stack and handle the unlock in runtime. __ bind(_push_and_slow_path); @@ -91,61 +89,9 @@ void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) { __ movptr(Address(_thread, _t), _obj); #endif __ addl(Address(_thread, JavaThread::lock_stack_top_offset()), oopSize); - } - - { // Restore held monitor count and slow path. - - __ bind(restore_held_monitor_count_and_slow_path); - __ bind(_slow_path); - // Restore held monitor count. - __ increment(Address(_thread, JavaThread::held_monitor_count_offset())); - // increment will always result in ZF = 0 (no overflows). + // addl will always result in ZF = 0 (no overflows). __ jmp(slow_path_continuation()); } - - { // Handle monitor medium path. - - __ bind(_check_successor); - - Label fix_zf_and_unlocked; - const Register monitor = _mark; - -#ifndef _LP64 - __ jmpb(restore_held_monitor_count_and_slow_path); -#else // _LP64 - const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); - const Address succ_address(monitor, ObjectMonitor::succ_offset() - monitor_tag); - const Address owner_address(monitor, ObjectMonitor::owner_offset() - monitor_tag); - - // successor null check. - __ cmpptr(succ_address, NULL_WORD); - __ jccb(Assembler::equal, restore_held_monitor_count_and_slow_path); - - // Release lock. - __ movptr(owner_address, NULL_WORD); - - // Fence. - // Instead of MFENCE we use a dummy locked add of 0 to the top-of-stack. - __ lock(); __ addl(Address(rsp, 0), 0); - - // Recheck successor. - __ cmpptr(succ_address, NULL_WORD); - // Observed a successor after the release -> fence we have handed off the monitor - __ jccb(Assembler::notEqual, fix_zf_and_unlocked); - - // Try to relock, if it fails the monitor has been handed over - // TODO: Caveat, this may fail due to deflation, which does - // not handle the monitor handoff. Currently only works - // due to the responsible thread. - __ xorptr(rax, rax); - __ lock(); __ cmpxchgptr(_thread, owner_address); - __ jccb (Assembler::equal, restore_held_monitor_count_and_slow_path); -#endif - - __ bind(fix_zf_and_unlocked); - __ xorl(rax, rax); - __ jmp(unlocked_continuation()); - } } #undef __ diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index c2801a791cb5a..aba5344b7e434 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -459,87 +459,43 @@ void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register t // IA32's memory-model is SPO, so STs are ordered with respect to // each other and there's no need for an explicit barrier (fence). // See also http://gee.cs.oswego.edu/dl/jmm/cookbook.html. -#ifndef _LP64 - // Note that we could employ various encoding schemes to reduce - // the number of loads below (currently 4) to just 2 or 3. - // Refer to the comments in synchronizer.cpp. - // In practice the chain of fetches doesn't seem to impact performance, however. - xorptr(boxReg, boxReg); - orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - jccb (Assembler::notZero, DONE_LABEL); - movptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList))); - orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq))); - jccb (Assembler::notZero, DONE_LABEL); - movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD); - jmpb (DONE_LABEL); -#else // _LP64 - // It's inflated - Label CheckSucc, LNotRecursive, LSuccess, LGoSlowPath; + Label LSuccess, LNotRecursive; cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0); jccb(Assembler::equal, LNotRecursive); // Recursive inflated unlock - decq(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); + decrement(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); jmpb(LSuccess); bind(LNotRecursive); - movptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq))); - orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList))); - jccb (Assembler::notZero, CheckSucc); - // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. + + // Set owner to null. + // Release to satisfy the JMM movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD); - jmpb (DONE_LABEL); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); - // Try to avoid passing control into the slow_path ... - bind (CheckSucc); + // Check if the entry lists are empty. + movptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq))); + orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList))); + jccb(Assembler::zero, LSuccess); // If so we are done. - // The following optional optimization can be elided if necessary - // Effectively: if (succ == null) goto slow path - // The code reduces the window for a race, however, - // and thus benefits performance. + // Check if there is a successor. cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD); - jccb (Assembler::zero, LGoSlowPath); - - xorptr(boxReg, boxReg); - // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. - movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD); + jccb(Assembler::notZero, LSuccess); // If so we are done. - // Memory barrier/fence - // Dekker pivot point -- fulcrum : ST Owner; MEMBAR; LD Succ - // Instead of MFENCE we use a dummy locked add of 0 to the top-of-stack. - // This is faster on Nehalem and AMD Shanghai/Barcelona. - // See https://blogs.oracle.com/dave/entry/instruction_selection_for_volatile_fences - // We might also restructure (ST Owner=0;barrier;LD _Succ) to - // (mov box,0; xchgq box, &m->Owner; LD _succ) . - lock(); addl(Address(rsp, 0), 0); + // Save the monitor pointer in the current thread, so we can try to + // reacquire the lock in SharedRuntime::monitor_exit_helper(). + andptr(tmpReg, ~(int32_t)markWord::monitor_value); +#ifndef _LP64 + get_thread(boxReg); + movptr(Address(boxReg, JavaThread::unlocked_inflated_monitor_offset()), tmpReg); +#else // _LP64 + movptr(Address(r15_thread, JavaThread::unlocked_inflated_monitor_offset()), tmpReg); +#endif - cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD); - jccb (Assembler::notZero, LSuccess); - - // Rare inopportune interleaving - race. - // The successor vanished in the small window above. - // The lock is contended -- (cxq|EntryList) != null -- and there's no apparent successor. - // We need to ensure progress and succession. - // Try to reacquire the lock. - // If that fails then the new owner is responsible for succession and this - // thread needs to take no further action and can exit via the fast path (success). - // If the re-acquire succeeds then pass control into the slow path. - // As implemented, this latter mode is horrible because we generated more - // coherence traffic on the lock *and* artificially extended the critical section - // length while by virtue of passing control into the slow path. - - // box is really RAX -- the following CMPXCHG depends on that binding - // cmpxchg R,[M] is equivalent to rax = CAS(M,rax,R) - lock(); - cmpxchgptr(r15_thread, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); - // There's no successor so we tried to regrab the lock. - // If that didn't work, then another thread grabbed the - // lock so we're done (and exit was a success). - jccb (Assembler::notEqual, LSuccess); - // Intentional fall-through into slow path - - bind (LGoSlowPath); orl (boxReg, 1); // set ICC.ZF=0 to indicate failure jmpb (DONE_LABEL); @@ -547,7 +503,6 @@ void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register t testl (boxReg, 0); // set ICC.ZF=1 to indicate success jmpb (DONE_LABEL); -#endif if (LockingMode == LM_LEGACY) { bind (Stacked); movptr(tmpReg, Address (boxReg, 0)); // re-fetch @@ -744,10 +699,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, // Handle inflated monitor. Label inflated, inflated_check_lock_stack; // Finish fast unlock successfully. MUST jump with ZF == 1 - Label unlocked; - - // Assume success. - decrement(Address(thread, JavaThread::held_monitor_count_offset())); + Label unlocked, slow_path; const Register mark = t; const Register monitor = t; @@ -763,8 +715,6 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, } Label& push_and_slow_path = stub == nullptr ? dummy : stub->push_and_slow_path(); - Label& check_successor = stub == nullptr ? dummy : stub->check_successor(); - Label& slow_path = stub == nullptr ? dummy : stub->slow_path(); { // Lightweight Unlock @@ -839,6 +789,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag}; const Address cxq_address{monitor, ObjectMonitor::cxq_offset() - monitor_tag}; + const Address succ_address{monitor, ObjectMonitor::succ_offset() - monitor_tag}; const Address EntryList_address{monitor, ObjectMonitor::EntryList_offset() - monitor_tag}; const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag}; @@ -846,27 +797,42 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, // Check if recursive. cmpptr(recursions_address, 0); - jccb(Assembler::notEqual, recursive); + jccb(Assembler::notZero, recursive); + + // Set owner to null. + // Release to satisfy the JMM + movptr(owner_address, NULL_WORD); + // We need a full fence after clearing owner to avoid stranding. + // StoreLoad achieves this. + membar(StoreLoad); // Check if the entry lists are empty. movptr(reg_rax, cxq_address); orptr(reg_rax, EntryList_address); - jcc(Assembler::notZero, check_successor); + jccb(Assembler::zero, unlocked); // If so we are done. - // Release lock. - movptr(owner_address, NULL_WORD); - jmpb(unlocked); + // Check if there is a successor. + cmpptr(succ_address, NULL_WORD); + jccb(Assembler::notZero, unlocked); // If so we are done. + + // Save the monitor pointer in the current thread, so we can try to + // reacquire the lock in SharedRuntime::monitor_exit_helper(). + if (!UseObjectMonitorTable) { + andptr(monitor, ~(int32_t)markWord::monitor_value); + } + movptr(Address(thread, JavaThread::unlocked_inflated_monitor_offset()), monitor); + + orl(t, 1); // Fast Unlock ZF = 0 + jmpb(slow_path); // Recursive unlock. bind(recursive); decrement(recursions_address); - xorl(t, t); } bind(unlocked); - if (stub != nullptr) { - bind(stub->unlocked_continuation()); - } + decrement(Address(thread, JavaThread::held_monitor_count_offset())); + xorl(t, t); // Fast Unlock ZF = 1 #ifdef ASSERT // Check that unlocked label is reached with ZF set. @@ -875,6 +841,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, stop("Fast Unlock ZF != 1"); #endif + bind(slow_path); if (stub != nullptr) { bind(stub->slow_path_continuation()); } diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp index b52be627776b8..b6be4012519a0 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp @@ -38,7 +38,10 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/g1/c1/g1BarrierSetC1.hpp" -#endif +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#endif // COMPILER2 #define __ masm-> @@ -160,6 +163,56 @@ void G1BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorator } } +static void generate_queue_insertion(MacroAssembler* masm, ByteSize index_offset, ByteSize buffer_offset, Label& runtime, + const Register thread, const Register value, const Register temp) { + // This code assumes that buffer index is pointer sized. + STATIC_ASSERT(in_bytes(SATBMarkQueue::byte_width_of_index()) == sizeof(intptr_t)); + // Can we store a value in the given thread's buffer? + // (The index field is typed as size_t.) + __ movptr(temp, Address(thread, in_bytes(index_offset))); // temp := *(index address) + __ testptr(temp, temp); // index == 0? + __ jcc(Assembler::zero, runtime); // jump to runtime if index == 0 (full buffer) + // The buffer is not full, store value into it. + __ subptr(temp, wordSize); // temp := next index + __ movptr(Address(thread, in_bytes(index_offset)), temp); // *(index address) := next index + __ addptr(temp, Address(thread, in_bytes(buffer_offset))); // temp := buffer address + next index + __ movptr(Address(temp, 0), value); // *(buffer address + next index) := value +} + +static void generate_pre_barrier_fast_path(MacroAssembler* masm, + const Register thread) { + Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); + // Is marking active? + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { + __ cmpl(in_progress, 0); + } else { + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); + __ cmpb(in_progress, 0); + } +} + +static void generate_pre_barrier_slow_path(MacroAssembler* masm, + const Register obj, + const Register pre_val, + const Register thread, + const Register tmp, + Label& done, + Label& runtime) { + // Do we need to load the previous value? + if (obj != noreg) { + __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); + } + // Is the previous value null? + __ cmpptr(pre_val, NULL_WORD); + __ jcc(Assembler::equal, done); + generate_queue_insertion(masm, + G1ThreadLocalData::satb_mark_queue_index_offset(), + G1ThreadLocalData::satb_mark_queue_buffer_offset(), + runtime, + thread, pre_val, tmp); + __ jmp(done); +} + void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, Register obj, Register pre_val, @@ -185,43 +238,10 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, assert(pre_val != rax, "check this code"); } - Address in_progress(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset())); - Address index(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset())); - - // Is marking active? - if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { - __ cmpl(in_progress, 0); - } else { - assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); - __ cmpb(in_progress, 0); - } - __ jcc(Assembler::equal, done); - - // Do we need to load the previous value? - if (obj != noreg) { - __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); - } - - // Is the previous value null? - __ cmpptr(pre_val, NULL_WORD); + generate_pre_barrier_fast_path(masm, thread); + // If marking is not active (*(mark queue active address) == 0), jump to done __ jcc(Assembler::equal, done); - - // Can we store original value in the thread's buffer? - // Is index == 0? - // (The index field is typed as size_t.) - - __ movptr(tmp, index); // tmp := *index_adr - __ cmpptr(tmp, 0); // tmp == 0? - __ jcc(Assembler::equal, runtime); // If yes, goto runtime - - __ subptr(tmp, wordSize); // tmp := tmp - wordSize - __ movptr(index, tmp); // *index_adr := tmp - __ addptr(tmp, buffer); // tmp := tmp + *buffer_adr - - // Record the previous value - __ movptr(Address(tmp, 0), pre_val); - __ jmp(done); + generate_pre_barrier_slow_path(masm, obj, pre_val, thread, tmp, done, runtime); __ bind(runtime); @@ -263,6 +283,54 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm, __ bind(done); } +static void generate_post_barrier_fast_path(MacroAssembler* masm, + const Register store_addr, + const Register new_val, + const Register tmp, + const Register tmp2, + Label& done, + bool new_val_may_be_null) { + CardTableBarrierSet* ct = barrier_set_cast(BarrierSet::barrier_set()); + // Does store cross heap regions? + __ movptr(tmp, store_addr); // tmp := store address + __ xorptr(tmp, new_val); // tmp := store address ^ new value + __ shrptr(tmp, G1HeapRegion::LogOfHRGrainBytes); // ((store address ^ new value) >> LogOfHRGrainBytes) == 0? + __ jcc(Assembler::equal, done); + // Crosses regions, storing null? + if (new_val_may_be_null) { + __ cmpptr(new_val, NULL_WORD); // new value == null? + __ jcc(Assembler::equal, done); + } + // Storing region crossing non-null, is card young? + __ movptr(tmp, store_addr); // tmp := store address + __ shrptr(tmp, CardTable::card_shift()); // tmp := card address relative to card table base + // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT + // a valid address and therefore is not properly handled by the relocation code. + __ movptr(tmp2, (intptr_t)ct->card_table()->byte_map_base()); // tmp2 := card table base address + __ addptr(tmp, tmp2); // tmp := card address + __ cmpb(Address(tmp, 0), G1CardTable::g1_young_card_val()); // *(card address) == young_card_val? +} + +static void generate_post_barrier_slow_path(MacroAssembler* masm, + const Register thread, + const Register tmp, + const Register tmp2, + Label& done, + Label& runtime) { + __ membar(Assembler::Membar_mask_bits(Assembler::StoreLoad)); // StoreLoad membar + __ cmpb(Address(tmp, 0), G1CardTable::dirty_card_val()); // *(card address) == dirty_card_val? + __ jcc(Assembler::equal, done); + // Storing a region crossing, non-null oop, card is clean. + // Dirty card and log. + __ movb(Address(tmp, 0), G1CardTable::dirty_card_val()); // *(card address) := dirty_card_val + generate_queue_insertion(masm, + G1ThreadLocalData::dirty_card_queue_index_offset(), + G1ThreadLocalData::dirty_card_queue_buffer_offset(), + runtime, + thread, tmp, tmp2); + __ jmp(done); +} + void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, Register store_addr, Register new_val, @@ -273,74 +341,125 @@ void G1BarrierSetAssembler::g1_write_barrier_post(MacroAssembler* masm, assert(thread == r15_thread, "must be"); #endif // _LP64 - Address queue_index(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset())); - Address buffer(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset())); - - CardTableBarrierSet* ct = - barrier_set_cast(BarrierSet::barrier_set()); - Label done; Label runtime; - // Does store cross heap regions? - - __ movptr(tmp, store_addr); - __ xorptr(tmp, new_val); - __ shrptr(tmp, G1HeapRegion::LogOfHRGrainBytes); + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp, tmp2, done, true /* new_val_may_be_null */); + // If card is young, jump to done __ jcc(Assembler::equal, done); + generate_post_barrier_slow_path(masm, thread, tmp, tmp2, done, runtime); - // crosses regions, storing null? + __ bind(runtime); + // save the live input values + RegSet saved = RegSet::of(store_addr NOT_LP64(COMMA thread)); + __ push_set(saved); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), tmp, thread); + __ pop_set(saved); - __ cmpptr(new_val, NULL_WORD); - __ jcc(Assembler::equal, done); + __ bind(done); +} - // storing region crossing non-null, is card already dirty? +#if defined(COMPILER2) - const Register card_addr = tmp; - const Register cardtable = tmp2; +static void generate_c2_barrier_runtime_call(MacroAssembler* masm, G1BarrierStubC2* stub, const Register arg, const address runtime_path) { +#ifdef _LP64 + SaveLiveRegisters save_registers(masm, stub); + if (c_rarg0 != arg) { + __ mov(c_rarg0, arg); + } + __ mov(c_rarg1, r15_thread); + // rax is a caller-saved, non-argument-passing register, so it does not + // interfere with c_rarg0 or c_rarg1. If it contained any live value before + // entering this stub, it is saved at this point, and restored after the + // call. If it did not contain any live value, it is free to be used. In + // either case, it is safe to use it here as a call scratch register. + __ call(RuntimeAddress(runtime_path), rax); +#else + Unimplemented(); +#endif // _LP64 +} - __ movptr(card_addr, store_addr); - __ shrptr(card_addr, CardTable::card_shift()); - // Do not use ExternalAddress to load 'byte_map_base', since 'byte_map_base' is NOT - // a valid address and therefore is not properly handled by the relocation code. - __ movptr(cardtable, (intptr_t)ct->card_table()->byte_map_base()); - __ addptr(card_addr, cardtable); +void G1BarrierSetAssembler::g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp, + G1PreBarrierStubC2* stub) { +#ifdef _LP64 + assert(thread == r15_thread, "must be"); +#endif // _LP64 + assert(pre_val != noreg, "check this code"); + if (obj != noreg) { + assert_different_registers(obj, pre_val, tmp); + } - __ cmpb(Address(card_addr, 0), G1CardTable::g1_young_card_val()); - __ jcc(Assembler::equal, done); + stub->initialize_registers(obj, pre_val, thread, tmp); - __ membar(Assembler::Membar_mask_bits(Assembler::StoreLoad)); - __ cmpb(Address(card_addr, 0), G1CardTable::dirty_card_val()); - __ jcc(Assembler::equal, done); + generate_pre_barrier_fast_path(masm, thread); + // If marking is active (*(mark queue active address) != 0), jump to stub (slow path) + __ jcc(Assembler::notEqual, *stub->entry()); + __ bind(*stub->continuation()); +} - // storing a region crossing, non-null oop, card is clean. - // dirty card and log. +void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register obj = stub->obj(); + Register pre_val = stub->pre_val(); + Register thread = stub->thread(); + Register tmp = stub->tmp1(); + assert(stub->tmp2() == noreg, "not needed in this platform"); - __ movb(Address(card_addr, 0), G1CardTable::dirty_card_val()); + __ bind(*stub->entry()); + generate_pre_barrier_slow_path(masm, obj, pre_val, thread, tmp, *stub->continuation(), runtime); - // The code below assumes that buffer index is pointer sized. - STATIC_ASSERT(in_bytes(G1DirtyCardQueue::byte_width_of_index()) == sizeof(intptr_t)); + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, pre_val, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry)); + __ jmp(*stub->continuation()); +} - __ movptr(tmp2, queue_index); - __ testptr(tmp2, tmp2); - __ jcc(Assembler::zero, runtime); - __ subptr(tmp2, wordSize); - __ movptr(queue_index, tmp2); - __ addptr(tmp2, buffer); - __ movptr(Address(tmp2, 0), card_addr); - __ jmp(done); +void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp, + Register tmp2, + G1PostBarrierStubC2* stub) { +#ifdef _LP64 + assert(thread == r15_thread, "must be"); +#endif // _LP64 - __ bind(runtime); - // save the live input values - RegSet saved = RegSet::of(store_addr NOT_LP64(COMMA thread)); - __ push_set(saved); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), card_addr, thread); - __ pop_set(saved); + stub->initialize_registers(thread, tmp, tmp2); - __ bind(done); + bool new_val_may_be_null = (stub->barrier_data() & G1C2BarrierPostNotNull) == 0; + generate_post_barrier_fast_path(masm, store_addr, new_val, tmp, tmp2, *stub->continuation(), new_val_may_be_null); + // If card is not young, jump to stub (slow path) + __ jcc(Assembler::notEqual, *stub->entry()); + + __ bind(*stub->continuation()); +} + +void G1BarrierSetAssembler::generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + Label runtime; + Register thread = stub->thread(); + Register tmp = stub->tmp1(); // tmp holds the card address. + Register tmp2 = stub->tmp2(); + assert(stub->tmp3() == noreg, "not needed in this platform"); + + __ bind(*stub->entry()); + generate_post_barrier_slow_path(masm, thread, tmp, tmp2, *stub->continuation(), runtime); + + __ bind(runtime); + generate_c2_barrier_runtime_call(masm, stub, tmp, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry)); + __ jmp(*stub->continuation()); } +#endif // COMPILER2 + void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { bool in_heap = (decorators & IN_HEAP) != 0; diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp index a5695f5657a4a..4dbb1efd885ea 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.hpp @@ -32,6 +32,9 @@ class LIR_Assembler; class StubAssembler; class G1PreBarrierStub; class G1PostBarrierStub; +class G1BarrierStubC2; +class G1PreBarrierStubC2; +class G1PostBarrierStubC2; class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { protected: @@ -65,6 +68,26 @@ class G1BarrierSetAssembler: public ModRefBarrierSetAssembler { virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Register dst, Address src, Register tmp1, Register tmp_thread); + +#ifdef COMPILER2 + void g1_write_barrier_pre_c2(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp, + G1PreBarrierStubC2* c2_stub); + void generate_c2_pre_barrier_stub(MacroAssembler* masm, + G1PreBarrierStubC2* stub) const; + void g1_write_barrier_post_c2(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp, + Register tmp2, + G1PostBarrierStubC2* c2_stub); + void generate_c2_post_barrier_stub(MacroAssembler* masm, + G1PostBarrierStubC2* stub) const; +#endif // COMPILER2 }; #endif // CPU_X86_GC_G1_G1BARRIERSETASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/gc/g1/g1_x86_64.ad b/src/hotspot/cpu/x86/gc/g1/g1_x86_64.ad new file mode 100644 index 0000000000000..8c1559f90f46d --- /dev/null +++ b/src/hotspot/cpu/x86/gc/g1/g1_x86_64.ad @@ -0,0 +1,371 @@ +// +// 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. +// + +source_hpp %{ + +#include "gc/g1/c2/g1BarrierSetC2.hpp" +#include "gc/shared/gc_globals.hpp" + +%} + +source %{ + +#include "gc/g1/g1BarrierSetAssembler_x86.hpp" +#include "gc/g1/g1BarrierSetRuntime.hpp" + +static void write_barrier_pre(MacroAssembler* masm, + const MachNode* node, + Register obj, + Register pre_val, + Register tmp, + RegSet preserve = RegSet(), + RegSet no_preserve = RegSet()) { + if (!G1PreBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PreBarrierStubC2* const stub = G1PreBarrierStubC2::create(node); + for (RegSetIterator reg = preserve.begin(); *reg != noreg; ++reg) { + stub->preserve(*reg); + } + for (RegSetIterator reg = no_preserve.begin(); *reg != noreg; ++reg) { + stub->dont_preserve(*reg); + } + g1_asm->g1_write_barrier_pre_c2(masm, obj, pre_val, r15_thread, tmp, stub); +} + +static void write_barrier_post(MacroAssembler* masm, + const MachNode* node, + Register store_addr, + Register new_val, + Register tmp1, + Register tmp2) { + if (!G1PostBarrierStubC2::needs_barrier(node)) { + return; + } + Assembler::InlineSkippedInstructionsCounter skip_counter(masm); + G1BarrierSetAssembler* g1_asm = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + G1PostBarrierStubC2* const stub = G1PostBarrierStubC2::create(node); + g1_asm->g1_write_barrier_post_c2(masm, store_addr, new_val, r15_thread, tmp1, tmp2, stub); +} + +%} + +instruct g1StoreP(memory mem, any_RegP src, rRegP tmp1, rRegP tmp2, rRegP tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(125); // XXX + format %{ "movq $mem, $src\t# ptr" %} + ins_encode %{ + // Materialize the store address internally (as opposed to defining 'mem' as + // an indirect memory operand) to reduce the overhead of LCM when processing + // large basic blocks with many stores. Such basic blocks arise, for + // instance, from static initializations of large String arrays. + // The same holds for g1StoreN and g1EncodePAndStoreN. + __ lea($tmp1$$Register, $mem$$Address); + write_barrier_pre(masm, this, + $tmp1$$Register /* obj */, + $tmp2$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($tmp1$$Register, $src$$Register) /* preserve */); + __ movq(Address($tmp1$$Register, 0), $src$$Register); + write_barrier_post(masm, this, + $tmp1$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp3$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(ialu_mem_reg); +%} + +instruct g1StoreN(memory mem, rRegN src, rRegP tmp1, rRegP tmp2, rRegP tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem src)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(125); // XXX + format %{ "movl $mem, $src\t# ptr" %} + ins_encode %{ + __ lea($tmp1$$Register, $mem$$Address); + write_barrier_pre(masm, this, + $tmp1$$Register /* obj */, + $tmp2$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($tmp1$$Register, $src$$Register) /* preserve */); + __ movl(Address($tmp1$$Register, 0), $src$$Register); + if ((barrier_data() & G1C2BarrierPost) != 0) { + __ movl($tmp2$$Register, $src$$Register); + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ decode_heap_oop($tmp2$$Register); + } else { + __ decode_heap_oop_not_null($tmp2$$Register); + } + } + write_barrier_post(masm, this, + $tmp1$$Register /* store_addr */, + $tmp2$$Register /* new_val */, + $tmp3$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(ialu_mem_reg); +%} + +instruct g1EncodePAndStoreN(memory mem, any_RegP src, rRegP tmp1, rRegP tmp2, rRegP tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreN mem (EncodeP src))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + ins_cost(125); // XXX + format %{ "encode_heap_oop $src\n\t" + "movl $mem, $src\t# ptr" %} + ins_encode %{ + __ lea($tmp1$$Register, $mem$$Address); + write_barrier_pre(masm, this, + $tmp1$$Register /* obj */, + $tmp2$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($tmp1$$Register, $src$$Register) /* preserve */); + __ movq($tmp2$$Register, $src$$Register); + if ((barrier_data() & G1C2BarrierPostNotNull) == 0) { + __ encode_heap_oop($tmp2$$Register); + } else { + __ encode_heap_oop_not_null($tmp2$$Register); + } + __ movl(Address($tmp1$$Register, 0), $tmp2$$Register); + write_barrier_post(masm, this, + $tmp1$$Register /* store_addr */, + $src$$Register /* new_val */, + $tmp3$$Register /* tmp1 */, + $tmp2$$Register /* tmp2 */); + %} + ins_pipe(ialu_mem_reg); +%} + +instruct g1CompareAndExchangeP(indirect mem, rRegP newval, rRegP tmp1, rRegP tmp2, rRegP tmp3, rax_RegP oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set oldval (CompareAndExchangeP mem (Binary oldval newval))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + format %{ "lock\n\t" + "cmpxchgq $newval, $mem" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + // Pass $oldval to the pre-barrier (instead of loading from $mem), because + // $oldval is the only value that can be overwritten. + // The same holds for g1CompareAndSwapP. + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($mem$$Register, $newval$$Register, $oldval$$Register) /* preserve */); + __ movq($tmp1$$Register, $newval$$Register); + __ lock(); + __ cmpxchgq($tmp1$$Register, Address($mem$$Register, 0)); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_cmpxchg); +%} + +instruct g1CompareAndExchangeN(indirect mem, rRegN newval, rRegP tmp1, rRegP tmp2, rRegP tmp3, rax_RegN oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set oldval (CompareAndExchangeN mem (Binary oldval newval))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + format %{ "lock\n\t" + "cmpxchgq $newval, $mem" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp2$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($mem$$Register, $newval$$Register, $oldval$$Register) /* preserve */); + __ movl($tmp1$$Register, $newval$$Register); + __ lock(); + __ cmpxchgl($tmp1$$Register, Address($mem$$Register, 0)); + __ decode_heap_oop($tmp1$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_cmpxchg); +%} + +instruct g1CompareAndSwapP(rRegI res, indirect mem, rRegP newval, rRegP tmp1, rRegP tmp2, rRegP tmp3, rax_RegP oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL oldval, KILL cr); + format %{ "lock\n\t" + "cmpxchgq $newval, $mem\n\t" + "sete $res\n\t" + "movzbl $res, $res" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $oldval$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($mem$$Register, $newval$$Register, $oldval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ movq($tmp1$$Register, $newval$$Register); + __ lock(); + __ cmpxchgq($tmp1$$Register, Address($mem$$Register, 0)); + __ setb(Assembler::equal, $res$$Register); + __ movzbl($res$$Register, $res$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_cmpxchg); +%} + +instruct g1CompareAndSwapN(rRegI res, indirect mem, rRegN newval, rRegP tmp1, rRegP tmp2, rRegP tmp3, rax_RegN oldval, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set res (CompareAndSwapN mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); + effect(TEMP res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL oldval, KILL cr); + format %{ "lock\n\t" + "cmpxchgq $newval, $mem\n\t" + "sete $res\n\t" + "movzbl $res, $res" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp2$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($mem$$Register, $newval$$Register, $oldval$$Register) /* preserve */, + RegSet::of($res$$Register) /* no_preserve */); + __ movl($tmp1$$Register, $newval$$Register); + __ lock(); + __ cmpxchgl($tmp1$$Register, Address($mem$$Register, 0)); + __ setb(Assembler::equal, $res$$Register); + __ movzbl($res$$Register, $res$$Register); + __ decode_heap_oop($tmp1$$Register); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_cmpxchg); +%} + +instruct g1GetAndSetP(indirect mem, rRegP newval, rRegP tmp1, rRegP tmp2, rRegP tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set newval (GetAndSetP mem newval)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + format %{ "xchgq $newval, $mem" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp2$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + __ movq($tmp1$$Register, $newval$$Register); + __ xchgq($newval$$Register, Address($mem$$Register, 0)); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_cmpxchg); +%} + +instruct g1GetAndSetN(indirect mem, rRegN newval, rRegP tmp1, rRegP tmp2, rRegP tmp3, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_LoadStore()->barrier_data() != 0); + match(Set newval (GetAndSetN mem newval)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); + format %{ "xchgq $newval, $mem" %} + ins_encode %{ + assert_different_registers($mem$$Register, $newval$$Register); + write_barrier_pre(masm, this, + $mem$$Register /* obj */, + $tmp2$$Register /* pre_val */, + $tmp3$$Register /* tmp */, + RegSet::of($mem$$Register, $newval$$Register) /* preserve */); + __ movl($tmp1$$Register, $newval$$Register); + __ decode_heap_oop($tmp1$$Register); + __ xchgl($newval$$Register, Address($mem$$Register, 0)); + write_barrier_post(masm, this, + $mem$$Register /* store_addr */, + $tmp1$$Register /* new_val */, + $tmp2$$Register /* tmp1 */, + $tmp3$$Register /* tmp2 */); + %} + ins_pipe(pipe_cmpxchg); +%} + +instruct g1LoadP(rRegP dst, memory mem, rRegP tmp, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadP mem)); + effect(TEMP dst, TEMP tmp, KILL cr); + ins_cost(125); // XXX + format %{ "movq $dst, $mem\t# ptr" %} + ins_encode %{ + __ movq($dst$$Register, $mem$$Address); + write_barrier_pre(masm, this, + noreg /* obj */, + $dst$$Register /* pre_val */, + $tmp$$Register /* tmp */); + %} + ins_pipe(ialu_reg_mem); // XXX +%} + +instruct g1LoadN(rRegN dst, memory mem, rRegP tmp1, rRegP tmp2, rFlagsReg cr) +%{ + predicate(UseG1GC && n->as_Load()->barrier_data() != 0); + match(Set dst (LoadN mem)); + effect(TEMP dst, TEMP tmp1, TEMP tmp2, KILL cr); + ins_cost(125); // XXX + format %{ "movl $dst, $mem\t# compressed ptr" %} + ins_encode %{ + __ movl($dst$$Register, $mem$$Address); + __ movl($tmp1$$Register, $dst$$Register); + __ decode_heap_oop($tmp1$$Register); + write_barrier_pre(masm, this, + noreg /* obj */, + $tmp1$$Register /* pre_val */, + $tmp2$$Register /* tmp */); + %} + ins_pipe(ialu_reg_mem); // XXX +%} diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 47078dff90738..a7682fe0c3879 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -163,12 +163,12 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec assert(dst == rsi, "expected"); assert(count == rdx, "expected"); if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop_entry), + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop), src, dst, count); } else #endif { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop_entry), + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop), src, dst, count); } @@ -296,9 +296,9 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, __ push(thread); __ push(pre_val); #endif - __ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), 2); + __ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), 2); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), LP64_ONLY(c_rarg0) NOT_LP64(pre_val), thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), LP64_ONLY(c_rarg0) NOT_LP64(pre_val), thread); } NOT_LP64( __ pop(thread); ) @@ -925,7 +925,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss // load the pre-value __ load_parameter(0, rcx); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), rcx, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), rcx, thread); __ restore_live_registers(true); diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index f5f0d6c884198..bc51a2b446848 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -636,7 +636,7 @@ void ZBarrierSetAssembler::copy_load_at(MacroAssembler* masm, // Remove metadata bits so that the store side (vectorized or non-vectorized) can // inject the store-good color with an or instruction. - __ andq(dst, _zpointer_address_mask); + __ andq(dst, ZPointerAddressMask); if ((decorators & ARRAYCOPY_CHECKCAST) != 0) { // The checkcast arraycopy needs to be able to dereference the oops in order to perform a typechecks. @@ -1260,6 +1260,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ call(RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp index 7c3716ba0da9f..5fbc7ea1be16e 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp @@ -64,7 +64,7 @@ class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { GrowableArrayCHeap _store_good_relocations; public: - static const int32_t _zpointer_address_mask = 0xFFFF0000; + static const int32_t ZPointerAddressMask = 0xFFFF0000; ZBarrierSetAssembler(); diff --git a/src/hotspot/cpu/x86/gc/z/z_x86_64.ad b/src/hotspot/cpu/x86/gc/z/z_x86_64.ad index 30220c0629e5e..f55ad70e8616e 100644 --- a/src/hotspot/cpu/x86/gc/z/z_x86_64.ad +++ b/src/hotspot/cpu/x86/gc/z/z_x86_64.ad @@ -91,7 +91,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address } } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } @@ -141,7 +142,7 @@ instruct zLoadPNullCheck(rFlagsReg cr, memory op, immP0 zero) ins_encode %{ // A null pointer will have all address bits 0. This mask sign extends // all address bits, so we can test if the address is 0. - __ testq($op$$Address, ZBarrierSetAssembler::_zpointer_address_mask); + __ testq($op$$Address, ZBarrierSetAssembler::ZPointerAddressMask); %} ins_pipe(ialu_cr_reg_imm); %} diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index d634c1e575799..018258a012e57 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5084,7 +5084,8 @@ void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fa L_slow_path = &L_fallthrough; } - // Fast path check: class is fully initialized + // Fast path check: class is fully initialized. + // init_state needs acquire, but x86 is TSO, and so we are already good. cmpb(Address(klass, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); jcc(Assembler::equal, *L_fast_path); @@ -5756,7 +5757,7 @@ void MacroAssembler::verify_heapbase(const char* msg) { assert (Universe::heap() != nullptr, "java heap should be initialized"); if (CheckCompressedOops) { Label ok; - ExternalAddress src2(CompressedOops::ptrs_base_addr()); + ExternalAddress src2(CompressedOops::base_addr()); const bool is_src2_reachable = reachable(src2); if (!is_src2_reachable) { push(rscratch1); // cmpptr trashes rscratch1 @@ -6047,10 +6048,10 @@ void MacroAssembler::reinit_heapbase() { if (CompressedOops::base() == nullptr) { MacroAssembler::xorptr(r12_heapbase, r12_heapbase); } else { - mov64(r12_heapbase, (int64_t)CompressedOops::ptrs_base()); + mov64(r12_heapbase, (int64_t)CompressedOops::base()); } } else { - movptr(r12_heapbase, ExternalAddress(CompressedOops::ptrs_base_addr())); + movptr(r12_heapbase, ExternalAddress(CompressedOops::base_addr())); } } } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_md5.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_md5.cpp index 439c17b10d37a..09d379a4296d4 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_md5.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_md5.cpp @@ -81,8 +81,8 @@ void MacroAssembler::fast_md5(Register buf, Address state, Address ofs, Address notl(rsi); \ andl(rdi, r2); \ andl(rsi, r3); \ - orl(rsi, rdi); \ addl(r1, rsi); \ + addl(r1, rdi); \ roll(r1, s); \ addl(r1, r2); diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 4bd91f640fca7..174e2e0277903 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -2674,7 +2674,7 @@ void SharedRuntime::generate_deopt_blob() { int reexecute_offset = __ pc() - start; #if INCLUDE_JVMCI && !defined(COMPILER1) - if (EnableJVMCI && UseJVMCICompiler) { + if (UseJVMCICompiler) { // JVMCI does not use this kind of deoptimization __ should_not_reach_here(); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 2bc4a0a9cba94..ee6311c25f6fe 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "classfile/vmIntrinsics.hpp" #include "compiler/oopMap.hpp" #include "gc/shared/barrierSet.hpp" @@ -3573,6 +3574,9 @@ void StubGenerator::generate_libm_stubs() { if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_dtan)) { StubRoutines::_dtan = generate_libmTan(); // from stubGenerator_x86_64_tan.cpp } + if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_dtanh)) { + StubRoutines::_dtanh = generate_libmTanh(); // from stubGenerator_x86_64_tanh.cpp + } if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_dexp)) { StubRoutines::_dexp = generate_libmExp(); // from stubGenerator_x86_64_exp.cpp } @@ -3793,6 +3797,28 @@ address StubGenerator::generate_upcall_stub_exception_handler() { return start; } +// load Method* target of MethodHandle +// j_rarg0 = jobject receiver +// rbx = result +address StubGenerator::generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, r15_thread, rscratch1); + // Load target method from receiver + __ load_heap_oop(rbx, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), rscratch1); + __ load_heap_oop(rbx, Address(rbx, java_lang_invoke_LambdaForm::vmentry_offset()), rscratch1); + __ load_heap_oop(rbx, Address(rbx, java_lang_invoke_MemberName::method_offset()), rscratch1); + __ access_load_at(T_ADDRESS, IN_HEAP, rbx, + Address(rbx, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ movptr(Address(r15_thread, JavaThread::callee_target_offset()), rbx); // just in case callee is deoptimized + + __ ret(0); + + return start; +} + address StubGenerator::generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { StubCodeMark mark(this, "StubRoutines", "lookup_secondary_supers_table"); @@ -3952,6 +3978,7 @@ void StubGenerator::generate_final_stubs() { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void StubGenerator::generate_compiler_stubs() { @@ -4157,41 +4184,41 @@ void StubGenerator::generate_compiler_stubs() { log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, JNI_LIB_PREFIX "jsvml" JNI_LIB_SUFFIX, p2i(libjsvml)); if (UseAVX > 2) { - for (int op = 0; op < VectorSupport::NUM_SVML_OP; op++) { - int vop = VectorSupport::VECTOR_OP_SVML_START + op; + for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { + int vop = VectorSupport::VECTOR_OP_MATH_START + op; if ((!VM_Version::supports_avx512dq()) && (vop == VectorSupport::VECTOR_OP_LOG || vop == VectorSupport::VECTOR_OP_LOG10 || vop == VectorSupport::VECTOR_OP_POW)) { continue; } - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf16_ha_z0", VectorSupport::svmlname[op]); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf16_ha_z0", VectorSupport::mathname[op]); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_512][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s8_ha_z0", VectorSupport::svmlname[op]); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s8_ha_z0", VectorSupport::mathname[op]); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_512][op] = (address)os::dll_lookup(libjsvml, ebuf); } } const char* avx_sse_str = (UseAVX >= 2) ? "l9" : ((UseAVX == 1) ? "e9" : "ex"); - for (int op = 0; op < VectorSupport::NUM_SVML_OP; op++) { - int vop = VectorSupport::VECTOR_OP_SVML_START + op; + for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { + int vop = VectorSupport::VECTOR_OP_MATH_START + op; if (vop == VectorSupport::VECTOR_OP_POW) { continue; } - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_64][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf8_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf8_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_256][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s1_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s1_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_64][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s2_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s2_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s4_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s4_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_256][op] = (address)os::dll_lookup(libjsvml, ebuf); } } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index d65c681585d6d..7280e9fbe957e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -376,11 +376,22 @@ class StubGenerator: public StubCodeGenerator { void roundDec(XMMRegister key, int rnum); void lastroundDec(XMMRegister key, int rnum); void gfmul_avx512(XMMRegister ghash, XMMRegister hkey); - void generateHtbl_48_block_zmm(Register htbl, Register avx512_subkeyHtbl, Register rscratch); - void ghash16_encrypt16_parallel(Register key, Register subkeyHtbl, XMMRegister ctr_blockx, - XMMRegister aad_hashx, Register in, Register out, Register data, Register pos, bool reduction, - XMMRegister addmask, bool no_ghash_input, Register rounds, Register ghash_pos, - bool final_reduction, int index, XMMRegister counter_inc_mask); + void ghash16_encrypt_parallel16_avx512(Register in, Register out, Register ct, Register pos, Register avx512_subkeyHtbl, + Register CTR_CHECK, Register NROUNDS, Register key, XMMRegister CTR, XMMRegister GHASH, + XMMRegister ADDBE_4x4, XMMRegister ADDBE_1234, XMMRegister ADD_1234, XMMRegister SHUF_MASK, + bool hk_broadcast, bool is_hash_start, bool do_hash_reduction, bool do_hash_hxor, + bool no_ghash_in, int ghashin_offset, int aesout_offset, int hashkey_offset); + void generateHtbl_32_blocks_avx512(Register htbl, Register avx512_htbl); + void initial_blocks_16_avx512(Register in, Register out, Register ct, Register pos, Register key, Register avx512_subkeyHtbl, + Register CTR_CHECK, Register rounds, XMMRegister CTR, XMMRegister GHASH, XMMRegister ADDBE_4x4, + XMMRegister ADDBE_1234, XMMRegister ADD_1234, XMMRegister SHUF_MASK, int stack_offset); + void gcm_enc_dec_last_avx512(Register len, Register in, Register pos, XMMRegister HASH, XMMRegister SHUFM, Register subkeyHtbl, + int ghashin_offset, int hashkey_offset, bool start_ghash, bool do_reduction); + void ghash16_avx512(bool start_ghash, bool do_reduction, bool uload_shuffle, bool hk_broadcast, bool do_hxor, + Register in, Register pos, Register subkeyHtbl, XMMRegister HASH, XMMRegister SHUFM, int in_offset, + int in_disp, int displacement, int hashkey_offset); + void aesgcm_avx512(Register in, Register len, Register ct, Register out, Register key, + Register state, Register subkeyHtbl, Register avx512_subkeyHtbl, Register counter); // AVX2 AES-GCM related functions void initial_blocks_avx2(XMMRegister ctr, Register rounds, Register key, Register len, Register in, Register out, Register ct, XMMRegister aad_hashx, Register pos); @@ -546,6 +557,7 @@ class StubGenerator: public StubCodeGenerator { address generate_libmSin(); address generate_libmCos(); address generate_libmTan(); + address generate_libmTanh(); address generate_libmExp(); address generate_libmPow(); address generate_libmLog(); @@ -608,6 +620,7 @@ class StubGenerator: public StubCodeGenerator { // shared exception handler for FFM upcall stubs address generate_upcall_stub_exception_handler(); + address generate_upcall_stub_load_target(); // Specialized stub implementations for UseSecondarySupersTable. address generate_lookup_secondary_supers_table_stub(u1 super_klass_index); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 9744169498c8b..f14d368c376e1 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2023, Intel Corporation. All rights reserved. +* Copyright (c) 2019, 2024, Intel Corporation. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -172,6 +172,38 @@ static address ghash_polynomial_two_one_addr() { return (address)GHASH_POLYNOMIAL_TWO_ONE; } +// This mask is used for incrementing counter value +ATTRIBUTE_ALIGNED(64) static const uint64_t COUNTER_MASK_ADDBE_4444[] = { + 0x0000000000000000ULL, 0x0400000000000000ULL, + 0x0000000000000000ULL, 0x0400000000000000ULL, + 0x0000000000000000ULL, 0x0400000000000000ULL, + 0x0000000000000000ULL, 0x0400000000000000ULL, +}; +static address counter_mask_addbe_4444_addr() { + return (address)COUNTER_MASK_ADDBE_4444; +} + +// This mask is used for incrementing counter value +ATTRIBUTE_ALIGNED(64) static const uint64_t COUNTER_MASK_ADDBE_1234[] = { + 0x0000000000000000ULL, 0x0100000000000000ULL, + 0x0000000000000000ULL, 0x0200000000000000ULL, + 0x0000000000000000ULL, 0x0300000000000000ULL, + 0x0000000000000000ULL, 0x0400000000000000ULL, +}; +static address counter_mask_addbe_1234_addr() { + return (address)COUNTER_MASK_ADDBE_1234; +} + +// This mask is used for incrementing counter value +ATTRIBUTE_ALIGNED(64) static const uint64_t COUNTER_MASK_ADD_1234[] = { + 0x0000000000000001ULL, 0x0000000000000000ULL, + 0x0000000000000002ULL, 0x0000000000000000ULL, + 0x0000000000000003ULL, 0x0000000000000000ULL, + 0x0000000000000004ULL, 0x0000000000000000ULL, +}; +static address counter_mask_add_1234_addr() { + return (address)COUNTER_MASK_ADD_1234; +} // AES intrinsic stubs @@ -209,10 +241,10 @@ void StubGenerator::generate_aes_stubs() { // len = rdx (c_rarg1) | rdi (c_rarg1) // ct = r8 (c_rarg2) | rdx (c_rarg2) // out = r9 (c_rarg3) | rcx (c_rarg3) -// key = r10 | r8 (c_rarg4) -// state = r13 | r9 (c_rarg5) -// subkeyHtbl = r14 | r11 -// counter = rsi | r12 +// key = rsi | r8 (c_rarg4) +// state = rdi | r9 (c_rarg5) +// subkeyHtbl = r10 | r10 +// counter = r11 | r11 // // Output: // rax - number of processed bytes @@ -230,31 +262,31 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() { const Register key = c_rarg4; const Register state = c_rarg5; const Address subkeyH_mem(rbp, 2 * wordSize); - const Register subkeyHtbl = r11; - const Register avx512_subkeyHtbl = r13; + const Register subkeyHtbl = r10; + const Register avx512_subkeyHtbl = r12; const Address counter_mem(rbp, 3 * wordSize); - const Register counter = r12; + const Register counter = r11; #else const Address key_mem(rbp, 6 * wordSize); - const Register key = r10; + const Register key = rsi; const Address state_mem(rbp, 7 * wordSize); - const Register state = r13; + const Register state = rdi; const Address subkeyH_mem(rbp, 8 * wordSize); - const Register subkeyHtbl = r14; + const Register subkeyHtbl = r10; const Register avx512_subkeyHtbl = r12; const Address counter_mem(rbp, 9 * wordSize); - const Register counter = rsi; + const Register counter = r11; #endif __ enter(); // Save state before entering routine - __ push(r12); - __ push(r13); - __ push(r14); - __ push(r15); - __ push(rbx); + __ push(r12);//holds pointer to avx512_subkeyHtbl + __ push(r14);//holds CTR_CHECK value to check for overflow + __ push(r15);//holds number of rounds + __ push(rbx);//scratch register #ifdef _WIN64 // on win64, fill len_reg from stack position __ push(rsi); + __ push(rdi); __ movptr(key, key_mem); __ movptr(state, state_mem); #endif @@ -262,24 +294,24 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() { __ movptr(counter, counter_mem); // Align stack __ andq(rsp, -64); - __ subptr(rsp, 96 * longSize); // Create space on the stack for htbl entries + __ subptr(rsp, 200 * longSize); // Create space on the stack for 64 htbl entries and 8 zmm AES entries __ movptr(avx512_subkeyHtbl, rsp); - aesgcm_encrypt(in, len, ct, out, key, state, subkeyHtbl, avx512_subkeyHtbl, counter); + aesgcm_avx512(in, len, ct, out, key, state, subkeyHtbl, avx512_subkeyHtbl, counter); __ vzeroupper(); // Restore state before leaving routine #ifdef _WIN64 __ lea(rsp, Address(rbp, -6 * wordSize)); + __ pop(rdi); __ pop(rsi); #else - __ lea(rsp, Address(rbp, -5 * wordSize)); + __ lea(rsp, Address(rbp, -4 * wordSize)); #endif __ pop(rbx); __ pop(r15); __ pop(r14); - __ pop(r13); __ pop(r12); __ leave(); // required for proper stackwalking of RuntimeStub frame @@ -2708,87 +2740,100 @@ void StubGenerator::gfmul_avx512(XMMRegister GH, XMMRegister HK) { __ vpternlogq(GH, 0x96, TMP1, TMP2, Assembler::AVX_512bit); } -void StubGenerator::generateHtbl_48_block_zmm(Register htbl, Register avx512_htbl, Register rscratch) { +// Holds 64 Htbl entries, 32 HKey and 32 HkKey (derived from HKey) +void StubGenerator::generateHtbl_32_blocks_avx512(Register htbl, Register avx512_htbl) { const XMMRegister HK = xmm6; - const XMMRegister ZT5 = xmm4; - const XMMRegister ZT7 = xmm7; - const XMMRegister ZT8 = xmm8; - - Label GFMUL_AVX512; + const XMMRegister ZT1 = xmm0, ZT2 = xmm1, ZT3 = xmm2, ZT4 = xmm3; + const XMMRegister ZT5 = xmm4, ZT6 = xmm5, ZT7 = xmm7, ZT8 = xmm8; + const XMMRegister ZT10 = xmm10, ZT11 = xmm11, ZT12 = xmm12; __ movdqu(HK, Address(htbl, 0)); - __ movdqu(xmm10, ExternalAddress(ghash_long_swap_mask_addr()), rscratch); - __ vpshufb(HK, HK, xmm10, Assembler::AVX_128bit); - - __ movdqu(xmm11, ExternalAddress(ghash_polynomial_addr()), rscratch); - __ movdqu(xmm12, ExternalAddress(ghash_polynomial_two_one_addr()), rscratch); + __ movdqu(ZT10, ExternalAddress(ghash_long_swap_mask_addr()), r15); + __ vpshufb(HK, HK, ZT10, Assembler::AVX_128bit); + __ movdqu(ZT11, ExternalAddress(ghash_polynomial_addr()), r15); + __ movdqu(ZT12, ExternalAddress(ghash_polynomial_two_one_addr()), r15); // Compute H ^ 2 from the input subkeyH - __ movdqu(xmm2, xmm6); - __ vpsllq(xmm6, xmm6, 1, Assembler::AVX_128bit); - __ vpsrlq(xmm2, xmm2, 63, Assembler::AVX_128bit); - __ movdqu(xmm1, xmm2); - __ vpslldq(xmm2, xmm2, 8, Assembler::AVX_128bit); - __ vpsrldq(xmm1, xmm1, 8, Assembler::AVX_128bit); - __ vpor(xmm6, xmm6, xmm2, Assembler::AVX_128bit); + __ movdqu(ZT3, HK); + __ vpsllq(HK, HK, 1, Assembler::AVX_128bit); + __ vpsrlq(ZT3, ZT3, 63, Assembler::AVX_128bit); + __ movdqu(ZT2, ZT3); + __ vpslldq(ZT3, ZT3, 8, Assembler::AVX_128bit); + __ vpsrldq(ZT2, ZT2, 8, Assembler::AVX_128bit); + __ vpor(HK, HK, ZT3, Assembler::AVX_128bit); + __ vpshufd(ZT3, ZT2, 0x24, Assembler::AVX_128bit); + __ vpcmpeqd(ZT3, ZT3, ZT12, Assembler::AVX_128bit); + __ vpand(ZT3, ZT3, ZT11, Assembler::AVX_128bit); + __ vpxor(HK, HK, ZT3, Assembler::AVX_128bit); + __ movdqu(Address(avx512_htbl, 16 * 31), HK); // H ^ 2 - __ vpshufd(xmm2, xmm1, 0x24, Assembler::AVX_128bit); - __ vpcmpeqd(xmm2, xmm2, xmm12, Assembler::AVX_128bit); - __ vpand(xmm2, xmm2, xmm11, Assembler::AVX_128bit); - __ vpxor(xmm6, xmm6, xmm2, Assembler::AVX_128bit); - __ movdqu(Address(avx512_htbl, 16 * 47), xmm6); // H ^ 2 - // Compute the remaining three powers of H using XMM registers and all following powers using ZMM __ movdqu(ZT5, HK); - __ vinserti32x4(ZT7, ZT7, HK, 3); + __ evinserti64x2(ZT7, ZT7, HK, 3, Assembler::AVX_512bit); + //calculate HashKey ^ 2 << 1 mod poly gfmul_avx512(ZT5, HK); - __ movdqu(Address(avx512_htbl, 16 * 46), ZT5); // H ^ 2 * 2 - __ vinserti32x4(ZT7, ZT7, ZT5, 2); + __ movdqu(Address(avx512_htbl, 16 * 30), ZT5); + __ evinserti64x2(ZT7, ZT7, ZT5, 2, Assembler::AVX_512bit); + //calculate HashKey ^ 3 << 1 mod poly gfmul_avx512(ZT5, HK); - __ movdqu(Address(avx512_htbl, 16 * 45), ZT5); // H ^ 2 * 3 - __ vinserti32x4(ZT7, ZT7, ZT5, 1); + __ movdqu(Address(avx512_htbl, 16 * 29), ZT5); + __ evinserti64x2(ZT7, ZT7, ZT5, 1, Assembler::AVX_512bit); + //calculate HashKey ^ 4 << 1 mod poly gfmul_avx512(ZT5, HK); - __ movdqu(Address(avx512_htbl, 16 * 44), ZT5); // H ^ 2 * 4 - __ vinserti32x4(ZT7, ZT7, ZT5, 0); - - __ evshufi64x2(ZT5, ZT5, ZT5, 0x00, Assembler::AVX_512bit); - __ evmovdquq(ZT8, ZT7, Assembler::AVX_512bit); - gfmul_avx512(ZT7, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 40), ZT7, Assembler::AVX_512bit); - __ evshufi64x2(ZT5, ZT7, ZT7, 0x00, Assembler::AVX_512bit); - gfmul_avx512(ZT8, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 36), ZT8, Assembler::AVX_512bit); + __ movdqu(Address(avx512_htbl, 16 * 28), ZT5); + __ evinserti64x2(ZT7, ZT7, ZT5, 0, Assembler::AVX_512bit); + // ZT5 amd ZT7 to be cleared(hash key) + //calculate HashKeyK = HashKey x POLY + __ evmovdquq(xmm11, ExternalAddress(ghash_polynomial_addr()), Assembler::AVX_512bit, r15); + __ evpclmulqdq(ZT1, ZT7, xmm11, 0x10, Assembler::AVX_512bit); + __ vpshufd(ZT2, ZT7, 78, Assembler::AVX_512bit); + __ evpxorq(ZT1, ZT1, ZT2, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_htbl, 16 * 60), ZT1, Assembler::AVX_512bit); + //**ZT1 amd ZT2 to be cleared(hash key) + + //switch to 4x128 - bit computations now + __ evshufi64x2(ZT5, ZT5, ZT5, 0x00, Assembler::AVX_512bit); //;; broadcast HashKey ^ 4 across all ZT5 + __ evmovdquq(ZT8, ZT7, Assembler::AVX_512bit);//; save HashKey ^ 4 to HashKey ^ 1 in ZT8 + //**ZT8 to be cleared(hash key) + + //calculate HashKey ^ 5 << 1 mod poly, HashKey ^ 6 << 1 mod poly, ... HashKey ^ 8 << 1 mod poly gfmul_avx512(ZT7, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 32), ZT7, Assembler::AVX_512bit); - gfmul_avx512(ZT8, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 28), ZT8, Assembler::AVX_512bit); - gfmul_avx512(ZT7, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 24), ZT7, Assembler::AVX_512bit); - gfmul_avx512(ZT8, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 20), ZT8, Assembler::AVX_512bit); - gfmul_avx512(ZT7, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 16), ZT7, Assembler::AVX_512bit); - gfmul_avx512(ZT8, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 12), ZT8, Assembler::AVX_512bit); - gfmul_avx512(ZT7, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 8), ZT7, Assembler::AVX_512bit); - gfmul_avx512(ZT8, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 4), ZT8, Assembler::AVX_512bit); - gfmul_avx512(ZT7, ZT5); - __ evmovdquq(Address(avx512_htbl, 16 * 0), ZT7, Assembler::AVX_512bit); - __ ret(0); -} - -#define vclmul_reduce(out, poly, hi128, lo128, tmp0, tmp1) \ -__ evpclmulqdq(tmp0, poly, lo128, 0x01, Assembler::AVX_512bit); \ -__ vpslldq(tmp0, tmp0, 8, Assembler::AVX_512bit); \ -__ evpxorq(tmp0, lo128, tmp0, Assembler::AVX_512bit); \ -__ evpclmulqdq(tmp1, poly, tmp0, 0x00, Assembler::AVX_512bit); \ -__ vpsrldq(tmp1, tmp1, 4, Assembler::AVX_512bit); \ -__ evpclmulqdq(out, poly, tmp0, 0x10, Assembler::AVX_512bit); \ -__ vpslldq(out, out, 4, Assembler::AVX_512bit); \ -__ vpternlogq(out, 0x96, tmp1, hi128, Assembler::AVX_512bit); \ + __ evmovdquq(Address(avx512_htbl, 16 * 24), ZT7, Assembler::AVX_512bit);//; HashKey ^ 8 to HashKey ^ 5 in ZT7 now + + //calculate HashKeyX = HashKey x POLY + __ evpclmulqdq(ZT1, ZT7, xmm11, 0x10, Assembler::AVX_512bit); + __ vpshufd(ZT2, ZT7, 78, Assembler::AVX_512bit); + __ evpxorq(ZT1, ZT1, ZT2, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_htbl, 16 * 56), ZT1, Assembler::AVX_512bit); + + __ evshufi64x2(ZT5, ZT7, ZT7, 0x00, Assembler::AVX_512bit);//;; broadcast HashKey ^ 8 across all ZT5 + + for (int i = 20, j = 52; i > 0;) { + gfmul_avx512(ZT8, ZT5); + __ evmovdquq(Address(avx512_htbl, 16 * i), ZT8, Assembler::AVX_512bit); + //calculate HashKeyK = HashKey x POLY + __ evpclmulqdq(ZT1, ZT8, xmm11, 0x10, Assembler::AVX_512bit); + __ vpshufd(ZT2, ZT8, 78, Assembler::AVX_512bit); + __ evpxorq(ZT1, ZT1, ZT2, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_htbl, 16 * j), ZT1, Assembler::AVX_512bit); + + i -= 4; + j -= 4; + //compute HashKey ^ (8 + n), HashKey ^ (7 + n), ... HashKey ^ (5 + n) + gfmul_avx512(ZT7, ZT5); + __ evmovdquq(Address(avx512_htbl, 16 * i), ZT7, Assembler::AVX_512bit); + + //calculate HashKeyK = HashKey x POLY + __ evpclmulqdq(ZT1, ZT7, xmm11, 0x10, Assembler::AVX_512bit); + __ vpshufd(ZT2, ZT7, 78, Assembler::AVX_512bit); + __ evpxorq(ZT1, ZT1, ZT2, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_htbl, 16 * j), ZT1, Assembler::AVX_512bit); + + i -= 4; + j -= 4; + } + } #define vhpxori4x128(reg, tmp) \ __ vextracti64x4(tmp, reg, 1); \ @@ -2820,21 +2865,17 @@ __ evmovdquq(dst2, Address(src, position, Address::times_1, 1 * 64), Assembler:: __ evmovdquq(dst3, Address(src, position, Address::times_1, 2 * 64), Assembler::AVX_512bit); \ __ evmovdquq(dst4, Address(src, position, Address::times_1, 3 * 64), Assembler::AVX_512bit); \ -#define carrylessMultiply(dst00, dst01, dst10, dst11, ghdata, hkey) \ -__ evpclmulqdq(dst00, ghdata, hkey, 0x00, Assembler::AVX_512bit); \ -__ evpclmulqdq(dst01, ghdata, hkey, 0x01, Assembler::AVX_512bit); \ -__ evpclmulqdq(dst10, ghdata, hkey, 0x10, Assembler::AVX_512bit); \ -__ evpclmulqdq(dst11, ghdata, hkey, 0x11, Assembler::AVX_512bit); \ - -#define shuffleExorRnd1Key(dst0, dst1, dst2, dst3, shufmask, rndkey) \ -__ vpshufb(dst0, dst0, shufmask, Assembler::AVX_512bit); \ -__ evpxorq(dst0, dst0, rndkey, Assembler::AVX_512bit); \ -__ vpshufb(dst1, dst1, shufmask, Assembler::AVX_512bit); \ -__ evpxorq(dst1, dst1, rndkey, Assembler::AVX_512bit); \ -__ vpshufb(dst2, dst2, shufmask, Assembler::AVX_512bit); \ -__ evpxorq(dst2, dst2, rndkey, Assembler::AVX_512bit); \ -__ vpshufb(dst3, dst3, shufmask, Assembler::AVX_512bit); \ -__ evpxorq(dst3, dst3, rndkey, Assembler::AVX_512bit); \ +#define carrylessMultiply(dst00, dst01, dst10, dst11, ghdata, hkey2, hkey1) \ +__ evpclmulqdq(dst00, ghdata, hkey2, 0x00, Assembler::AVX_512bit); \ +__ evpclmulqdq(dst01, ghdata, hkey2, 0x10, Assembler::AVX_512bit); \ +__ evpclmulqdq(dst10, ghdata, hkey1, 0x01, Assembler::AVX_512bit); \ +__ evpclmulqdq(dst11, ghdata, hkey1, 0x11, Assembler::AVX_512bit); \ + +#define shuffle(dst0, dst1, dst2, dst3, src0, src1, src2, src3, shufmask) \ +__ vpshufb(dst0, src0, shufmask, Assembler::AVX_512bit); \ +__ vpshufb(dst1, src1, shufmask, Assembler::AVX_512bit); \ +__ vpshufb(dst2, src2, shufmask, Assembler::AVX_512bit); \ +__ vpshufb(dst3, src3, shufmask, Assembler::AVX_512bit); \ #define xorBeforeStore(dst0, dst1, dst2, dst3, src0, src1, src2, src3) \ __ evpxorq(dst0, dst0, src0, Assembler::AVX_512bit); \ @@ -2848,211 +2889,462 @@ __ vpternlogq(dst1, 0x96, src12, src13, Assembler::AVX_512bit); \ __ vpternlogq(dst2, 0x96, src22, src23, Assembler::AVX_512bit); \ __ vpternlogq(dst3, 0x96, src32, src33, Assembler::AVX_512bit); \ -void StubGenerator::ghash16_encrypt16_parallel(Register key, Register subkeyHtbl, XMMRegister ctr_blockx, XMMRegister aad_hashx, - Register in, Register out, Register data, Register pos, bool first_time_reduction, XMMRegister addmask, bool ghash_input, Register rounds, - Register ghash_pos, bool final_reduction, int i, XMMRegister counter_inc_mask) { - Label AES_192, AES_256, LAST_AES_RND; +//schoolbook multiply of 16 blocks(8 x 16 bytes) +//it is assumed that data read is already shuffledand +void StubGenerator::ghash16_avx512(bool start_ghash, bool do_reduction, bool uload_shuffle, bool hk_broadcast, bool do_hxor, + Register in, Register pos, Register subkeyHtbl, XMMRegister HASH, XMMRegister SHUFM, int in_offset, + int in_disp, int displacement, int hashkey_offset) { const XMMRegister ZTMP0 = xmm0; const XMMRegister ZTMP1 = xmm3; const XMMRegister ZTMP2 = xmm4; const XMMRegister ZTMP3 = xmm5; + const XMMRegister ZTMP4 = xmm6; const XMMRegister ZTMP5 = xmm7; const XMMRegister ZTMP6 = xmm10; const XMMRegister ZTMP7 = xmm11; const XMMRegister ZTMP8 = xmm12; const XMMRegister ZTMP9 = xmm13; - const XMMRegister ZTMP10 = xmm15; - const XMMRegister ZTMP11 = xmm16; - const XMMRegister ZTMP12 = xmm17; + const XMMRegister ZTMPA = xmm26; + const XMMRegister ZTMPB = xmm23; + const XMMRegister GH = xmm24; + const XMMRegister GL = xmm25; + const int hkey_gap = 16 * 32; + + if (uload_shuffle) { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp), Assembler::AVX_512bit); + __ vpshufb(ZTMP9, ZTMP9, SHUFM, Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp), Assembler::AVX_512bit); + } - const XMMRegister ZTMP13 = xmm19; - const XMMRegister ZTMP14 = xmm20; - const XMMRegister ZTMP15 = xmm21; - const XMMRegister ZTMP16 = xmm30; - const XMMRegister ZTMP17 = xmm31; - const XMMRegister ZTMP18 = xmm1; - const XMMRegister ZTMP19 = xmm2; - const XMMRegister ZTMP20 = xmm8; - const XMMRegister ZTMP21 = xmm22; - const XMMRegister ZTMP22 = xmm23; + if (start_ghash) { + __ evpxorq(ZTMP9, ZTMP9, HASH, Assembler::AVX_512bit); + } + if (hk_broadcast) { + __ evbroadcastf64x2(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 0 * 64), Assembler::AVX_512bit); + __ evbroadcastf64x2(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 0 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 0 * 64), Assembler::AVX_512bit); + __ evmovdquq(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 0 * 64), Assembler::AVX_512bit); + } + + carrylessMultiply(ZTMP0, ZTMP1, ZTMP2, ZTMP3, ZTMP9, ZTMPA, ZTMP8); + + //ghash blocks 4 - 7 + if (uload_shuffle) { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp + 64), Assembler::AVX_512bit); + __ vpshufb(ZTMP9, ZTMP9, SHUFM, Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp + 64), Assembler::AVX_512bit); + } + + if (hk_broadcast) { + __ evbroadcastf64x2(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 1 * 64), Assembler::AVX_512bit);; + __ evbroadcastf64x2(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 1 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 1 * 64), Assembler::AVX_512bit); + __ evmovdquq(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 1 * 64), Assembler::AVX_512bit); + } + + carrylessMultiply(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP9, ZTMPA, ZTMP8); + + //update sums + if (start_ghash) { + __ evpxorq(GL, ZTMP0, ZTMP2, Assembler::AVX_512bit);//T2 = THL + TLL + __ evpxorq(GH, ZTMP1, ZTMP3, Assembler::AVX_512bit);//T1 = THH + TLH + } else { //mid, end, end_reduce + __ vpternlogq(GL, 0x96, ZTMP0, ZTMP2, Assembler::AVX_512bit);//T2 = THL + TLL + __ vpternlogq(GH, 0x96, ZTMP1, ZTMP3, Assembler::AVX_512bit);//T1 = THH + TLH + } + //ghash blocks 8 - 11 + if (uload_shuffle) { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp + 128), Assembler::AVX_512bit); + __ vpshufb(ZTMP9, ZTMP9, SHUFM, Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp + 128), Assembler::AVX_512bit); + } + if (hk_broadcast) { + __ evbroadcastf64x2(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 2 * 64), Assembler::AVX_512bit); + __ evbroadcastf64x2(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 2 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 2 * 64), Assembler::AVX_512bit); + __ evmovdquq(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 2 * 64), Assembler::AVX_512bit); + } + + carrylessMultiply(ZTMP0, ZTMP1, ZTMP2, ZTMP3, ZTMP9, ZTMPA, ZTMP8); + + //update sums + __ vpternlogq(GL, 0x96, ZTMP6, ZTMP4, Assembler::AVX_512bit);//T2 = THL + TLL + __ vpternlogq(GH, 0x96, ZTMP7, ZTMP5, Assembler::AVX_512bit);//T1 = THH + TLH + //ghash blocks 12 - 15 + if (uload_shuffle) { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp + 192), Assembler::AVX_512bit); + __ vpshufb(ZTMP9, ZTMP9, SHUFM, Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP9, Address(subkeyHtbl, in_offset * 16 + in_disp + 192), Assembler::AVX_512bit); + } + + if (hk_broadcast) { + __ evbroadcastf64x2(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 3 * 64), Assembler::AVX_512bit); + __ evbroadcastf64x2(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 3 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(ZTMP8, Address(subkeyHtbl, hashkey_offset + displacement + 3 * 64), Assembler::AVX_512bit); + __ evmovdquq(ZTMPA, Address(subkeyHtbl, hashkey_offset + displacement + hkey_gap + 3 * 64), Assembler::AVX_512bit); + } + carrylessMultiply(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP9, ZTMPA, ZTMP8); + + //update sums + xorGHASH(GL, GH, GL, GH, ZTMP0, ZTMP2, ZTMP1, ZTMP3, ZTMP6, ZTMP4, ZTMP7, ZTMP5); + + if (do_reduction) { + //new reduction + __ evmovdquq(ZTMPB, ExternalAddress(ghash_polynomial_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + __ evpclmulqdq(HASH, GL, ZTMPB, 0x10, Assembler::AVX_512bit); + __ vpshufd(ZTMP0, GL, 78, Assembler::AVX_512bit); + __ vpternlogq(HASH, 0x96, GH, ZTMP0, Assembler::AVX_512bit); + if (do_hxor) { + vhpxori4x128(HASH, ZTMP0); + } + } +} - // Pre increment counters - __ vpaddd(ZTMP0, ctr_blockx, counter_inc_mask, Assembler::AVX_512bit); - __ vpaddd(ZTMP1, ZTMP0, counter_inc_mask, Assembler::AVX_512bit); - __ vpaddd(ZTMP2, ZTMP1, counter_inc_mask, Assembler::AVX_512bit); - __ vpaddd(ZTMP3, ZTMP2, counter_inc_mask, Assembler::AVX_512bit); - // Save counter value - __ evmovdquq(ctr_blockx, ZTMP3, Assembler::AVX_512bit); - - // Reuse ZTMP17 / ZTMP18 for loading AES Keys - // Pre-load AES round keys - ev_load_key(ZTMP17, key, 0, xmm29); - ev_load_key(ZTMP18, key, 1 * 16, xmm29); - - // ZTMP19 & ZTMP20 used for loading hash key - // Pre-load hash key - __ evmovdquq(ZTMP19, Address(subkeyHtbl, i * 64), Assembler::AVX_512bit); - __ evmovdquq(ZTMP20, Address(subkeyHtbl, ++i * 64), Assembler::AVX_512bit); - // Load data for computing ghash - __ evmovdquq(ZTMP21, Address(data, ghash_pos, Address::times_1, 0 * 64), Assembler::AVX_512bit); - __ vpshufb(ZTMP21, ZTMP21, xmm24, Assembler::AVX_512bit); - - // Xor cipher block 0 with input ghash, if available - if (ghash_input) { - __ evpxorq(ZTMP21, ZTMP21, aad_hashx, Assembler::AVX_512bit); +//Stitched GHASH of 16 blocks(with reduction) with encryption of 0 blocks +void StubGenerator::gcm_enc_dec_last_avx512(Register len, Register in, Register pos, XMMRegister HASH, XMMRegister SHUFM, Register subkeyHtbl, + int ghashin_offset, int hashkey_offset, bool start_ghash, bool do_reduction) { + //there is 0 blocks to cipher so there are only 16 blocks for ghash and reduction + ghash16_avx512(start_ghash, do_reduction, false, false, true, in, pos, subkeyHtbl, HASH, SHUFM, ghashin_offset, 0, 0, hashkey_offset); +} + +//Main GCM macro stitching cipher with GHASH +//encrypts 16 blocks at a time +//ghash the 16 previously encrypted ciphertext blocks +void StubGenerator::ghash16_encrypt_parallel16_avx512(Register in, Register out, Register ct, Register pos, Register avx512_subkeyHtbl, + Register CTR_CHECK, Register NROUNDS, Register key, XMMRegister CTR_BE, XMMRegister GHASH_IN, + XMMRegister ADDBE_4x4, XMMRegister ADDBE_1234, XMMRegister ADD_1234, XMMRegister SHFMSK, + bool hk_broadcast, bool is_hash_start, bool do_hash_reduction, bool do_hash_hxor, + bool no_ghash_in, int ghashin_offset, int aesout_offset, int hashkey_offset) { + const XMMRegister B00_03 = xmm0; + const XMMRegister B04_07 = xmm3; + const XMMRegister B08_11 = xmm4; + const XMMRegister B12_15 = xmm5; + const XMMRegister THH1 = xmm6; + const XMMRegister THL1 = xmm7; + const XMMRegister TLH1 = xmm10; + const XMMRegister TLL1 = xmm11, THH2 = xmm12, THL2 = xmm13, TLH2 = xmm15; + const XMMRegister TLL2 = xmm16, THH3 = xmm17, THL3 = xmm19, TLH3 = xmm20; + const XMMRegister TLL3 = xmm21, DATA1 = xmm17, DATA2 = xmm19, DATA3 = xmm20, DATA4 = xmm21; + const XMMRegister AESKEY1 = xmm30, AESKEY2 = xmm31; + const XMMRegister GHKEY1 = xmm1, GHKEY2 = xmm18, GHDAT1 = xmm8, GHDAT2 = xmm22; + const XMMRegister ZT = xmm23, TO_REDUCE_L = xmm25, TO_REDUCE_H = xmm24; + const int hkey_gap = 16 * 32; + + Label blocks_overflow, blocks_ok, skip_shuffle, cont, aes_256, aes_192, last_aes_rnd; + + __ cmpb(CTR_CHECK, (256 - 16)); + __ jcc(Assembler::aboveEqual, blocks_overflow); + __ vpaddd(B00_03, CTR_BE, ADDBE_1234, Assembler::AVX_512bit); + __ vpaddd(B04_07, B00_03, ADDBE_4x4, Assembler::AVX_512bit); + __ vpaddd(B08_11, B04_07, ADDBE_4x4, Assembler::AVX_512bit); + __ vpaddd(B12_15, B08_11, ADDBE_4x4, Assembler::AVX_512bit); + __ jmp(blocks_ok); + __ bind(blocks_overflow); + __ vpshufb(CTR_BE, CTR_BE, SHFMSK, Assembler::AVX_512bit); + __ evmovdquq(B12_15, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + __ vpaddd(B00_03, CTR_BE, ADD_1234, Assembler::AVX_512bit); + __ vpaddd(B04_07, B00_03, B12_15, Assembler::AVX_512bit); + __ vpaddd(B08_11, B04_07, B12_15, Assembler::AVX_512bit); + __ vpaddd(B12_15, B08_11, B12_15, Assembler::AVX_512bit); + shuffle(B00_03, B04_07, B08_11, B12_15, B00_03, B04_07, B08_11, B12_15, SHFMSK); + + __ bind(blocks_ok); + + //pre - load constants + ev_load_key(AESKEY1, key, 0, rbx); + if (!no_ghash_in) { + __ evpxorq(GHDAT1, GHASH_IN, Address(avx512_subkeyHtbl, 16 * ghashin_offset), Assembler::AVX_512bit); + } else { + __ evmovdquq(GHDAT1, Address(avx512_subkeyHtbl, 16 * ghashin_offset), Assembler::AVX_512bit); + } + + if (hk_broadcast) { + __ evbroadcastf64x2(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 0 * 64), Assembler::AVX_512bit); + __ evbroadcastf64x2(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 0 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 0 * 64), Assembler::AVX_512bit); + __ evmovdquq(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 0 * 64), Assembler::AVX_512bit); + } + + //save counter for the next round + //increment counter overflow check register + __ evshufi64x2(CTR_BE, B12_15, B12_15, 255, Assembler::AVX_512bit); + __ addb(CTR_CHECK, 16); + + //pre - load constants + ev_load_key(AESKEY2, key, 1 * 16, rbx); + __ evmovdquq(GHDAT2, Address(avx512_subkeyHtbl, 16 * (ghashin_offset +4)), Assembler::AVX_512bit); + + //stitch AES rounds with GHASH + //AES round 0 + __ evpxorq(B00_03, B00_03, AESKEY1, Assembler::AVX_512bit); + __ evpxorq(B04_07, B04_07, AESKEY1, Assembler::AVX_512bit); + __ evpxorq(B08_11, B08_11, AESKEY1, Assembler::AVX_512bit); + __ evpxorq(B12_15, B12_15, AESKEY1, Assembler::AVX_512bit); + ev_load_key(AESKEY1, key, 2 * 16, rbx); + + //GHASH 4 blocks(15 to 12) + carrylessMultiply(TLL1, TLH1, THL1, THH1, GHDAT1, GHKEY2, GHKEY1); + + if (hk_broadcast) { + __ evbroadcastf64x2(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 1 * 64), Assembler::AVX_512bit); + __ evbroadcastf64x2(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 1 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 1 * 64), Assembler::AVX_512bit); + __ evmovdquq(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 1 * 64), Assembler::AVX_512bit); + } + + __ evmovdquq(GHDAT1, Address(avx512_subkeyHtbl, 16 * (ghashin_offset + 8)), Assembler::AVX_512bit); + + //AES round 1 + roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); + + ev_load_key(AESKEY2, key, 3 * 16, rbx); + + //GHASH 4 blocks(11 to 8) + carrylessMultiply(TLL2, TLH2, THL2, THH2, GHDAT2, GHKEY2, GHKEY1); + + if (hk_broadcast) { + __ evbroadcastf64x2(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 2 * 64), Assembler::AVX_512bit); + __ evbroadcastf64x2(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 2 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 2 * 64 ), Assembler::AVX_512bit); + __ evmovdquq(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 2 * 64), Assembler::AVX_512bit); } - // Load data for computing ghash - __ evmovdquq(ZTMP22, Address(data, ghash_pos, Address::times_1, 1 * 64), Assembler::AVX_512bit); - __ vpshufb(ZTMP22, ZTMP22, xmm24, Assembler::AVX_512bit); - - // stitch AES rounds with GHASH - // AES round 0, xmm24 has shuffle mask - shuffleExorRnd1Key(ZTMP0, ZTMP1, ZTMP2, ZTMP3, xmm24, ZTMP17); - // Reuse ZTMP17 / ZTMP18 for loading remaining AES Keys - ev_load_key(ZTMP17, key, 2 * 16, xmm29); - // GHASH 4 blocks - carrylessMultiply(ZTMP6, ZTMP7, ZTMP8, ZTMP5, ZTMP21, ZTMP19); - // Load the next hkey and Ghash data - __ evmovdquq(ZTMP19, Address(subkeyHtbl, ++i * 64), Assembler::AVX_512bit); - __ evmovdquq(ZTMP21, Address(data, ghash_pos, Address::times_1, 2 * 64), Assembler::AVX_512bit); - __ vpshufb(ZTMP21, ZTMP21, xmm24, Assembler::AVX_512bit); - - // AES round 1 - roundEncode(ZTMP18, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP18, key, 3 * 16, xmm29); - - // GHASH 4 blocks(11 to 8) - carrylessMultiply(ZTMP10, ZTMP12, ZTMP11, ZTMP9, ZTMP22, ZTMP20); - // Load the next hkey and GDATA - __ evmovdquq(ZTMP20, Address(subkeyHtbl, ++i * 64), Assembler::AVX_512bit); - __ evmovdquq(ZTMP22, Address(data, ghash_pos, Address::times_1, 3 * 64), Assembler::AVX_512bit); - __ vpshufb(ZTMP22, ZTMP22, xmm24, Assembler::AVX_512bit); - - // AES round 2 - roundEncode(ZTMP17, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP17, key, 4 * 16, xmm29); - - // GHASH 4 blocks(7 to 4) - carrylessMultiply(ZTMP14, ZTMP16, ZTMP15, ZTMP13, ZTMP21, ZTMP19); - // AES rounds 3 - roundEncode(ZTMP18, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP18, key, 5 * 16, xmm29); - - // Gather(XOR) GHASH for 12 blocks - xorGHASH(ZTMP5, ZTMP6, ZTMP8, ZTMP7, ZTMP9, ZTMP13, ZTMP10, ZTMP14, ZTMP12, ZTMP16, ZTMP11, ZTMP15); - - // AES rounds 4 - roundEncode(ZTMP17, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP17, key, 6 * 16, xmm29); - - // load plain / cipher text(recycle registers) - loadData(in, pos, ZTMP13, ZTMP14, ZTMP15, ZTMP16); - - // AES rounds 5 - roundEncode(ZTMP18, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP18, key, 7 * 16, xmm29); - // GHASH 4 blocks(3 to 0) - carrylessMultiply(ZTMP10, ZTMP12, ZTMP11, ZTMP9, ZTMP22, ZTMP20); - - // AES round 6 - roundEncode(ZTMP17, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP17, key, 8 * 16, xmm29); - - // gather GHASH in ZTMP6(low) and ZTMP5(high) - if (first_time_reduction) { - __ vpternlogq(ZTMP7, 0x96, ZTMP8, ZTMP12, Assembler::AVX_512bit); - __ evpxorq(xmm25, ZTMP7, ZTMP11, Assembler::AVX_512bit); - __ evpxorq(xmm27, ZTMP5, ZTMP9, Assembler::AVX_512bit); - __ evpxorq(xmm26, ZTMP6, ZTMP10, Assembler::AVX_512bit); - } else if (!first_time_reduction && !final_reduction) { - xorGHASH(ZTMP7, xmm25, xmm27, xmm26, ZTMP8, ZTMP12, ZTMP7, ZTMP11, ZTMP5, ZTMP9, ZTMP6, ZTMP10); + __ evmovdquq(GHDAT2, Address(avx512_subkeyHtbl, 16 * (ghashin_offset + 12)), Assembler::AVX_512bit); + + //AES round 2 + roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY1, key, 4 * 16, rbx); + + //GHASH 4 blocks(7 to 4) + carrylessMultiply(TLL3, TLH3, THL3, THH3, GHDAT1, GHKEY2, GHKEY1); + + if (hk_broadcast) { + __ evbroadcastf64x2(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 3 * 64), Assembler::AVX_512bit); + __ evbroadcastf64x2(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 3 * 64), Assembler::AVX_512bit); + } else { + __ evmovdquq(GHKEY1, Address(avx512_subkeyHtbl, hashkey_offset + 3 * 64), Assembler::AVX_512bit); + __ evmovdquq(GHKEY2, Address(avx512_subkeyHtbl, hashkey_offset + hkey_gap + 3 * 64), Assembler::AVX_512bit); } - if (final_reduction) { - // Phase one: Add mid products together - // Also load polynomial constant for reduction - __ vpternlogq(ZTMP7, 0x96, ZTMP8, ZTMP12, Assembler::AVX_512bit); - __ vpternlogq(ZTMP7, 0x96, xmm25, ZTMP11, Assembler::AVX_512bit); - __ vpsrldq(ZTMP11, ZTMP7, 8, Assembler::AVX_512bit); - __ vpslldq(ZTMP7, ZTMP7, 8, Assembler::AVX_512bit); - __ evmovdquq(ZTMP12, ExternalAddress(ghash_polynomial_reduction_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + //AES rounds 3 + roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY2, key, 5 * 16, rbx); + + //Gather(XOR) GHASH for 12 blocks + xorGHASH(TLL1, TLH1, THL1, THH1, TLL2, TLL3, TLH2, TLH3, THL2, THL3, THH2, THH3); + + //AES rounds 4 + roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY1, key, 6 * 16, rbx); + + //load plain / cipher text(recycle GH3xx registers) + loadData(in, pos, DATA1, DATA2, DATA3, DATA4); + + //AES rounds 5 + roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY2, key, 7 * 16, rbx); + + //GHASH 4 blocks(3 to 0) + carrylessMultiply(TLL2, TLH2, THL2, THH2, GHDAT2, GHKEY2, GHKEY1); + + //AES round 6 + roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY1, key, 8 * 16, rbx); + + //gather GHASH in TO_REDUCE_H / L + if (is_hash_start) { + __ evpxorq(TO_REDUCE_L, TLL2, THL2, Assembler::AVX_512bit); + __ evpxorq(TO_REDUCE_H, THH2, TLH2, Assembler::AVX_512bit); + __ vpternlogq(TO_REDUCE_L, 0x96, TLL1, THL1, Assembler::AVX_512bit); + __ vpternlogq(TO_REDUCE_H, 0x96, THH1, TLH1, Assembler::AVX_512bit); + } else { + //not the first round so sums need to be updated + xorGHASH(TO_REDUCE_L, TO_REDUCE_H, TO_REDUCE_L, TO_REDUCE_H, TLL2, THL2, THH2, TLH2, TLL1, THL1, THH1, TLH1); } - // AES round 7 - roundEncode(ZTMP18, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP18, key, 9 * 16, xmm29); - if (final_reduction) { - __ vpternlogq(ZTMP5, 0x96, ZTMP9, ZTMP11, Assembler::AVX_512bit); - __ evpxorq(ZTMP5, ZTMP5, xmm27, Assembler::AVX_512bit); - __ vpternlogq(ZTMP6, 0x96, ZTMP10, ZTMP7, Assembler::AVX_512bit); - __ evpxorq(ZTMP6, ZTMP6, xmm26, Assembler::AVX_512bit); + + //AES round 7 + roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY2, key, 9 * 16, rbx); + + //new reduction + if (do_hash_reduction) { + __ evmovdquq(ZT, ExternalAddress(ghash_polynomial_reduction_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + __ evpclmulqdq(THH1, TO_REDUCE_L, ZT, 0x10, Assembler::AVX_512bit); + __ vpshufd(TO_REDUCE_L, TO_REDUCE_L, 78, Assembler::AVX_512bit); + __ vpternlogq(THH1, 0x96, TO_REDUCE_H, TO_REDUCE_L, Assembler::AVX_512bit); } - // AES round 8 - roundEncode(ZTMP17, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP17, key, 10 * 16, xmm29); - - // Horizontal xor of low and high 4*128 - if (final_reduction) { - vhpxori4x128(ZTMP5, ZTMP9); - vhpxori4x128(ZTMP6, ZTMP10); + + //AES round 8 + roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY1, key, 10 * 16, rbx); + + //horizontalxor of 4 reduced hashes + if (do_hash_hxor) { + vhpxori4x128(THH1, TLL1); } - // AES round 9 - roundEncode(ZTMP18, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - // First phase of reduction - if (final_reduction) { - __ evpclmulqdq(ZTMP10, ZTMP12, ZTMP6, 0x01, Assembler::AVX_128bit); - __ vpslldq(ZTMP10, ZTMP10, 8, Assembler::AVX_128bit); - __ evpxorq(ZTMP10, ZTMP6, ZTMP10, Assembler::AVX_128bit); + + //AES round 9 + roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY2, key, 11 * 16, rbx); + //AES rounds up to 11 (AES192) or 13 (AES256) + //AES128 is done + __ cmpl(NROUNDS, 52); + __ jcc(Assembler::less, last_aes_rnd); + __ bind(aes_192); + roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY1, key, 12 * 16, rbx); + roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); + __ cmpl(NROUNDS, 60); + __ jcc(Assembler::less, last_aes_rnd); + __ bind(aes_256); + ev_load_key(AESKEY2, key, 13 * 16, rbx); + roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); + ev_load_key(AESKEY1, key, 14 * 16, rbx); + roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15); + + __ bind(last_aes_rnd); + //the last AES round + lastroundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15); + //AESKEY1and AESKEY2 contain AES round keys + + //XOR against plain / cipher text + xorBeforeStore(B00_03, B04_07, B08_11, B12_15, DATA1, DATA2, DATA3, DATA4); + + //store cipher / plain text + storeData(out, pos, B00_03, B04_07, B08_11, B12_15); + //**B00_03, B04_07, B08_011, B12_B15 may contain sensitive data + + //shuffle cipher text blocks for GHASH computation + __ cmpptr(ct, out); + __ jcc(Assembler::notEqual, skip_shuffle); + shuffle(B00_03, B04_07, B08_11, B12_15, B00_03, B04_07, B08_11, B12_15, SHFMSK); + __ jmp(cont); + __ bind(skip_shuffle); + shuffle(B00_03, B04_07, B08_11, B12_15, DATA1, DATA2, DATA3, DATA4, SHFMSK); + + //**B00_03, B04_07, B08_011, B12_B15 overwritten with shuffled cipher text + __ bind(cont); + //store shuffled cipher text for ghashing + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * aesout_offset), B00_03, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * (aesout_offset + 4)), B04_07, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * (aesout_offset + 8)), B08_11, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * (aesout_offset + 12)), B12_15, Assembler::AVX_512bit); +} + + +//Encrypt / decrypt the initial 16 blocks +void StubGenerator::initial_blocks_16_avx512(Register in, Register out, Register ct, Register pos, Register key, Register avx512_subkeyHtbl, + Register CTR_CHECK, Register rounds, XMMRegister CTR, XMMRegister GHASH, XMMRegister ADDBE_4x4, + XMMRegister ADDBE_1234, XMMRegister ADD_1234, XMMRegister SHUF_MASK, int stack_offset) { + const XMMRegister B00_03 = xmm7; + const XMMRegister B04_07 = xmm10; + const XMMRegister B08_11 = xmm11; + const XMMRegister B12_15 = xmm12; + const XMMRegister T0 = xmm0; + const XMMRegister T1 = xmm3; + const XMMRegister T2 = xmm4; + const XMMRegister T3 = xmm5; + const XMMRegister T4 = xmm6; + const XMMRegister T5 = xmm30; + + Label next_16_overflow, next_16_ok, cont, skip_shuffle, aes_256, aes_192, last_aes_rnd; + //prepare counter blocks + __ cmpb(CTR_CHECK, (256 - 16)); + __ jcc(Assembler::aboveEqual, next_16_overflow); + __ vpaddd(B00_03, CTR, ADDBE_1234, Assembler::AVX_512bit); + __ vpaddd(B04_07, B00_03, ADDBE_4x4, Assembler::AVX_512bit); + __ vpaddd(B08_11, B04_07, ADDBE_4x4, Assembler::AVX_512bit); + __ vpaddd(B12_15, B08_11, ADDBE_4x4, Assembler::AVX_512bit); + __ jmp(next_16_ok); + __ bind(next_16_overflow); + __ vpshufb(CTR, CTR, SHUF_MASK, Assembler::AVX_512bit); + __ evmovdquq(B12_15, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, rbx); + __ vpaddd(B00_03, CTR, ADD_1234, Assembler::AVX_512bit); + __ vpaddd(B04_07, B00_03, B12_15, Assembler::AVX_512bit); + __ vpaddd(B08_11, B04_07, B12_15, Assembler::AVX_512bit); + __ vpaddd(B12_15, B08_11, B12_15, Assembler::AVX_512bit); + shuffle(B00_03, B04_07, B08_11, B12_15, B00_03, B04_07, B08_11, B12_15, SHUF_MASK); + __ bind(next_16_ok); + __ evshufi64x2(CTR, B12_15, B12_15, 255, Assembler::AVX_512bit); + __ addb(CTR_CHECK, 16); + + //load 16 blocks of data + loadData(in, pos, T0, T1, T2, T3); + + //move to AES encryption rounds + __ movdqu(T5, ExternalAddress(key_shuffle_mask_addr()), rbx /*rscratch*/); + ev_load_key(T4, key, 0, T5); + __ evpxorq(B00_03, B00_03, T4, Assembler::AVX_512bit); + __ evpxorq(B04_07, B04_07, T4, Assembler::AVX_512bit); + __ evpxorq(B08_11, B08_11, T4, Assembler::AVX_512bit); + __ evpxorq(B12_15, B12_15, T4, Assembler::AVX_512bit); + + for (int i = 1; i < 10; i++) { + ev_load_key(T4, key, i * 16, T5); + roundEncode(T4, B00_03, B04_07, B08_11, B12_15); } + + ev_load_key(T4, key, 10 * 16, T5); __ cmpl(rounds, 52); - __ jcc(Assembler::greaterEqual, AES_192); - __ jmp(LAST_AES_RND); - // AES rounds up to 11 (AES192) or 13 (AES256) - __ bind(AES_192); - roundEncode(ZTMP17, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP18, key, 11 * 16, xmm29); - roundEncode(ZTMP18, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP17, key, 12 * 16, xmm29); + __ jcc(Assembler::less, last_aes_rnd); + __ bind(aes_192); + roundEncode(T4, B00_03, B04_07, B08_11, B12_15); + ev_load_key(T4, key, 16 * 11, T5); + roundEncode(T4, B00_03, B04_07, B08_11, B12_15); + ev_load_key(T4, key, 16 * 12, T5); __ cmpl(rounds, 60); - __ jcc(Assembler::aboveEqual, AES_256); - __ jmp(LAST_AES_RND); - - __ bind(AES_256); - roundEncode(ZTMP17, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP18, key, 13 * 16, xmm29); - roundEncode(ZTMP18, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - ev_load_key(ZTMP17, key, 14 * 16, xmm29); - - __ bind(LAST_AES_RND); - // Second phase of reduction - if (final_reduction) { - __ evpclmulqdq(ZTMP9, ZTMP12, ZTMP10, 0x00, Assembler::AVX_128bit); - __ vpsrldq(ZTMP9, ZTMP9, 4, Assembler::AVX_128bit); // Shift-R 1-DW to obtain 2-DWs shift-R - __ evpclmulqdq(ZTMP11, ZTMP12, ZTMP10, 0x10, Assembler::AVX_128bit); - __ vpslldq(ZTMP11, ZTMP11, 4, Assembler::AVX_128bit); // Shift-L 1-DW for result - // ZTMP5 = ZTMP5 X ZTMP11 X ZTMP9 - __ vpternlogq(ZTMP5, 0x96, ZTMP11, ZTMP9, Assembler::AVX_128bit); - } - // Last AES round - lastroundEncode(ZTMP17, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - // XOR against plain / cipher text - xorBeforeStore(ZTMP0, ZTMP1, ZTMP2, ZTMP3, ZTMP13, ZTMP14, ZTMP15, ZTMP16); - // store cipher / plain text - storeData(out, pos, ZTMP0, ZTMP1, ZTMP2, ZTMP3); + __ jcc(Assembler::less, last_aes_rnd); + __ bind(aes_256); + roundEncode(T4, B00_03, B04_07, B08_11, B12_15); + ev_load_key(T4, key, 16 * 13, T5); + roundEncode(T4, B00_03, B04_07, B08_11, B12_15); + ev_load_key(T4, key, 16 * 14, T5); + + __ bind(last_aes_rnd); + lastroundEncode(T4, B00_03, B04_07, B08_11, B12_15); + + //xor against text + xorBeforeStore(B00_03, B04_07, B08_11, B12_15, T0, T1, T2, T3); + + //store + storeData(out, pos, B00_03, B04_07, B08_11, B12_15); + + __ cmpptr(ct, out); + __ jcc(Assembler::equal, skip_shuffle); + //decryption - cipher text needs to go to GHASH phase + shuffle(B00_03, B04_07, B08_11, B12_15, T0, T1, T2, T3, SHUF_MASK); + __ jmp(cont); + __ bind(skip_shuffle); + shuffle(B00_03, B04_07, B08_11, B12_15, B00_03, B04_07, B08_11, B12_15, SHUF_MASK); + + //B00_03, B04_07, B08_11, B12_15 overwritten with shuffled cipher text + __ bind(cont); + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * stack_offset), B00_03, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * (stack_offset + 4)), B04_07, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * (stack_offset + 8)), B08_11, Assembler::AVX_512bit); + __ evmovdquq(Address(avx512_subkeyHtbl, 16 * (stack_offset + 12)), B12_15, Assembler::AVX_512bit); } -void StubGenerator::aesgcm_encrypt(Register in, Register len, Register ct, Register out, Register key, - Register state, Register subkeyHtbl, Register avx512_subkeyHtbl, Register counter) { - Label ENC_DEC_DONE, GENERATE_HTBL_48_BLKS, AES_192, AES_256, STORE_CT, GHASH_LAST_32, - AES_32_BLOCKS, GHASH_AES_PARALLEL, LOOP, ACCUMULATE, GHASH_16_AES_16; - const XMMRegister CTR_BLOCKx = xmm9; +void StubGenerator::aesgcm_avx512(Register in, Register len, Register ct, Register out, Register key, Register state, + Register subkeyHtbl, Register avx512_subkeyHtbl, Register counter) { + Label ENC_DEC_DONE, MESG_BELOW_32_BLKS, NO_BIG_BLKS, ENCRYPT_BIG_BLKS_NO_HXOR, + ENCRYPT_BIG_NBLKS, ENCRYPT_16_BLKS, ENCRYPT_N_GHASH_32_N_BLKS, GHASH_DONE; + const XMMRegister CTR_BLOCKx = xmm2; const XMMRegister AAD_HASHx = xmm14; - const Register pos = rax; - const Register rounds = r15; - const Register ghash_pos = NOT_WIN64( r14) WIN64_ONLY( r11 ); const XMMRegister ZTMP0 = xmm0; - const XMMRegister ZTMP1 = xmm3; - const XMMRegister ZTMP2 = xmm4; - const XMMRegister ZTMP3 = xmm5; + const XMMRegister ZTMP1 = xmm3; //**sensitive + const XMMRegister ZTMP2 = xmm4; //**sensitive(small data) + const XMMRegister ZTMP3 = xmm5; //**sensitive(small data) const XMMRegister ZTMP4 = xmm6; const XMMRegister ZTMP5 = xmm7; const XMMRegister ZTMP6 = xmm10; @@ -3066,235 +3358,170 @@ void StubGenerator::aesgcm_encrypt(Register in, Register len, Register ct, Regis const XMMRegister ZTMP14 = xmm20; const XMMRegister ZTMP15 = xmm21; const XMMRegister ZTMP16 = xmm30; - const XMMRegister COUNTER_INC_MASK = xmm18; - - __ movl(pos, 0); // Total length processed - // Min data size processed = 768 bytes - __ cmpl(len, 768); - __ jcc(Assembler::less, ENC_DEC_DONE); + const XMMRegister ZTMP17 = xmm31; + const XMMRegister ZTMP18 = xmm1; + const XMMRegister ZTMP19 = xmm18; + const XMMRegister ZTMP20 = xmm8; + const XMMRegister ZTMP21 = xmm22; + const XMMRegister ZTMP22 = xmm23; + const XMMRegister ZTMP23 = xmm26; + const XMMRegister GH = xmm24; + const XMMRegister GL = xmm25; + const XMMRegister SHUF_MASK = xmm29; + const XMMRegister ADDBE_4x4 = xmm27; + const XMMRegister ADDBE_1234 = xmm28; + const XMMRegister ADD_1234 = xmm9; + const KRegister MASKREG = k1; + const Register pos = rax; + const Register rounds = r15; + const Register CTR_CHECK = r14; - // Generate 48 constants for htbl - __ call(GENERATE_HTBL_48_BLKS, relocInfo::none); - int index = 0; // Index for choosing subkeyHtbl entry - __ movl(ghash_pos, 0); // Pointer for ghash read and store operations + const int stack_offset = 64; + const int ghashin_offset = 64; + const int aesout_offset = 64; + const int hashkey_offset = 0; + const int hashkey_gap = 16 * 32; + const int HashKey_32 = 0; + const int HashKey_16 = 16 * 16; - // Move initial counter value and STATE value into variables + __ movl(pos, 0); + __ cmpl(len, 256); + __ jcc(Assembler::lessEqual, ENC_DEC_DONE); + + /* Structure of the Htbl is as follows: + * Where 0 - 31 we have 32 Hashkey's and 32-63 we have 32 HashKeyK (derived from HashKey) + * Rest 8 entries are for storing CTR values post AES rounds + * ---------------------------------------------------------------------------------------- + Hashkey32 -> 16 * 0 + Hashkey31 -> 16 * 1 + Hashkey30 -> 16 * 2 + ........ + Hashkey1 -> 16 * 31 + --------------------- + HaskeyK32 -> 16 * 32 + HashkeyK31 -> 16 * 33 + ......... + HashkeyK1 -> 16 * 63 + --------------------- + 1st set of AES Entries + B00_03 -> 16 * 64 + B04_07 -> 16 * 68 + B08_11 -> 16 * 72 + B12_15 -> 16 * 80 + --------------------- + 2nd set of AES Entries + B00_03 -> 16 * 84 + B04_07 -> 16 * 88 + B08_11 -> 16 * 92 + B12_15 -> 16 * 96 + ---------------------*/ + generateHtbl_32_blocks_avx512(subkeyHtbl, avx512_subkeyHtbl); + + //Move initial counter value and STATE value into variables __ movdqu(CTR_BLOCKx, Address(counter, 0)); __ movdqu(AAD_HASHx, Address(state, 0)); - // Load lswap mask for ghash + + //Load lswap mask for ghash __ movdqu(xmm24, ExternalAddress(ghash_long_swap_mask_addr()), rbx /*rscratch*/); - // Shuffle input state using lswap mask + //Shuffle input state using lswap mask __ vpshufb(AAD_HASHx, AAD_HASHx, xmm24, Assembler::AVX_128bit); // Compute #rounds for AES based on the length of the key array __ movl(rounds, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); - // Broadcast counter value to 512 bit register + __ evmovdquq(ADDBE_4x4, ExternalAddress(counter_mask_addbe_4444_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + __ evmovdquq(ADDBE_1234, ExternalAddress(counter_mask_addbe_1234_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + __ evmovdquq(SHUF_MASK, ExternalAddress(counter_shuffle_mask_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + __ evmovdquq(ADD_1234, ExternalAddress(counter_mask_add_1234_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); + + //Shuffle counter, subtract 1 from the pre-incremented counter value and broadcast counter value to 512 bit register + __ vpshufb(CTR_BLOCKx, CTR_BLOCKx, SHUF_MASK, Assembler::AVX_128bit); + __ vpsubd(CTR_BLOCKx, CTR_BLOCKx, ADD_1234, Assembler::AVX_128bit); __ evshufi64x2(CTR_BLOCKx, CTR_BLOCKx, CTR_BLOCKx, 0, Assembler::AVX_512bit); - // Load counter shuffle mask - __ evmovdquq(xmm24, ExternalAddress(counter_shuffle_mask_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); - // Shuffle counter - __ vpshufb(CTR_BLOCKx, CTR_BLOCKx, xmm24, Assembler::AVX_512bit); - - // Load mask for incrementing counter - __ evmovdquq(COUNTER_INC_MASK, ExternalAddress(counter_mask_linc4_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); - // Pre-increment counter - __ vpaddd(ZTMP5, CTR_BLOCKx, ExternalAddress(counter_mask_linc0_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); - __ vpaddd(ZTMP6, ZTMP5, COUNTER_INC_MASK, Assembler::AVX_512bit); - __ vpaddd(ZTMP7, ZTMP6, COUNTER_INC_MASK, Assembler::AVX_512bit); - __ vpaddd(ZTMP8, ZTMP7, COUNTER_INC_MASK, Assembler::AVX_512bit); - - // Begin 32 blocks of AES processing - __ bind(AES_32_BLOCKS); - // Save incremented counter before overwriting it with AES data - __ evmovdquq(CTR_BLOCKx, ZTMP8, Assembler::AVX_512bit); - - // Move 256 bytes of data - loadData(in, pos, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - // Load key shuffle mask - __ movdqu(xmm29, ExternalAddress(key_shuffle_mask_addr()), rbx /*rscratch*/); - // Load 0th AES round key - ev_load_key(ZTMP4, key, 0, xmm29); - // AES-ROUND0, xmm24 has the shuffle mask - shuffleExorRnd1Key(ZTMP5, ZTMP6, ZTMP7, ZTMP8, xmm24, ZTMP4); - - for (int j = 1; j < 10; j++) { - ev_load_key(ZTMP4, key, j * 16, xmm29); - roundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - } - ev_load_key(ZTMP4, key, 10 * 16, xmm29); - // AES rounds up to 11 (AES192) or 13 (AES256) - __ cmpl(rounds, 52); - __ jcc(Assembler::greaterEqual, AES_192); - lastroundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - __ jmp(STORE_CT); - - __ bind(AES_192); - roundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - ev_load_key(ZTMP4, key, 11 * 16, xmm29); - roundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - __ cmpl(rounds, 60); - __ jcc(Assembler::aboveEqual, AES_256); - ev_load_key(ZTMP4, key, 12 * 16, xmm29); - lastroundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - __ jmp(STORE_CT); - - __ bind(AES_256); - ev_load_key(ZTMP4, key, 12 * 16, xmm29); - roundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - ev_load_key(ZTMP4, key, 13 * 16, xmm29); - roundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - ev_load_key(ZTMP4, key, 14 * 16, xmm29); - // Last AES round - lastroundEncode(ZTMP4, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - - __ bind(STORE_CT); - // Xor the encrypted key with PT to obtain CT - xorBeforeStore(ZTMP5, ZTMP6, ZTMP7, ZTMP8, ZTMP0, ZTMP1, ZTMP2, ZTMP3); - storeData(out, pos, ZTMP5, ZTMP6, ZTMP7, ZTMP8); - // 16 blocks encryption completed - __ addl(pos, 256); - __ cmpl(pos, 512); - __ jcc(Assembler::aboveEqual, GHASH_AES_PARALLEL); - __ vpaddd(ZTMP5, CTR_BLOCKx, COUNTER_INC_MASK, Assembler::AVX_512bit); - __ vpaddd(ZTMP6, ZTMP5, COUNTER_INC_MASK, Assembler::AVX_512bit); - __ vpaddd(ZTMP7, ZTMP6, COUNTER_INC_MASK, Assembler::AVX_512bit); - __ vpaddd(ZTMP8, ZTMP7, COUNTER_INC_MASK, Assembler::AVX_512bit); - __ jmp(AES_32_BLOCKS); - - __ bind(GHASH_AES_PARALLEL); - // Ghash16_encrypt16_parallel takes place in the order with three reduction values: - // 1) First time -> cipher xor input ghash - // 2) No reduction -> accumulate multiplication values - // 3) Final reduction post 48 blocks -> new ghash value is computed for the next round - // Reduction value = first time - ghash16_encrypt16_parallel(key, avx512_subkeyHtbl, CTR_BLOCKx, AAD_HASHx, in, out, ct, pos, true, xmm24, true, rounds, ghash_pos, false, index, COUNTER_INC_MASK); - __ addl(pos, 256); - __ addl(ghash_pos, 256); - index += 4; - - // At this point we have processed 768 bytes of AES and 256 bytes of GHASH. - // If the remaining length is less than 768, process remaining 512 bytes of ghash in GHASH_LAST_32 code - __ subl(len, 768); - __ cmpl(len, 768); - __ jcc(Assembler::less, GHASH_LAST_32); - - // AES 16 blocks and GHASH 16 blocks in parallel - // For multiples of 48 blocks we will do ghash16_encrypt16 interleaved multiple times - // Reduction value = no reduction means that the carryless multiplication values are accumulated for further calculations - // Each call uses 4 subkeyHtbl values, so increment the index by 4. - __ bind(GHASH_16_AES_16); - // Reduction value = no reduction - ghash16_encrypt16_parallel(key, avx512_subkeyHtbl, CTR_BLOCKx, AAD_HASHx, in, out, ct, pos, false, xmm24, false, rounds, ghash_pos, false, index, COUNTER_INC_MASK); - __ addl(pos, 256); - __ addl(ghash_pos, 256); - index += 4; - // Reduction value = final reduction means that the accumulated values have to be reduced as we have completed 48 blocks of ghash - ghash16_encrypt16_parallel(key, avx512_subkeyHtbl, CTR_BLOCKx, AAD_HASHx, in, out, ct, pos, false, xmm24, false, rounds, ghash_pos, true, index, COUNTER_INC_MASK); - __ addl(pos, 256); - __ addl(ghash_pos, 256); - // Calculated ghash value needs to be __ moved to AAD_HASHX so that we can restart the ghash16-aes16 pipeline - __ movdqu(AAD_HASHx, ZTMP5); - index = 0; // Reset subkeyHtbl index - - // Restart the pipeline - // Reduction value = first time - ghash16_encrypt16_parallel(key, avx512_subkeyHtbl, CTR_BLOCKx, AAD_HASHx, in, out, ct, pos, true, xmm24, true, rounds, ghash_pos, false, index, COUNTER_INC_MASK); - __ addl(pos, 256); - __ addl(ghash_pos, 256); - index += 4; - - __ subl(len, 768); - __ cmpl(len, 768); - __ jcc(Assembler::greaterEqual, GHASH_16_AES_16); - - // GHASH last 32 blocks processed here - // GHASH products accumulated in ZMM27, ZMM25 and ZMM26 during GHASH16-AES16 operation is used - __ bind(GHASH_LAST_32); - // Use rbx as a pointer to the htbl; For last 32 blocks of GHASH, use key# 4-11 entry in subkeyHtbl - __ movl(rbx, 256); - // Load cipher blocks - __ evmovdquq(ZTMP13, Address(ct, ghash_pos, Address::times_1, 0 * 64), Assembler::AVX_512bit); - __ evmovdquq(ZTMP14, Address(ct, ghash_pos, Address::times_1, 1 * 64), Assembler::AVX_512bit); - __ vpshufb(ZTMP13, ZTMP13, xmm24, Assembler::AVX_512bit); - __ vpshufb(ZTMP14, ZTMP14, xmm24, Assembler::AVX_512bit); - // Load ghash keys - __ evmovdquq(ZTMP15, Address(avx512_subkeyHtbl, rbx, Address::times_1, 0 * 64), Assembler::AVX_512bit); - __ evmovdquq(ZTMP16, Address(avx512_subkeyHtbl, rbx, Address::times_1, 1 * 64), Assembler::AVX_512bit); - - // Ghash blocks 0 - 3 - carrylessMultiply(ZTMP2, ZTMP3, ZTMP4, ZTMP1, ZTMP13, ZTMP15); - // Ghash blocks 4 - 7 - carrylessMultiply(ZTMP6, ZTMP7, ZTMP8, ZTMP5, ZTMP14, ZTMP16); - - __ vpternlogq(ZTMP1, 0x96, ZTMP5, xmm27, Assembler::AVX_512bit); // ZTMP1 = ZTMP1 + ZTMP5 + zmm27 - __ vpternlogq(ZTMP2, 0x96, ZTMP6, xmm26, Assembler::AVX_512bit); // ZTMP2 = ZTMP2 + ZTMP6 + zmm26 - __ vpternlogq(ZTMP3, 0x96, ZTMP7, xmm25, Assembler::AVX_512bit); // ZTMP3 = ZTMP3 + ZTMP7 + zmm25 - __ evpxorq(ZTMP4, ZTMP4, ZTMP8, Assembler::AVX_512bit); // ZTMP4 = ZTMP4 + ZTMP8 - - __ addl(ghash_pos, 128); - __ addl(rbx, 128); - - // Ghash remaining blocks - __ bind(LOOP); - __ cmpl(ghash_pos, pos); - __ jcc(Assembler::aboveEqual, ACCUMULATE); - // Load next cipher blocks and corresponding ghash keys - __ evmovdquq(ZTMP13, Address(ct, ghash_pos, Address::times_1, 0 * 64), Assembler::AVX_512bit); - __ evmovdquq(ZTMP14, Address(ct, ghash_pos, Address::times_1, 1 * 64), Assembler::AVX_512bit); - __ vpshufb(ZTMP13, ZTMP13, xmm24, Assembler::AVX_512bit); - __ vpshufb(ZTMP14, ZTMP14, xmm24, Assembler::AVX_512bit); - __ evmovdquq(ZTMP15, Address(avx512_subkeyHtbl, rbx, Address::times_1, 0 * 64), Assembler::AVX_512bit); - __ evmovdquq(ZTMP16, Address(avx512_subkeyHtbl, rbx, Address::times_1, 1 * 64), Assembler::AVX_512bit); - - // ghash blocks 0 - 3 - carrylessMultiply(ZTMP6, ZTMP7, ZTMP8, ZTMP5, ZTMP13, ZTMP15); - - // ghash blocks 4 - 7 - carrylessMultiply(ZTMP10, ZTMP11, ZTMP12, ZTMP9, ZTMP14, ZTMP16); - - // update sums - // ZTMP1 = ZTMP1 + ZTMP5 + ZTMP9 - // ZTMP2 = ZTMP2 + ZTMP6 + ZTMP10 - // ZTMP3 = ZTMP3 + ZTMP7 xor ZTMP11 - // ZTMP4 = ZTMP4 + ZTMP8 xor ZTMP12 - xorGHASH(ZTMP1, ZTMP2, ZTMP3, ZTMP4, ZTMP5, ZTMP9, ZTMP6, ZTMP10, ZTMP7, ZTMP11, ZTMP8, ZTMP12); - __ addl(ghash_pos, 128); - __ addl(rbx, 128); - __ jmp(LOOP); - // Integrate ZTMP3/ZTMP4 into ZTMP1 and ZTMP2 - __ bind(ACCUMULATE); - __ evpxorq(ZTMP3, ZTMP3, ZTMP4, Assembler::AVX_512bit); - __ vpsrldq(ZTMP7, ZTMP3, 8, Assembler::AVX_512bit); - __ vpslldq(ZTMP8, ZTMP3, 8, Assembler::AVX_512bit); - __ evpxorq(ZTMP1, ZTMP1, ZTMP7, Assembler::AVX_512bit); - __ evpxorq(ZTMP2, ZTMP2, ZTMP8, Assembler::AVX_512bit); - - // Add ZTMP1 and ZTMP2 128 - bit words horizontally - vhpxori4x128(ZTMP1, ZTMP11); - vhpxori4x128(ZTMP2, ZTMP12); - // Load reduction polynomial and compute final reduction - __ evmovdquq(ZTMP15, ExternalAddress(ghash_polynomial_reduction_addr()), Assembler::AVX_512bit, rbx /*rscratch*/); - vclmul_reduce(AAD_HASHx, ZTMP15, ZTMP1, ZTMP2, ZTMP3, ZTMP4); - - // Pre-increment counter for next operation - __ vpaddd(CTR_BLOCKx, CTR_BLOCKx, xmm18, Assembler::AVX_128bit); - // Shuffle counter and save the updated value - __ vpshufb(CTR_BLOCKx, CTR_BLOCKx, xmm24, Assembler::AVX_512bit); + __ movdl(CTR_CHECK, CTR_BLOCKx); + __ andl(CTR_CHECK, 255); + + // Reshuffle counter + __ vpshufb(CTR_BLOCKx, CTR_BLOCKx, SHUF_MASK, Assembler::AVX_512bit); + + initial_blocks_16_avx512(in, out, ct, pos, key, avx512_subkeyHtbl, CTR_CHECK, rounds, CTR_BLOCKx, AAD_HASHx, ADDBE_4x4, ADDBE_1234, ADD_1234, SHUF_MASK, stack_offset); + __ addl(pos, 16 * 16); + __ cmpl(len, 32 * 16); + __ jcc(Assembler::below, MESG_BELOW_32_BLKS); + + initial_blocks_16_avx512(in, out, ct, pos, key, avx512_subkeyHtbl, CTR_CHECK, rounds, CTR_BLOCKx, AAD_HASHx, ADDBE_4x4, ADDBE_1234, ADD_1234, SHUF_MASK, stack_offset + 16); + __ addl(pos, 16 * 16); + __ subl(len, 32 * 16); + + __ cmpl(len, 32 * 16); + __ jcc(Assembler::below, NO_BIG_BLKS); + + __ bind(ENCRYPT_BIG_BLKS_NO_HXOR); + __ cmpl(len, 2 * 32 * 16); + __ jcc(Assembler::below, ENCRYPT_BIG_NBLKS); + ghash16_encrypt_parallel16_avx512(in, out, ct, pos, avx512_subkeyHtbl, CTR_CHECK, rounds, key, CTR_BLOCKx, AAD_HASHx, ADDBE_4x4, ADDBE_1234, ADD_1234, SHUF_MASK, + true, true, false, false, false, ghashin_offset, aesout_offset, HashKey_32); + __ addl(pos, 16 * 16); + + ghash16_encrypt_parallel16_avx512(in, out, ct, pos, avx512_subkeyHtbl, CTR_CHECK, rounds, key, CTR_BLOCKx, AAD_HASHx, ADDBE_4x4, ADDBE_1234, ADD_1234, SHUF_MASK, + true, false, true, false, true, ghashin_offset + 16, aesout_offset + 16, HashKey_16); + __ evmovdquq(AAD_HASHx, ZTMP4, Assembler::AVX_512bit); + __ addl(pos, 16 * 16); + __ subl(len, 32 * 16); + __ jmp(ENCRYPT_BIG_BLKS_NO_HXOR); + + __ bind(ENCRYPT_BIG_NBLKS); + ghash16_encrypt_parallel16_avx512(in, out, ct, pos, avx512_subkeyHtbl, CTR_CHECK, rounds, key, CTR_BLOCKx, AAD_HASHx, ADDBE_4x4, ADDBE_1234, ADD_1234, SHUF_MASK, + false, true, false, false, false, ghashin_offset, aesout_offset, HashKey_32); + __ addl(pos, 16 * 16); + ghash16_encrypt_parallel16_avx512(in, out, ct, pos, avx512_subkeyHtbl, CTR_CHECK, rounds, key, CTR_BLOCKx, AAD_HASHx, ADDBE_4x4, ADDBE_1234, ADD_1234, SHUF_MASK, + false, false, true, true, true, ghashin_offset + 16, aesout_offset + 16, HashKey_16); + + __ movdqu(AAD_HASHx, ZTMP4); + __ addl(pos, 16 * 16); + __ subl(len, 32 * 16); + + __ bind(NO_BIG_BLKS); + __ cmpl(len, 16 * 16); + __ jcc(Assembler::aboveEqual, ENCRYPT_16_BLKS); + + __ bind(ENCRYPT_N_GHASH_32_N_BLKS); + ghash16_avx512(true, false, false, false, true, in, pos, avx512_subkeyHtbl, AAD_HASHx, SHUF_MASK, stack_offset, 0, 0, HashKey_32); + gcm_enc_dec_last_avx512(len, in, pos, AAD_HASHx, SHUF_MASK, avx512_subkeyHtbl, ghashin_offset + 16, HashKey_16, false, true); + __ jmp(GHASH_DONE); + + __ bind(ENCRYPT_16_BLKS); + ghash16_encrypt_parallel16_avx512(in, out, ct, pos, avx512_subkeyHtbl, CTR_CHECK, rounds, key, CTR_BLOCKx, AAD_HASHx, ADDBE_4x4, ADDBE_1234, ADD_1234, SHUF_MASK, + false, true, false, false, false, ghashin_offset, aesout_offset, HashKey_32); + + ghash16_avx512(false, true, false, false, true, in, pos, avx512_subkeyHtbl, AAD_HASHx, SHUF_MASK, stack_offset, 16 * 16, 0, HashKey_16); + + __ bind(MESG_BELOW_32_BLKS); + __ subl(len, 16 * 16); + __ addl(pos, 16 * 16); + gcm_enc_dec_last_avx512(len, in, pos, AAD_HASHx, SHUF_MASK, avx512_subkeyHtbl, ghashin_offset, HashKey_16, true, true); + + __ bind(GHASH_DONE); + //Pre-increment counter for next operation, make sure that counter value is incremented on the LSB + __ vpshufb(CTR_BLOCKx, CTR_BLOCKx, SHUF_MASK, Assembler::AVX_128bit); + __ vpaddd(CTR_BLOCKx, CTR_BLOCKx, ADD_1234, Assembler::AVX_128bit); + __ vpshufb(CTR_BLOCKx, CTR_BLOCKx, SHUF_MASK, Assembler::AVX_128bit); __ movdqu(Address(counter, 0), CTR_BLOCKx); - // Load ghash lswap mask + //Load ghash lswap mask __ movdqu(xmm24, ExternalAddress(ghash_long_swap_mask_addr()), rbx /*rscratch*/); - // Shuffle ghash using lbswap_mask and store it + //Shuffle ghash using lbswap_mask and store it __ vpshufb(AAD_HASHx, AAD_HASHx, xmm24, Assembler::AVX_128bit); __ movdqu(Address(state, 0), AAD_HASHx); - __ jmp(ENC_DEC_DONE); - __ bind(GENERATE_HTBL_48_BLKS); - generateHtbl_48_block_zmm(subkeyHtbl, avx512_subkeyHtbl, rbx /*rscratch*/); + //Zero out sensitive data + __ evpxorq(ZTMP21, ZTMP21, ZTMP21, Assembler::AVX_512bit); + __ evpxorq(ZTMP0, ZTMP0, ZTMP0, Assembler::AVX_512bit); + __ evpxorq(ZTMP1, ZTMP1, ZTMP1, Assembler::AVX_512bit); + __ evpxorq(ZTMP2, ZTMP2, ZTMP2, Assembler::AVX_512bit); + __ evpxorq(ZTMP3, ZTMP3, ZTMP3, Assembler::AVX_512bit); __ bind(ENC_DEC_DONE); - __ movq(rax, pos); } //Implements data * hashkey mod (128, 127, 126, 121, 0) diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp index 2056fa057654e..5a9b084841376 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2021, Intel Corporation. All rights reserved. +* Copyright (c) 2019, 2024, Intel Corporation. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -57,7 +57,10 @@ address StubGenerator::ghash_byte_swap_mask_addr() { // Polynomial x^128+x^127+x^126+x^121+1 ATTRIBUTE_ALIGNED(16) static const uint64_t GHASH_POLYNOMIAL[] = { - 0x0000000000000001UL, 0xC200000000000000UL, + 0x0000000000000001ULL, 0xC200000000000000ULL, + 0x0000000000000001ULL, 0xC200000000000000ULL, + 0x0000000000000001ULL, 0xC200000000000000ULL, + 0x0000000000000001ULL, 0xC200000000000000ULL }; address StubGenerator::ghash_polynomial_addr() { return (address)GHASH_POLYNOMIAL; diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp new file mode 100644 index 0000000000000..92ac78e15cba9 --- /dev/null +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp @@ -0,0 +1,502 @@ +/* +* Copyright (c) 2024, Intel Corporation. All rights reserved. +* Intel Math Library (LIBM) Source Code +* +* 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. +* +*/ + +#include "precompiled.hpp" +#include "macroAssembler_x86.hpp" +#include "stubGenerator_x86_64.hpp" + +/******************************************************************************/ +// ALGORITHM DESCRIPTION +// --------------------- +// +// tanh(x)=(exp(x)-exp(-x))/(exp(x)+exp(-x))=(1-exp(-2*x))/(1+exp(-2*x)) +// +// Let |x|=xH+xL (upper 26 bits, lower 27 bits) +// log2(e) rounded to 26 bits (high part) plus a double precision low part is +// L2EH+L2EL (upper 26, lower 53 bits) +// +// Let xH*L2EH=k+f+r`, where (k+f)*2^8*2=int(xH*L2EH*2^9), +// f=0.b1 b2 ... b8, k integer +// 2^{-f} is approximated as Tn[f]+Dn[f] +// Tn stores the high 53 bits, Dn stores (2^{-f}-Tn[f]) rounded to double precision +// +// r=r`+xL*L2EH+|x|*L2EL, |r|<2^{-9}+2^{-14}, +// for |x| in [23/64,3*2^7) +// e^{-2*|x|}=2^{-k-f}*2^{-r} ~ 2^{-k}*(Tn+Dn)*(1+p)=(T0+D0)*(1+p) +// +// For |x| in [2^{-4},2^5): +// 2^{-r}-1 ~ p=c1*r+c2*r^2+..+c5*r^5 +// Let R=1/(1+T0+p*T0), truncated to 35 significant bits +// R=1/(1+T0+D0+p*(T0+D0))*(1+eps), |eps|<2^{-33} +// 1+T0+D0+p*(T0+D0)=KH+KL, where +// KH=(1+T0+c1*r*T0)_high (leading 17 bits) +// KL=T0_low+D0+(c1*r*T0)_low+c1*r*D0+(c2*r^2+..c5*r^5)*T0 +// eps ~ (R*KH-1)+R*KL +// 1/(1+T0+D0+p*(T0+D0)) ~ R-R*eps +// The result is approximated as (1-T0-D0-(T0+D0)*p)*(R-R*eps) +// 1-T0-D0-(T0+D0)*p=-((KH-2)+KL) +// The result is formed as +// (KH-2)*R+(-(KH-2)*R*eps+(KL*R-KL*R*eps)), with the correct sign +// set at the end +// +// For |x| in [2^{-64},2^{-4}): +// A Taylor series expansion is used (x+p3*x^3+..+p13*x^{13}) +// +// For |x|<2^{-64}: x is returned +// +// For |x|>=2^32: return +/-1 +// +// Special cases: +// tanh(NaN) = quiet NaN, and raise invalid exception +// tanh(INF) = that INF +// tanh(+/-0) = +/-0 +// +/******************************************************************************/ + +ATTRIBUTE_ALIGNED(4) static const juint _HALFMASK[] = +{ + 4160749568, 2147483647 +}; + +ATTRIBUTE_ALIGNED(4) static const juint _ONEMASK[] = +{ + 0, 1072693248 +}; + +ATTRIBUTE_ALIGNED(4) static const juint _TWOMASK[] = +{ + 0, 1073741824 +}; + +ATTRIBUTE_ALIGNED(16) static const juint _MASK3[] = +{ + 0, 4294967280, 0, 4294967280 +}; + +ATTRIBUTE_ALIGNED(16) static const juint _RMASK[] = +{ + 4294705152, 4294967295, 4294705152, 4294967295 +}; + +ATTRIBUTE_ALIGNED(16) static const juint _L2E[] = +{ + 1610612736, 1082594631, 4166901572, 1055174155 +}; + +ATTRIBUTE_ALIGNED(16) static const juint _Shifter[] = +{ + 0, 1127743488, 0, 3275227136 +}; + +ATTRIBUTE_ALIGNED(16) static const juint _cv[] = +{ + 3884607281, 3168131199, 3607404735, 3190582024, 1874480759, + 1032041131, 4286760334, 1053736893, 4277811695, 3211144770, + 0, 0 +}; + +ATTRIBUTE_ALIGNED(4) static const juint _pv[] = +{ + 236289503, 1064135997, 463583772, 3215696314, 1441186365, + 3212977891, 286331153, 1069617425, 2284589306, 1066820852, + 1431655765, 3218429269 +}; + +ATTRIBUTE_ALIGNED(16) static const juint _T2_neg_f[] = +{ + 0, 1072693248, 0, 0, 1797923801, 1072687577, + 1950547427, 1013229059, 730821105, 1072681922, 2523232743, 1012067188, + 915592468, 1072676282, 352947894, 3161024371, 2174652632, 1072670657, + 4087714590, 1014450259, 35929225, 1072665048, 2809788041, 3159436968, + 2912730644, 1072659453, 3490067722, 3163405074, 2038973688, 1072653874, + 892941374, 1016046459, 1533953344, 1072648310, 769171851, 1015665633, + 1222472308, 1072642761, 1054357470, 3161021018, 929806999, 1072637227, + 3205336643, 1015259557, 481706282, 1072631708, 1696079173, 3162710528, + 3999357479, 1072626203, 2258941616, 1015924724, 2719515920, 1072620714, + 2760332941, 1015137933, 764307441, 1072615240, 3021057420, 3163329523, + 2256325230, 1072609780, 580117746, 1015317295, 2728693978, 1072604335, + 396109971, 3163462691, 2009970496, 1072598905, 2159039665, 3162572948, + 4224142467, 1072593489, 3389820386, 1015207202, 610758006, 1072588089, + 1965209397, 3161866232, 3884662774, 1072582702, 2158611599, 1014210185, + 991358482, 1072577331, 838715019, 3163157668, 351641897, 1072571974, + 2172261526, 3163010599, 1796832535, 1072566631, 3176955716, 3160585513, + 863738719, 1072561303, 1326992220, 3162613197, 1679558232, 1072555989, + 2390342287, 3163333970, 4076975200, 1072550689, 2029000899, 1015208535, + 3594158869, 1072545404, 2456521700, 3163256561, 64696965, 1072540134, + 1768797490, 1015816960, 1912561781, 1072534877, 3147495102, 1015678253, + 382305176, 1072529635, 2347622376, 3162578625, 3898795731, 1072524406, + 1249994144, 1011869818, 3707479175, 1072519192, 3613079303, 1014164738, + 3939148246, 1072513992, 3210352148, 1015274323, 135105010, 1072508807, + 1906148728, 3163375739, 721996136, 1072503635, 563754734, 1015371318, + 1242007932, 1072498477, 1132034716, 3163339831, 1532734324, 1072493333, + 3094216535, 3163162857, 1432208378, 1072488203, 1401068914, 3162363963, + 778901109, 1072483087, 2248183955, 3161268751, 3706687593, 1072477984, + 3521726940, 1013253067, 1464976603, 1072472896, 3507292405, 3161977534, + 2483480501, 1072467821, 1216371780, 1013034172, 2307442995, 1072462760, + 3190117721, 3162404539, 777507147, 1072457713, 4282924205, 1015187533, + 2029714210, 1072452679, 613660079, 1015099143, 1610600570, 1072447659, + 3766732298, 1015760183, 3657065772, 1072442652, 399025623, 3162957078, + 3716502172, 1072437659, 2303740125, 1014042725, 1631695677, 1072432680, + 2717633076, 3162344026, 1540824585, 1072427714, 1064017011, 3163487690, + 3287523847, 1072422761, 1625971539, 3157009955, 2420883922, 1072417822, + 2049810052, 1014119888, 3080351519, 1072412896, 3379126788, 3157218001, + 815859274, 1072407984, 240396590, 3163487443, 4062661092, 1072403084, + 1422616006, 3163255318, 4076559943, 1072398198, 2119478331, 3160758351, + 703710506, 1072393326, 1384660846, 1015195891, 2380618042, 1072388466, + 3149557219, 3163320799, 364333489, 1072383620, 3923737744, 3161421373, + 3092190715, 1072378786, 814012168, 3159523422, 1822067026, 1072373966, + 1241994956, 1015340290, 697153126, 1072369159, 1283515429, 3163283189, + 3861050111, 1072364364, 254893773, 3162813180, 2572866477, 1072359583, + 878562433, 1015521741, 977020788, 1072354815, 3065100517, 1015541563, + 3218338682, 1072350059, 3404164304, 3162477108, 557149882, 1072345317, + 3672720709, 1014537265, 1434058175, 1072340587, 251133233, 1015085769, + 1405169241, 1072335870, 2998539689, 3162830951, 321958744, 1072331166, + 3401933767, 1015794558, 2331271250, 1072326474, 812057446, 1012207446, + 2990417245, 1072321795, 3683467745, 3163369326, 2152073944, 1072317129, + 1486860576, 3163203456, 3964284211, 1072312475, 2111583915, 1015427164, + 3985553595, 1072307834, 4002146062, 1015834136, 2069751141, 1072303206, + 1562170675, 3162724681, 2366108318, 1072298590, 2867985102, 3161762254, + 434316067, 1072293987, 2028358766, 1013458122, 424392917, 1072289396, + 2749202995, 3162838718, 2191782032, 1072284817, 2960257726, 1013742662, + 1297350157, 1072280251, 1308022040, 3163412558, 1892288442, 1072275697, + 2446255666, 3162600381, 3833209506, 1072271155, 2722920684, 1013754842, + 2682146384, 1072266626, 2082178513, 3163363419, 2591453363, 1072262109, + 2132396182, 3159074198, 3418903055, 1072257604, 2527457337, 3160820604, + 727685349, 1072253112, 2038246809, 3162358742, 2966275557, 1072248631, + 2176155324, 3159842759, 1403662306, 1072244163, 2788809599, 3161671007, + 194117574, 1072239707, 777528612, 3163412089, 3492293770, 1072235262, + 2248032210, 1015386826, 2568320822, 1072230830, 2732824428, 1014352915, + 1577608921, 1072226410, 1875489510, 3162968394, 380978316, 1072222002, + 854188970, 3160462686, 3134592888, 1072217605, 4232266862, 1015991134, + 1110089947, 1072213221, 1451641639, 1015474673, 2759350287, 1072208848, + 1148526634, 1015894933, 3649726105, 1072204487, 4085036346, 1015649474, + 3643909174, 1072200138, 3537586109, 1014354647, 2604962541, 1072195801, + 2614425274, 3163539192, 396319521, 1072191476, 4172420816, 3159074632, + 1176749997, 1072187162, 2738998779, 3162035844, 515457527, 1072182860, + 836709333, 1015651226, 2571947539, 1072178569, 3558159064, 3163376669, + 2916157145, 1072174290, 219487565, 1015309367, 1413356050, 1072170023, + 1651349291, 3162668166, 2224145553, 1072165767, 3482522030, 3161489169, + 919555682, 1072161523, 3121969534, 1012948226, 1660913392, 1072157290, + 4218599604, 1015135707, 19972402, 1072153069, 3507899862, 1016009292, + 158781403, 1072148859, 2221464712, 3163286453, 1944781191, 1072144660, + 3993278767, 3161724279, 950803702, 1072140473, 1655364926, 1015237032, + 1339972927, 1072136297, 167908909, 1015572152, 2980802057, 1072132132, + 378619896, 1015773303, 1447192521, 1072127979, 1462857171, 3162514521, + 903334909, 1072123837, 1636462108, 1015039997, 1218806132, 1072119706, + 1818613052, 3162548441, 2263535754, 1072115586, 752233586, 3162639008, + 3907805044, 1072111477, 2257091225, 3161550407, 1727278727, 1072107380, + 3562710623, 1011471940, 4182873220, 1072103293, 629542646, 3161996303, + 2555984613, 1072099218, 2652555442, 3162552692, 1013258799, 1072095154, + 1748797611, 3160129082, 3721688645, 1072091100, 3069276937, 1015839401, + 1963711167, 1072087058, 1744767757, 3160574294, 4201977662, 1072083026, + 748330254, 1013594357, 1719614413, 1072079006, 330458198, 3163282740, + 2979960120, 1072074996, 2599109725, 1014498493, 3561793907, 1072070997, + 1157054053, 1011890350, 3339203574, 1072067009, 1483497780, 3162408754, + 2186617381, 1072063032, 2270764084, 3163272713, 4273770423, 1072059065, + 3383180809, 3163218901, 885834528, 1072055110, 1973258547, 3162261564, + 488188413, 1072051165, 3199821029, 1015564048, 2956612997, 1072047230, + 2118169751, 3162735553, 3872257780, 1072043306, 1253592103, 1015958334, + 3111574537, 1072039393, 2606161479, 3162759746, 551349105, 1072035491, + 3821916050, 3162106589, 363667784, 1072031599, 813753950, 1015785209, + 2425981843, 1072027717, 2830390851, 3163346599, 2321106615, 1072023846, + 2171176610, 1009535771, 4222122499, 1072019985, 1277378074, 3163256737, + 3712504873, 1072016135, 88491949, 1015427660, 671025100, 1072012296, + 3832014351, 3163022030, 3566716925, 1072008466, 1536826856, 1014142433, + 3689071823, 1072004647, 2321004996, 3162552716, 917841882, 1072000839, + 18715565, 1015659308, 3723038930, 1071997040, 378465264, 3162569582, + 3395129871, 1071993252, 4025345435, 3162335388, 4109806887, 1071989474, + 422403966, 1014469229, 1453150082, 1071985707, 498154669, 3161488062, + 3896463087, 1071981949, 1139797873, 3161233805, 2731501122, 1071978202, + 1774031855, 3162470021, 2135241198, 1071974465, 1236747871, 1013589147, + 1990012071, 1071970738, 3529070563, 3162813193, 2178460671, 1071967021, + 777878098, 3162842493, 2583551245, 1071963314, 3161094195, 1015606491, + 3088564500, 1071959617, 1762311517, 1015045673, 3577096743, 1071955930, + 2951496418, 1013793687, 3933059031, 1071952253, 2133366768, 3161531832, + 4040676318, 1071948586, 4090609238, 1015663458, 3784486610, 1071944929, + 1581883040, 3161698953, 3049340112, 1071941282, 3062915824, 1013170595, + 1720398391, 1071937645, 3980678963, 3163300080, 3978100823, 1071934017, + 3513027190, 1015845963, 1118294578, 1071930400, 2197495694, 3159909401, + 1617004845, 1071926792, 82804944, 1010342778, 1065662932, 1071923194, + 2533670915, 1014530238, 3645941911, 1071919605, 3814685081, 3161573341, + 654919306, 1071916027, 3232961757, 3163047469, 569847338, 1071912458, + 472945272, 3159290729, 3278348324, 1071908898, 3069497416, 1014750712, + 78413852, 1071905349, 4183226867, 3163017251, 3743175029, 1071901808, + 2072812490, 3162175075, 1276261410, 1071898278, 300981948, 1014684169, + 1156440435, 1071894757, 2351451249, 1013967056, 3272845541, 1071891245, + 928852419, 3163488248, 3219942644, 1071887743, 3798990616, 1015368806, + 887463927, 1071884251, 3596744163, 3160794166, 460407023, 1071880768, + 4237175092, 3163138469, 1829099622, 1071877294, 1016661181, 3163461005, + 589198666, 1071873830, 2664346172, 3163157962, 926591435, 1071870375, + 3208833762, 3162913514, 2732492859, 1071866929, 2691479646, 3162255684, + 1603444721, 1071863493, 1548633640, 3162201326, 1726216749, 1071860066, + 2466808228, 3161676405, 2992903935, 1071856648, 2218154406, 1015228193, + 1000925746, 1071853240, 1018491672, 3163309544, 4232894513, 1071849840, + 2383938684, 1014668519, 3991843581, 1071846450, 4092853457, 1014585763, + 171030293, 1071843070, 3526460132, 1014428778, 1253935211, 1071839698, + 1395382931, 3159702613, 2839424854, 1071836335, 1171596163, 1013041679, + 526652809, 1071832982, 4223459736, 1015879375, 2799960843, 1071829637, + 1423655381, 1015022151, 964107055, 1071826302, 2800439588, 3162833221, + 3504003472, 1071822975, 3594001060, 3157330652, 1724976915, 1071819658, + 420909223, 3163117379, 4112506593, 1071816349, 2947355221, 1014371048, + 1972484976, 1071813050, 675290301, 3161640050, 3790955393, 1071809759, + 2352942462, 3163180090, 874372905, 1071806478, 100263788, 1015940732, + 1709341917, 1071803205, 2571168217, 1014152499, 1897844341, 1071799941, + 1254300460, 1015275938, 1337108031, 1071796686, 3203724452, 1014677845, + 4219606026, 1071793439, 2434574742, 1014681548, 1853186616, 1071790202, + 3066496371, 1015656574, 2725843665, 1071786973, 1433917087, 1014838523, + 2440944790, 1071783753, 2492769774, 1014147454, 897099801, 1071780542, + 754756297, 1015241005, 2288159958, 1071777339, 2169144469, 1014876021, + 2218315341, 1071774145, 2694295388, 3163288868, 586995997, 1071770960, + 41662348, 3162627992, 1588871207, 1071767783, 143439582, 3162963416, + 828946858, 1071764615, 10642492, 1015939438, 2502433899, 1071761455, + 2148595913, 1015023991, 2214878420, 1071758304, 892270087, 3163116422, + 4162030108, 1071755161, 2763428480, 1015529349, 3949972341, 1071752027, + 2068408548, 1014913868, 1480023343, 1071748902, 2247196168, 1015327453, + 948735466, 1071745785, 3516338028, 3162574883, 2257959872, 1071742676, + 3802946148, 1012964927, 1014845819, 1071739576, 3117910646, 3161559105, + 1416741826, 1071736484, 2196380210, 1011413563, 3366293073, 1071733400, + 3119426314, 1014120554, 2471440686, 1071730325, 968836267, 3162214888, + 2930322912, 1071727258, 2599499422, 3162714047, 351405227, 1071724200, + 3125337328, 3159822479, 3228316108, 1071721149, 3010241991, 3158422804, + 2875075254, 1071718107, 4144233330, 3163333716, 3490863953, 1071715073, + 960797498, 3162948880, 685187902, 1071712048, 378731989, 1014843115, + 2952712987, 1071709030, 3293494651, 3160120301, 1608493509, 1071706021, + 3159622171, 3162807737, 852742562, 1071703020, 667253586, 1009793559, + 590962156, 1071700027, 3829346666, 3163275597, 728909815, 1071697042, + 383930225, 1015029468, 1172597893, 1071694065, 114433263, 1015347593, + 1828292879, 1071691096, 1255956747, 1015588398, 2602514713, 1071688135, + 2268929336, 1014354284, 3402036099, 1071685182, 405889334, 1015105656, + 4133881824, 1071682237, 2148155345, 3162931299, 410360776, 1071679301, + 1269990655, 1011975870, 728934454, 1071676372, 1413842688, 1014178612, + 702412510, 1071673451, 3803266087, 3162280415, 238821257, 1071670538, + 1469694871, 3162884987, 3541402996, 1071667632, 2759177317, 1014854626, + 1928746161, 1071664735, 983617676, 1014285177, 3899555717, 1071661845, + 427280750, 3162546972, 772914124, 1071658964, 4004372762, 1012230161, + 1048019041, 1071656090, 1398474845, 3160510595, 339411585, 1071653224, + 264588982, 3161636657, 2851812149, 1071650365, 2595802551, 1015767337, + 4200250559, 1071647514, 2808127345, 3161781938 +}; + +#define __ _masm-> + +address StubGenerator::generate_libmTanh() { + StubCodeMark mark(this, "StubRoutines", "libmTanh"); + address start = __ pc(); + + Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; + Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1; + Label B1_2, B1_4; + + address HALFMASK = (address)_HALFMASK; + address ONEMASK = (address)_ONEMASK; + address TWOMASK = (address)_TWOMASK; + address MASK3 = (address)_MASK3; + address RMASK = (address)_RMASK; + address L2E = (address)_L2E; + address Shifter = (address)_Shifter; + address cv = (address)_cv; + address pv = (address)_pv; + address T2_neg_f = (address) _T2_neg_f; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + __ bind(B1_2); + __ movsd(xmm3, ExternalAddress(HALFMASK), r11 /*rscratch*/); + __ xorpd(xmm4, xmm4); + __ movsd(xmm1, ExternalAddress(L2E), r11 /*rscratch*/); + __ movsd(xmm2, ExternalAddress(L2E + 8), r11 /*rscratch*/); + __ movl(rax, 32768); + __ pinsrw(xmm4, rax, 3); + __ movsd(xmm6, ExternalAddress(Shifter), r11 /*rscratch*/); + __ pextrw(rcx, xmm0, 3); + __ andpd(xmm3, xmm0); + __ andnpd(xmm4, xmm0); + __ pshufd(xmm5, xmm4, 68); + __ movl(rdx, 32768); + __ andl(rdx, rcx); + __ andl(rcx, 32767); + __ subl(rcx, 16304); + __ cmpl(rcx, 144); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_1); + __ subsd(xmm4, xmm3); + __ mulsd(xmm3, xmm1); + __ mulsd(xmm2, xmm5); + __ cvtsd2siq(rax, xmm3); + __ movq(xmm7, xmm3); + __ addsd(xmm3, xmm6); + __ mulsd(xmm1, xmm4); + __ movsd(xmm4, ExternalAddress(ONEMASK), r11 /*rscratch*/); + __ subsd(xmm3, xmm6); + __ xorpd(xmm0, xmm0); + __ addsd(xmm2, xmm1); + __ subsd(xmm7, xmm3); + __ movdqu(xmm6, ExternalAddress(cv), r11 /*rscratch*/); + __ addsd(xmm2, xmm7); + __ movl(rcx, 255); + __ andl(rcx, rax); + __ addl(rcx, rcx); + __ lea(r8, ExternalAddress(T2_neg_f)); + __ movdqu(xmm5, Address(r8, rcx, Address::times(8))); + __ shrl(rax, 4); + __ andl(rax, 65520); + __ subl(rax, 16368); + __ negl(rax); + __ pinsrw(xmm0, rax, 3); + __ movdqu(xmm1, ExternalAddress(cv + 16), r11 /*rscratch*/); + __ pshufd(xmm0, xmm0, 68); + __ mulpd(xmm0, xmm5); + __ movsd(xmm7, ExternalAddress(cv + 32), r11 /*rscratch*/); + __ pshufd(xmm2, xmm2, 68); + __ movq(xmm5, xmm4); + __ addsd(xmm4, xmm0); + __ mulpd(xmm6, xmm2); + __ mulsd(xmm7, xmm2); + __ mulpd(xmm2, xmm2); + __ addpd(xmm1, xmm6); + __ mulsd(xmm2, xmm2); + __ movsd(xmm3, ExternalAddress(ONEMASK), r11 /*rscratch*/); + __ mulpd(xmm1, xmm2); + __ pshufd(xmm6, xmm1, 78); + __ addsd(xmm1, xmm6); + __ movq(xmm6, xmm1); + __ addsd(xmm1, xmm7); + __ mulsd(xmm1, xmm0); + __ addsd(xmm1, xmm4); + __ andpd(xmm4, ExternalAddress(MASK3), r11 /*rscratch*/); + __ divsd(xmm5, xmm1); + __ subsd(xmm3, xmm4); + __ pshufd(xmm1, xmm0, 238); + __ addsd(xmm3, xmm0); + __ movq(xmm2, xmm4); + __ addsd(xmm3, xmm1); + __ mulsd(xmm1, xmm7); + __ mulsd(xmm7, xmm0); + __ addsd(xmm3, xmm1); + __ addsd(xmm4, xmm7); + __ movsd(xmm1, ExternalAddress(RMASK), r11 /*rscratch*/); + __ mulsd(xmm6, xmm0); + __ andpd(xmm4, ExternalAddress(MASK3), r11 /*rscratch*/); + __ addsd(xmm3, xmm6); + __ movq(xmm6, xmm4); + __ subsd(xmm2, xmm4); + __ addsd(xmm2, xmm7); + __ movsd(xmm7, ExternalAddress(ONEMASK), r11 /*rscratch*/); + __ andpd(xmm5, xmm1); + __ addsd(xmm3, xmm2); + __ mulsd(xmm4, xmm5); + __ xorpd(xmm2, xmm2); + __ mulsd(xmm3, xmm5); + __ subsd(xmm6, ExternalAddress(TWOMASK), r11 /*rscratch*/); + __ subsd(xmm4, xmm7); + __ xorl(rdx, 32768); + __ pinsrw(xmm2, rdx, 3); + __ addsd(xmm4, xmm3); + __ mulsd(xmm6, xmm5); + __ movq(xmm1, xmm3); + __ mulsd(xmm3, xmm4); + __ movq(xmm0, xmm6); + __ mulsd(xmm6, xmm4); + __ subsd(xmm1, xmm3); + __ subsd(xmm1, xmm6); + __ addsd(xmm0, xmm1); + __ xorpd(xmm0, xmm2); + __ jmp(B1_4); + + __ bind(L_2TAG_PACKET_0_0_1); + __ addl(rcx, 960); + __ cmpl(rcx, 1104); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_1_0_1); + __ movdqu(xmm2, ExternalAddress(pv), r11 /*rscratch*/); + __ pshufd(xmm1, xmm0, 68); + __ movdqu(xmm3, ExternalAddress(pv + 16), r11 /*rscratch*/); + __ mulpd(xmm1, xmm1); + __ movdqu(xmm4, ExternalAddress(pv + 32), r11 /*rscratch*/); + __ mulpd(xmm2, xmm1); + __ pshufd(xmm5, xmm1, 68); + __ addpd(xmm2, xmm3); + __ mulsd(xmm5, xmm5); + __ mulpd(xmm2, xmm1); + __ mulsd(xmm5, xmm5); + __ addpd(xmm2, xmm4); + __ mulpd(xmm2, xmm5); + __ pshufd(xmm5, xmm2, 238); + __ addsd(xmm2, xmm5); + __ mulsd(xmm2, xmm0); + __ addsd(xmm0, xmm2); + __ jmp(B1_4); + + __ bind(L_2TAG_PACKET_1_0_1); + __ addl(rcx, 15344); + __ cmpl(rcx, 16448); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_2_0_1); + __ cmpl(rcx, 16); + __ jcc(Assembler::below, L_2TAG_PACKET_3_0_1); + __ xorpd(xmm2, xmm2); + __ movl(rax, 17392); + __ pinsrw(xmm2, rax, 3); + __ mulsd(xmm2, xmm0); + __ addsd(xmm2, xmm0); + __ jmp(B1_4); + + __ bind(L_2TAG_PACKET_3_0_1); + __ movq(xmm2, xmm0); + __ mulsd(xmm2, xmm2); + __ jmp(B1_4); + + __ bind(L_2TAG_PACKET_2_0_1); + __ cmpl(rcx, 32752); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_4_0_1); + __ xorpd(xmm2, xmm2); + __ movl(rcx, 15344); + __ pinsrw(xmm2, rcx, 3); + __ movq(xmm3, xmm2); + __ mulsd(xmm2, xmm2); + __ addsd(xmm2, xmm3); + + __ bind(L_2TAG_PACKET_5_0_1); + __ xorpd(xmm0, xmm0); + __ orl(rdx, 16368); + __ pinsrw(xmm0, rdx, 3); + __ jmp(B1_4); + + __ bind(L_2TAG_PACKET_4_0_1); + __ movq(xmm2, xmm0); + __ movdl(rax, xmm0); + __ psrlq(xmm2, 20); + __ movdl(rcx, xmm2); + __ orl(rcx, rax); + __ cmpl(rcx, 0); + __ jcc(Assembler::equal, L_2TAG_PACKET_5_0_1); + __ addsd(xmm0, xmm0); + + __ bind(B1_4); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; +} + +#undef __ diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp index ba9eb32e8c13e..75611524e3b0a 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -373,6 +373,10 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M // [ lo(arg) ] // [ hi(arg) ] // + if (kind == Interpreter::java_lang_math_tanh) { + return nullptr; + } + if (kind == Interpreter::java_lang_math_fmaD) { if (!UseFMA) { return nullptr; // Generate a vanilla entry diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp index 26eea4c1d6a5f..5ea2d8eba259b 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp @@ -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 @@ -465,6 +465,10 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M } else { __ call_VM_leaf0(CAST_FROM_FN_PTR(address, SharedRuntime::dtan)); } + } else if (kind == Interpreter::java_lang_math_tanh) { + assert(StubRoutines::dtanh() != nullptr, "not initialized"); + __ movdbl(xmm0, Address(rsp, wordSize)); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dtanh()))); } else if (kind == Interpreter::java_lang_math_abs) { assert(StubRoutines::x86::double_sign_mask() != nullptr, "not initialized"); __ movdbl(xmm0, Address(rsp, wordSize)); diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 5e783225fcbfc..527d961259ecc 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -4048,6 +4048,7 @@ void TemplateTable::_new() { __ push(rcx); // save the contexts of klass for initializing the header // make sure klass is initialized + // init_state needs acquire, but x86 is TSO, and so we are already good. #ifdef _LP64 assert(VM_Version::supports_fast_class_init_checks(), "must support fast class initialization checks"); __ clinit_barrier(rcx, r15_thread, nullptr /*L_fast_path*/, &slow_case); diff --git a/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp b/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp index e5075e180d9d6..d795c751d02b5 100644 --- a/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp +++ b/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "prims/upcallLinker.hpp" -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp index 82179f9022e92..bc261bfd93f44 100644 --- a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp @@ -23,7 +23,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" -#include "code/codeBlob.hpp" +#include "classfile/javaClasses.hpp" #include "code/codeBlob.hpp" #include "code/vmreg.inline.hpp" #include "compiler/disassembler.hpp" @@ -169,10 +169,10 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr __ block_comment("} restore_callee_saved_regs "); } -static const int upcall_stub_code_base_size = 1024; +static const int upcall_stub_code_base_size = 1200; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -281,7 +281,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ vzeroupper(); __ lea(c_rarg0, Address(rsp, frame_data_offset)); - __ movptr(c_rarg1, (intptr_t)receiver); // stack already aligned __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry))); __ movptr(r15_thread, rax); @@ -297,12 +296,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, shuffle_reg, abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, r15_thread); - __ block_comment("} receiver "); - - __ mov_metadata(rbx, entry); - __ movptr(Address(r15_thread, JavaThread::callee_target_offset()), rbx); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (intptr_t)receiver); + __ call(RuntimeAddress(StubRoutines::upcall_stub_load_target())); // puts target Method* in rbx + __ block_comment("} load target "); __ push_cont_fastpath(); @@ -377,7 +374,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.freeze()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 2549feb8a4069..038797924a92d 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -437,6 +437,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ cmpl(rax, 0x80000); __ jcc(Assembler::notEqual, vector_save_restore); +#ifndef PRODUCT bool save_apx = UseAPX; VM_Version::set_apx_cpuFeatures(); UseAPX = true; @@ -453,6 +454,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movq(Address(rsi, 8), r31); UseAPX = save_apx; +#endif #endif __ bind(vector_save_restore); // diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 2b29dd14e4b27..c88fa1ec5ce15 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2231,10 +2231,6 @@ const RegMask* Matcher::predicate_reg_mask(void) { return &_VECTMASK_REG_mask; } -const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { - return new TypeVectMask(elemTy, length); -} - // Max vector size in bytes. 0 if not supported. int Matcher::vector_width_in_bytes(BasicType bt) { assert(is_java_primitive(bt), "only primitive type vectors"); @@ -2457,6 +2453,10 @@ bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { mstack.push(m, Visit); // m = ShiftCntV return true; } + if (is_encode_and_store_pattern(n, m)) { + mstack.push(m, Visit); + return true; + } return false; } diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 7aa4f6a29a116..7c9695571daec 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -6322,17 +6322,6 @@ instruct storeImmB(memory mem, immI8 src) %{ ins_pipe( ialu_mem_imm ); %} -// Store CMS card-mark Immediate -instruct storeImmCM(memory mem, immI8 src) %{ - match(Set mem (StoreCM mem src)); - - ins_cost(150); - format %{ "MOV8 $mem,$src\t! CMS card-mark imm0" %} - opcode(0xC6); /* C6 /0 */ - ins_encode( SetInstMark, OpcP, RMopc_Mem(0x00,mem), Con8or32(src), ClearInstMark); - ins_pipe( ialu_mem_imm ); -%} - // Store Double instruct storeDPR( memory mem, regDPR1 src) %{ predicate(UseSSE<=1); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 1b271683bd60d..c3fa4c16e553f 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -4341,6 +4341,7 @@ instruct loadP(rRegP dst, memory mem) // Load Compressed Pointer instruct loadN(rRegN dst, memory mem) %{ + predicate(n->as_Load()->barrier_data() == 0); match(Set dst (LoadN mem)); ins_cost(125); // XXX @@ -5126,6 +5127,7 @@ instruct storeImmP(memory mem, immP31 src) // Store Compressed Pointer instruct storeN(memory mem, rRegN src) %{ + predicate(n->as_Store()->barrier_data() == 0); match(Set mem (StoreN mem src)); ins_cost(125); // XXX @@ -5150,7 +5152,7 @@ instruct storeNKlass(memory mem, rRegN src) instruct storeImmN0(memory mem, immN0 zero) %{ - predicate(CompressedOops::base() == nullptr); + predicate(CompressedOops::base() == nullptr && n->as_Store()->barrier_data() == 0); match(Set mem (StoreN mem zero)); ins_cost(125); // XXX @@ -5163,6 +5165,7 @@ instruct storeImmN0(memory mem, immN0 zero) instruct storeImmN(memory mem, immN src) %{ + predicate(n->as_Store()->barrier_data() == 0); match(Set mem (StoreN mem src)); ins_cost(150); // XXX @@ -5295,32 +5298,6 @@ instruct storeImmB(memory mem, immI8 src) ins_pipe(ialu_mem_imm); %} -// Store CMS card-mark Immediate -instruct storeImmCM0_reg(memory mem, immI_0 zero) -%{ - predicate(UseCompressedOops && (CompressedOops::base() == nullptr)); - match(Set mem (StoreCM mem zero)); - - ins_cost(125); // XXX - format %{ "movb $mem, R12\t# CMS card-mark byte 0 (R12_heapbase==0)" %} - ins_encode %{ - __ movb($mem$$Address, r12); - %} - ins_pipe(ialu_mem_reg); -%} - -instruct storeImmCM0(memory mem, immI_0 src) -%{ - match(Set mem (StoreCM mem src)); - - ins_cost(150); // XXX - format %{ "movb $mem, $src\t# CMS card-mark byte 0" %} - ins_encode %{ - __ movb($mem$$Address, $src$$constant); - %} - ins_pipe(ialu_mem_imm); -%} - // Store Float instruct storeF(memory mem, regF src) %{ @@ -7162,6 +7139,7 @@ instruct compareAndSwapN(rRegI res, memory mem_ptr, rax_RegN oldval, rRegN newval, rFlagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapN mem_ptr (Binary oldval newval))); match(Set res (WeakCompareAndSwapN mem_ptr (Binary oldval newval))); effect(KILL cr, KILL oldval); @@ -7249,6 +7227,7 @@ instruct compareAndExchangeN( memory mem_ptr, rax_RegN oldval, rRegN newval, rFlagsReg cr) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set oldval (CompareAndExchangeN mem_ptr (Binary oldval newval))); effect(KILL cr); @@ -7470,6 +7449,7 @@ instruct xchgP( memory mem, rRegP newval) %{ %} instruct xchgN( memory mem, rRegN newval) %{ + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set newval (GetAndSetN mem newval)); format %{ "XCHGL $newval,$mem]" %} ins_encode %{ @@ -11659,6 +11639,7 @@ instruct compN_rReg(rFlagsRegU cr, rRegN op1, rRegN op2) instruct compN_rReg_mem(rFlagsRegU cr, rRegN src, memory mem) %{ + predicate(n->in(2)->as_Load()->barrier_data() == 0); match(Set cr (CmpN src (LoadN mem))); format %{ "cmpl $src, $mem\t# compressed ptr" %} @@ -11680,6 +11661,7 @@ instruct compN_rReg_imm(rFlagsRegU cr, rRegN op1, immN op2) %{ instruct compN_mem_imm(rFlagsRegU cr, memory mem, immN src) %{ + predicate(n->in(2)->as_Load()->barrier_data() == 0); match(Set cr (CmpN src (LoadN mem))); format %{ "cmpl $mem, $src\t# compressed ptr" %} @@ -11720,7 +11702,8 @@ instruct testN_reg(rFlagsReg cr, rRegN src, immN0 zero) %{ instruct testN_mem(rFlagsReg cr, memory mem, immN0 zero) %{ - predicate(CompressedOops::base() != nullptr); + predicate(CompressedOops::base() != nullptr && + n->in(1)->as_Load()->barrier_data() == 0); match(Set cr (CmpN (LoadN mem) zero)); ins_cost(500); // XXX @@ -11733,7 +11716,8 @@ instruct testN_mem(rFlagsReg cr, memory mem, immN0 zero) instruct testN_mem_reg0(rFlagsReg cr, memory mem, immN0 zero) %{ - predicate(CompressedOops::base() == nullptr); + predicate(CompressedOops::base() == nullptr && + n->in(1)->as_Load()->barrier_data() == 0); match(Set cr (CmpN (LoadN mem) zero)); format %{ "cmpl R12, $mem\t# compressed ptr (R12_heapbase==0)" %} diff --git a/src/hotspot/cpu/zero/upcallLinker_zero.cpp b/src/hotspot/cpu/zero/upcallLinker_zero.cpp index 6447dac86c915..408ebc328205d 100644 --- a/src/hotspot/cpu/zero/upcallLinker_zero.cpp +++ b/src/hotspot/cpu/zero/upcallLinker_zero.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "prims/upcallLinker.hpp" -address UpcallLinker::make_upcall_stub(jobject mh, Method* entry, +address UpcallLinker::make_upcall_stub(jobject mh, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/cpu/zero/vm_version_zero.cpp b/src/hotspot/cpu/zero/vm_version_zero.cpp index 1fcf4b1086253..7312dd116468c 100644 --- a/src/hotspot/cpu/zero/vm_version_zero.cpp +++ b/src/hotspot/cpu/zero/vm_version_zero.cpp @@ -116,11 +116,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); } - if ((LockingMode != LM_LEGACY) && (LockingMode != LM_MONITOR)) { - warning("Unsupported locking mode for this CPU."); - FLAG_SET_DEFAULT(LockingMode, LM_LEGACY); - } - // Enable error context decoding on known platforms #if defined(IA32) || defined(AMD64) || defined(ARM) || \ defined(AARCH64) || defined(PPC) || defined(RISCV) || \ diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp index 2b53042ef1017..aab43e733964e 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp @@ -485,26 +485,30 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) { // Unlock if necessary if (monitor) { - BasicLock *lock = monitor->lock(); - markWord header = lock->displaced_header(); - oop rcvr = monitor->obj(); - monitor->set_obj(nullptr); - - bool dec_monitor_count = true; - if (header.to_pointer() != nullptr) { - markWord old_header = markWord::encode(lock); - if (rcvr->cas_set_mark(header, old_header) != old_header) { - monitor->set_obj(rcvr); - dec_monitor_count = false; - InterpreterRuntime::monitorexit(monitor); + bool success = false; + if (LockingMode == LM_LEGACY) { + BasicLock* lock = monitor->lock(); + oop rcvr = monitor->obj(); + monitor->set_obj(nullptr); + success = true; + markWord header = lock->displaced_header(); + if (header.to_pointer() != nullptr) { // Check for recursive lock + markWord old_header = markWord::encode(lock); + if (rcvr->cas_set_mark(header, old_header) != old_header) { + monitor->set_obj(rcvr); + success = false; + } + } + if (success) { + THREAD->dec_held_monitor_count(); } } - if (dec_monitor_count) { - THREAD->dec_held_monitor_count(); + if (!success) { + InterpreterRuntime::monitorexit(monitor); } } - unwind_and_return: + unwind_and_return: // Unwind the current activation thread->pop_zero_frame(); diff --git a/src/hotspot/os/aix/osThread_aix.cpp b/src/hotspot/os/aix/osThread_aix.cpp index 4049d6b58b777..ab08a766156fe 100644 --- a/src/hotspot/os/aix/osThread_aix.cpp +++ b/src/hotspot/os/aix/osThread_aix.cpp @@ -23,32 +23,27 @@ * */ -// no precompiled headers - -#include "memory/allocation.inline.hpp" -#include "runtime/handles.inline.hpp" -#include "runtime/mutexLocker.hpp" -#include "runtime/os.hpp" +#include "precompiled.hpp" +#include "memory/allocation.hpp" +#include "runtime/mutex.hpp" #include "runtime/osThread.hpp" -#include "runtime/safepoint.hpp" -#include "runtime/vmThread.hpp" - -void OSThread::pd_initialize() { - _thread_id = 0; - _kernel_thread_id = 0; - _siginfo = nullptr; - _ucontext = nullptr; - _expanding_stack = 0; - _alt_sig_stack = nullptr; - _last_cpu_times.sys = _last_cpu_times.user = 0L; +#include +OSThread::OSThread() + : _thread_id(0), + _thread_type(), + _kernel_thread_id(0), + _caller_sigmask(), + sr(), + _siginfo(nullptr), + _ucontext(nullptr), + _expanding_stack(0), + _alt_sig_stack(nullptr), + _startThread_lock(new Monitor(Mutex::event, "startThread_lock")) { sigemptyset(&_caller_sigmask); - - _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); - assert(_startThread_lock != nullptr, "check"); } -void OSThread::pd_destroy() { +OSThread::~OSThread() { delete _startThread_lock; } diff --git a/src/hotspot/os/aix/osThread_aix.hpp b/src/hotspot/os/aix/osThread_aix.hpp index 5feb3c5799aa0..8f3799d070142 100644 --- a/src/hotspot/os/aix/osThread_aix.hpp +++ b/src/hotspot/os/aix/osThread_aix.hpp @@ -26,22 +26,17 @@ #ifndef OS_AIX_OSTHREAD_AIX_HPP #define OS_AIX_OSTHREAD_AIX_HPP - public: - typedef pthread_t thread_id_t; +#include "runtime/osThreadBase.hpp" +#include "suspendResume_posix.hpp" +#include "utilities/globalDefinitions.hpp" - private: - int _thread_type; +class OSThread : public OSThreadBase { + friend class VMStructs; - public: - - int thread_type() const { - return _thread_type; - } - void set_thread_type(int type) { - _thread_type = type; - } + typedef pthread_t thread_id_t; - private: + thread_id_t _thread_id; + int _thread_type; // On AIX, we use the pthread id as OSThread::thread_id and keep the kernel thread id // separately for diagnostic purposes. @@ -54,15 +49,27 @@ sigset_t _caller_sigmask; // Caller's signal mask public: + OSThread(); + ~OSThread(); + + int thread_type() const { + return _thread_type; + } + void set_thread_type(int type) { + _thread_type = type; + } // Methods to save/restore caller's signal mask sigset_t caller_sigmask() const { return _caller_sigmask; } void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - int thread_identifier() const { return _thread_id; } -#endif + thread_id_t thread_id() const { + return _thread_id; + } + void set_thread_id(thread_id_t id) { + _thread_id = id; + } + tid_t kernel_thread_id() const { return _kernel_thread_id; } @@ -71,7 +78,7 @@ } pthread_t pthread_id() const { - // Here: same as OSThread::thread_id() + // Here: same as thread_id() return _thread_id; } @@ -79,7 +86,6 @@ // suspension support. // *************************************************************** - public: // flags that support signal based suspend/resume on Aix are in a // separate class to avoid confusion with many flags in OSThread that // are used by VM level suspend/resume. @@ -125,22 +131,10 @@ return _startThread_lock; } - // *************************************************************** - // Platform dependent initialization and cleanup - // *************************************************************** - - private: - - void pd_initialize(); - void pd_destroy(); - - public: - - // The last measured values of cpu timing to prevent the "stale - // value return" bug in thread_cpu_time. - volatile struct { - jlong sys; - jlong user; - } _last_cpu_times; + // Printing + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_AIX_OSTHREAD_AIX_HPP diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index fd16a7984a6f3..63aa53f0a2350 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2483,16 +2483,6 @@ int os::open(const char *path, int oflag, int mode) { return fd; } -// return current position of file pointer -jlong os::current_file_offset(int fd) { - 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)::lseek(fd, (off_t)offset, SEEK_SET); -} - // current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) // are used by JVM M&M and JVMTI to get user+sys or user CPU time // of a thread. diff --git a/src/hotspot/os/aix/vmStructs_aix.hpp b/src/hotspot/os/aix/vmStructs_aix.hpp index 1a2f4c4bf6e21..f3bbc80e62c72 100644 --- a/src/hotspot/os/aix/vmStructs_aix.hpp +++ b/src/hotspot/os/aix/vmStructs_aix.hpp @@ -29,9 +29,20 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pthread_t) \ + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_unsigned_integer_type(pthread_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp b/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp index 29825a9eab291..2e56c092a79b5 100644 --- a/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp +++ b/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp @@ -22,10 +22,10 @@ */ #include "precompiled.hpp" -#include "gc/shared/gcLogPrecious.hpp" #include "gc/z/zAddress.inline.hpp" #include "gc/z/zErrno.hpp" #include "gc/z/zGlobals.hpp" +#include "gc/z/zInitialize.hpp" #include "gc/z/zLargePages.inline.hpp" #include "gc/z/zPhysicalMemory.inline.hpp" #include "gc/z/zPhysicalMemoryBacking_bsd.hpp" @@ -82,7 +82,7 @@ ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity) _base = (uintptr_t)os::reserve_memory(max_capacity); if (_base == 0) { // Failed - log_error_pd(gc)("Failed to reserve address space for backing memory"); + ZInitialize::error("Failed to reserve address space for backing memory"); return; } diff --git a/src/hotspot/os/bsd/osThread_bsd.cpp b/src/hotspot/os/bsd/osThread_bsd.cpp index 7b9ad1f76a855..4080ea1bf297b 100644 --- a/src/hotspot/os/bsd/osThread_bsd.cpp +++ b/src/hotspot/os/bsd/osThread_bsd.cpp @@ -22,30 +22,32 @@ * */ -// no precompiled headers -#include "memory/allocation.inline.hpp" -#include "runtime/mutexLocker.hpp" +#include "precompiled.hpp" +#include "memory/allocation.hpp" +#include "runtime/mutex.hpp" #include "runtime/osThread.hpp" #include -void OSThread::pd_initialize() { +OSThread::OSThread() + : _thread_id( #ifdef __APPLE__ - _thread_id = 0; + 0 #else - _thread_id = nullptr; + nullptr #endif - _unique_thread_id = 0; - _pthread_id = nullptr; - _siginfo = nullptr; - _ucontext = nullptr; - _expanding_stack = 0; - _alt_sig_stack = nullptr; - + ), + _thread_type(), + _pthread_id(nullptr), + _unique_thread_id(0), + _caller_sigmask(), + sr(), + _siginfo(nullptr), + _ucontext(nullptr), + _expanding_stack(0), + _alt_sig_stack(nullptr), + _startThread_lock(new Monitor(Mutex::event, "startThread_lock")) { sigemptyset(&_caller_sigmask); - - _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); - assert(_startThread_lock !=nullptr, "check"); } // Additional thread_id used to correlate threads in SA @@ -64,6 +66,6 @@ void OSThread::set_unique_thread_id() { #endif } -void OSThread::pd_destroy() { +OSThread::~OSThread() { delete _startThread_lock; } diff --git a/src/hotspot/os/bsd/osThread_bsd.hpp b/src/hotspot/os/bsd/osThread_bsd.hpp index 11376835063c4..e54e7195f9870 100644 --- a/src/hotspot/os/bsd/osThread_bsd.hpp +++ b/src/hotspot/os/bsd/osThread_bsd.hpp @@ -25,19 +25,12 @@ #ifndef OS_BSD_OSTHREAD_BSD_HPP #define OS_BSD_OSTHREAD_BSD_HPP - private: - int _thread_type; +#include "runtime/osThreadBase.hpp" +#include "suspendResume_posix.hpp" +#include "utilities/globalDefinitions.hpp" - public: - - int thread_type() const { - return _thread_type; - } - void set_thread_type(int type) { - _thread_type = type; - } - - private: +class OSThread : public OSThreadBase { + friend class VMStructs; #ifdef __APPLE__ typedef thread_t thread_id_t; @@ -45,6 +38,9 @@ typedef pid_t thread_id_t; #endif + thread_id_t _thread_id; + int _thread_type; + // _pthread_id is the pthread id, which is used by library calls // (e.g. pthread_kill). pthread_t _pthread_id; @@ -57,15 +53,26 @@ sigset_t _caller_sigmask; // Caller's signal mask public: + OSThread(); + ~OSThread(); + + int thread_type() const { + return _thread_type; + } + void set_thread_type(int type) { + _thread_type = type; + } // Methods to save/restore caller's signal mask sigset_t caller_sigmask() const { return _caller_sigmask; } void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - intptr_t thread_identifier() const { return (intptr_t)_pthread_id; } -#endif + thread_id_t thread_id() const { + return _thread_id; + } + void set_thread_id(thread_id_t id) { + _thread_id = id; + } pthread_t pthread_id() const { return _pthread_id; @@ -80,7 +87,6 @@ // suspension support. // *************************************************************** -public: // flags that support signal based suspend/resume on Bsd are in a // separate class to avoid confusion with many flags in OSThread that // are used by VM level suspend/resume. @@ -126,17 +132,9 @@ return _startThread_lock; } - // *************************************************************** - // Platform dependent initialization and cleanup - // *************************************************************** - -private: - - void pd_initialize(); - void pd_destroy(); - -// Reconciliation History -// osThread_solaris.hpp 1.24 99/08/27 13:11:54 -// End + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_BSD_OSTHREAD_BSD_HPP diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 9ad7c35e6bdef..18818268c1f9a 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -2400,16 +2400,6 @@ int os::open(const char *path, int oflag, int mode) { return fd; } -// return current position of file pointer -jlong os::current_file_offset(int fd) { - 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)::lseek(fd, (off_t)offset, SEEK_SET); -} - // current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) // are used by JVM M&M and JVMTI to get user+sys or user CPU time // of a thread. diff --git a/src/hotspot/os/bsd/vmStructs_bsd.hpp b/src/hotspot/os/bsd/vmStructs_bsd.hpp index 84c1be77374d0..8c9c132e1c25c 100644 --- a/src/hotspot/os/bsd/vmStructs_bsd.hpp +++ b/src/hotspot/os/bsd/vmStructs_bsd.hpp @@ -31,9 +31,21 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ + nonstatic_field(OSThread, _unique_thread_id, uint64_t) + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + /**********************/ \ + /* Thread IDs */ \ + /**********************/ \ + \ + declare_unsigned_integer_type(OSThread::thread_id_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 527573644a816..56dcadd670f82 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -64,16 +64,16 @@ class CgroupV2CpuController: public CgroupCpuController { bool is_read_only() override { return reader()->is_read_only(); } - const char* subsystem_path() { + const char* subsystem_path() override { return reader()->subsystem_path(); } bool needs_hierarchy_adjustment() override { return reader()->needs_hierarchy_adjustment(); } - void set_subsystem_path(const char* cgroup_path) { + void set_subsystem_path(const char* cgroup_path) override { reader()->set_subsystem_path(cgroup_path); } - const char* mount_point() { return reader()->mount_point(); } + const char* mount_point() override { return reader()->mount_point(); } const char* cgroup_path() override { return reader()->cgroup_path(); } }; @@ -97,16 +97,16 @@ class CgroupV2MemoryController final: public CgroupMemoryController { bool is_read_only() override { return reader()->is_read_only(); } - const char* subsystem_path() { + const char* subsystem_path() override { return reader()->subsystem_path(); } bool needs_hierarchy_adjustment() override { return reader()->needs_hierarchy_adjustment(); } - void set_subsystem_path(const char* cgroup_path) { + void set_subsystem_path(const char* cgroup_path) override { reader()->set_subsystem_path(cgroup_path); } - const char* mount_point() { return reader()->mount_point(); } + const char* mount_point() override { return reader()->mount_point(); } const char* cgroup_path() override { return reader()->cgroup_path(); } }; diff --git a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp index b80124cc34e43..b648876ac602c 100644 --- a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp @@ -27,6 +27,7 @@ #include "gc/z/zArray.inline.hpp" #include "gc/z/zErrno.hpp" #include "gc/z/zGlobals.hpp" +#include "gc/z/zInitialize.hpp" #include "gc/z/zLargePages.inline.hpp" #include "gc/z/zMountPoint_linux.hpp" #include "gc/z/zNUMA.inline.hpp" @@ -103,14 +104,14 @@ #define ZFILENAME_HEAP "java_heap" // Preferred tmpfs mount points, ordered by priority -static const char* z_preferred_tmpfs_mountpoints[] = { +static const char* ZPreferredTmpfsMountpoints[] = { "/dev/shm", "/run/shm", nullptr }; // Preferred hugetlbfs mount points, ordered by priority -static const char* z_preferred_hugetlbfs_mountpoints[] = { +static const char* ZPreferredHugetlbfsMountpoints[] = { "/dev/hugepages", "/hugepages", nullptr @@ -129,6 +130,7 @@ ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity) // Create backing file _fd = create_fd(ZFILENAME_HEAP); if (_fd == -1) { + ZInitialize::error("Failed to create heap backing file"); return; } @@ -136,7 +138,7 @@ ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity) while (ftruncate(_fd, max_capacity) == -1) { if (errno != EINTR) { ZErrno err; - log_error_p(gc)("Failed to truncate backing file (%s)", err.to_string()); + ZInitialize::error("Failed to truncate backing file (%s)", err.to_string()); return; } } @@ -145,7 +147,7 @@ ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity) struct statfs buf; if (fstatfs(_fd, &buf) == -1) { ZErrno err; - log_error_p(gc)("Failed to determine filesystem type for backing file (%s)", err.to_string()); + ZInitialize::error("Failed to determine filesystem type for backing file (%s)", err.to_string()); return; } @@ -158,39 +160,39 @@ ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity) // Make sure the filesystem type matches requested large page type if (ZLargePages::is_transparent() && !is_tmpfs()) { - log_error_p(gc)("-XX:+UseTransparentHugePages can only be enabled when using a %s filesystem", - ZFILESYSTEM_TMPFS); + ZInitialize::error("-XX:+UseTransparentHugePages can only be enabled when using a %s filesystem", + ZFILESYSTEM_TMPFS); return; } if (ZLargePages::is_transparent() && !tmpfs_supports_transparent_huge_pages()) { - log_error_p(gc)("-XX:+UseTransparentHugePages on a %s filesystem not supported by kernel", - ZFILESYSTEM_TMPFS); + ZInitialize::error("-XX:+UseTransparentHugePages on a %s filesystem not supported by kernel", + ZFILESYSTEM_TMPFS); return; } if (ZLargePages::is_explicit() && !is_hugetlbfs()) { - log_error_p(gc)("-XX:+UseLargePages (without -XX:+UseTransparentHugePages) can only be enabled " - "when using a %s filesystem", ZFILESYSTEM_HUGETLBFS); + ZInitialize::error("-XX:+UseLargePages (without -XX:+UseTransparentHugePages) can only be enabled " + "when using a %s filesystem", ZFILESYSTEM_HUGETLBFS); return; } if (!ZLargePages::is_explicit() && is_hugetlbfs()) { - log_error_p(gc)("-XX:+UseLargePages must be enabled when using a %s filesystem", - ZFILESYSTEM_HUGETLBFS); + ZInitialize::error("-XX:+UseLargePages must be enabled when using a %s filesystem", + ZFILESYSTEM_HUGETLBFS); return; } // Make sure the filesystem block size is compatible if (ZGranuleSize % _block_size != 0) { - log_error_p(gc)("Filesystem backing the heap has incompatible block size (" SIZE_FORMAT ")", - _block_size); + ZInitialize::error("Filesystem backing the heap has incompatible block size (" SIZE_FORMAT ")", + _block_size); return; } if (is_hugetlbfs() && _block_size != ZGranuleSize) { - log_error_p(gc)("%s filesystem has unexpected block size " SIZE_FORMAT " (expected " SIZE_FORMAT ")", - ZFILESYSTEM_HUGETLBFS, _block_size, ZGranuleSize); + ZInitialize::error("%s filesystem has unexpected block size " SIZE_FORMAT " (expected " SIZE_FORMAT ")", + ZFILESYSTEM_HUGETLBFS, _block_size, ZGranuleSize); return; } @@ -226,8 +228,8 @@ int ZPhysicalMemoryBacking::create_file_fd(const char* name) const { ? ZFILESYSTEM_HUGETLBFS : ZFILESYSTEM_TMPFS; const char** const preferred_mountpoints = ZLargePages::is_explicit() - ? z_preferred_hugetlbfs_mountpoints - : z_preferred_tmpfs_mountpoints; + ? ZPreferredHugetlbfsMountpoints + : ZPreferredTmpfsMountpoints; // Find mountpoint ZMountPoint mountpoint(filesystem, preferred_mountpoints); diff --git a/src/hotspot/os/linux/osThread_linux.cpp b/src/hotspot/os/linux/osThread_linux.cpp index 9c77cb32f6d1c..3dd6e3bbcd15c 100644 --- a/src/hotspot/os/linux/osThread_linux.cpp +++ b/src/hotspot/os/linux/osThread_linux.cpp @@ -22,27 +22,27 @@ * */ -// no precompiled headers -#include "memory/allocation.inline.hpp" +#include "precompiled.hpp" +#include "memory/allocation.hpp" #include "runtime/mutex.hpp" #include "runtime/osThread.hpp" #include -void OSThread::pd_initialize() { - _thread_id = 0; - _pthread_id = 0; - _siginfo = nullptr; - _ucontext = nullptr; - _expanding_stack = 0; - _alt_sig_stack = nullptr; - +OSThread::OSThread() + : _thread_id(0), + _thread_type(), + _pthread_id(0), + _caller_sigmask(), + sr(), + _siginfo(nullptr), + _ucontext(nullptr), + _expanding_stack(0), + _alt_sig_stack(nullptr), + _startThread_lock(new Monitor(Mutex::event, "startThread_lock")) { sigemptyset(&_caller_sigmask); - - _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); - assert(_startThread_lock !=nullptr, "check"); } -void OSThread::pd_destroy() { +OSThread::~OSThread() { delete _startThread_lock; } diff --git a/src/hotspot/os/linux/osThread_linux.hpp b/src/hotspot/os/linux/osThread_linux.hpp index a849673af62db..f8dfd5a213bbb 100644 --- a/src/hotspot/os/linux/osThread_linux.hpp +++ b/src/hotspot/os/linux/osThread_linux.hpp @@ -24,13 +24,28 @@ #ifndef OS_LINUX_OSTHREAD_LINUX_HPP #define OS_LINUX_OSTHREAD_LINUX_HPP - public: + +#include "runtime/osThreadBase.hpp" +#include "suspendResume_posix.hpp" +#include "utilities/globalDefinitions.hpp" + +class OSThread : public OSThreadBase { + friend class VMStructs; + typedef pid_t thread_id_t; - private: + thread_id_t _thread_id; int _thread_type; + // _pthread_id is the pthread id, which is used by library calls + // (e.g. pthread_kill). + pthread_t _pthread_id; + + sigset_t _caller_sigmask; // Caller's signal mask + public: + OSThread(); + ~OSThread(); int thread_type() const { return _thread_type; @@ -39,22 +54,16 @@ _thread_type = type; } - // _pthread_id is the pthread id, which is used by library calls - // (e.g. pthread_kill). - pthread_t _pthread_id; - - sigset_t _caller_sigmask; // Caller's signal mask - - public: - // Methods to save/restore caller's signal mask sigset_t caller_sigmask() const { return _caller_sigmask; } void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - int thread_identifier() const { return _thread_id; } -#endif + thread_id_t thread_id() const { + return _thread_id; + } + void set_thread_id(thread_id_t id) { + _thread_id = id; + } pthread_t pthread_id() const { return _pthread_id; @@ -67,7 +76,6 @@ // suspension support. // *************************************************************** -public: // flags that support signal based suspend/resume on Linux are in a // separate class to avoid confusion with many flags in OSThread that // are used by VM level suspend/resume. @@ -113,17 +121,10 @@ return _startThread_lock; } - // *************************************************************** - // Platform dependent initialization and cleanup - // *************************************************************** - -private: - - void pd_initialize(); - void pd_destroy(); - -// Reconciliation History -// osThread_solaris.hpp 1.24 99/08/27 13:11:54 -// End + // Printing + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_LINUX_OSTHREAD_LINUX_HPP diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index d4699567733b2..c80663fec3d3a 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -817,7 +817,7 @@ static void *thread_native_entry(Thread *thread) { OSThread* osthread = thread->osthread(); Monitor* sync = osthread->startThread_lock(); - osthread->set_thread_id(checked_cast(os::current_thread_id())); + osthread->set_thread_id(checked_cast(os::current_thread_id())); if (UseNUMA) { int lgrp_id = os::numa_get_group_id(); @@ -4602,7 +4602,7 @@ static void workaround_expand_exec_shield_cs_limit() { return; // No matter, we tried, best effort. } - MemTracker::record_virtual_memory_type((address)codebuf, mtInternal); + MemTracker::record_virtual_memory_tag((address)codebuf, mtInternal); log_info(os)("[CS limit NX emulation work-around, exec code at: %p]", codebuf); @@ -5053,16 +5053,6 @@ int os::open(const char *path, int oflag, int mode) { return fd; } -// return current position of file pointer -jlong os::current_file_offset(int fd) { - 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)::lseek(fd, (off_t)offset, SEEK_SET); -} - static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time); static jlong fast_cpu_time(Thread *thread) { diff --git a/src/hotspot/os/linux/vmStructs_linux.hpp b/src/hotspot/os/linux/vmStructs_linux.hpp index 818f6bb188fe8..3b82ac58ac697 100644 --- a/src/hotspot/os/linux/vmStructs_linux.hpp +++ b/src/hotspot/os/linux/vmStructs_linux.hpp @@ -31,9 +31,22 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pid_t) \ + nonstatic_field(OSThread, _pthread_id, pthread_t) + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_integer_type(pid_t) \ + declare_unsigned_integer_type(pthread_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 26bff6c8bd4e6..78fd2a678886e 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -104,49 +104,44 @@ static int clock_tics_per_sec = 100; size_t os::_os_min_stack_allowed = PTHREAD_STACK_MIN; // Check core dump limit and report possible place where core can be found -void os::check_dump_limit(char* buffer, size_t bufferSize) { +void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only) { if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) { jio_snprintf(buffer, bufferSize, "CreateCoredumpOnCrash is disabled from command line"); VMError::record_coredump_status(buffer, false); - return; - } - - int n; - struct rlimit rlim; - bool success; - - char core_path[PATH_MAX]; - n = get_core_path(core_path, PATH_MAX); - - if (n <= 0) { - jio_snprintf(buffer, bufferSize, "core.%d (may not exist)", current_process_id()); - success = true; + } else { + struct rlimit rlim; + bool success = true; + bool warn = true; + char core_path[PATH_MAX]; + if (get_core_path(core_path, PATH_MAX) <= 0) { + jio_snprintf(buffer, bufferSize, "core.%d (may not exist)", current_process_id()); #ifdef LINUX - } else if (core_path[0] == '"') { // redirect to user process - jio_snprintf(buffer, bufferSize, "Core dumps may be processed with %s", core_path); - success = true; + } else if (core_path[0] == '"') { // redirect to user process + jio_snprintf(buffer, bufferSize, "Core dumps may be processed with %s", core_path); #endif - } else if (getrlimit(RLIMIT_CORE, &rlim) != 0) { - jio_snprintf(buffer, bufferSize, "%s (may not exist)", core_path); - success = true; - } else { - switch(rlim.rlim_cur) { - case RLIM_INFINITY: - jio_snprintf(buffer, bufferSize, "%s", core_path); - success = true; - break; - case 0: - jio_snprintf(buffer, bufferSize, "Core dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again"); - success = false; - break; - default: - jio_snprintf(buffer, bufferSize, "%s (max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", core_path, uint64_t(rlim.rlim_cur) / K); - success = true; - break; + } else if (getrlimit(RLIMIT_CORE, &rlim) != 0) { + jio_snprintf(buffer, bufferSize, "%s (may not exist)", core_path); + } else { + switch(rlim.rlim_cur) { + case RLIM_INFINITY: + jio_snprintf(buffer, bufferSize, "%s", core_path); + warn = false; + break; + case 0: + jio_snprintf(buffer, bufferSize, "Core dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again"); + success = false; + break; + default: + jio_snprintf(buffer, bufferSize, "%s (max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", core_path, uint64_t(rlim.rlim_cur) / K); + break; + } + } + if (!check_only) { + VMError::record_coredump_status(buffer, success); + } else if (warn) { + warning("CreateCoredumpOnCrash specified, but %s", buffer); } } - - VMError::record_coredump_status(buffer, success); } bool os::committed_in_range(address start, size_t size, address& committed_start, size_t& committed_size) { @@ -348,6 +343,16 @@ int os::create_file_for_heap(const char* dir) { return fd; } +// return current position of file pointer +jlong os::current_file_offset(int fd) { + 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)::lseek(fd, (off_t)offset, SEEK_SET); +} + // Is a (classpath) directory empty? bool os::dir_is_empty(const char* path) { DIR *dir = nullptr; @@ -367,7 +372,7 @@ bool os::dir_is_empty(const char* path) { return result; } -static char* reserve_mmapped_memory(size_t bytes, char* requested_addr, MEMFLAGS flag) { +static char* reserve_mmapped_memory(size_t bytes, char* requested_addr, MemTag mem_tag) { char * addr; int flags = MAP_PRIVATE NOT_AIX( | MAP_NORESERVE ) | MAP_ANONYMOUS; if (requested_addr != nullptr) { @@ -382,7 +387,7 @@ static char* reserve_mmapped_memory(size_t bytes, char* requested_addr, MEMFLAGS flags, -1, 0); if (addr != MAP_FAILED) { - MemTracker::record_virtual_memory_reserve((address)addr, bytes, CALLER_PC, flag); + MemTracker::record_virtual_memory_reserve((address)addr, bytes, CALLER_PC, mem_tag); return addr; } return nullptr; @@ -495,7 +500,7 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment, bool exec) { return chop_extra_memory(size, alignment, extra_base, extra_size); } -char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int file_desc, MEMFLAGS flag) { +char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag) { size_t extra_size = calculate_aligned_extra_size(size, alignment); // For file mapping, we do not call os:map_memory_to_file(size,fd) since: // - we later chop away parts of the mapping using os::release_memory and that could fail if the @@ -503,7 +508,7 @@ char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int file_des // - The memory API os::reserve_memory uses is an implementation detail. It may (and usually is) // mmap but it also may System V shared memory which cannot be uncommitted as a whole, so // chopping off and unmapping excess bits back and front (see below) would not work. - char* extra_base = reserve_mmapped_memory(extra_size, nullptr, flag); + char* extra_base = reserve_mmapped_memory(extra_size, nullptr, mem_tag); if (extra_base == nullptr) { return nullptr; } diff --git a/src/hotspot/os/windows/globals_windows.hpp b/src/hotspot/os/windows/globals_windows.hpp index 78cbac6e9ccc5..8f0a6261cc0db 100644 --- a/src/hotspot/os/windows/globals_windows.hpp +++ b/src/hotspot/os/windows/globals_windows.hpp @@ -38,6 +38,10 @@ product(bool, UseAllWindowsProcessorGroups, false, \ "Use all processor groups on supported Windows versions") \ \ +product(bool, EnableAllLargePageSizesForWindows, false, \ + "Enable support for multiple large page sizes on " \ + "Windows Server") \ + \ product(bool, UseOSErrorReporting, false, \ "Let VM fatal error propagate to the OS (ie. WER on Windows)") diff --git a/src/hotspot/os/windows/memMapPrinter_windows.cpp b/src/hotspot/os/windows/memMapPrinter_windows.cpp new file mode 100644 index 0000000000000..eb6b24a9d139a --- /dev/null +++ b/src/hotspot/os/windows/memMapPrinter_windows.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Red Hat, Inc. and/or its affiliates. + * 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. + * + */ + +#include "precompiled.hpp" +#include "nmt/memMapPrinter.hpp" +#include "os_windows.hpp" +#include "runtime/vm_version.hpp" + +#include +#include +#include +#include + +/* maximum number of mapping records returned */ +static const int MAX_REGIONS_RETURNED = 1000000; + +class MappingInfo { +public: + stringStream _ap_buffer; + stringStream _state_buffer; + stringStream _protect_buffer; + stringStream _type_buffer; + char _file_name[MAX_PATH]; + + MappingInfo() {} + + void process(MEMORY_BASIC_INFORMATION& mem_info) { + _ap_buffer.reset(); + _state_buffer.reset(); + _protect_buffer.reset(); + _type_buffer.reset(); + get_protect_string(_ap_buffer, mem_info.AllocationProtect); + get_state_string(_state_buffer, mem_info); + get_protect_string(_protect_buffer, mem_info.Protect); + get_type_string(_type_buffer, mem_info); + _file_name[0] = 0; + if (mem_info.Type == MEM_IMAGE) { + HMODULE hModule = 0; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast(mem_info.AllocationBase), &hModule)) { + GetModuleFileName(hModule, _file_name, sizeof(_file_name)); + } + } + } + + void get_protect_string(outputStream& out, DWORD prot) { + const char read_c = prot & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY) ? 'r' : '-'; + const char write_c = prot & (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) ? 'w' : '-'; + const char execute_c = prot & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) ? 'x' : '-'; + out.print("%c%c%c", read_c, write_c, execute_c); + if (prot & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) { + out.put('c'); + } + if (prot & PAGE_GUARD) { + out.put('g'); + } + if (prot & PAGE_NOCACHE) { + out.put('n'); + } + if (prot & PAGE_WRITECOMBINE) { + out.put('W'); + } + const DWORD bits = PAGE_NOACCESS | PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE + | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_EXECUTE + | PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE; + if ((prot & bits) != prot) { + out.print_cr("Unknown Windows memory protection value: 0x%x unknown bits: 0x%x", prot, prot & ~bits); + assert(false, "Unknown Windows memory protection value: 0x%x unknown bits: 0x%x", prot, prot & ~bits); + } + } + + void get_state_string(outputStream& out, MEMORY_BASIC_INFORMATION& mem_info) { + if (mem_info.State == MEM_COMMIT) { + out.put('c'); + } else if (mem_info.State == MEM_FREE) { + out.put('f'); + } else if (mem_info.State == MEM_RESERVE) { + out.put('r'); + } else { + out.print_cr("Unknown Windows memory state value: 0x%x", mem_info.State); + assert(false, "Unknown Windows memory state value: 0x%x", mem_info.State); + } + } + + void get_type_string(outputStream& out, MEMORY_BASIC_INFORMATION& mem_info) { + if (mem_info.Type == MEM_IMAGE) { + out.print("img"); + } else if (mem_info.Type == MEM_MAPPED) { + out.print("map"); + } else if (mem_info.Type == MEM_PRIVATE) { + out.print("pvt"); + } else if (mem_info.Type == 0 && mem_info.State == MEM_FREE) { + out.print("---"); + } else { + out.print_cr("Unknown Windows memory type 0x%x", mem_info.Type); + assert(false, "Unknown Windows memory type 0x%x", mem_info.Type); + } + } +}; + +class MappingInfoSummary { + unsigned _num_mappings; + size_t _total_region_size; // combined resident set size + size_t _total_committed; // combined committed size + class WinOsInfo : public os::win32 { + public: + static void printOsInfo(outputStream* st) { + st->print("OS:"); + os::win32::print_windows_version(st); + os::win32::print_uptime_info(st); + VM_Version::print_platform_virtualization_info(st); + os::print_memory_info(st); + } + }; +public: + MappingInfoSummary() : _num_mappings(0), _total_region_size(0), + _total_committed(0) {} + + void add_mapping(const MEMORY_BASIC_INFORMATION& mem_info, const MappingInfo& mapping_info) { + if (mem_info.State != MEM_FREE) { + _num_mappings++; + _total_region_size += mem_info.RegionSize; + _total_committed += mem_info.State == MEM_COMMIT ? mem_info.RegionSize : 0; + } + } + + void print_on(const MappingPrintSession& session) const { + outputStream* st = session.out(); + WinOsInfo::printOsInfo(st); + st->print_cr("current process reserved memory: " PROPERFMT, PROPERFMTARGS(_total_region_size)); + st->print_cr("current process committed memory: " PROPERFMT, PROPERFMTARGS(_total_committed)); + st->print_cr("current process region count: " PROPERFMT, PROPERFMTARGS(_num_mappings)); + } +}; + +class MappingInfoPrinter { + const MappingPrintSession& _session; +public: + MappingInfoPrinter(const MappingPrintSession& session) : + _session(session) + {} + + void print_single_mapping(const MEMORY_BASIC_INFORMATION& mem_info, const MappingInfo& mapping_info) const { + outputStream* st = _session.out(); +#define INDENT_BY(n) \ + if (st->fill_to(n) == 0) { \ + st->print(" "); \ + } + st->print(PTR_FORMAT "-" PTR_FORMAT, mem_info.BaseAddress, static_cast(mem_info.BaseAddress) + mem_info.RegionSize); + INDENT_BY(38); + st->print("%12zu", mem_info.RegionSize); + INDENT_BY(51); + st->print("%s", mapping_info._protect_buffer.base()); + INDENT_BY(57); + st->print("%s-%s", mapping_info._state_buffer.base(), mapping_info._type_buffer.base()); + INDENT_BY(63); + st->print("%#11llx", reinterpret_cast(mem_info.BaseAddress) - reinterpret_cast(mem_info.AllocationBase)); + INDENT_BY(72); + if (_session.print_nmt_info_for_region(mem_info.BaseAddress, static_cast(mem_info.BaseAddress) + mem_info.RegionSize)) { + st->print(" "); + } + st->print_raw(mapping_info._file_name); + #undef INDENT_BY + st->cr(); + } + + void print_legend() const { + outputStream* st = _session.out(); + st->print_cr("from, to, vsize: address range and size"); + st->print_cr("prot: protection:"); + st->print_cr(" rwx: read / write / execute"); + st->print_cr(" c: copy on write"); + st->print_cr(" g: guard"); + st->print_cr(" n: no cache"); + st->print_cr(" W: write combine"); + st->print_cr("state: region state and type:"); + st->print_cr(" state: committed / reserved"); + st->print_cr(" type: image / mapped / private"); + st->print_cr("file: file mapped, if mapping is not anonymous"); + st->print_cr("vm info: VM information (requires NMT)"); + { + streamIndentor si(st, 16); + _session.print_nmt_flag_legend(); + } + } + + void print_header() const { + outputStream* st = _session.out(); + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 + // 0x00007ffb24565000-0x00007ffb24a7e000 5345280 r-- c-img 0x1155000 C:\work\jdk\build\fastdebug\jdk\bin\server\jvm.dll + st->print_cr("from to vsize prot state offset vminfo/file"); + st->print_cr("==========================================================================================="); + } +}; + +void MemMapPrinter::pd_print_all_mappings(const MappingPrintSession& session) { + + HANDLE hProcess = GetCurrentProcess(); + + MappingInfoPrinter printer(session); + MappingInfoSummary summary; + + outputStream* const st = session.out(); + + printer.print_legend(); + st->cr(); + printer.print_header(); + + MEMORY_BASIC_INFORMATION mem_info; + MappingInfo mapping_info; + + int region_count = 0; + ::memset(&mem_info, 0, sizeof(mem_info)); + for (char* ptr = 0; VirtualQueryEx(hProcess, ptr, &mem_info, sizeof(mem_info)) == sizeof(mem_info); ) { + assert(mem_info.RegionSize > 0, "RegionSize is not greater than zero"); + if (++region_count > MAX_REGIONS_RETURNED) { + st->print_cr("limit of %d regions reached (results inaccurate)", region_count); + break; + } + mapping_info.process(mem_info); + if (mem_info.State != MEM_FREE) { + printer.print_single_mapping(mem_info, mapping_info); + summary.add_mapping(mem_info, mapping_info); + } + ptr += mem_info.RegionSize; + ::memset(&mem_info, 0, sizeof(mem_info)); + } + st->cr(); + summary.print_on(session); + st->cr(); +} diff --git a/src/hotspot/os/windows/osThread_windows.cpp b/src/hotspot/os/windows/osThread_windows.cpp index 5f369bb7aa05b..922b4b0104be4 100644 --- a/src/hotspot/os/windows/osThread_windows.cpp +++ b/src/hotspot/os/windows/osThread_windows.cpp @@ -22,17 +22,17 @@ * */ -// no precompiled headers -#include "runtime/os.hpp" +#include "precompiled.hpp" #include "runtime/osThread.hpp" -void OSThread::pd_initialize() { - set_thread_handle(nullptr); - set_thread_id(0); - set_interrupt_event(nullptr); -} +#include + +OSThread::OSThread() + : _thread_id(0), + _thread_handle(nullptr), + _interrupt_event(nullptr) {} -void OSThread::pd_destroy() { +OSThread::~OSThread() { if (_interrupt_event != nullptr) { CloseHandle(_interrupt_event); } diff --git a/src/hotspot/os/windows/osThread_windows.hpp b/src/hotspot/os/windows/osThread_windows.hpp index 5bd07646b1718..e54783aef1c15 100644 --- a/src/hotspot/os/windows/osThread_windows.hpp +++ b/src/hotspot/os/windows/osThread_windows.hpp @@ -25,17 +25,29 @@ #ifndef OS_WINDOWS_OSTHREAD_WINDOWS_HPP #define OS_WINDOWS_OSTHREAD_WINDOWS_HPP - typedef void* HANDLE; - public: +#include "runtime/osThreadBase.hpp" +#include "utilities/globalDefinitions.hpp" + +class OSThread : public OSThreadBase { + friend class VMStructs; + typedef unsigned long thread_id_t; + typedef void* HANDLE; + + thread_id_t _thread_id; - private: // Win32-specific thread information HANDLE _thread_handle; // Win32 thread handle HANDLE _interrupt_event; // Event signalled on thread interrupt for use by // Process.waitFor(). public: + OSThread(); + ~OSThread(); + + thread_id_t thread_id() const { return _thread_id; } + void set_thread_id(thread_id_t id) { _thread_id = id; } + // The following will only apply in the Win32 implementation, and should only // be visible in the concrete class, not this which should be an abstract base class HANDLE thread_handle() const { return _thread_handle; } @@ -45,13 +57,9 @@ // This is specialized on Windows to interact with the _interrupt_event. void set_interrupted(bool z); -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - int thread_identifier() const { return _thread_id; } -#endif - - private: - void pd_initialize(); - void pd_destroy(); + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_WINDOWS_OSTHREAD_WINDOWS_HPP diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index a1e0a78837f74..fe093aee116b6 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -63,6 +63,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/statSampler.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/suspendedThreadTask.hpp" #include "runtime/threadCritical.hpp" #include "runtime/threads.hpp" #include "runtime/timer.hpp" @@ -1286,38 +1287,50 @@ void os::shutdown() { static HANDLE dumpFile = nullptr; -// Check if dump file can be created. -void os::check_dump_limit(char* buffer, size_t buffsz) { - bool status = true; +// Check if core dump is active and if a core dump file can be created +void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only) { if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) { - jio_snprintf(buffer, buffsz, "CreateCoredumpOnCrash is disabled from command line"); - status = false; - } - + jio_snprintf(buffer, bufferSize, "CreateCoredumpOnCrash is disabled from command line"); + VMError::record_coredump_status(buffer, false); + } else { + bool success = true; + bool warn = true; #ifndef ASSERT - if (!os::win32::is_windows_server() && FLAG_IS_DEFAULT(CreateCoredumpOnCrash)) { - jio_snprintf(buffer, buffsz, "Minidumps are not enabled by default on client versions of Windows"); - status = false; - } + if (!os::win32::is_windows_server() && FLAG_IS_DEFAULT(CreateCoredumpOnCrash)) { + jio_snprintf(buffer, bufferSize, "Minidumps are not enabled by default on client versions of Windows"); + success = false; + warn = true; + } #endif - if (status) { - const char* cwd = get_current_directory(nullptr, 0); - int pid = current_process_id(); - if (cwd != nullptr) { - jio_snprintf(buffer, buffsz, "%s\\hs_err_pid%u.mdmp", cwd, pid); - } else { - jio_snprintf(buffer, buffsz, ".\\hs_err_pid%u.mdmp", pid); + if (success) { + if (!check_only) { + const char* cwd = get_current_directory(nullptr, 0); + int pid = current_process_id(); + if (cwd != nullptr) { + jio_snprintf(buffer, bufferSize, "%s\\hs_err_pid%u.mdmp", cwd, pid); + } else { + jio_snprintf(buffer, bufferSize, ".\\hs_err_pid%u.mdmp", pid); + } + + if (dumpFile == nullptr && + (dumpFile = CreateFile(buffer, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)) + == INVALID_HANDLE_VALUE) { + jio_snprintf(buffer, bufferSize, "Failed to create minidump file (0x%x).", GetLastError()); + success = false; + } + } else { + // For now on Windows, there are no more checks that we can do + warn = false; + } } - if (dumpFile == nullptr && - (dumpFile = CreateFile(buffer, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)) - == INVALID_HANDLE_VALUE) { - jio_snprintf(buffer, buffsz, "Failed to create minidump file (0x%x).", GetLastError()); - status = false; + if (!check_only) { + VMError::record_coredump_status(buffer, success); + } else if (warn) { + warning("CreateCoredumpOnCrash specified, but %s", buffer); } } - VMError::record_coredump_status(buffer, status); } void os::abort(bool dump_core, void* siginfo, const void* context) { @@ -1947,7 +1960,10 @@ void os::win32::print_windows_version(outputStream* st) { // - 2016 GA 10/2016 build: 14393 // - 2019 GA 11/2018 build: 17763 // - 2022 GA 08/2021 build: 20348 - if (build_number > 20347) { + // - 2025 Preview build : 26040 + if (build_number > 26039) { + st->print("Server 2025"); + } else if (build_number > 20347) { st->print("Server 2022"); } else if (build_number > 17762) { st->print("Server 2019"); @@ -3123,7 +3139,7 @@ class NUMANodeListHolder { static size_t _large_page_size = 0; -static bool request_lock_memory_privilege() { +bool os::win32::request_lock_memory_privilege() { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, os::current_process_id()); @@ -3307,14 +3323,14 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, return p_buf; } -static size_t large_page_init_decide_size() { +size_t os::win32::large_page_init_decide_size() { // print a warning if any large page related flag is specified on command line bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) || !FLAG_IS_DEFAULT(LargePageSizeInBytes); -#define WARN(msg) if (warn_on_failure) { warning(msg); } +#define WARN(...) if (warn_on_failure) { warning(__VA_ARGS__); } - if (!request_lock_memory_privilege()) { + if (!os::win32::request_lock_memory_privilege()) { WARN("JVM cannot use large page memory because it does not have enough privilege to lock pages in memory."); return 0; } @@ -3325,15 +3341,26 @@ static size_t large_page_init_decide_size() { return 0; } -#if defined(IA32) || defined(AMD64) - if (size > 4*M || LargePageSizeInBytes > 4*M) { +#if defined(IA32) + if (size > 4 * M || LargePageSizeInBytes > 4 * M) { WARN("JVM cannot use large pages bigger than 4mb."); return 0; } +#elif defined(AMD64) + if (!EnableAllLargePageSizesForWindows) { + if (size > 4 * M || LargePageSizeInBytes > 4 * M) { + WARN("JVM cannot use large pages bigger than 4mb."); + return 0; + } + } #endif - if (LargePageSizeInBytes > 0 && LargePageSizeInBytes % size == 0) { - size = LargePageSizeInBytes; + if (LargePageSizeInBytes > 0) { + if (LargePageSizeInBytes % size == 0) { + size = LargePageSizeInBytes; + } else { + WARN("The specified large page size (%d) is not a multiple of the minimum large page size (%d), defaulting to minimum page size.", LargePageSizeInBytes, size); + } } #undef WARN @@ -3346,12 +3373,23 @@ void os::large_page_init() { return; } - _large_page_size = large_page_init_decide_size(); + _large_page_size = os::win32::large_page_init_decide_size(); const size_t default_page_size = os::vm_page_size(); if (_large_page_size > default_page_size) { +#if !defined(IA32) + if (EnableAllLargePageSizesForWindows) { + size_t min_size = GetLargePageMinimum(); + + // Populate _page_sizes with large page sizes less than or equal to _large_page_size, ensuring each page size is double the size of the previous one. + for (size_t page_size = min_size; page_size < _large_page_size; page_size *= 2) { + _page_sizes.add(page_size); + } + } +#endif + _page_sizes.add(_large_page_size); } - + // Set UseLargePages based on whether a large page size was successfully determined UseLargePages = _large_page_size != 0; } @@ -3428,7 +3466,7 @@ char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, in // Multiple threads can race in this code but it's not possible to unmap small sections of // virtual space to get requested alignment, like posix-like os's. // Windows prevents multiple thread from remapping over each other so this loop is thread-safe. -static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc, MEMFLAGS flag = mtNone) { +static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag = mtNone) { assert(is_aligned(alignment, os::vm_allocation_granularity()), "Alignment must be a multiple of allocation granularity (page size)"); assert(is_aligned(size, os::vm_allocation_granularity()), @@ -3441,8 +3479,8 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi static const int max_attempts = 20; for (int attempt = 0; attempt < max_attempts && aligned_base == nullptr; attempt ++) { - char* extra_base = file_desc != -1 ? os::map_memory_to_file(extra_size, file_desc, flag) : - os::reserve_memory(extra_size, false, flag); + char* extra_base = file_desc != -1 ? os::map_memory_to_file(extra_size, file_desc, mem_tag) : + os::reserve_memory(extra_size, false, mem_tag); if (extra_base == nullptr) { return nullptr; } @@ -3458,8 +3496,8 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi // Attempt to map, into the just vacated space, the slightly smaller aligned area. // Which may fail, hence the loop. - aligned_base = file_desc != -1 ? os::attempt_map_memory_to_file_at(aligned_base, size, file_desc, flag) : - os::attempt_reserve_memory_at(aligned_base, size, false, flag); + aligned_base = file_desc != -1 ? os::attempt_map_memory_to_file_at(aligned_base, size, file_desc, mem_tag) : + os::attempt_reserve_memory_at(aligned_base, size, false, mem_tag); } assert(aligned_base != nullptr, @@ -3473,8 +3511,8 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment, bool exec) { return map_or_reserve_memory_aligned(size, alignment, -1 /* file_desc */); } -char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MEMFLAGS flag) { - return map_or_reserve_memory_aligned(size, alignment, fd, flag); +char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag) { + return map_or_reserve_memory_aligned(size, alignment, fd, mem_tag); } char* os::pd_reserve_memory(size_t bytes, bool exec) { @@ -3615,7 +3653,6 @@ static char* reserve_large_pages_aligned(size_t size, size_t alignment, bool exe char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_size, char* addr, bool exec) { assert(UseLargePages, "only for large pages"); - assert(page_size == os::large_page_size(), "Currently only support one large page size on Windows"); assert(is_aligned(addr, alignment), "Must be"); assert(is_aligned(addr, page_size), "Must be"); @@ -3624,11 +3661,17 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_ return nullptr; } + // Ensure GetLargePageMinimum() returns a valid positive value + size_t large_page_min = GetLargePageMinimum(); + if (large_page_min <= 0) { + return nullptr; + } + // The requested alignment can be larger than the page size, for example with G1 // the alignment is bound to the heap region size. So this reservation needs to // ensure that the requested alignment is met. When there is a requested address // this solves it self, since it must be properly aligned already. - if (addr == nullptr && alignment > page_size) { + if (addr == nullptr && alignment > large_page_min) { return reserve_large_pages_aligned(bytes, alignment, exec); } @@ -4090,6 +4133,39 @@ int os::win32::_build_minor = 0; bool os::win32::_processor_group_warning_displayed = false; bool os::win32::_job_object_processor_group_warning_displayed = false; +void getWindowsInstallationType(char* buffer, int bufferSize) { + HKEY hKey; + const char* subKey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; + const char* valueName = "InstallationType"; + + DWORD valueLength = bufferSize; + + // Initialize buffer with empty string + buffer[0] = '\0'; + + // Open the registry key + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) { + // Return empty buffer if key cannot be opened + return; + } + + // Query the value + if (RegQueryValueExA(hKey, valueName, NULL, NULL, (LPBYTE)buffer, &valueLength) != ERROR_SUCCESS) { + RegCloseKey(hKey); + buffer[0] = '\0'; + return; + } + + RegCloseKey(hKey); +} + +bool isNanoServer() { + const int BUFFER_SIZE = 256; + char installationType[BUFFER_SIZE]; + getWindowsInstallationType(installationType, BUFFER_SIZE); + return (strcmp(installationType, "Nano Server") == 0); +} + void os::win32::initialize_windows_version() { assert(_major_version == 0, "windows version already initialized."); @@ -4107,7 +4183,13 @@ void os::win32::initialize_windows_version() { warning("Attempt to determine system directory failed: %s", buf_len != 0 ? error_msg_buffer : ""); return; } - strncat(kernel32_path, "\\kernel32.dll", MAX_PATH - ret); + + if (isNanoServer()) { + // On Windows Nanoserver the kernel32.dll is located in the forwarders subdirectory + strncat(kernel32_path, "\\forwarders\\kernel32.dll", MAX_PATH - ret); + } else { + strncat(kernel32_path, "\\kernel32.dll", MAX_PATH - ret); + } DWORD version_size = GetFileVersionInfoSize(kernel32_path, nullptr); if (version_size == 0) { @@ -5923,7 +6005,7 @@ static void do_resume(HANDLE* h) { // retrieve a suspend/resume context capable handle // from the tid. Caller validates handle return value. void get_thread_handle_for_extended_context(HANDLE* h, - OSThread::thread_id_t tid) { + DWORD tid) { if (h != nullptr) { *h = OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, tid); } diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp index 3bc5ab9eef1f3..1d5237243000b 100644 --- a/src/hotspot/os/windows/os_windows.hpp +++ b/src/hotspot/os/windows/os_windows.hpp @@ -65,6 +65,8 @@ class os::win32 { static void setmode_streams(); static bool is_windows_11_or_greater(); static bool is_windows_server_2022_or_greater(); + static bool request_lock_memory_privilege(); + static size_t large_page_init_decide_size(); static int windows_major_version() { assert(_major_version > 0, "windows version not initialized."); return _major_version; diff --git a/src/hotspot/os/windows/vmStructs_windows.hpp b/src/hotspot/os/windows/vmStructs_windows.hpp index 2550e685f16e2..93f4ea7c8111d 100644 --- a/src/hotspot/os/windows/vmStructs_windows.hpp +++ b/src/hotspot/os/windows/vmStructs_windows.hpp @@ -29,9 +29,18 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + \ + nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ + unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + declare_unsigned_integer_type(OSThread::thread_id_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp b/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp index 157d57f8e0fa2..123cd67248f86 100644 --- a/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp +++ b/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp @@ -30,21 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, pthread_t) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp b/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp index 07b878106cfcd..c384afac7ecff 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp @@ -31,22 +31,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _unique_thread_id, uint64_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Thread IDs */ \ - /**********************/ \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp b/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp index fb43541fa775a..b48ea82712ecd 100644 --- a/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp +++ b/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp @@ -29,22 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _unique_thread_id, uint64_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Thread IDs */ \ - /**********************/ \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp index f2ad002996b5c..3c8e9c4441477 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp index 9b4bd0faf0ad9..120726bf55fcd 100644 --- a/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp @@ -29,22 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp b/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp index 9464c35977078..ae948c7303101 100644 --- a/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp +++ b/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, pid_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(pid_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp index a7dc84770f84c..368d6c971fae0 100644 --- a/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp @@ -54,6 +54,24 @@ inline void OrderAccess::fence() { } inline void OrderAccess::cross_modify_fence_impl() { + // From 3 “Zifencei” Instruction-Fetch Fence, Version 2.0 + // "RISC-V does not guarantee that stores to instruction memory will be made + // visible to instruction fetches on a RISC-V hart until that hart executes a + // FENCE.I instruction. A FENCE.I instruction ensures that a subsequent + // instruction fetch on a RISC-V hart will see any previous data stores + // already visible to the same RISC-V hart. FENCE.I does not ensure that other + // RISC-V harts’ instruction fetches will observe the local hart’s stores in a + // multiprocessor system." + // + // Hence to be able to use fence.i directly we need a kernel that supports + // PR_RISCV_CTX_SW_FENCEI_ON. Thus if context switch to another hart we are + // ensured that instruction fetch will see any previous data stores + // + // The alternative is using full system IPI (system wide icache sync) then + // this barrier is not strictly needed. As this is emitted in runtime slow-path + // we will just always emit it, typically after a safepoint. + guarantee(VM_Version::supports_fencei_barrier(), "Linux kernel require fence.i"); + __asm__ volatile("fence.i" : : : "memory"); } #endif // OS_CPU_LINUX_RISCV_ORDERACCESS_LINUX_RISCV_HPP diff --git a/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp index 6cf7683a58602..3946394c19b1f 100644 --- a/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index 3f9f26b525ba5..a3a226502f6fc 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #ifndef HWCAP_ISA_I #define HWCAP_ISA_I nth_bit('I' - 'A') @@ -82,6 +83,23 @@ __v; \ }) +// prctl PR_RISCV_SET_ICACHE_FLUSH_CTX is from Linux 6.9 +#ifndef PR_RISCV_SET_ICACHE_FLUSH_CTX +#define PR_RISCV_SET_ICACHE_FLUSH_CTX 71 +#endif +#ifndef PR_RISCV_CTX_SW_FENCEI_ON +#define PR_RISCV_CTX_SW_FENCEI_ON 0 +#endif +#ifndef PR_RISCV_CTX_SW_FENCEI_OFF +#define PR_RISCV_CTX_SW_FENCEI_OFF 1 +#endif +#ifndef PR_RISCV_SCOPE_PER_PROCESS +#define PR_RISCV_SCOPE_PER_PROCESS 0 +#endif +#ifndef PR_RISCV_SCOPE_PER_THREAD +#define PR_RISCV_SCOPE_PER_THREAD 1 +#endif + uint32_t VM_Version::cpu_vector_length() { assert(ext_V.enabled(), "should not call this"); return (uint32_t)read_csr(CSR_VLENB); @@ -102,6 +120,7 @@ void VM_Version::setup_cpu_available_features() { if (!RiscvHwprobe::probe_features()) { os_aux_features(); } + char* uarch = os_uarch_additional_features(); vendor_features(); @@ -155,6 +174,24 @@ void VM_Version::setup_cpu_available_features() { i++; } + // Linux kernel require Zifencei + if (!ext_Zifencei.enabled()) { + log_info(os, cpu)("Zifencei not found, required by Linux, enabling."); + ext_Zifencei.enable_feature(); + } + + if (UseCtxFencei) { + // Note that we can set this up only for effected threads + // via PR_RISCV_SCOPE_PER_THREAD, i.e. on VM attach/deattach. + int ret = prctl(PR_RISCV_SET_ICACHE_FLUSH_CTX, PR_RISCV_CTX_SW_FENCEI_ON, PR_RISCV_SCOPE_PER_PROCESS); + if (ret == 0) { + log_debug(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) enabled."); + } else { + FLAG_SET_ERGO(UseCtxFencei, false); + log_info(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) disabled, unsupported by kernel."); + } + } + _features_string = os::strdup(buf); } diff --git a/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp b/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp index 0442510fa247a..a0fb5eb1a6ab9 100644 --- a/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp +++ b/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, pid_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(pid_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp b/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp index 277486549c035..8f6d365723700 100644 --- a/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp +++ b/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp @@ -29,23 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp index 220787823dc69..18a5588b743b9 100644 --- a/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp @@ -29,18 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp b/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp index 9f50a7ed9ae29..985a6a331daba 100644 --- a/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp +++ b/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp @@ -29,18 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/share/adlc/adlArena.cpp b/src/hotspot/share/adlc/adlArena.cpp index d5a1dd500fa66..ebd1f74911d57 100644 --- a/src/hotspot/share/adlc/adlArena.cpp +++ b/src/hotspot/share/adlc/adlArena.cpp @@ -63,8 +63,6 @@ void AdlChunk::chop() { AdlChunk *k = this; while( k ) { AdlChunk *tmp = k->_next; - // clear out this chunk (to detect allocation bugs) - memset(k, 0xBE, k->_len); free(k); // Free chunk (was malloc'd) k = tmp; } diff --git a/src/hotspot/share/adlc/forms.cpp b/src/hotspot/share/adlc/forms.cpp index 068d745254e3d..c34a73ea1e13f 100644 --- a/src/hotspot/share/adlc/forms.cpp +++ b/src/hotspot/share/adlc/forms.cpp @@ -276,7 +276,6 @@ Form::DataType Form::is_load_from_memory(const char *opType) const { Form::DataType Form::is_store_to_memory(const char *opType) const { if( strcmp(opType,"StoreB")==0) return Form::idealB; - if( strcmp(opType,"StoreCM")==0) return Form::idealB; if( strcmp(opType,"StoreC")==0) return Form::idealC; if( strcmp(opType,"StoreD")==0) return Form::idealD; if( strcmp(opType,"StoreF")==0) return Form::idealF; diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index e7df38ff221a9..ac2d3d94153f7 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -3654,7 +3654,6 @@ int MatchNode::needs_ideal_memory_edge(FormDict &globals) const { #if INCLUDE_SHENANDOAHGC "ShenandoahCompareAndSwapN", "ShenandoahCompareAndSwapP", "ShenandoahWeakCompareAndSwapP", "ShenandoahWeakCompareAndSwapN", "ShenandoahCompareAndExchangeP", "ShenandoahCompareAndExchangeN", #endif - "StoreCM", "GetAndSetB", "GetAndSetS", "GetAndAddI", "GetAndSetI", "GetAndSetP", "GetAndAddB", "GetAndAddS", "GetAndAddL", "GetAndSetL", "GetAndSetN", "ClearArray" @@ -4357,7 +4356,7 @@ bool MatchRule::is_vector() const { "RoundDoubleModeV","RotateLeftV" , "RotateRightV", "LoadVector","StoreVector", "LoadVectorGather", "StoreVectorScatter", "LoadVectorGatherMasked", "StoreVectorScatterMasked", "VectorTest", "VectorLoadMask", "VectorStoreMask", "VectorBlend", "VectorInsert", - "VectorRearrange","VectorLoadShuffle", "VectorLoadConst", + "VectorRearrange", "VectorLoadShuffle", "VectorLoadConst", "VectorCastB2X", "VectorCastS2X", "VectorCastI2X", "VectorCastL2X", "VectorCastF2X", "VectorCastD2X", "VectorCastF2HF", "VectorCastHF2F", "VectorUCastB2X", "VectorUCastS2X", "VectorUCastI2X", diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index e1c4e90d0637d..a0944c864e68f 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -167,6 +167,9 @@ bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + #if defined(AMD64) + case vmIntrinsics::_dtanh: + #endif case vmIntrinsics::_dlog: case vmIntrinsics::_dlog10: case vmIntrinsics::_dexp: diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index a2e903edc342f..02be6f8d49e4a 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -3339,6 +3339,7 @@ GraphBuilder::GraphBuilder(Compilation* compilation, IRScope* scope) case vmIntrinsics::_dsin : // fall through case vmIntrinsics::_dcos : // fall through case vmIntrinsics::_dtan : // fall through + case vmIntrinsics::_dtanh : // fall through case vmIntrinsics::_dlog : // fall through case vmIntrinsics::_dlog10 : // fall through case vmIntrinsics::_dexp : // fall through diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp index 5d73ab5b88dba..c568caeca4b30 100644 --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -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 @@ -29,6 +29,7 @@ #include "c1/c1_ValueType.hpp" #include "oops/method.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" class BlockBegin; class BlockList; @@ -1122,7 +1123,7 @@ class LIR_Op: public CompilationResourceObj { } #endif - virtual const char * name() const PRODUCT_RETURN0; + virtual const char * name() const PRODUCT_RETURN_NULL; virtual void visit(LIR_OpVisitState* state); int id() const { return _id; } @@ -1400,7 +1401,7 @@ class LIR_Op1: public LIR_Op { virtual bool is_patching() { return _patch != lir_patch_none; } virtual void emit_code(LIR_Assembler* masm); virtual LIR_Op1* as_Op1() { return this; } - virtual const char * name() const PRODUCT_RETURN0; + virtual const char * name() const PRODUCT_RETURN_NULL; void set_in_opr(LIR_Opr opr) { _opr = opr; } @@ -2033,8 +2034,9 @@ class LIR_OpProfileCall : public LIR_Op { virtual void print_instr(outputStream* out) const PRODUCT_RETURN; bool should_profile_receiver_type() const { bool callee_is_static = _profiled_callee->is_loaded() && _profiled_callee->is_static(); + bool callee_is_private = _profiled_callee->is_loaded() && _profiled_callee->is_private(); Bytecodes::Code bc = _profiled_method->java_code_at_bci(_profiled_bci); - bool call_is_virtual = (bc == Bytecodes::_invokevirtual && !_profiled_callee->can_be_statically_bound()) || bc == Bytecodes::_invokeinterface; + bool call_is_virtual = (bc == Bytecodes::_invokevirtual && !callee_is_private) || bc == Bytecodes::_invokeinterface; return C1ProfileVirtualCalls && call_is_virtual && !callee_is_static; } }; diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 4e63736503fe0..74fdf7a5b76a3 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -2971,6 +2971,7 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) { case vmIntrinsics::_dsqrt: // fall through case vmIntrinsics::_dsqrt_strict: // fall through case vmIntrinsics::_dtan: // fall through + case vmIntrinsics::_dtanh: // fall through case vmIntrinsics::_dsin : // fall through case vmIntrinsics::_dcos : // fall through case vmIntrinsics::_dexp : // fall through diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 5b44d5c0f1983..915f00f77c523 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -347,6 +347,7 @@ const char* Runtime1::name_for_address(address entry) { FUNCTION_CASE(entry, StubRoutines::dsin()); FUNCTION_CASE(entry, StubRoutines::dcos()); FUNCTION_CASE(entry, StubRoutines::dtan()); + FUNCTION_CASE(entry, StubRoutines::dtanh()); #undef FUNCTION_CASE diff --git a/src/hotspot/share/cds/archiveHeapLoader.cpp b/src/hotspot/share/cds/archiveHeapLoader.cpp index 6325fb6f49d73..0e7ef08064c37 100644 --- a/src/hotspot/share/cds/archiveHeapLoader.cpp +++ b/src/hotspot/share/cds/archiveHeapLoader.cpp @@ -376,13 +376,12 @@ void ArchiveHeapLoader::finish_initialization() { intptr_t bottom = is_loaded() ? _loaded_heap_bottom : _mapped_heap_bottom; // The heap roots are stored in one or more segments that are laid out consecutively. - // The byte size of each segment (except for the last one) is max_size. + // The size of each segment (except for the last one) is max_size_in_{elems,bytes}. HeapRootSegments segments = FileMapInfo::current_info()->heap_root_segments(); - int max_size = segments.max_size_in_bytes(); - HeapShared::init_root_segment_sizes(max_size); + HeapShared::init_root_segment_sizes(segments.max_size_in_elems()); intptr_t first_segment_addr = bottom + segments.base_offset(); for (size_t c = 0; c < segments.count(); c++) { - oop segment_oop = cast_to_oop(first_segment_addr + (c * max_size)); + oop segment_oop = cast_to_oop(first_segment_addr + (c * segments.max_size_in_bytes())); assert(segment_oop->is_objArray(), "Must be"); HeapShared::add_root_segment((objArrayOop)segment_oop); } diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index d8ee7155452ff..710e693bfdb14 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -88,7 +88,6 @@ void ArchiveHeapWriter::init() { _native_pointers = new GrowableArrayCHeap(2048); _source_objs = new GrowableArrayCHeap(10000); - guarantee(UseG1GC, "implementation limitation"); guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be"); } } @@ -224,6 +223,7 @@ void ArchiveHeapWriter::copy_roots_to_buffer(GrowableArrayCHeapat(root_index++)); @@ -246,14 +245,21 @@ void ArchiveHeapWriter::copy_roots_to_buffer(GrowableArrayCHeaplength(), "Post-condition: All roots are handled"); + _heap_root_segments = segments; } +// The goal is to sort the objects in increasing order of: +// - objects that have only oop pointers +// - objects that have both native and oop pointers +// - objects that have only native pointers +// - objects that have no pointers static int oop_sorting_rank(oop o) { bool has_oop_ptr, has_native_ptr; HeapShared::get_pointer_info(o, has_oop_ptr, has_native_ptr); - if (!has_oop_ptr) { + if (has_oop_ptr) { if (!has_native_ptr) { return 0; } else { @@ -268,11 +274,6 @@ static int oop_sorting_rank(oop o) { } } -// The goal is to sort the objects in increasing order of: -// - objects that have no pointers -// - objects that have only native pointers -// - objects that have both native and oop pointers -// - objects that have only oop pointers int ArchiveHeapWriter::compare_objs_by_oop_fields(HeapObjOrder* a, HeapObjOrder* b) { int rank_a = a->_rank; int rank_b = b->_rank; @@ -452,26 +453,30 @@ size_t ArchiveHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { void ArchiveHeapWriter::set_requested_address(ArchiveHeapInfo* info) { assert(!info->is_used(), "only set once"); - assert(UseG1GC, "must be"); - address heap_end = (address)G1CollectedHeap::heap()->reserved().end(); - log_info(cds, heap)("Heap end = %p", heap_end); size_t heap_region_byte_size = _buffer_used; assert(heap_region_byte_size > 0, "must archived at least one object!"); - if (UseCompressedOops) { - _requested_bottom = align_down(heap_end - heap_region_byte_size, G1HeapRegion::GrainBytes); + if (UseG1GC) { + address heap_end = (address)G1CollectedHeap::heap()->reserved().end(); + log_info(cds, heap)("Heap end = %p", heap_end); + _requested_bottom = align_down(heap_end - heap_region_byte_size, G1HeapRegion::GrainBytes); + _requested_bottom = align_down(_requested_bottom, MIN_GC_REGION_ALIGNMENT); + assert(is_aligned(_requested_bottom, G1HeapRegion::GrainBytes), "sanity"); + } else { + _requested_bottom = align_up(CompressedOops::begin(), MIN_GC_REGION_ALIGNMENT); + } } else { // We always write the objects as if the heap started at this address. This // makes the contents of the archive heap deterministic. // // Note that at runtime, the heap address is selected by the OS, so the archive // heap will not be mapped at 0x10000000, and the contents need to be patched. - _requested_bottom = (address)NOCOOPS_REQUESTED_BASE; + _requested_bottom = align_up((address)NOCOOPS_REQUESTED_BASE, MIN_GC_REGION_ALIGNMENT); } - assert(is_aligned(_requested_bottom, G1HeapRegion::GrainBytes), "sanity"); + assert(is_aligned(_requested_bottom, MIN_GC_REGION_ALIGNMENT), "sanity"); _requested_top = _requested_bottom + _buffer_used; diff --git a/src/hotspot/share/cds/archiveHeapWriter.hpp b/src/hotspot/share/cds/archiveHeapWriter.hpp index 961d2b52133f8..29ea50ba5fe86 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.hpp +++ b/src/hotspot/share/cds/archiveHeapWriter.hpp @@ -111,11 +111,10 @@ class ArchiveHeapWriter : AllStatic { public: static const intptr_t NOCOOPS_REQUESTED_BASE = 0x10000000; - // The minimum region size of all collectors that are supported by CDS in - // ArchiveHeapLoader::can_map() mode. Currently only G1 is supported. G1's region size - // depends on -Xmx, but can never be smaller than 1 * M. - // (TODO: Perhaps change to 256K to be compatible with Shenandoah) - static constexpr int MIN_GC_REGION_ALIGNMENT = 1 * M; + // The minimum region size of all collectors that are supported by CDS. + // G1 heap region size can never be smaller than 1M. + // Shenandoah heap region size can never be smaller than 256K. + static constexpr int MIN_GC_REGION_ALIGNMENT = 256 * K; private: class EmbeddedOopRelocator; diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp index 2e361ab0c4650..5a78bc26ee627 100644 --- a/src/hotspot/share/cds/archiveUtils.hpp +++ b/src/hotspot/share/cds/archiveUtils.hpp @@ -277,7 +277,6 @@ class HeapRootSegments { memset(this, 0, sizeof(*this)); } HeapRootSegments(size_t base_offset, int roots_count, int max_size_in_bytes, int max_size_in_elems) { - assert(is_power_of_2(max_size_in_bytes), "must be"); memset(this, 0, sizeof(*this)); _base_offset = base_offset; _count = (roots_count + max_size_in_elems - 1) / max_size_in_elems; diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index a0a562eca21a0..5915424c4fe87 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -236,7 +236,7 @@ void CDSConfig::init_shared_archive_paths() { } void CDSConfig::check_internal_module_property(const char* key, const char* value) { - if (Arguments::is_internal_module_property(key)) { + if (Arguments::is_internal_module_property(key) && !Arguments::is_module_path_property(key)) { stop_using_optimized_module_handling(); log_info(cds)("optimized module handling: disabled due to incompatible property: %s=%s", key, value); } diff --git a/src/hotspot/share/cds/classListParser.cpp b/src/hotspot/share/cds/classListParser.cpp index f8d24295a12e5..694a179d7ee6c 100644 --- a/src/hotspot/share/cds/classListParser.cpp +++ b/src/hotspot/share/cds/classListParser.cpp @@ -508,7 +508,9 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS THROW_NULL(vmSymbols::java_lang_ClassNotFoundException()); } - InstanceKlass* k = UnregisteredClasses::load_class(class_name, _source, CHECK_NULL); + ResourceMark rm; + char * source_path = os::strdup_check_oom(ClassLoader::uri_to_path(_source)); + InstanceKlass* k = UnregisteredClasses::load_class(class_name, source_path, CHECK_NULL); if (k->local_interfaces()->length() != _interfaces->length()) { print_specified_interfaces(); print_actual_interfaces(k); diff --git a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp index 78cd092445b70..1b9f589f1c5e5 100644 --- a/src/hotspot/share/cds/classListWriter.cpp +++ b/src/hotspot/share/cds/classListWriter.cpp @@ -174,6 +174,8 @@ void ClassListWriter::write_to_stream(const InstanceKlass* k, outputStream* stre } } + // NB: the string following "source: " is not really a proper file name, but rather + // a truncated URI referring to a file. It must be decoded after reading. #ifdef _WINDOWS // "file:/C:/dir/foo.jar" -> "C:/dir/foo.jar" stream->print(" source: %s", cfs->source() + 6); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index b86118c686886..715fce5f3fc86 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -581,7 +581,7 @@ int FileMapInfo::get_module_shared_path_index(Symbol* location) { // skip_uri_protocol was also called during dump time -- see ClassLoaderExt::process_module_table() ResourceMark rm; - const char* file = ClassLoader::skip_uri_protocol(location->as_C_string()); + const char* file = ClassLoader::uri_to_path(location->as_C_string()); for (int i = ClassLoaderExt::app_module_paths_start_index(); i < get_number_of_shared_paths(); i++) { SharedClassPathEntry* ent = shared_path(i); if (!ent->is_non_existent()) { @@ -781,12 +781,12 @@ bool FileMapInfo::check_paths(int shared_path_start_idx, int num_paths, Growable assert(strlen(rp_array->at(i)) > (size_t)runtime_prefix_len, "sanity"); const char* runtime_path = rp_array->at(i) + runtime_prefix_len; if (!os::same_files(dumptime_path, runtime_path)) { - return true; + return false; } i++; j++; } - return false; + return true; } bool FileMapInfo::validate_boot_class_paths() { @@ -810,7 +810,7 @@ bool FileMapInfo::validate_boot_class_paths() { char* rp = skip_first_path_entry(runtime_boot_path); assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image"); int dp_len = header()->app_class_paths_start_index() - 1; // ignore the first path to the module image - bool mismatch = false; + bool match = true; bool relaxed_check = !header()->has_platform_or_app_classes(); if (dp_len == 0 && rp == nullptr) { @@ -823,7 +823,7 @@ bool FileMapInfo::validate_boot_class_paths() { if (check_paths_existence(rp)) { // If a path exists in the runtime boot paths, it is considered a mismatch // since there's no boot path specified during dump time. - mismatch = true; + match = false; } } } else if (dp_len > 0 && rp != nullptr) { @@ -840,16 +840,16 @@ bool FileMapInfo::validate_boot_class_paths() { // check the full runtime boot path, must match with dump time num = rp_len; } - mismatch = check_paths(1, num, rp_array, 0, 0); + match = check_paths(1, num, rp_array, 0, 0); } else { // create_path_array() ignores non-existing paths. Although the dump time and runtime boot classpath lengths // are the same initially, after the call to create_path_array(), the runtime boot classpath length could become // shorter. We consider boot classpath mismatch in this case. - mismatch = true; + match = false; } } - if (mismatch) { + if (!match) { // The paths are different return classpath_failure("[BOOT classpath mismatch, actual =", runtime_boot_path); } @@ -860,7 +860,7 @@ bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) { const char *appcp = Arguments::get_appclasspath(); assert(appcp != nullptr, "null app classpath"); int rp_len = num_paths(appcp); - bool mismatch = false; + bool match = false; if (rp_len < shared_app_paths_len) { return classpath_failure("Run time APP classpath is shorter than the one at dump time: ", appcp); } @@ -889,8 +889,8 @@ bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) { // run 2: -cp x.jar:NE4:b.jar -> x.jar:b.jar -> mismatched int j = header()->app_class_paths_start_index(); - mismatch = check_paths(j, shared_app_paths_len, rp_array, 0, 0); - if (mismatch) { + match = check_paths(j, shared_app_paths_len, rp_array, 0, 0); + if (!match) { // To facilitate app deployment, we allow the JAR files to be moved *together* to // a different location, as long as they are still stored under the same directory // structure. E.g., the following is OK. @@ -901,10 +901,10 @@ bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) { if (dumptime_prefix_len != 0 || runtime_prefix_len != 0) { log_info(class, path)("LCP length for app classpath (dumptime: %u, runtime: %u)", dumptime_prefix_len, runtime_prefix_len); - mismatch = check_paths(j, shared_app_paths_len, rp_array, + match = check_paths(j, shared_app_paths_len, rp_array, dumptime_prefix_len, runtime_prefix_len); } - if (mismatch) { + if (!match) { return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp); } } @@ -926,15 +926,35 @@ void FileMapInfo::log_paths(const char* msg, int start_idx, int end_idx) { } } +void FileMapInfo::extract_module_paths(const char* runtime_path, GrowableArray* module_paths) { + GrowableArray* path_array = create_path_array(runtime_path); + int num_paths = path_array->length(); + for (int i = 0; i < num_paths; i++) { + const char* name = path_array->at(i); + ClassLoaderExt::extract_jar_files_from_path(name, module_paths); + } + // module paths are stored in sorted order in the CDS archive. + module_paths->sort(ClassLoaderExt::compare_module_path_by_name); +} + bool FileMapInfo::check_module_paths() { - const char* rp = Arguments::get_property("jdk.module.path"); - int num_paths = CDSConfig::num_archives(rp); - if (num_paths != header()->num_module_paths()) { + const char* runtime_path = Arguments::get_property("jdk.module.path"); + int archived_num_module_paths = header()->num_module_paths(); + if (runtime_path == nullptr && archived_num_module_paths == 0) { + return true; + } + if ((runtime_path == nullptr && archived_num_module_paths > 0) || + (runtime_path != nullptr && archived_num_module_paths == 0)) { return false; } ResourceMark rm; - GrowableArray* rp_array = create_path_array(rp); - return check_paths(header()->app_module_paths_start_index(), num_paths, rp_array, 0, 0); + GrowableArray* module_paths = new GrowableArray(3); + extract_module_paths(runtime_path, module_paths); + int num_paths = module_paths->length(); + if (num_paths != archived_num_module_paths) { + return false; + } + return check_paths(header()->app_module_paths_start_index(), num_paths, module_paths, 0, 0); } bool FileMapInfo::validate_shared_path_table() { @@ -944,6 +964,16 @@ bool FileMapInfo::validate_shared_path_table() { // Load the shared path table info from the archive header _shared_path_table = header()->shared_path_table(); + + bool matched_module_paths = true; + if (CDSConfig::is_dumping_dynamic_archive() || header()->has_full_module_graph()) { + matched_module_paths = check_module_paths(); + } + if (header()->has_full_module_graph() && !matched_module_paths) { + CDSConfig::stop_using_optimized_module_handling(); + log_info(cds)("optimized module handling: disabled because of mismatched module paths"); + } + if (CDSConfig::is_dumping_dynamic_archive()) { // Only support dynamic dumping with the usage of the default CDS archive // or a simple base archive. @@ -959,7 +989,7 @@ bool FileMapInfo::validate_shared_path_table() { "Dynamic archiving is disabled because base layer archive has appended boot classpath"); } if (header()->num_module_paths() > 0) { - if (!check_module_paths()) { + if (!matched_module_paths) { CDSConfig::disable_dumping_dynamic_archive(); log_warning(cds)( "Dynamic archiving is disabled because base layer archive has a different module path"); @@ -1581,39 +1611,38 @@ static size_t write_bitmap(const CHeapBitMap* map, char* output, size_t offset) return offset + size_in_bytes; } -// The start of the archived heap has many primitive arrays (String -// bodies) that are not marked by the oop/ptr maps. So we must have -// lots of leading zeros. -size_t FileMapInfo::remove_bitmap_leading_zeros(CHeapBitMap* map) { - size_t old_zeros = map->find_first_set_bit(0); +// The sorting code groups the objects with non-null oop/ptrs together. +// Relevant bitmaps then have lots of leading and trailing zeros, which +// we do not have to store. +size_t FileMapInfo::remove_bitmap_zeros(CHeapBitMap* map) { + BitMap::idx_t first_set = map->find_first_set_bit(0); + BitMap::idx_t last_set = map->find_last_set_bit(0); size_t old_size = map->size(); // Slice and resize bitmap - map->truncate(old_zeros, map->size()); + map->truncate(first_set, last_set + 1); - DEBUG_ONLY( - size_t new_zeros = map->find_first_set_bit(0); - assert(new_zeros == 0, "Should have removed leading zeros"); - ) + assert(map->at(0), "First bit should be set"); + assert(map->at(map->size() - 1), "Last bit should be set"); assert(map->size() <= old_size, "sanity"); - return old_zeros; + + return first_set; } char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, ArchiveHeapInfo* heap_info, size_t &size_in_bytes) { - size_t removed_rw_zeros = remove_bitmap_leading_zeros(rw_ptrmap); - size_t removed_ro_zeros = remove_bitmap_leading_zeros(ro_ptrmap); - header()->set_rw_ptrmap_start_pos(removed_rw_zeros); - header()->set_ro_ptrmap_start_pos(removed_ro_zeros); + size_t removed_rw_leading_zeros = remove_bitmap_zeros(rw_ptrmap); + size_t removed_ro_leading_zeros = remove_bitmap_zeros(ro_ptrmap); + header()->set_rw_ptrmap_start_pos(removed_rw_leading_zeros); + header()->set_ro_ptrmap_start_pos(removed_ro_leading_zeros); size_in_bytes = rw_ptrmap->size_in_bytes() + ro_ptrmap->size_in_bytes(); if (heap_info->is_used()) { - // Remove leading zeros - size_t removed_oop_zeros = remove_bitmap_leading_zeros(heap_info->oopmap()); - size_t removed_ptr_zeros = remove_bitmap_leading_zeros(heap_info->ptrmap()); - - header()->set_heap_oopmap_start_pos(removed_oop_zeros); - header()->set_heap_ptrmap_start_pos(removed_ptr_zeros); + // Remove leading and trailing zeros + size_t removed_oop_leading_zeros = remove_bitmap_zeros(heap_info->oopmap()); + size_t removed_ptr_leading_zeros = remove_bitmap_zeros(heap_info->ptrmap()); + header()->set_heap_oopmap_start_pos(removed_oop_leading_zeros); + header()->set_heap_ptrmap_start_pos(removed_ptr_leading_zeros); size_in_bytes += heap_info->oopmap()->size_in_bytes(); size_in_bytes += heap_info->ptrmap()->size_in_bytes(); @@ -1716,10 +1745,10 @@ void FileMapInfo::close() { */ static char* map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, - bool allow_exec, MEMFLAGS flags = mtNone) { + bool allow_exec, MemTag mem_tag = mtNone) { char* mem = os::map_memory(fd, file_name, file_offset, addr, bytes, AlwaysPreTouch ? false : read_only, - allow_exec, flags); + allow_exec, mem_tag); if (mem != nullptr && AlwaysPreTouch) { os::pretouch_memory(mem, mem + bytes); } @@ -2178,7 +2207,7 @@ bool FileMapInfo::map_heap_region_impl() { _mapped_heap_memregion = MemRegion(start, word_size); - // Map the archived heap data. No need to call MemTracker::record_virtual_memory_type() + // Map the archived heap data. No need to call MemTracker::record_virtual_memory_tag() // for mapped region as it is part of the reserved java heap, which is already recorded. char* addr = (char*)_mapped_heap_memregion.start(); char* base; diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index aa728b9d4949f..6650f52440881 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -271,6 +271,7 @@ class FileMapHeader: private CDSFileMapHeaderBase { bool compressed_oops() const { return _compressed_oops; } bool compressed_class_pointers() const { return _compressed_class_ptrs; } HeapRootSegments heap_root_segments() const { return _heap_root_segments; } + bool has_full_module_graph() const { return _has_full_module_graph; } size_t heap_oopmap_start_pos() const { return _heap_oopmap_start_pos; } size_t heap_ptrmap_start_pos() const { return _heap_ptrmap_start_pos; } size_t rw_ptrmap_start_pos() const { return _rw_ptrmap_start_pos; } @@ -445,7 +446,7 @@ class FileMapInfo : public CHeapObj { void write_header(); void write_region(int region, char* base, size_t size, bool read_only, bool allow_exec); - size_t remove_bitmap_leading_zeros(CHeapBitMap* map); + size_t remove_bitmap_zeros(CHeapBitMap* map); char* write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, ArchiveHeapInfo* heap_info, size_t &size_in_bytes); size_t write_heap_region(ArchiveHeapInfo* heap_info); @@ -554,6 +555,7 @@ class FileMapInfo : public CHeapObj { GrowableArray* rp_array, unsigned int dumptime_prefix_len, unsigned int runtime_prefix_len) NOT_CDS_RETURN_(false); + void extract_module_paths(const char* runtime_path, GrowableArray* module_paths); bool validate_boot_class_paths() NOT_CDS_RETURN_(false); bool validate_app_class_paths(int shared_app_paths_len) NOT_CDS_RETURN_(false); bool map_heap_region_impl() NOT_CDS_JAVA_HEAP_RETURN_(false); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 2bf75a5ba6512..81aa7ac94dc21 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -33,6 +33,7 @@ #include "cds/heapShared.hpp" #include "cds/metaspaceShared.hpp" #include "classfile/classLoaderData.hpp" +#include "classfile/classLoaderExt.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/modules.hpp" #include "classfile/stringTable.hpp" @@ -55,6 +56,7 @@ #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiExport.hpp" +#include "runtime/arguments.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/init.hpp" #include "runtime/javaCalls.hpp" @@ -134,8 +136,7 @@ static ArchivableStaticFieldInfo fmg_archive_subgraph_entry_fields[] = { KlassSubGraphInfo* HeapShared::_default_subgraph_info; GrowableArrayCHeap* HeapShared::_pending_roots = nullptr; GrowableArrayCHeap* HeapShared::_root_segments; -int HeapShared::_root_segment_max_size_shift; -int HeapShared::_root_segment_max_size_mask; +int HeapShared::_root_segment_max_size_elems; OopHandle HeapShared::_scratch_basic_type_mirrors[T_VOID+1]; MetaspaceObjToOopHandleTable* HeapShared::_scratch_java_mirror_table = nullptr; MetaspaceObjToOopHandleTable* HeapShared::_scratch_references_table = nullptr; @@ -242,15 +243,29 @@ objArrayOop HeapShared::root_segment(int segment_idx) { return segment; } +void HeapShared::get_segment_indexes(int idx, int& seg_idx, int& int_idx) { + assert(_root_segment_max_size_elems > 0, "sanity"); + + // Try to avoid divisions for the common case. + if (idx < _root_segment_max_size_elems) { + seg_idx = 0; + int_idx = idx; + } else { + seg_idx = idx / _root_segment_max_size_elems; + int_idx = idx % _root_segment_max_size_elems; + } + + assert(idx == seg_idx * _root_segment_max_size_elems + int_idx, + "sanity: %d index maps to %d segment and %d internal", idx, seg_idx, int_idx); +} + // Returns an objArray that contains all the roots of the archived objects oop HeapShared::get_root(int index, bool clear) { - assert(_root_segment_max_size_shift > 0, "sanity"); - assert(_root_segment_max_size_mask > 0, "sanity"); assert(index >= 0, "sanity"); assert(!CDSConfig::is_dumping_heap() && CDSConfig::is_using_archive(), "runtime only"); assert(!_root_segments->is_empty(), "must have loaded shared heap"); - int seg_idx = index >> _root_segment_max_size_shift; - int int_idx = index & _root_segment_max_size_mask; + int seg_idx, int_idx; + get_segment_indexes(index, seg_idx, int_idx); oop result = root_segment(seg_idx)->obj_at(int_idx); if (clear) { clear_root(index); @@ -262,10 +277,8 @@ void HeapShared::clear_root(int index) { assert(index >= 0, "sanity"); assert(CDSConfig::is_using_archive(), "must be"); if (ArchiveHeapLoader::is_in_use()) { - assert(_root_segment_max_size_shift > 0, "sanity"); - assert(_root_segment_max_size_mask > 0, "sanity"); - int seg_idx = index >> _root_segment_max_size_shift; - int int_idx = index & _root_segment_max_size_mask; + int seg_idx, int_idx; + get_segment_indexes(index, seg_idx, int_idx); if (log_is_enabled(Debug, cds, heap)) { oop old = root_segment(seg_idx)->obj_at(int_idx); log_debug(cds, heap)("Clearing root %d: was " PTR_FORMAT, index, p2i(old)); @@ -471,11 +484,13 @@ void HeapShared::archive_objects(ArchiveHeapInfo *heap_info) { // Cache for recording where the archived objects are copied to create_archived_object_cache(); - log_info(cds)("Heap range = [" PTR_FORMAT " - " PTR_FORMAT "]", - UseCompressedOops ? p2i(CompressedOops::begin()) : - p2i((address)G1CollectedHeap::heap()->reserved().start()), - UseCompressedOops ? p2i(CompressedOops::end()) : - p2i((address)G1CollectedHeap::heap()->reserved().end())); + if (UseCompressedOops || UseG1GC) { + log_info(cds)("Heap range = [" PTR_FORMAT " - " PTR_FORMAT "]", + UseCompressedOops ? p2i(CompressedOops::begin()) : + p2i((address)G1CollectedHeap::heap()->reserved().start()), + UseCompressedOops ? p2i(CompressedOops::end()) : + p2i((address)G1CollectedHeap::heap()->reserved().end())); + } copy_objects(); CDSHeapVerifier::verify(); @@ -783,10 +798,8 @@ void HeapShared::add_root_segment(objArrayOop segment_oop) { _root_segments->push(OopHandle(Universe::vm_global(), segment_oop)); } -void HeapShared::init_root_segment_sizes(int max_size) { - assert(is_power_of_2(max_size), "must be"); - _root_segment_max_size_shift = log2i_exact(max_size); - _root_segment_max_size_mask = max_size - 1; +void HeapShared::init_root_segment_sizes(int max_size_elems) { + _root_segment_max_size_elems = max_size_elems; } void HeapShared::serialize_tables(SerializeClosure* soc) { @@ -873,6 +886,17 @@ void HeapShared::initialize_from_archived_subgraph(JavaThread* current, Klass* k return; // nothing to do } + if (k->name()->equals("jdk/internal/module/ArchivedModuleGraph") && + !CDSConfig::is_using_optimized_module_handling() && + // archive was created with --module-path + ClassLoaderExt::num_module_paths() > 0) { + // ArchivedModuleGraph was created with a --module-path that's different than the runtime --module-path. + // Thus, it might contain references to modules that do not exist at runtime. We cannot use it. + log_info(cds, heap)("Skip initializing ArchivedModuleGraph subgraph: is_using_optimized_module_handling=%s num_module_paths=%d", + BOOL_TO_STR(CDSConfig::is_using_optimized_module_handling()), ClassLoaderExt::num_module_paths()); + return; + } + ExceptionMark em(THREAD); const ArchivedKlassSubGraphInfoRecord* record = resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/true, THREAD); @@ -1121,6 +1145,13 @@ bool HeapShared::archive_reachable_objects_from(int level, // these objects that are referenced (directly or indirectly) by static fields. ResourceMark rm; log_error(cds, heap)("Cannot archive object of class %s", orig_obj->klass()->external_name()); + if (log_is_enabled(Trace, cds, heap)) { + WalkOopAndArchiveClosure* walker = WalkOopAndArchiveClosure::current(); + if (walker != nullptr) { + LogStream ls(Log(cds, heap)::trace()); + CDSHeapVerifier::trace_to_root(&ls, walker->referencing_obj()); + } + } MetaspaceShared::unrecoverable_writing_error(); } diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 9bb85db0fe9d2..01d664945ee74 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -143,13 +143,13 @@ class HeapShared: AllStatic { friend class VerifySharedOopClosure; public: - // Can this VM write a heap region into the CDS archive? Currently only G1+compressed{oops,cp} + // Can this VM write a heap region into the CDS archive? Currently only {G1|Parallel|Serial}+compressed_cp static bool can_write() { CDS_JAVA_HEAP_ONLY( if (_disable_writing) { return false; } - return (UseG1GC && UseCompressedClassPointers); + return (UseG1GC || UseParallelGC || UseSerialGC) && UseCompressedClassPointers; ) NOT_CDS_JAVA_HEAP(return false;) } @@ -291,8 +291,7 @@ class HeapShared: AllStatic { static GrowableArrayCHeap* _pending_roots; static GrowableArrayCHeap* _root_segments; - static int _root_segment_max_size_shift; - static int _root_segment_max_size_mask; + static int _root_segment_max_size_elems; static OopHandle _scratch_basic_type_mirrors[T_VOID+1]; static MetaspaceObjToOopHandleTable* _scratch_java_mirror_table; static MetaspaceObjToOopHandleTable* _scratch_references_table; @@ -407,6 +406,8 @@ class HeapShared: AllStatic { // Run-time only static void clear_root(int index); + static void get_segment_indexes(int index, int& segment_index, int& internal_index); + static void setup_test_class(const char* test_class_name) PRODUCT_RETURN; #endif // INCLUDE_CDS_JAVA_HEAP @@ -425,7 +426,7 @@ class HeapShared: AllStatic { static void init_for_dumping(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; static void write_subgraph_info_table() NOT_CDS_JAVA_HEAP_RETURN; static void add_root_segment(objArrayOop segment_oop) NOT_CDS_JAVA_HEAP_RETURN; - static void init_root_segment_sizes(int max_size) NOT_CDS_JAVA_HEAP_RETURN; + static void init_root_segment_sizes(int max_size_elems) NOT_CDS_JAVA_HEAP_RETURN; static void serialize_tables(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; #ifndef PRODUCT diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 4d978a7ad880f..6f646e162ecac 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -77,6 +77,7 @@ #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" #include "runtime/os.inline.hpp" #include "runtime/safepointVerifiers.hpp" #include "runtime/sharedRuntime.hpp" @@ -300,6 +301,7 @@ void MetaspaceShared::post_initialize(TRAPS) { } ClassLoaderExt::init_paths_start_index(info->app_class_paths_start_index()); ClassLoaderExt::init_app_module_paths_start_index(info->app_module_paths_start_index()); + ClassLoaderExt::init_num_module_paths(info->header()->num_module_paths()); } } } @@ -791,9 +793,22 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS // Do this at the very end, when no Java code will be executed. Otherwise // some new strings may be added to the intern table. StringTable::allocate_shared_strings_array(CHECK); + } else { + log_info(cds)("Not dumping heap, reset CDSConfig::_is_using_optimized_module_handling"); + CDSConfig::stop_using_optimized_module_handling(); } #endif + // Dummy call to load classes used at CDS runtime + JavaValue result(T_OBJECT); + Handle path_string = java_lang_String::create_from_str("dummy.jar", CHECK); + JavaCalls::call_static(&result, + vmClasses::jdk_internal_loader_ClassLoaders_klass(), + vmSymbols::toFileURL_name(), + vmSymbols::toFileURL_signature(), + path_string, + CHECK); + VM_PopulateDumpSharedSpace op(builder); VMThread::execute(&op); @@ -1299,7 +1314,7 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma assert(base_address == nullptr || (address)archive_space_rs.base() == base_address, "Sanity"); // Register archive space with NMT. - MemTracker::record_virtual_memory_type(archive_space_rs.base(), mtClassShared); + MemTracker::record_virtual_memory_tag(archive_space_rs.base(), mtClassShared); return archive_space_rs.base(); } return nullptr; @@ -1361,8 +1376,8 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma return nullptr; } // NMT: fix up the space tags - MemTracker::record_virtual_memory_type(archive_space_rs.base(), mtClassShared); - MemTracker::record_virtual_memory_type(class_space_rs.base(), mtClass); + MemTracker::record_virtual_memory_tag(archive_space_rs.base(), mtClassShared); + MemTracker::record_virtual_memory_tag(class_space_rs.base(), mtClass); } else { if (use_archive_base_addr && base_address != nullptr) { total_space_rs = ReservedSpace(total_range_size, base_address_alignment, diff --git a/src/hotspot/share/ci/ciEnv.cpp b/src/hotspot/share/ci/ciEnv.cpp index 9caf89628ccfd..155ce032400e8 100644 --- a/src/hotspot/share/ci/ciEnv.cpp +++ b/src/hotspot/share/ci/ciEnv.cpp @@ -1616,7 +1616,10 @@ void ciEnv::dump_replay_data_helper(outputStream* out) { for (int i = 0; i < objects->length(); i++) { objects->at(i)->dump_replay_data(out); } - dump_compile_data(out); + + if (this->task() != nullptr) { + dump_compile_data(out); + } out->flush(); } diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index 94b405cdbfacd..a74a812c6a23b 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -1249,7 +1249,6 @@ bool ciMethod::has_jsrs () const { FETCH_FLAG_FROM_VM(has_jsrs); bool ciMethod::is_getter () const { FETCH_FLAG_FROM_VM(is_getter); } bool ciMethod::is_setter () const { FETCH_FLAG_FROM_VM(is_setter); } bool ciMethod::is_accessor () const { FETCH_FLAG_FROM_VM(is_accessor); } -bool ciMethod::is_initializer () const { FETCH_FLAG_FROM_VM(is_initializer); } bool ciMethod::is_empty () const { FETCH_FLAG_FROM_VM(is_empty_method); } bool ciMethod::is_boxing_method() const { diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index 5cb63204d0b72..cc524930192cd 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -352,7 +352,6 @@ class ciMethod : public ciMetadata { bool is_getter () const; bool is_setter () const; bool is_accessor () const; - bool is_initializer () const; bool is_empty () const; bool can_be_statically_bound() const { return _can_be_statically_bound; } bool has_reserved_stack_access() const { return _has_reserved_stack_access; } diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 60fed287df594..c8e95149b7c1a 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -1150,30 +1150,40 @@ static void parse_annotations(const ConstantPool* const cp, if (AnnotationCollector::_unknown == id) continue; coll->set_annotation(id); if (AnnotationCollector::_java_lang_Deprecated == id) { - assert(count <= 2, "change this if more element-value pairs are added to the @Deprecated annotation"); - // @Deprecated can specify forRemoval=true + // @Deprecated can specify forRemoval=true, which we need + // to record for JFR to use. If the annotation is not well-formed + // then we may not be able to determine that. const u1* offset = abase + member_off; - for (int i = 0; i < count; ++i) { + // There are only 2 members in @Deprecated. + int n_members = MIN2(count, 2); + for (int i = 0; i < n_members; ++i) { int member_index = Bytes::get_Java_u2((address)offset); offset += 2; member = check_symbol_at(cp, member_index); - if (member == vmSymbols::since()) { - assert(*((address)offset) == s_tag_val, "invariant"); + if (member == vmSymbols::since() && + (*((address)offset) == s_tag_val)) { + // Found `since` first so skip over it offset += 3; - continue; } - if (member == vmSymbols::for_removal()) { - assert(*((address)offset) == b_tag_val, "invariant"); + else if (member == vmSymbols::for_removal() && + (*((address)offset) == b_tag_val)) { const u2 boolean_value_index = Bytes::get_Java_u2((address)offset + 1); - if (cp->int_at(boolean_value_index) == 1) { + // No guarantee the entry is valid so check it refers to an int in the CP. + if (cp->is_within_bounds(boolean_value_index) && + cp->tag_at(boolean_value_index).is_int() && + cp->int_at(boolean_value_index) == 1) { // forRemoval == true coll->set_annotation(AnnotationCollector::_java_lang_Deprecated_for_removal); } + break; // no need to check further + } + else { + // This @Deprecated annotation is malformed so we don't try to + // determine whether forRemoval is set. break; } - } - continue; + continue; // proceed to next annotation } if (AnnotationCollector::_jdk_internal_vm_annotation_Contended == id) { @@ -1194,11 +1204,21 @@ static void parse_annotations(const ConstantPool* const cp, && s_tag_val == *(abase + tag_off) && member == vmSymbols::value_name()) { group_index = Bytes::get_Java_u2((address)abase + s_con_off); - if (cp->symbol_at(group_index)->utf8_length() == 0) { - group_index = 0; // default contended group + // No guarantee the group_index is valid so check it refers to a + // symbol in the CP. + if (cp->is_within_bounds(group_index) && + cp->tag_at(group_index).is_utf8()) { + // Seems valid, so check for empty string and reset + if (cp->symbol_at(group_index)->utf8_length() == 0) { + group_index = 0; // default contended group + } + } else { + // Not valid so use the default + group_index = 0; } } coll->set_contended_group(group_index); + continue; // proceed to next annotation } } } diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index e410824e3001c..9a68e2640443f 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -81,6 +81,9 @@ #include "utilities/ostream.hpp" #include "utilities/utf8.hpp" +#include +#include + // Entry point in java.dll for path canonicalization typedef int (*canonicalize_fn_t)(const char *orig, char *out, int len); @@ -579,6 +582,8 @@ void ClassLoader::setup_module_search_path(JavaThread* current, const char* path new_entry = create_class_path_entry(current, path, &st, false /*is_boot_append */, false /* from_class_path_attr */); if (new_entry != nullptr) { + // ClassLoaderExt::process_module_table() filters out non-jar entries before calling this function. + assert(new_entry->is_jar_file(), "module path entry %s is not a jar file", new_entry->name()); add_to_module_path_entries(path, new_entry); } } @@ -834,7 +839,8 @@ bool ClassLoader::add_to_app_classpath_entries(JavaThread* current, ClassPathEntry* e = _app_classpath_entries; if (check_for_duplicates) { while (e != nullptr) { - if (strcmp(e->name(), entry->name()) == 0) { + if (strcmp(e->name(), entry->name()) == 0 && + e->from_class_path_attr() == entry->from_class_path_attr()) { // entry already exists return false; } @@ -1208,7 +1214,7 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, PackageEntry* pkg_entry, bo } #if INCLUDE_CDS -char* ClassLoader::skip_uri_protocol(char* source) { +static const char* skip_uri_protocol(const char* source) { if (strncmp(source, "file:", 5) == 0) { // file: protocol path could start with file:/ or file:/// // locate the char after all the forward slashes @@ -1227,6 +1233,47 @@ char* ClassLoader::skip_uri_protocol(char* source) { return source; } +static char decode_percent_encoded(const char *str, size_t& index) { + if (str[index] == '%' + && isxdigit(str[index + 1]) + && isxdigit(str[index + 2])) { + char hex[3]; + hex[0] = str[index + 1]; + hex[1] = str[index + 2]; + hex[2] = '\0'; + index += 2; + return (char) strtol(hex, NULL, 16); + } + return str[index]; +} + +char* ClassLoader::uri_to_path(const char* uri) { + const size_t len = strlen(uri) + 1; + char* path = NEW_RESOURCE_ARRAY(char, len); + + uri = skip_uri_protocol(uri); + + if (strncmp(uri, "//", 2) == 0) { + // Skip the empty "authority" part + uri += 2; + } + +#ifdef _WINDOWS + if (uri[0] == '/') { + // Absolute path name on Windows does not begin with a slash + uri += 1; + } +#endif + + size_t path_index = 0; + for (size_t i = 0; i < strlen(uri); ++i) { + char decoded = decode_percent_encoded(uri, i); + path[path_index++] = decoded; + } + path[path_index] = '\0'; + return path; +} + // Record the shared classpath index and loader type for classes loaded // by the builtin loaders at dump time. void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, @@ -1260,7 +1307,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, // Save the path from the file: protocol or the module name from the jrt: protocol // if no protocol prefix is found, path is the same as stream->source(). This path // must be valid since the class has been successfully parsed. - char* path = skip_uri_protocol(src); + const char* path = ClassLoader::uri_to_path(src); assert(path != nullptr, "sanity"); for (int i = 0; i < FileMapInfo::get_number_of_shared_paths(); i++) { SharedClassPathEntry* ent = FileMapInfo::shared_path(i); diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp index af625082ddabf..e44059b724769 100644 --- a/src/hotspot/share/classfile/classLoader.hpp +++ b/src/hotspot/share/classfile/classLoader.hpp @@ -382,7 +382,7 @@ class ClassLoader: AllStatic { // entries during shared classpath setup time. static int num_module_path_entries(); static void exit_with_path_failure(const char* error, const char* message); - static char* skip_uri_protocol(char* source); + static char* uri_to_path(const char* uri); static void record_result(JavaThread* current, InstanceKlass* ik, const ClassFileStream* stream, bool redefined); #endif diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp index 3cd7dd7cd3ba6..16981669deb3a 100644 --- a/src/hotspot/share/classfile/classLoaderExt.cpp +++ b/src/hotspot/share/classfile/classLoaderExt.cpp @@ -55,6 +55,7 @@ jshort ClassLoaderExt::_app_class_paths_start_index = ClassLoaderExt::max_classpath_index; jshort ClassLoaderExt::_app_module_paths_start_index = ClassLoaderExt::max_classpath_index; jshort ClassLoaderExt::_max_used_path_index = 0; +int ClassLoaderExt::_num_module_paths = 0; bool ClassLoaderExt::_has_app_classes = false; bool ClassLoaderExt::_has_platform_classes = false; bool ClassLoaderExt::_has_non_jar_in_classpath = false; @@ -89,23 +90,25 @@ void ClassLoaderExt::setup_app_search_path(JavaThread* current) { os::free(app_class_path); } +int ClassLoaderExt::compare_module_path_by_name(const char** p1, const char** p2) { + return strcmp(*p1, *p2); +} + void ClassLoaderExt::process_module_table(JavaThread* current, ModuleEntryTable* met) { ResourceMark rm(current); - GrowableArray* module_paths = new GrowableArray(5); + GrowableArray* module_paths = new GrowableArray(5); class ModulePathsGatherer : public ModuleClosure { JavaThread* _current; - GrowableArray* _module_paths; + GrowableArray* _module_paths; public: - ModulePathsGatherer(JavaThread* current, GrowableArray* module_paths) : + ModulePathsGatherer(JavaThread* current, GrowableArray* module_paths) : _current(current), _module_paths(module_paths) {} void do_module(ModuleEntry* m) { - char* path = m->location()->as_C_string(); - if (strncmp(path, "file:", 5) == 0) { - path = ClassLoader::skip_uri_protocol(path); - char* path_copy = NEW_RESOURCE_ARRAY(char, strlen(path) + 1); - strcpy(path_copy, path); - _module_paths->append(path_copy); + char* uri = m->location()->as_C_string(); + if (strncmp(uri, "file:", 5) == 0) { + char* path = ClassLoader::uri_to_path(uri); + extract_jar_files_from_path(path, _module_paths); } } }; @@ -116,6 +119,10 @@ void ClassLoaderExt::process_module_table(JavaThread* current, ModuleEntryTable* met->modules_do(&gatherer); } + // Sort the module paths before storing into CDS archive for simpler + // checking at runtime. + module_paths->sort(compare_module_path_by_name); + for (int i = 0; i < module_paths->length(); i++) { ClassLoader::setup_module_search_path(current, module_paths->at(i)); } @@ -131,6 +138,38 @@ void ClassLoaderExt::setup_module_paths(JavaThread* current) { process_module_table(current, met); } +bool ClassLoaderExt::has_jar_suffix(const char* filename) { + // In jdk.internal.module.ModulePath.readModule(), it checks for the ".jar" suffix. + // Performing the same check here. + const char* dot = strrchr(filename, '.'); + if (dot != nullptr && strcmp(dot + 1, "jar") == 0) { + return true; + } + return false; +} + +void ClassLoaderExt::extract_jar_files_from_path(const char* path, GrowableArray* module_paths) { + DIR* dirp = os::opendir(path); + if (dirp == nullptr && errno == ENOTDIR && has_jar_suffix(path)) { + module_paths->append(path); + } else { + if (dirp != nullptr) { + struct dirent* dentry; + while ((dentry = os::readdir(dirp)) != nullptr) { + const char* file_name = dentry->d_name; + if (has_jar_suffix(file_name)) { + size_t full_name_len = strlen(path) + strlen(file_name) + strlen(os::file_separator()) + 1; + char* full_name = NEW_RESOURCE_ARRAY(char, full_name_len); + int n = os::snprintf(full_name, full_name_len, "%s%s%s", path, os::file_separator(), file_name); + assert((size_t)n == full_name_len - 1, "Unexpected number of characters in string"); + module_paths->append(full_name); + } + } + os::closedir(dirp); + } + } +} + char* ClassLoaderExt::read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size, bool clean_text) { const char* name = "META-INF/MANIFEST.MF"; diff --git a/src/hotspot/share/classfile/classLoaderExt.hpp b/src/hotspot/share/classfile/classLoaderExt.hpp index b76ce3ff33a32..c3c0b00d55e43 100644 --- a/src/hotspot/share/classfile/classLoaderExt.hpp +++ b/src/hotspot/share/classfile/classLoaderExt.hpp @@ -53,12 +53,15 @@ class ClassLoaderExt: public ClassLoader { // AllStatic static jshort _app_module_paths_start_index; // the largest path index being used during CDS dump time static jshort _max_used_path_index; + // number of module paths + static int _num_module_paths; static bool _has_app_classes; static bool _has_platform_classes; static bool _has_non_jar_in_classpath; static char* read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size, bool clean_text); + static bool has_jar_suffix(const char* filename); public: static void process_jar_manifest(JavaThread* current, ClassPathEntry* entry); @@ -68,6 +71,8 @@ class ClassLoaderExt: public ClassLoader { // AllStatic static void setup_search_paths(JavaThread* current); static void setup_module_paths(JavaThread* current); + static void extract_jar_files_from_path(const char* path, GrowableArray* module_paths); + static int compare_module_path_by_name(const char** p1, const char** p2); static char* read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size) { // Remove all the new-line continuations (which wrap long lines at 72 characters, see @@ -87,6 +92,8 @@ class ClassLoaderExt: public ClassLoader { // AllStatic static jshort max_used_path_index() { return _max_used_path_index; } + static int num_module_paths() { return _num_module_paths; } + static void set_max_used_path_index(jshort used_index) { _max_used_path_index = used_index; } @@ -99,6 +106,10 @@ class ClassLoaderExt: public ClassLoader { // AllStatic _app_module_paths_start_index = module_start; } + static void init_num_module_paths(int num_module_paths) { + _num_module_paths = num_module_paths; + } + static bool is_boot_classpath(int classpath_index) { return classpath_index < _app_class_paths_start_index; } diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp index cda64788acffa..9b0d80b2a5583 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp @@ -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 @@ -101,17 +101,13 @@ class LayoutRawBlock : public ResourceObj { // sort fields in decreasing order. // Note: with line types, the comparison should include alignment constraint if sizes are equals static int compare_size_inverted(LayoutRawBlock** x, LayoutRawBlock** y) { -#ifdef _WINDOWS - // qsort() on Windows reverse the order of fields with the same size - // the extension of the comparison function below preserves this order int diff = (*y)->size() - (*x)->size(); + // qsort() may reverse the order of fields with the same size. + // The extension is to ensure stable sort. if (diff == 0) { diff = (*x)->field_index() - (*y)->field_index(); } return diff; -#else - return (*y)->size() - (*x)->size(); -#endif // _WINDOWS } }; diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index b6ef682ae0965..0ad36cd21dbf3 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -3052,9 +3052,10 @@ void java_lang_ClassFrameInfo::serialize_offsets(SerializeClosure* f) { static int get_flags(const methodHandle& m) { int flags = (jushort)( m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS ); - if (m->is_initializer()) { + if (m->is_object_initializer()) { flags |= java_lang_invoke_MemberName::MN_IS_CONSTRUCTOR; } else { + // Note: Static initializers can be here. Record them as plain methods. flags |= java_lang_invoke_MemberName::MN_IS_METHOD; } if (m->caller_sensitive()) { diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index b9a559cf9779f..7b307a0b8a37c 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1069,7 +1069,7 @@ bool SystemDictionary::check_shared_class_super_type(InstanceKlass* klass, Insta } Klass *found = resolve_with_circularity_detection(klass->name(), super_type->name(), - class_loader, protection_domain, is_superclass, CHECK_0); + class_loader, protection_domain, is_superclass, CHECK_false); if (found == super_type) { return true; } else { @@ -1088,16 +1088,21 @@ bool SystemDictionary::check_shared_class_super_types(InstanceKlass* ik, Handle // If unexpected superclass or interfaces are found, we cannot // load from the shared archive. - if (ik->super() != nullptr && - !check_shared_class_super_type(ik, InstanceKlass::cast(ik->super()), - class_loader, protection_domain, true, THREAD)) { - return false; + if (ik->super() != nullptr) { + bool check_super = check_shared_class_super_type(ik, InstanceKlass::cast(ik->super()), + class_loader, protection_domain, true, + CHECK_false); + if (!check_super) { + return false; + } } Array* interfaces = ik->local_interfaces(); int num_interfaces = interfaces->length(); for (int index = 0; index < num_interfaces; index++) { - if (!check_shared_class_super_type(ik, interfaces->at(index), class_loader, protection_domain, false, THREAD)) { + bool check_interface = check_shared_class_super_type(ik, interfaces->at(index), class_loader, protection_domain, false, + CHECK_false); + if (!check_interface) { return false; } } @@ -1149,10 +1154,13 @@ InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik, Symbol* class_name = ik->name(); if (!is_shared_class_visible(class_name, ik, pkg_entry, class_loader)) { + ik->set_shared_loading_failed(); return nullptr; } - if (!check_shared_class_super_types(ik, class_loader, protection_domain, THREAD)) { + bool check = check_shared_class_super_types(ik, class_loader, protection_domain, CHECK_NULL); + if (!check) { + ik->set_shared_loading_failed(); return nullptr; } diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index ee50aa38dd0cf..04980291716c7 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -293,13 +293,6 @@ class SystemDictionary : AllStatic { const char* message); static const char* find_nest_host_error(const constantPoolHandle& pool, int which); -protected: - static InstanceKlass* _well_known_klasses[]; - -private: - // table of box klasses (int_klass, etc.) - static InstanceKlass* _box_klasses[T_VOID+1]; - static OopHandle _java_system_loader; static OopHandle _java_platform_loader; diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index a66fbf645f55f..375570cf19691 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" @@ -212,6 +213,11 @@ 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 (CDSConfig::is_dumping_dynamic_archive()) { + SystemDictionaryShared::warn_excluded(klass, "Failed over class verification while dynamic dumping"); + 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/classfile/vmClasses.cpp b/src/hotspot/share/classfile/vmClasses.cpp index 0b9b437c67b78..b62d699dfe20e 100644 --- a/src/hotspot/share/classfile/vmClasses.cpp +++ b/src/hotspot/share/classfile/vmClasses.cpp @@ -45,14 +45,6 @@ InstanceKlass* vmClasses::_klasses[static_cast(vmClassID::LIMIT)] = { nullptr /*, nullptr...*/ }; InstanceKlass* vmClasses::_box_klasses[T_VOID+1] = { nullptr /*, nullptr...*/ }; - -// CDS: scan and relocate all classes referenced by _klasses[]. -void vmClasses::metaspace_pointers_do(MetaspaceClosure* it) { - for (auto id : EnumRange{}) { - it->push(klass_addr_at(id)); - } -} - bool vmClasses::is_loaded(InstanceKlass* klass) { return klass != nullptr && klass->is_loaded(); } @@ -205,8 +197,6 @@ void vmClasses::resolve_all(TRAPS) { _box_klasses[T_SHORT] = vmClasses::Short_klass(); _box_klasses[T_INT] = vmClasses::Integer_klass(); _box_klasses[T_LONG] = vmClasses::Long_klass(); - //_box_klasses[T_OBJECT] = vmClasses::object_klass(); - //_box_klasses[T_ARRAY] = vmClasses::object_klass(); #ifdef ASSERT if (CDSConfig::is_using_archive()) { diff --git a/src/hotspot/share/classfile/vmClasses.hpp b/src/hotspot/share/classfile/vmClasses.hpp index f2b8c5666eeb1..4fa078c50cd80 100644 --- a/src/hotspot/share/classfile/vmClasses.hpp +++ b/src/hotspot/share/classfile/vmClasses.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,6 @@ class ClassLoaderData; class InstanceKlass; -class MetaspaceClosure; class vmClasses : AllStatic { friend class VMStructs; @@ -95,7 +94,6 @@ class vmClasses : AllStatic { return &_klasses[as_int(id)]; } - static void metaspace_pointers_do(MetaspaceClosure* it); static void resolve_all(TRAPS); static BasicType box_klass_type(Klass* k); // inverse of box_klass diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index b470eb9b8380d..5e352e42efbc1 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -90,6 +90,7 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dtanh: case vmIntrinsics::_dlog: case vmIntrinsics::_dlog10: case vmIntrinsics::_dexp: @@ -141,6 +142,7 @@ bool vmIntrinsics::can_trap(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dtanh: case vmIntrinsics::_dlog: case vmIntrinsics::_dlog10: case vmIntrinsics::_dexp: @@ -288,6 +290,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dtanh: case vmIntrinsics::_dlog: case vmIntrinsics::_dexp: case vmIntrinsics::_dpow: diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 4b772c171d5a6..9bb8b2179ae01 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -135,7 +135,7 @@ class methodHandle; do_name(log_name,"log") do_name(log10_name,"log10") do_name(pow_name,"pow") \ do_name(exp_name,"exp") do_name(min_name,"min") do_name(max_name,"max") \ do_name(floor_name, "floor") do_name(ceil_name, "ceil") do_name(rint_name, "rint") \ - do_name(round_name, "round") \ + do_name(round_name, "round") do_name(tanh_name,"tanh") \ \ do_name(addExact_name,"addExact") \ do_name(decrementExact_name,"decrementExact") \ @@ -161,6 +161,7 @@ class methodHandle; do_intrinsic(_dcos, java_lang_Math, cos_name, double_double_signature, F_S) \ do_intrinsic(_dtan, java_lang_Math, tan_name, double_double_signature, F_S) \ do_intrinsic(_datan2, java_lang_Math, atan2_name, double2_double_signature, F_S) \ + do_intrinsic(_dtanh, java_lang_Math, tanh_name, double_double_signature, F_S) \ do_intrinsic(_dsqrt, java_lang_Math, sqrt_name, double_double_signature, F_S) \ do_intrinsic(_dlog, java_lang_Math, log_name, double_double_signature, F_S) \ do_intrinsic(_dlog10, java_lang_Math, log10_name, double_double_signature, F_S) \ @@ -468,6 +469,8 @@ class methodHandle; do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ do_intrinsic(_Reference_refersTo0, java_lang_ref_Reference, refersTo0_name, object_boolean_signature, F_RN) \ do_intrinsic(_PhantomReference_refersTo0, java_lang_ref_PhantomReference, refersTo0_name, object_boolean_signature, F_RN) \ + do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \ + do_intrinsic(_PhantomReference_clear0, java_lang_ref_PhantomReference, clear0_name, void_method_signature, F_RN) \ \ /* support for com.sun.crypto.provider.AESCrypt and some of its callers */ \ do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AESCrypt") \ @@ -1007,6 +1010,15 @@ class methodHandle; "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ do_name(vector_shuffle_to_vector_name, "shuffleToVector") \ \ + do_intrinsic(_VectorWrapShuffleIndexes, jdk_internal_vm_vector_VectorSupport, vector_wrap_shuffle_indexes_name, \ + vector_wrap_shuffle_indexes_sig, F_S) \ + do_signature(vector_wrap_shuffle_indexes_sig, "(Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorShuffle;" \ + "ILjdk/internal/vm/vector/VectorSupport$WrapShuffleIndexesOperation;)" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorShuffle;") \ + do_name(vector_wrap_shuffle_indexes_name, "wrapShuffleIndexes") \ + \ do_intrinsic(_VectorLoadOp, jdk_internal_vm_vector_VectorSupport, vector_load_op_name, vector_load_op_sig, F_S) \ do_signature(vector_load_op_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ @@ -1128,6 +1140,18 @@ class methodHandle; "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ do_name(vector_rearrange_name, "rearrangeOp") \ \ + do_intrinsic(_VectorSelectFrom, jdk_internal_vm_vector_VectorSupport, vector_select_from_name, vector_select_from_sig, F_S) \ + do_signature(vector_select_from_sig, "(Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "I" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorSelectFromOp;)" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_select_from_name, "selectFromOp") \ + \ do_intrinsic(_VectorExtract, jdk_internal_vm_vector_VectorSupport, vector_extract_name, vector_extract_sig, F_S) \ do_signature(vector_extract_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index a65ab86fa0a8d..3de6d81f10607 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -426,6 +426,7 @@ class SerializeClosure; template(cs_name, "cs") \ template(get_name, "get") \ template(refersTo0_name, "refersTo0") \ + template(clear0_name, "clear0") \ template(put_name, "put") \ template(type_name, "type") \ template(findNative_name, "findNative") \ diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 81c4d001078cb..23f621ffec832 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -41,7 +41,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaFrameAnchor.hpp" -#include "runtime/jniHandles.hpp" +#include "runtime/jniHandles.inline.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" #include "runtime/sharedRuntime.hpp" @@ -623,7 +623,7 @@ UpcallStub* UpcallStub::create(const char* name, CodeBuffer* cb, jobject receive // Track memory usage statistic after releasing CodeCache_lock MemoryService::track_code_cache_memory_usage(); - trace_new_stub(blob, "UpcallStub"); + trace_new_stub(blob, "UpcallStub - ", name); return blob; } @@ -772,6 +772,10 @@ void UpcallStub::verify() { void UpcallStub::print_on(outputStream* st) const { RuntimeBlob::print_on(st); print_value_on(st); + st->print_cr("Frame data offset: %d", (int) _frame_data_offset); + oop recv = JNIHandles::resolve(_receiver); + st->print("Receiver MH="); + recv->print_on(st); Disassembler::decode((RuntimeBlob*)this, st); } diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp index 079c8199b1870..684aee509ee53 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -83,6 +83,7 @@ void CompiledICData::initialize(CallInfo* call_info, Klass* receiver_klass) { _speculated_klass = (uintptr_t)receiver_klass; } if (call_info->call_kind() == CallInfo::itable_call) { + assert(call_info->resolved_method() != nullptr, "virtual or interface method must be found"); _itable_defc_klass = call_info->resolved_method()->method_holder(); _itable_refc_klass = call_info->resolved_klass(); } @@ -238,6 +239,7 @@ void CompiledIC::set_to_megamorphic(CallInfo* call_info) { return; } #ifdef ASSERT + assert(call_info->resolved_method() != nullptr, "virtual or interface method must be found"); int index = call_info->resolved_method()->itable_index(); assert(index == itable_index, "CallInfo pre-computes this"); InstanceKlass* k = call_info->resolved_method()->method_holder(); @@ -254,6 +256,7 @@ void CompiledIC::set_to_megamorphic(CallInfo* call_info) { } } + assert(call_info->selected_method() != nullptr, "virtual or interface method must be found"); log_trace(inlinecache)("IC@" INTPTR_FORMAT ": to megamorphic %s entry: " INTPTR_FORMAT, p2i(_call->instruction_address()), call_info->selected_method()->print_value_string(), p2i(entry)); @@ -290,7 +293,7 @@ bool CompiledIC::is_monomorphic() const { } bool CompiledIC::is_megamorphic() const { - return VtableStubs::entry_point(destination()) != nullptr;; + return VtableStubs::entry_point(destination()) != nullptr; } bool CompiledIC::is_speculated_klass(Klass* receiver_klass) { diff --git a/src/hotspot/share/code/dependencyContext.cpp b/src/hotspot/share/code/dependencyContext.cpp index d7ce8e92acf37..0e6b99d172dcb 100644 --- a/src/hotspot/share/code/dependencyContext.cpp +++ b/src/hotspot/share/code/dependencyContext.cpp @@ -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 @@ -227,6 +227,10 @@ void DependencyContext::remove_and_mark_for_deoptimization_all_dependents(Deopti } #ifndef PRODUCT +bool DependencyContext::is_empty() { + return dependencies() == nullptr; +} + void DependencyContext::print_dependent_nmethods(bool verbose) { int idx = 0; for (nmethodBucket* b = dependencies_not_unloading(); b != nullptr; b = b->next_not_unloading()) { diff --git a/src/hotspot/share/code/dependencyContext.hpp b/src/hotspot/share/code/dependencyContext.hpp index e8d2ac41d0d1d..13b845cb59dde 100644 --- a/src/hotspot/share/code/dependencyContext.hpp +++ b/src/hotspot/share/code/dependencyContext.hpp @@ -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 @@ -124,6 +124,7 @@ class DependencyContext : public StackObj { #ifndef PRODUCT void print_dependent_nmethods(bool verbose); + bool is_empty(); #endif //PRODUCT bool is_dependent_nmethod(nmethod* nm); }; diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index ee0c73254f180..7b091d8ade50c 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -497,11 +497,6 @@ bool CompilerConfig::check_args_consistency(bool status) { "Invalid NonNMethodCodeHeapSize=%dK. Must be at least %uK.\n", NonNMethodCodeHeapSize/K, min_code_cache_size/K); status = false; - } else if (InlineCacheBufferSize > NonNMethodCodeHeapSize / 2) { - jio_fprintf(defaultStream::error_stream(), - "Invalid InlineCacheBufferSize=" SIZE_FORMAT "K. Must be less than or equal to " SIZE_FORMAT "K.\n", - InlineCacheBufferSize/K, NonNMethodCodeHeapSize/2/K); - status = false; } #ifdef _LP64 diff --git a/src/hotspot/share/compiler/oopMap.inline.hpp b/src/hotspot/share/compiler/oopMap.inline.hpp index f2a3b3ba834df..05ef53f823142 100644 --- a/src/hotspot/share/compiler/oopMap.inline.hpp +++ b/src/hotspot/share/compiler/oopMap.inline.hpp @@ -66,12 +66,10 @@ void OopMapDo::iterate_oops_do(const frame continue; #ifndef COMPILER2 - COMPILER1_PRESENT(ShouldNotReachHere();) #if INCLUDE_JVMCI - if (UseJVMCICompiler) { - ShouldNotReachHere(); - } + if (!EnableJVMCI) #endif + ShouldNotReachHere(); #endif // !COMPILER2 address loc = fr->oopmapreg_to_location(omv.reg(), reg_map); diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp index 13b993546cde4..edbf0e902392b 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp @@ -24,49 +24,32 @@ #include "precompiled.hpp" #include "classfile/javaClasses.hpp" +#include "code/vmreg.inline.hpp" #include "gc/g1/c2/g1BarrierSetC2.hpp" #include "gc/g1/g1BarrierSet.hpp" +#include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" #include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1ThreadLocalData.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "opto/arraycopynode.hpp" +#include "opto/block.hpp" #include "opto/compile.hpp" #include "opto/escape.hpp" #include "opto/graphKit.hpp" #include "opto/idealKit.hpp" +#include "opto/machnode.hpp" #include "opto/macro.hpp" +#include "opto/memnode.hpp" +#include "opto/node.hpp" +#include "opto/output.hpp" +#include "opto/regalloc.hpp" #include "opto/rootnode.hpp" +#include "opto/runtime.hpp" #include "opto/type.hpp" +#include "utilities/growableArray.hpp" #include "utilities/macros.hpp" -const TypeFunc *G1BarrierSetC2::write_ref_field_pre_entry_Type() { - const Type **fields = TypeTuple::fields(2); - fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // original field value - fields[TypeFunc::Parms+1] = TypeRawPtr::NOTNULL; // thread - const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); - - // create result type (range) - fields = TypeTuple::fields(0); - const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); - - return TypeFunc::make(domain, range); -} - -const TypeFunc *G1BarrierSetC2::write_ref_field_post_entry_Type() { - const Type **fields = TypeTuple::fields(2); - fields[TypeFunc::Parms+0] = TypeRawPtr::NOTNULL; // Card addr - fields[TypeFunc::Parms+1] = TypeRawPtr::NOTNULL; // thread - const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); - - // create result type (range) - fields = TypeTuple::fields(0); - const TypeTuple *range = TypeTuple::make(TypeFunc::Parms, fields); - - return TypeFunc::make(domain, range); -} - -#define __ ideal. /* * Determine if the G1 pre-barrier can be removed. The pre-barrier is * required by SATB to make sure all objects live at the start of the @@ -84,8 +67,6 @@ const TypeFunc *G1BarrierSetC2::write_ref_field_post_entry_Type() { * The compiler needs to determine that the object in which a field is about * to be written is newly allocated, and that no prior store to the same field * has happened since the allocation. - * - * Returns true if the pre-barrier can be removed */ bool G1BarrierSetC2::g1_can_remove_pre_barrier(GraphKit* kit, PhaseValues* phase, @@ -97,34 +78,28 @@ bool G1BarrierSetC2::g1_can_remove_pre_barrier(GraphKit* kit, AllocateNode* alloc = AllocateNode::Ideal_allocation(base); if (offset == Type::OffsetBot) { - return false; // cannot unalias unless there are precise offsets + return false; // Cannot unalias unless there are precise offsets. } - if (alloc == nullptr) { - return false; // No allocation found + return false; // No allocation found. } intptr_t size_in_bytes = type2aelembytes(bt); - - Node* mem = kit->memory(adr_idx); // start searching here... + Node* mem = kit->memory(adr_idx); // Start searching here. for (int cnt = 0; cnt < 50; cnt++) { - if (mem->is_Store()) { - Node* st_adr = mem->in(MemNode::Address); intptr_t st_offset = 0; Node* st_base = AddPNode::Ideal_base_and_offset(st_adr, phase, st_offset); if (st_base == nullptr) { - break; // inscrutable pointer + break; // Inscrutable pointer. } - - // Break we have found a store with same base and offset as ours so break if (st_base == base && st_offset == offset) { + // We have found a store with same base and offset as ours. break; } - if (st_offset != offset && st_offset != Type::OffsetBot) { const int MAX_STORE = BytesPerLong; if (st_offset >= offset + size_in_bytes || @@ -136,20 +111,18 @@ bool G1BarrierSetC2::g1_can_remove_pre_barrier(GraphKit* kit, // in the same sequence of RawMem effects. We sometimes initialize // a whole 'tile' of array elements with a single jint or jlong.) mem = mem->in(MemNode::Memory); - continue; // advance through independent store memory + continue; // Advance through independent store memory. } } - if (st_base != base && MemNode::detect_ptr_independence(base, alloc, st_base, AllocateNode::Ideal_allocation(st_base), phase)) { - // Success: The bases are provably independent. + // Success: the bases are provably independent. mem = mem->in(MemNode::Memory); - continue; // advance through independent store memory + continue; // Advance through independent store memory. } } else if (mem->is_Proj() && mem->in(0)->is_Initialize()) { - InitializeNode* st_init = mem->in(0)->as_Initialize(); AllocateNode* st_alloc = st_init->allocation(); @@ -157,7 +130,7 @@ bool G1BarrierSetC2::g1_can_remove_pre_barrier(GraphKit* kit, // The alloc variable is guaranteed to not be null here from earlier check. if (alloc == st_alloc) { // Check that the initialization is storing null so that no previous store - // has been moved up and directly write a reference + // has been moved up and directly write a reference. Node* captured_store = st_init->find_captured_store(offset, type2aelembytes(T_OBJECT), phase); @@ -166,164 +139,55 @@ bool G1BarrierSetC2::g1_can_remove_pre_barrier(GraphKit* kit, } } } - // Unless there is an explicit 'continue', we must bail out here, // because 'mem' is an inscrutable memory state (e.g., a call). break; } - return false; } -// G1 pre/post barriers -void G1BarrierSetC2::pre_barrier(GraphKit* kit, - bool do_load, - Node* ctl, - Node* obj, - Node* adr, - uint alias_idx, - Node* val, - const TypeOopPtr* val_type, - Node* pre_val, - BasicType bt) const { - // Some sanity checks - // Note: val is unused in this routine. - - if (do_load) { - // We need to generate the load of the previous value - assert(obj != nullptr, "must have a base"); - assert(adr != nullptr, "where are loading from?"); - assert(pre_val == nullptr, "loaded already?"); - assert(val_type != nullptr, "need a type"); - - if (use_ReduceInitialCardMarks() - && g1_can_remove_pre_barrier(kit, &kit->gvn(), adr, bt, alias_idx)) { - return; - } - - } else { - // In this case both val_type and alias_idx are unused. - assert(pre_val != nullptr, "must be loaded already"); - // Nothing to be done if pre_val is null. - if (pre_val->bottom_type() == TypePtr::NULL_PTR) return; - assert(pre_val->bottom_type()->basic_type() == T_OBJECT, "or we shouldn't be here"); - } - assert(bt == T_OBJECT, "or we shouldn't be here"); - - IdealKit ideal(kit, true); - - Node* tls = __ thread(); // ThreadLocalStorage - - Node* no_base = __ top(); - Node* zero = __ ConI(0); - Node* zeroX = __ ConX(0); - - float likely = PROB_LIKELY(0.999); - float unlikely = PROB_UNLIKELY(0.999); - - BasicType active_type = in_bytes(SATBMarkQueue::byte_width_of_active()) == 4 ? T_INT : T_BYTE; - assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 4 || in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "flag width"); - - // Offsets into the thread - const int marking_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()); - const int index_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_index_offset()); - const int buffer_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset()); - - // Now the actual pointers into the thread - Node* marking_adr = __ AddP(no_base, tls, __ ConX(marking_offset)); - Node* buffer_adr = __ AddP(no_base, tls, __ ConX(buffer_offset)); - Node* index_adr = __ AddP(no_base, tls, __ ConX(index_offset)); - - // Now some of the values - Node* marking = __ load(__ ctrl(), marking_adr, TypeInt::INT, active_type, Compile::AliasIdxRaw); - - // if (!marking) - __ if_then(marking, BoolTest::ne, zero, unlikely); { - BasicType index_bt = TypeX_X->basic_type(); - assert(sizeof(size_t) == type2aelembytes(index_bt), "Loading G1 SATBMarkQueue::_index with wrong size."); - Node* index = __ load(__ ctrl(), index_adr, TypeX_X, index_bt, Compile::AliasIdxRaw); - - if (do_load) { - // load original value - pre_val = __ load(__ ctrl(), adr, val_type, bt, alias_idx, false, MemNode::unordered, LoadNode::Pinned); - } - - // if (pre_val != nullptr) - __ if_then(pre_val, BoolTest::ne, kit->null()); { - Node* buffer = __ load(__ ctrl(), buffer_adr, TypeRawPtr::NOTNULL, T_ADDRESS, Compile::AliasIdxRaw); - - // is the queue for this thread full? - __ if_then(index, BoolTest::ne, zeroX, likely); { - - // decrement the index - Node* next_index = kit->gvn().transform(new SubXNode(index, __ ConX(sizeof(intptr_t)))); - - // Now get the buffer location we will log the previous value into and store it - Node *log_addr = __ AddP(no_base, buffer, next_index); - __ store(__ ctrl(), log_addr, pre_val, T_OBJECT, Compile::AliasIdxRaw, MemNode::unordered); - // update the index - __ store(__ ctrl(), index_adr, next_index, index_bt, Compile::AliasIdxRaw, MemNode::unordered); - - } __ else_(); { - - // logging buffer is full, call the runtime - const TypeFunc *tf = write_ref_field_pre_entry_Type(); - __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), "write_ref_field_pre_entry", pre_val, tls); - } __ end_if(); // (!index) - } __ end_if(); // (pre_val != nullptr) - } __ end_if(); // (!marking) - - // Final sync IdealKit and GraphKit. - kit->final_sync(ideal); -} - /* - * G1 similar to any GC with a Young Generation requires a way to keep track of - * references from Old Generation to Young Generation to make sure all live + * G1, similar to any GC with a Young Generation, requires a way to keep track + * of references from Old Generation to Young Generation to make sure all live * objects are found. G1 also requires to keep track of object references * between different regions to enable evacuation of old regions, which is done - * as part of mixed collections. References are tracked in remembered sets and - * is continuously updated as reference are written to with the help of the - * post-barrier. + * as part of mixed collections. References are tracked in remembered sets, + * which are continuously updated as references are written to with the help of + * the post-barrier. * - * To reduce the number of updates to the remembered set the post-barrier - * filters updates to fields in objects located in the Young Generation, - * the same region as the reference, when the null is being written or - * if the card is already marked as dirty by an earlier write. + * To reduce the number of updates to the remembered set, the post-barrier + * filters out updates to fields in objects located in the Young Generation, the + * same region as the reference, when null is being written, or if the card is + * already marked as dirty by an earlier write. * * Under certain circumstances it is possible to avoid generating the - * post-barrier completely if it is possible during compile time to prove - * the object is newly allocated and that no safepoint exists between the - * allocation and the store. - * - * In the case of slow allocation the allocation code must handle the barrier - * as part of the allocation in the case the allocated object is not located - * in the nursery; this would happen for humongous objects. + * post-barrier completely, if it is possible during compile time to prove the + * object is newly allocated and that no safepoint exists between the allocation + * and the store. This can be seen as a compile-time version of the + * above-mentioned Young Generation filter. * - * Returns true if the post barrier can be removed + * In the case of a slow allocation, the allocation code must handle the barrier + * as part of the allocation if the allocated object is not located in the + * nursery; this would happen for humongous objects. */ bool G1BarrierSetC2::g1_can_remove_post_barrier(GraphKit* kit, - PhaseValues* phase, Node* store, + PhaseValues* phase, Node* store_ctrl, Node* adr) const { intptr_t offset = 0; Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset); AllocateNode* alloc = AllocateNode::Ideal_allocation(base); if (offset == Type::OffsetBot) { - return false; // cannot unalias unless there are precise offsets + return false; // Cannot unalias unless there are precise offsets. } - if (alloc == nullptr) { - return false; // No allocation found + return false; // No allocation found. } - // Start search from Store node - Node* mem = store->in(MemNode::Control); + Node* mem = store_ctrl; // Start search from Store node. if (mem->is_Proj() && mem->in(0)->is_Initialize()) { - InitializeNode* st_init = mem->in(0)->as_Initialize(); AllocateNode* st_alloc = st_init->allocation(); - // Make sure we are looking at the same allocation if (alloc == st_alloc) { return true; @@ -333,725 +197,367 @@ bool G1BarrierSetC2::g1_can_remove_post_barrier(GraphKit* kit, return false; } -// -// Update the card table and add card address to the queue -// -void G1BarrierSetC2::g1_mark_card(GraphKit* kit, - IdealKit& ideal, - Node* card_adr, - Node* oop_store, - uint oop_alias_idx, - Node* index, - Node* index_adr, - Node* buffer, - const TypeFunc* tf) const { - Node* zero = __ ConI(0); - Node* zeroX = __ ConX(0); - Node* no_base = __ top(); - BasicType card_bt = T_BYTE; - // Smash zero into card. MUST BE ORDERED WRT TO STORE - __ storeCM(__ ctrl(), card_adr, zero, oop_store, oop_alias_idx, card_bt, Compile::AliasIdxRaw); - - // Now do the queue work - __ if_then(index, BoolTest::ne, zeroX); { - - Node* next_index = kit->gvn().transform(new SubXNode(index, __ ConX(sizeof(intptr_t)))); - Node* log_addr = __ AddP(no_base, buffer, next_index); - - // Order, see storeCM. - __ store(__ ctrl(), log_addr, card_adr, T_ADDRESS, Compile::AliasIdxRaw, MemNode::unordered); - __ store(__ ctrl(), index_adr, next_index, TypeX_X->basic_type(), Compile::AliasIdxRaw, MemNode::unordered); - - } __ else_(); { - __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry), "write_ref_field_post_entry", card_adr, __ thread()); - } __ end_if(); - +Node* G1BarrierSetC2::load_at_resolved(C2Access& access, const Type* val_type) const { + DecoratorSet decorators = access.decorators(); + bool on_weak = (decorators & ON_WEAK_OOP_REF) != 0; + bool on_phantom = (decorators & ON_PHANTOM_OOP_REF) != 0; + bool no_keepalive = (decorators & AS_NO_KEEPALIVE) != 0; + // If we are reading the value of the referent field of a Reference object, we + // need to record the referent in an SATB log buffer using the pre-barrier + // mechanism. Also we need to add a memory barrier to prevent commoning reads + // from this field across safepoints, since GC can change its value. + bool need_read_barrier = ((on_weak || on_phantom) && !no_keepalive); + if (access.is_oop() && need_read_barrier) { + access.set_barrier_data(G1C2BarrierPre); + } + return CardTableBarrierSetC2::load_at_resolved(access, val_type); } -void G1BarrierSetC2::post_barrier(GraphKit* kit, - Node* ctl, - Node* oop_store, - Node* obj, - Node* adr, - uint alias_idx, - Node* val, - BasicType bt, - bool use_precise) const { - // If we are writing a null then we need no post barrier +void G1BarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { + eliminate_gc_barrier_data(node); +} - if (val != nullptr && val->is_Con() && val->bottom_type() == TypePtr::NULL_PTR) { - // Must be null - const Type* t = val->bottom_type(); - assert(t == Type::TOP || t == TypePtr::NULL_PTR, "must be null"); - // No post barrier if writing null - return; +void G1BarrierSetC2::eliminate_gc_barrier_data(Node* node) const { + if (node->is_LoadStore()) { + LoadStoreNode* loadstore = node->as_LoadStore(); + loadstore->set_barrier_data(0); + } else if (node->is_Mem()) { + MemNode* mem = node->as_Mem(); + mem->set_barrier_data(0); } +} - if (use_ReduceInitialCardMarks() && obj == kit->just_allocated_object(kit->control())) { - // We can skip marks on a freshly-allocated object in Eden. - // Keep this code in sync with CardTableBarrierSet::on_slowpath_allocation_exit. - // That routine informs GC to take appropriate compensating steps, - // upon a slow-path allocation, so as to make this card-mark - // elision safe. +static void refine_barrier_by_new_val_type(const Node* n) { + if (n->Opcode() != Op_StoreP && + n->Opcode() != Op_StoreN) { return; } - - if (use_ReduceInitialCardMarks() - && g1_can_remove_post_barrier(kit, &kit->gvn(), oop_store, adr)) { + MemNode* store = n->as_Mem(); + const Node* newval = n->in(MemNode::ValueIn); + assert(newval != nullptr, ""); + const Type* newval_bottom = newval->bottom_type(); + TypePtr::PTR newval_type = newval_bottom->make_ptr()->ptr(); + uint8_t barrier_data = store->barrier_data(); + if (!newval_bottom->isa_oopptr() && + !newval_bottom->isa_narrowoop() && + newval_type != TypePtr::Null) { + // newval is neither an OOP nor null, so there is no barrier to refine. + assert(barrier_data == 0, "non-OOP stores should have no barrier data"); return; } - - if (!use_precise) { - // All card marks for a (non-array) instance are in one place: - adr = obj; + if (barrier_data == 0) { + // No barrier to refine. + return; } - // (Else it's an array (or unknown), and we want more precise card marks.) - assert(adr != nullptr, ""); - - IdealKit ideal(kit, true); - - Node* tls = __ thread(); // ThreadLocalStorage - - Node* no_base = __ top(); - float likely = PROB_LIKELY_MAG(3); - float unlikely = PROB_UNLIKELY_MAG(3); - Node* young_card = __ ConI((jint)G1CardTable::g1_young_card_val()); - Node* dirty_card = __ ConI((jint)G1CardTable::dirty_card_val()); - Node* zeroX = __ ConX(0); - - const TypeFunc *tf = write_ref_field_post_entry_Type(); - - // Offsets into the thread - const int index_offset = in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset()); - const int buffer_offset = in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset()); - - // Pointers into the thread - - Node* buffer_adr = __ AddP(no_base, tls, __ ConX(buffer_offset)); - Node* index_adr = __ AddP(no_base, tls, __ ConX(index_offset)); - - // Now some values - // Use ctrl to avoid hoisting these values past a safepoint, which could - // potentially reset these fields in the JavaThread. - Node* index = __ load(__ ctrl(), index_adr, TypeX_X, TypeX_X->basic_type(), Compile::AliasIdxRaw); - Node* buffer = __ load(__ ctrl(), buffer_adr, TypeRawPtr::NOTNULL, T_ADDRESS, Compile::AliasIdxRaw); - - // Convert the store obj pointer to an int prior to doing math on it - // Must use ctrl to prevent "integerized oop" existing across safepoint - Node* cast = __ CastPX(__ ctrl(), adr); - - // Divide pointer by card size - Node* card_offset = __ URShiftX( cast, __ ConI(CardTable::card_shift()) ); - - // Combine card table base and card offset - Node* card_adr = __ AddP(no_base, byte_map_base_node(kit), card_offset ); - - // If we know the value being stored does it cross regions? - - if (val != nullptr) { - // Does the store cause us to cross regions? - - // Should be able to do an unsigned compare of region_size instead of - // and extra shift. Do we have an unsigned compare?? - // Node* region_size = __ ConI(1 << G1HeapRegion::LogOfHRGrainBytes); - Node* xor_res = __ URShiftX ( __ XorX( cast, __ CastPX(__ ctrl(), val)), __ ConI(checked_cast(G1HeapRegion::LogOfHRGrainBytes))); - - // if (xor_res == 0) same region so skip - __ if_then(xor_res, BoolTest::ne, zeroX, likely); { - - // No barrier if we are storing a null. - __ if_then(val, BoolTest::ne, kit->null(), likely); { - - // Ok must mark the card if not already dirty - - // load the original value of the card - Node* card_val = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw); - - __ if_then(card_val, BoolTest::ne, young_card, unlikely); { - kit->sync_kit(ideal); - kit->insert_mem_bar(Op_MemBarVolatile, oop_store); - __ sync_kit(kit); - - Node* card_val_reload = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw); - __ if_then(card_val_reload, BoolTest::ne, dirty_card); { - g1_mark_card(kit, ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf); - } __ end_if(); - } __ end_if(); - } __ end_if(); - } __ end_if(); - } else { - // The Object.clone() intrinsic uses this path if !ReduceInitialCardMarks. - // We don't need a barrier here if the destination is a newly allocated object - // in Eden. Otherwise, GC verification breaks because we assume that cards in Eden - // are set to 'g1_young_gen' (see G1CardTable::verify_g1_young_region()). - assert(!use_ReduceInitialCardMarks(), "can only happen with card marking"); - Node* card_val = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw); - __ if_then(card_val, BoolTest::ne, young_card); { - g1_mark_card(kit, ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf); - } __ end_if(); + if (newval_type == TypePtr::Null) { + // Simply elide post-barrier if writing null. + barrier_data &= ~G1C2BarrierPost; + barrier_data &= ~G1C2BarrierPostNotNull; + } else if (((barrier_data & G1C2BarrierPost) != 0) && + newval_type == TypePtr::NotNull) { + // If the post-barrier has not been elided yet (e.g. due to newval being + // freshly allocated), mark it as not-null (simplifies barrier tests and + // compressed OOPs logic). + barrier_data |= G1C2BarrierPostNotNull; } - - // Final sync IdealKit and GraphKit. - kit->final_sync(ideal); + store->set_barrier_data(barrier_data); + return; } -// Helper that guards and inserts a pre-barrier. -void G1BarrierSetC2::insert_pre_barrier(GraphKit* kit, Node* base_oop, Node* offset, - Node* pre_val, bool need_mem_bar) const { - // We could be accessing the referent field of a reference object. If so, when G1 - // is enabled, we need to log the value in the referent field in an SATB buffer. - // This routine performs some compile time filters and generates suitable - // runtime filters that guard the pre-barrier code. - // Also add memory barrier for non volatile load from the referent field - // to prevent commoning of loads across safepoint. - - // Some compile time checks. - - // If offset is a constant, is it java_lang_ref_Reference::_reference_offset? - const TypeX* otype = offset->find_intptr_t_type(); - if (otype != nullptr && otype->is_con() && - otype->get_con() != java_lang_ref_Reference::referent_offset()) { - // Constant offset but not the reference_offset so just return - return; - } - - // We only need to generate the runtime guards for instances. - const TypeOopPtr* btype = base_oop->bottom_type()->isa_oopptr(); - if (btype != nullptr) { - if (btype->isa_aryptr()) { - // Array type so nothing to do - return; +// Refine (not really expand) G1 barriers by looking at the new value type +// (whether it is necessarily null or necessarily non-null). +bool G1BarrierSetC2::expand_barriers(Compile* C, PhaseIterGVN& igvn) const { + ResourceMark rm; + VectorSet visited; + Node_List worklist; + worklist.push(C->root()); + while (worklist.size() > 0) { + Node* n = worklist.pop(); + if (visited.test_set(n->_idx)) { + continue; } - - const TypeInstPtr* itype = btype->isa_instptr(); - if (itype != nullptr) { - // Can the klass of base_oop be statically determined to be - // _not_ a sub-class of Reference and _not_ Object? - ciKlass* klass = itype->instance_klass(); - if (klass->is_loaded() && - !klass->is_subtype_of(kit->env()->Reference_klass()) && - !kit->env()->Object_klass()->is_subtype_of(klass)) { - return; + refine_barrier_by_new_val_type(n); + for (uint j = 0; j < n->req(); j++) { + Node* in = n->in(j); + if (in != nullptr) { + worklist.push(in); } } } + return false; +} - // The compile time filters did not reject base_oop/offset so - // we need to generate the following runtime filters - // - // if (offset == java_lang_ref_Reference::_reference_offset) { - // if (instance_of(base, java.lang.ref.Reference)) { - // pre_barrier(_, pre_val, ...); +uint G1BarrierSetC2::estimated_barrier_size(const Node* node) const { + // These Ideal node counts are extracted from the pre-matching Ideal graph + // generated when compiling the following method with early barrier expansion: + // static void write(MyObject obj1, Object o) { + // obj1.o1 = o; // } - // } - - float likely = PROB_LIKELY( 0.999); - float unlikely = PROB_UNLIKELY(0.999); - - IdealKit ideal(kit); - - Node* referent_off = __ ConX(java_lang_ref_Reference::referent_offset()); - - __ if_then(offset, BoolTest::eq, referent_off, unlikely); { - // Update graphKit memory and control from IdealKit. - kit->sync_kit(ideal); - - Node* ref_klass_con = kit->makecon(TypeKlassPtr::make(kit->env()->Reference_klass())); - Node* is_instof = kit->gen_instanceof(base_oop, ref_klass_con); - - // Update IdealKit memory and control from graphKit. - __ sync_kit(kit); - - Node* one = __ ConI(1); - // is_instof == 0 if base_oop == nullptr - __ if_then(is_instof, BoolTest::eq, one, unlikely); { - - // Update graphKit from IdeakKit. - kit->sync_kit(ideal); - - // Use the pre-barrier to record the value in the referent field - pre_barrier(kit, false /* do_load */, - __ ctrl(), - nullptr /* obj */, nullptr /* adr */, max_juint /* alias_idx */, nullptr /* val */, nullptr /* val_type */, - pre_val /* pre_val */, - T_OBJECT); - if (need_mem_bar) { - // Add memory barrier to prevent commoning reads from this field - // across safepoint since GC can change its value. - kit->insert_mem_bar(Op_MemBarCPUOrder); - } - // Update IdealKit from graphKit. - __ sync_kit(kit); - - } __ end_if(); // _ref_type != ref_none - } __ end_if(); // offset == referent_offset + uint8_t barrier_data = MemNode::barrier_data(node); + uint nodes = 0; + if ((barrier_data & G1C2BarrierPre) != 0) { + nodes += 50; + } + if ((barrier_data & G1C2BarrierPost) != 0) { + nodes += 60; + } + return nodes; +} - // Final sync IdealKit and GraphKit. - kit->final_sync(ideal); +bool G1BarrierSetC2::can_initialize_object(const StoreNode* store) const { + assert(store->Opcode() == Op_StoreP || store->Opcode() == Op_StoreN, "OOP store expected"); + // It is OK to move the store across the object initialization boundary only + // if it does not have any barrier, or if it has barriers that can be safely + // elided (because of the compensation steps taken on the allocation slow path + // when ReduceInitialCardMarks is enabled). + return (MemNode::barrier_data(store) == 0) || use_ReduceInitialCardMarks(); } -#undef __ +void G1BarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCopyNode* ac) const { + if (ac->is_clone_inst() && !use_ReduceInitialCardMarks()) { + clone_in_runtime(phase, ac, G1BarrierSetRuntime::clone_addr(), "G1BarrierSetRuntime::clone"); + return; + } + BarrierSetC2::clone_at_expansion(phase, ac); +} -Node* G1BarrierSetC2::load_at_resolved(C2Access& access, const Type* val_type) const { +Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) const { DecoratorSet decorators = access.decorators(); - Node* adr = access.addr().node(); - Node* obj = access.base(); - - bool anonymous = (decorators & C2_UNSAFE_ACCESS) != 0; - bool mismatched = (decorators & C2_MISMATCHED) != 0; - bool unknown = (decorators & ON_UNKNOWN_OOP_REF) != 0; + bool anonymous = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool in_heap = (decorators & IN_HEAP) != 0; - bool in_native = (decorators & IN_NATIVE) != 0; - bool on_weak = (decorators & ON_WEAK_OOP_REF) != 0; - bool on_phantom = (decorators & ON_PHANTOM_OOP_REF) != 0; - bool is_unordered = (decorators & MO_UNORDERED) != 0; + bool tightly_coupled_alloc = (decorators & C2_TIGHTLY_COUPLED_ALLOC) != 0; + bool need_store_barrier = !(tightly_coupled_alloc && use_ReduceInitialCardMarks()) && (in_heap || anonymous); bool no_keepalive = (decorators & AS_NO_KEEPALIVE) != 0; - bool is_mixed = !in_heap && !in_native; - bool need_cpu_mem_bar = !is_unordered || mismatched || is_mixed; - - Node* top = Compile::current()->top(); - Node* offset = adr->is_AddP() ? adr->in(AddPNode::Offset) : top; - - // If we are reading the value of the referent field of a Reference - // object (either by using Unsafe directly or through reflection) - // then, if G1 is enabled, we need to record the referent in an - // SATB log buffer using the pre-barrier mechanism. - // Also we need to add memory barrier to prevent commoning reads - // from this field across safepoint since GC can change its value. - bool need_read_barrier = (((on_weak || on_phantom) && !no_keepalive) || - (in_heap && unknown && offset != top && obj != top)); + if (access.is_oop() && need_store_barrier) { + access.set_barrier_data(get_store_barrier(access)); + if (tightly_coupled_alloc) { + assert(!use_ReduceInitialCardMarks(), + "post-barriers are only needed for tightly-coupled initialization stores when ReduceInitialCardMarks is disabled"); + // Pre-barriers are unnecessary for tightly-coupled initialization stores. + access.set_barrier_data(access.barrier_data() & ~G1C2BarrierPre); + } + } + if (no_keepalive) { + // No keep-alive means no need for the pre-barrier. + access.set_barrier_data(access.barrier_data() & ~G1C2BarrierPre); + } + return BarrierSetC2::store_at_resolved(access, val); +} - if (!access.is_oop() || !need_read_barrier) { - return CardTableBarrierSetC2::load_at_resolved(access, val_type); +Node* G1BarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val, + Node* new_val, const Type* value_type) const { + GraphKit* kit = access.kit(); + if (!access.is_oop()) { + return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); } + access.set_barrier_data(G1C2BarrierPre | G1C2BarrierPost); + return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); +} - assert(access.is_parse_access(), "entry not supported at optimization time"); +Node* G1BarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& access, Node* expected_val, + Node* new_val, const Type* value_type) const { + GraphKit* kit = access.kit(); + if (!access.is_oop()) { + return BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type); + } + access.set_barrier_data(G1C2BarrierPre | G1C2BarrierPost); + return BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type); +} - C2ParseAccess& parse_access = static_cast(access); - GraphKit* kit = parse_access.kit(); - Node* load; +Node* G1BarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const { + GraphKit* kit = access.kit(); + if (!access.is_oop()) { + return BarrierSetC2::atomic_xchg_at_resolved(access, new_val, value_type); + } + access.set_barrier_data(G1C2BarrierPre | G1C2BarrierPost); + return BarrierSetC2::atomic_xchg_at_resolved(access, new_val, value_type); +} - Node* control = kit->control(); - const TypePtr* adr_type = access.addr().type(); - MemNode::MemOrd mo = access.mem_node_mo(); - bool requires_atomic_access = (decorators & MO_UNORDERED) == 0; - bool unaligned = (decorators & C2_UNALIGNED) != 0; - bool unsafe = (decorators & C2_UNSAFE_ACCESS) != 0; - // Pinned control dependency is the strictest. So it's ok to substitute it for any other. - load = kit->make_load(control, adr, val_type, access.type(), adr_type, mo, - LoadNode::Pinned, requires_atomic_access, unaligned, mismatched, unsafe, - access.barrier_data()); +class G1BarrierSetC2State : public BarrierSetC2State { +private: + GrowableArray* _stubs; +public: + G1BarrierSetC2State(Arena* arena) + : BarrierSetC2State(arena), + _stubs(new (arena) GrowableArray(arena, 8, 0, nullptr)) {} - if (on_weak || on_phantom) { - // Use the pre-barrier to record the value in the referent field - pre_barrier(kit, false /* do_load */, - kit->control(), - nullptr /* obj */, nullptr /* adr */, max_juint /* alias_idx */, nullptr /* val */, nullptr /* val_type */, - load /* pre_val */, T_OBJECT); - // Add memory barrier to prevent commoning reads from this field - // across safepoint since GC can change its value. - kit->insert_mem_bar(Op_MemBarCPUOrder); - } else if (unknown) { - // We do not require a mem bar inside pre_barrier if need_mem_bar - // is set: the barriers would be emitted by us. - insert_pre_barrier(kit, obj, offset, load, !need_cpu_mem_bar); + GrowableArray* stubs() { + return _stubs; } - return load; -} - -bool G1BarrierSetC2::is_gc_barrier_node(Node* node) const { - if (CardTableBarrierSetC2::is_gc_barrier_node(node)) { - return true; + bool needs_liveness_data(const MachNode* mach) const { + return G1PreBarrierStubC2::needs_barrier(mach) || + G1PostBarrierStubC2::needs_barrier(mach); } - if (node->Opcode() != Op_CallLeaf) { - return false; - } - CallLeafNode *call = node->as_CallLeaf(); - if (call->_name == nullptr) { + + bool needs_livein_data() const { return false; } +}; - return strcmp(call->_name, "write_ref_field_pre_entry") == 0 || strcmp(call->_name, "write_ref_field_post_entry") == 0; +static G1BarrierSetC2State* barrier_set_state() { + return reinterpret_cast(Compile::current()->barrier_set_state()); } -bool G1BarrierSetC2::is_g1_pre_val_load(Node* n) { - if (n->is_Load() && n->as_Load()->has_pinned_control_dependency()) { - // Make sure the only users of it are: CmpP, StoreP, and a call to write_ref_field_pre_entry +G1BarrierStubC2::G1BarrierStubC2(const MachNode* node) : BarrierStubC2(node) {} - // Skip possible decode - if (n->outcnt() == 1 && n->unique_out()->is_DecodeN()) { - n = n->unique_out(); - } +G1PreBarrierStubC2::G1PreBarrierStubC2(const MachNode* node) : G1BarrierStubC2(node) {} - if (n->outcnt() == 3) { - int found = 0; - for (SimpleDUIterator iter(n); iter.has_next(); iter.next()) { - Node* use = iter.get(); - if (use->is_Cmp() || use->is_Store()) { - ++found; - } else if (use->is_CallLeaf()) { - CallLeafNode* call = use->as_CallLeaf(); - if (strcmp(call->_name, "write_ref_field_pre_entry") == 0) { - ++found; - } - } - } - if (found == 3) { - return true; - } - } +bool G1PreBarrierStubC2::needs_barrier(const MachNode* node) { + return (node->barrier_data() & G1C2BarrierPre) != 0; +} + +G1PreBarrierStubC2* G1PreBarrierStubC2::create(const MachNode* node) { + G1PreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) G1PreBarrierStubC2(node); + if (!Compile::current()->output()->in_scratch_emit_size()) { + barrier_set_state()->stubs()->append(stub); } - return false; + return stub; } -bool G1BarrierSetC2::is_gc_pre_barrier_node(Node *node) const { - return is_g1_pre_val_load(node); +void G1PreBarrierStubC2::initialize_registers(Register obj, Register pre_val, Register thread, Register tmp1, Register tmp2) { + _obj = obj; + _pre_val = pre_val; + _thread = thread; + _tmp1 = tmp1; + _tmp2 = tmp2; } -void G1BarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { - if (is_g1_pre_val_load(node)) { - macro->replace_node(node, macro->zerocon(node->as_Load()->bottom_type()->basic_type())); - } else { - assert(node->Opcode() == Op_CastP2X, "ConvP2XNode required"); - assert(node->outcnt() <= 2, "expects 1 or 2 users: Xor and URShift nodes"); - // It could be only one user, URShift node, in Object.clone() intrinsic - // but the new allocation is passed to arraycopy stub and it could not - // be scalar replaced. So we don't check the case. +Register G1PreBarrierStubC2::obj() const { + return _obj; +} - // An other case of only one user (Xor) is when the value check for null - // in G1 post barrier is folded after CCP so the code which used URShift - // is removed. +Register G1PreBarrierStubC2::pre_val() const { + return _pre_val; +} - // Take Region node before eliminating post barrier since it also - // eliminates CastP2X node when it has only one user. - Node* this_region = node->in(0); - assert(this_region != nullptr, ""); +Register G1PreBarrierStubC2::thread() const { + return _thread; +} - // Remove G1 post barrier. +Register G1PreBarrierStubC2::tmp1() const { + return _tmp1; +} + +Register G1PreBarrierStubC2::tmp2() const { + return _tmp2; +} - // Search for CastP2X->Xor->URShift->Cmp path which - // checks if the store done to a different from the value's region. - // And replace Cmp with #0 (false) to collapse G1 post barrier. - Node* xorx = node->find_out_with(Op_XorX); - if (xorx != nullptr) { - Node* shift = xorx->unique_out(); - Node* cmpx = shift->unique_out(); - assert(cmpx->is_Cmp() && cmpx->unique_out()->is_Bool() && - cmpx->unique_out()->as_Bool()->_test._test == BoolTest::ne, - "missing region check in G1 post barrier"); - macro->replace_node(cmpx, macro->makecon(TypeInt::CC_EQ)); +void G1PreBarrierStubC2::emit_code(MacroAssembler& masm) { + G1BarrierSetAssembler* bs = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + bs->generate_c2_pre_barrier_stub(&masm, this); +} - // Remove G1 pre barrier. +G1PostBarrierStubC2::G1PostBarrierStubC2(const MachNode* node) : G1BarrierStubC2(node) {} - // Search "if (marking != 0)" check and set it to "false". - // There is no G1 pre barrier if previous stored value is null - // (for example, after initialization). - if (this_region->is_Region() && this_region->req() == 3) { - int ind = 1; - if (!this_region->in(ind)->is_IfFalse()) { - ind = 2; - } - if (this_region->in(ind)->is_IfFalse() && - this_region->in(ind)->in(0)->Opcode() == Op_If) { - Node* bol = this_region->in(ind)->in(0)->in(1); - assert(bol->is_Bool(), ""); - cmpx = bol->in(1); - if (bol->as_Bool()->_test._test == BoolTest::ne && - cmpx->is_Cmp() && cmpx->in(2) == macro->intcon(0) && - cmpx->in(1)->is_Load()) { - Node* adr = cmpx->in(1)->as_Load()->in(MemNode::Address); - const int marking_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()); - if (adr->is_AddP() && adr->in(AddPNode::Base) == macro->top() && - adr->in(AddPNode::Address)->Opcode() == Op_ThreadLocal && - adr->in(AddPNode::Offset) == macro->MakeConX(marking_offset)) { - macro->replace_node(cmpx, macro->makecon(TypeInt::CC_EQ)); - } - } - } - } - } else { - assert(!use_ReduceInitialCardMarks(), "can only happen with card marking"); - // This is a G1 post barrier emitted by the Object.clone() intrinsic. - // Search for the CastP2X->URShiftX->AddP->LoadB->Cmp path which checks if the card - // is marked as young_gen and replace the Cmp with 0 (false) to collapse the barrier. - Node* shift = node->find_out_with(Op_URShiftX); - assert(shift != nullptr, "missing G1 post barrier"); - Node* addp = shift->unique_out(); - Node* load = addp->find_out_with(Op_LoadB); - assert(load != nullptr, "missing G1 post barrier"); - Node* cmpx = load->unique_out(); - assert(cmpx->is_Cmp() && cmpx->unique_out()->is_Bool() && - cmpx->unique_out()->as_Bool()->_test._test == BoolTest::ne, - "missing card value check in G1 post barrier"); - macro->replace_node(cmpx, macro->makecon(TypeInt::CC_EQ)); - // There is no G1 pre barrier in this case - } - // Now CastP2X can be removed since it is used only on dead path - // which currently still alive until igvn optimize it. - assert(node->outcnt() == 0 || node->unique_out()->Opcode() == Op_URShiftX, ""); - macro->replace_node(node, macro->top()); - } +bool G1PostBarrierStubC2::needs_barrier(const MachNode* node) { + return (node->barrier_data() & G1C2BarrierPost) != 0; } -Node* G1BarrierSetC2::step_over_gc_barrier(Node* c) const { - if (!use_ReduceInitialCardMarks() && - c != nullptr && c->is_Region() && c->req() == 3) { - for (uint i = 1; i < c->req(); i++) { - if (c->in(i) != nullptr && c->in(i)->is_Region() && - c->in(i)->req() == 3) { - Node* r = c->in(i); - for (uint j = 1; j < r->req(); j++) { - if (r->in(j) != nullptr && r->in(j)->is_Proj() && - r->in(j)->in(0) != nullptr && - r->in(j)->in(0)->Opcode() == Op_CallLeaf && - r->in(j)->in(0)->as_Call()->entry_point() == CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_post_entry)) { - Node* call = r->in(j)->in(0); - c = c->in(i == 1 ? 2 : 1); - if (c != nullptr && c->Opcode() != Op_Parm) { - c = c->in(0); - if (c != nullptr) { - c = c->in(0); - assert(call->in(0) == nullptr || - call->in(0)->in(0) == nullptr || - call->in(0)->in(0)->in(0) == nullptr || - call->in(0)->in(0)->in(0)->in(0) == nullptr || - call->in(0)->in(0)->in(0)->in(0)->in(0) == nullptr || - c == call->in(0)->in(0)->in(0)->in(0)->in(0), "bad barrier shape"); - return c; - } - } - } - } - } - } +G1PostBarrierStubC2* G1PostBarrierStubC2::create(const MachNode* node) { + G1PostBarrierStubC2* const stub = new (Compile::current()->comp_arena()) G1PostBarrierStubC2(node); + if (!Compile::current()->output()->in_scratch_emit_size()) { + barrier_set_state()->stubs()->append(stub); } - return c; + return stub; } -#ifdef ASSERT -bool G1BarrierSetC2::has_cas_in_use_chain(Node *n) const { - Unique_Node_List visited; - Node_List worklist; - worklist.push(n); - while (worklist.size() > 0) { - Node* x = worklist.pop(); - if (visited.member(x)) { - continue; - } else { - visited.push(x); - } +void G1PostBarrierStubC2::initialize_registers(Register thread, Register tmp1, Register tmp2, Register tmp3) { + _thread = thread; + _tmp1 = tmp1; + _tmp2 = tmp2; + _tmp3 = tmp3; +} - if (x->is_LoadStore()) { - int op = x->Opcode(); - if (op == Op_CompareAndExchangeP || op == Op_CompareAndExchangeN || - op == Op_CompareAndSwapP || op == Op_CompareAndSwapN || - op == Op_WeakCompareAndSwapP || op == Op_WeakCompareAndSwapN) { - return true; - } - } - if (!x->is_CFG()) { - for (SimpleDUIterator iter(x); iter.has_next(); iter.next()) { - Node* use = iter.get(); - worklist.push(use); - } - } - } - return false; +Register G1PostBarrierStubC2::thread() const { + return _thread; } -void G1BarrierSetC2::verify_pre_load(Node* marking_if, Unique_Node_List& loads /*output*/) const { - assert(loads.size() == 0, "Loads list should be empty"); - Node* pre_val_if = marking_if->find_out_with(Op_IfTrue)->find_out_with(Op_If); - if (pre_val_if != nullptr) { - Unique_Node_List visited; - Node_List worklist; - Node* pre_val = pre_val_if->in(1)->in(1)->in(1); +Register G1PostBarrierStubC2::tmp1() const { + return _tmp1; +} - worklist.push(pre_val); - while (worklist.size() > 0) { - Node* x = worklist.pop(); - if (visited.member(x)) { - continue; - } else { - visited.push(x); - } +Register G1PostBarrierStubC2::tmp2() const { + return _tmp2; +} - if (has_cas_in_use_chain(x)) { - loads.clear(); - return; - } +Register G1PostBarrierStubC2::tmp3() const { + return _tmp3; +} - if (x->is_Con()) { - continue; - } - if (x->is_EncodeP() || x->is_DecodeN()) { - worklist.push(x->in(1)); - continue; - } - if (x->is_Load() || x->is_LoadStore()) { - assert(x->in(0) != nullptr, "Pre-val load has to have a control"); - loads.push(x); - continue; - } - if (x->is_Phi()) { - for (uint i = 1; i < x->req(); i++) { - worklist.push(x->in(i)); - } - continue; - } - assert(false, "Pre-val anomaly"); - } - } +void G1PostBarrierStubC2::emit_code(MacroAssembler& masm) { + G1BarrierSetAssembler* bs = static_cast(BarrierSet::barrier_set()->barrier_set_assembler()); + bs->generate_c2_post_barrier_stub(&masm, this); } -void G1BarrierSetC2::verify_no_safepoints(Compile* compile, Node* marking_check_if, const Unique_Node_List& loads) const { - if (loads.size() == 0) { - return; - } +void* G1BarrierSetC2::create_barrier_state(Arena* comp_arena) const { + return new (comp_arena) G1BarrierSetC2State(comp_arena); +} - if (loads.size() == 1) { // Handle the typical situation when there a single pre-value load - // that is dominated by the marking_check_if, that's true when the - // barrier itself does the pre-val load. - Node *pre_val = loads.at(0); - if (pre_val->in(0)->in(0) == marking_check_if) { // IfTrue->If - return; - } +int G1BarrierSetC2::get_store_barrier(C2Access& access) const { + if (!access.is_parse_access()) { + // Only support for eliding barriers at parse time for now. + return G1C2BarrierPre | G1C2BarrierPost; } - - // All other cases are when pre-value loads dominate the marking check. - Unique_Node_List controls; - for (uint i = 0; i < loads.size(); i++) { - Node *c = loads.at(i)->in(0); - controls.push(c); + GraphKit* kit = (static_cast(access)).kit(); + Node* ctl = kit->control(); + Node* adr = access.addr().node(); + uint adr_idx = kit->C->get_alias_index(access.addr().type()); + assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory"); + + bool can_remove_pre_barrier = g1_can_remove_pre_barrier(kit, &kit->gvn(), adr, access.type(), adr_idx); + + // We can skip marks on a freshly-allocated object in Eden. Keep this code in + // sync with CardTableBarrierSet::on_slowpath_allocation_exit. That routine + // informs GC to take appropriate compensating steps, upon a slow-path + // allocation, so as to make this card-mark elision safe. + // The post-barrier can also be removed if null is written. This case is + // handled by G1BarrierSetC2::expand_barriers, which runs at the end of C2's + // platform-independent optimizations to exploit stronger type information. + bool can_remove_post_barrier = use_ReduceInitialCardMarks() && + ((access.base() == kit->just_allocated_object(ctl)) || + g1_can_remove_post_barrier(kit, &kit->gvn(), ctl, adr)); + + int barriers = 0; + if (!can_remove_pre_barrier) { + barriers |= G1C2BarrierPre; + } + if (!can_remove_post_barrier) { + barriers |= G1C2BarrierPost; } - Unique_Node_List visited; - Unique_Node_List safepoints; - Node_List worklist; - uint found = 0; + return barriers; +} - worklist.push(marking_check_if); - while (worklist.size() > 0 && found < controls.size()) { - Node* x = worklist.pop(); - if (x == nullptr || x == compile->top()) continue; - if (visited.member(x)) { - continue; - } else { - visited.push(x); - } +void G1BarrierSetC2::late_barrier_analysis() const { + compute_liveness_at_stubs(); +} - if (controls.member(x)) { - found++; - } - if (x->is_Region()) { - for (uint i = 1; i < x->req(); i++) { - worklist.push(x->in(i)); - } - } else { - if (!x->is_SafePoint()) { - worklist.push(x->in(0)); - } else { - safepoints.push(x); - } +void G1BarrierSetC2::emit_stubs(CodeBuffer& cb) const { + MacroAssembler masm(&cb); + GrowableArray* const stubs = barrier_set_state()->stubs(); + for (int i = 0; i < stubs->length(); i++) { + // Make sure there is enough space in the code buffer + if (cb.insts()->maybe_expand_to_ensure_remaining(PhaseOutput::MAX_inst_size) && cb.blob() == nullptr) { + ciEnv::current()->record_failure("CodeCache is full"); + return; } + stubs->at(i)->emit_code(masm); } - assert(found == controls.size(), "Pre-barrier structure anomaly or possible safepoint"); + masm.flush(); } -void G1BarrierSetC2::verify_gc_barriers(Compile* compile, CompilePhase phase) const { - if (phase != BarrierSetC2::BeforeCodeGen) { - return; +#ifndef PRODUCT +void G1BarrierSetC2::dump_barrier_data(const MachNode* mach, outputStream* st) const { + if ((mach->barrier_data() & G1C2BarrierPre) != 0) { + st->print("pre "); } - // Verify G1 pre-barriers - const int marking_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_active_offset()); - - Unique_Node_List visited; - Node_List worklist; - // We're going to walk control flow backwards starting from the Root - worklist.push(compile->root()); - while (worklist.size() > 0) { - Node* x = worklist.pop(); - if (x == nullptr || x == compile->top()) continue; - if (visited.member(x)) { - continue; - } else { - visited.push(x); - } - - if (x->is_Region()) { - for (uint i = 1; i < x->req(); i++) { - worklist.push(x->in(i)); - } - } else { - worklist.push(x->in(0)); - // We are looking for the pattern: - // /->ThreadLocal - // If->Bool->CmpI->LoadB->AddP->ConL(marking_offset) - // \->ConI(0) - // We want to verify that the If and the LoadB have the same control - // See GraphKit::g1_write_barrier_pre() - if (x->is_If()) { - IfNode *iff = x->as_If(); - if (iff->in(1)->is_Bool() && iff->in(1)->in(1)->is_Cmp()) { - CmpNode *cmp = iff->in(1)->in(1)->as_Cmp(); - if (cmp->Opcode() == Op_CmpI && cmp->in(2)->is_Con() && cmp->in(2)->bottom_type()->is_int()->get_con() == 0 - && cmp->in(1)->is_Load()) { - LoadNode* load = cmp->in(1)->as_Load(); - if (load->Opcode() == Op_LoadB && load->in(2)->is_AddP() && load->in(2)->in(2)->Opcode() == Op_ThreadLocal - && load->in(2)->in(3)->is_Con() - && load->in(2)->in(3)->bottom_type()->is_intptr_t()->get_con() == marking_offset) { - - Node* if_ctrl = iff->in(0); - Node* load_ctrl = load->in(0); - - if (if_ctrl != load_ctrl) { - // Skip possible CProj->NeverBranch in infinite loops - if ((if_ctrl->is_Proj() && if_ctrl->Opcode() == Op_CProj) - && if_ctrl->in(0)->is_NeverBranch()) { - if_ctrl = if_ctrl->in(0)->in(0); - } - } - assert(load_ctrl != nullptr && if_ctrl == load_ctrl, "controls must match"); - - Unique_Node_List loads; - verify_pre_load(iff, loads); - verify_no_safepoints(compile, iff, loads); - } - } - } - } - } + if ((mach->barrier_data() & G1C2BarrierPost) != 0) { + st->print("post "); } -} -#endif - -bool G1BarrierSetC2::escape_add_to_con_graph(ConnectionGraph* conn_graph, PhaseGVN* gvn, Unique_Node_List* delayed_worklist, Node* n, uint opcode) const { - if (opcode == Op_StoreP) { - Node* adr = n->in(MemNode::Address); - const Type* adr_type = gvn->type(adr); - // Pointer stores in G1 barriers looks like unsafe access. - // Ignore such stores to be able scalar replace non-escaping - // allocations. - if (adr_type->isa_rawptr() && adr->is_AddP()) { - Node* base = conn_graph->get_addp_base(adr); - if (base->Opcode() == Op_LoadP && - base->in(MemNode::Address)->is_AddP()) { - adr = base->in(MemNode::Address); - Node* tls = conn_graph->get_addp_base(adr); - if (tls->Opcode() == Op_ThreadLocal) { - int offs = (int) gvn->find_intptr_t_con(adr->in(AddPNode::Offset), Type::OffsetBot); - const int buf_offset = in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset()); - if (offs == buf_offset) { - return true; // G1 pre barrier previous oop value store. - } - if (offs == in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset())) { - return true; // G1 post barrier card address store. - } - } - } - } + if ((mach->barrier_data() & G1C2BarrierPostNotNull) != 0) { + st->print("notnull "); } - return false; } +#endif // !PRODUCT diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp index c445a87d2e46d..dc333d8c33174 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.hpp @@ -31,29 +31,62 @@ class PhaseTransform; class Type; class TypeFunc; -class G1BarrierSetC2: public CardTableBarrierSetC2 { +const int G1C2BarrierPre = 1; +const int G1C2BarrierPost = 2; +const int G1C2BarrierPostNotNull = 4; + +class G1BarrierStubC2 : public BarrierStubC2 { +public: + G1BarrierStubC2(const MachNode* node); + virtual void emit_code(MacroAssembler& masm) = 0; +}; + +class G1PreBarrierStubC2 : public G1BarrierStubC2 { +private: + Register _obj; + Register _pre_val; + Register _thread; + Register _tmp1; + Register _tmp2; + +protected: + G1PreBarrierStubC2(const MachNode* node); + +public: + static bool needs_barrier(const MachNode* node); + static G1PreBarrierStubC2* create(const MachNode* node); + void initialize_registers(Register obj, Register pre_val, Register thread, Register tmp1 = noreg, Register tmp2 = noreg); + Register obj() const; + Register pre_val() const; + Register thread() const; + Register tmp1() const; + Register tmp2() const; + virtual void emit_code(MacroAssembler& masm); +}; + +class G1PostBarrierStubC2 : public G1BarrierStubC2 { +private: + Register _thread; + Register _tmp1; + Register _tmp2; + Register _tmp3; + protected: - virtual void pre_barrier(GraphKit* kit, - bool do_load, - Node* ctl, - Node* obj, - Node* adr, - uint adr_idx, - Node* val, - const TypeOopPtr* val_type, - Node* pre_val, - BasicType bt) const; - - virtual void post_barrier(GraphKit* kit, - Node* ctl, - Node* store, - Node* obj, - Node* adr, - uint adr_idx, - Node* val, - BasicType bt, - bool use_precise) const; + G1PostBarrierStubC2(const MachNode* node); +public: + static bool needs_barrier(const MachNode* node); + static G1PostBarrierStubC2* create(const MachNode* node); + void initialize_registers(Register thread, Register tmp1 = noreg, Register tmp2 = noreg, Register tmp3 = noreg); + Register thread() const; + Register tmp1() const; + Register tmp2() const; + Register tmp3() const; + virtual void emit_code(MacroAssembler& masm); +}; + +class G1BarrierSetC2: public CardTableBarrierSetC2 { +protected: bool g1_can_remove_pre_barrier(GraphKit* kit, PhaseValues* phase, Node* adr, @@ -64,44 +97,31 @@ class G1BarrierSetC2: public CardTableBarrierSetC2 { PhaseValues* phase, Node* store, Node* adr) const; - void g1_mark_card(GraphKit* kit, - IdealKit& ideal, - Node* card_adr, - Node* oop_store, - uint oop_alias_idx, - Node* index, - Node* index_adr, - Node* buffer, - const TypeFunc* tf) const; - - // Helper for unsafe accesses, that may or may not be on the referent field. - // Generates the guards that check whether the result of - // Unsafe.getReference should be recorded in an SATB log buffer. - void insert_pre_barrier(GraphKit* kit, Node* base_oop, Node* offset, Node* pre_val, bool need_mem_bar) const; - - static const TypeFunc* write_ref_field_pre_entry_Type(); - static const TypeFunc* write_ref_field_post_entry_Type(); + int get_store_barrier(C2Access& access) const; virtual Node* load_at_resolved(C2Access& access, const Type* val_type) const; + virtual Node* store_at_resolved(C2Access& access, C2AccessValue& val) const; + virtual Node* atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val, + Node* new_val, const Type* value_type) const; + virtual Node* atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& access, Node* expected_val, + Node* new_val, const Type* value_type) const; + virtual Node* atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const; -#ifdef ASSERT - bool has_cas_in_use_chain(Node* x) const; - void verify_pre_load(Node* marking_check_if, Unique_Node_List& loads /*output*/) const; - void verify_no_safepoints(Compile* compile, Node* marking_load, const Unique_Node_List& loads) const; -#endif - - static bool is_g1_pre_val_load(Node* n); public: - virtual bool is_gc_pre_barrier_node(Node* node) const; - virtual bool is_gc_barrier_node(Node* node) const; virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const; - virtual Node* step_over_gc_barrier(Node* c) const; - -#ifdef ASSERT - virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const; + virtual void eliminate_gc_barrier_data(Node* node) const; + virtual bool expand_barriers(Compile* C, PhaseIterGVN& igvn) const; + virtual uint estimated_barrier_size(const Node* node) const; + virtual bool can_initialize_object(const StoreNode* store) const; + virtual void clone_at_expansion(PhaseMacroExpand* phase, + ArrayCopyNode* ac) const; + virtual void* create_barrier_state(Arena* comp_arena) const; + virtual void emit_stubs(CodeBuffer& cb) const; + virtual void late_barrier_analysis() const; + +#ifndef PRODUCT + virtual void dump_barrier_data(const MachNode* mach, outputStream* st) const; #endif - - virtual bool escape_add_to_con_graph(ConnectionGraph* conn_graph, PhaseGVN* gvn, Unique_Node_List* delayed_worklist, Node* n, uint opcode) const; }; #endif // SHARE_GC_G1_C2_G1BARRIERSETC2_HPP diff --git a/src/hotspot/share/gc/g1/g1BarrierSetRuntime.cpp b/src/hotspot/share/gc/g1/g1BarrierSetRuntime.cpp index a0fce437807f4..2e247f46c93d8 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSetRuntime.cpp +++ b/src/hotspot/share/gc/g1/g1BarrierSetRuntime.cpp @@ -61,3 +61,11 @@ JRT_LEAF(void, G1BarrierSetRuntime::write_ref_field_post_entry(volatile G1CardTa G1DirtyCardQueue& queue = G1ThreadLocalData::dirty_card_queue(thread); G1BarrierSet::dirty_card_queue_set().enqueue(queue, card_addr); JRT_END + +JRT_LEAF(void, G1BarrierSetRuntime::clone(oopDesc* src, oopDesc* dst, size_t size)) + HeapAccess<>::clone(src, dst, size); +JRT_END + +address G1BarrierSetRuntime::clone_addr() { + return reinterpret_cast
(clone); +} diff --git a/src/hotspot/share/gc/g1/g1BarrierSetRuntime.hpp b/src/hotspot/share/gc/g1/g1BarrierSetRuntime.hpp index 366679f032ba9..f98e94096e72d 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSetRuntime.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSetRuntime.hpp @@ -35,6 +35,8 @@ class oopDesc; class JavaThread; class G1BarrierSetRuntime: public AllStatic { +private: + static void clone(oopDesc* src, oopDesc* dst, size_t size); public: using CardValue = G1CardTable::CardValue; @@ -46,6 +48,8 @@ class G1BarrierSetRuntime: public AllStatic { // C2 slow-path runtime calls. static void write_ref_field_pre_entry(oopDesc* orig, JavaThread *thread); static void write_ref_field_post_entry(volatile CardValue* card_addr, JavaThread* thread); + + static address clone_addr(); }; #endif // SHARE_GC_G1_G1BARRIERSETRUNTIME_HPP diff --git a/src/hotspot/share/gc/g1/g1BatchedTask.hpp b/src/hotspot/share/gc/g1/g1BatchedTask.hpp index aa16f4ddfd48d..020fda634e4b8 100644 --- a/src/hotspot/share/gc/g1/g1BatchedTask.hpp +++ b/src/hotspot/share/gc/g1/g1BatchedTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,7 +29,7 @@ #include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" -template +template class GrowableArrayCHeap; // G1AbstractSubTask represents a task to be performed either within a diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index d315497268f99..ec90fd377503d 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -26,7 +26,7 @@ #include "gc/g1/g1Analytics.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" -#include "gc/g1/g1CollectionSetCandidates.hpp" +#include "gc/g1/g1CollectionSetCandidates.inline.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" @@ -346,20 +346,16 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { G1CollectionCandidateRegionList pinned_retained_regions; if (collector_state()->in_mixed_phase()) { - time_remaining_ms = _policy->select_candidates_from_marking(&candidates()->marking_regions(), - time_remaining_ms, - &initial_old_regions, - &_optional_old_regions, - &pinned_marking_regions); + time_remaining_ms = select_candidates_from_marking(time_remaining_ms, + &initial_old_regions, + &pinned_marking_regions); } else { log_debug(gc, ergo, cset)("Do not add marking candidates to collection set due to pause type."); } - _policy->select_candidates_from_retained(&candidates()->retained_regions(), - time_remaining_ms, - &initial_old_regions, - &_optional_old_regions, - &pinned_retained_regions); + select_candidates_from_retained(time_remaining_ms, + &initial_old_regions, + &pinned_retained_regions); // Move initially selected old regions to collection set directly. move_candidates_to_collection_set(&initial_old_regions); @@ -394,6 +390,215 @@ void G1CollectionSet::move_candidates_to_collection_set(G1CollectionCandidateReg candidates()->remove(regions); } +static void print_finish_message(const char* reason, bool from_marking) { + log_debug(gc, ergo, cset)("Finish adding %s candidates to collection set (%s).", + from_marking ? "marking" : "retained", reason); +} + +double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions) { + uint num_expensive_regions = 0; + + uint num_initial_regions_selected = 0; + uint num_optional_regions_selected = 0; + uint num_pinned_regions = 0; + + double predicted_initial_time_ms = 0.0; + double predicted_optional_time_ms = 0.0; + + double optional_threshold_ms = time_remaining_ms * _policy->optional_prediction_fraction(); + + const uint min_old_cset_length = _policy->calc_min_old_cset_length(candidates()->last_marking_candidates_length()); + const uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length()); + const uint max_optional_regions = max_old_cset_length - min_old_cset_length; + bool check_time_remaining = _policy->use_adaptive_young_list_length(); + + G1CollectionCandidateList* marking_list = &candidates()->marking_regions(); + assert(marking_list != nullptr, "must be"); + + log_debug(gc, ergo, cset)("Start adding marking candidates to collection set. " + "Min %u regions, max %u regions, available %u regions" + "time remaining %1.2fms, optional threshold %1.2fms", + min_old_cset_length, max_old_cset_length, marking_list->length(), time_remaining_ms, optional_threshold_ms); + + G1CollectionCandidateListIterator iter = marking_list->begin(); + for (; iter != marking_list->end(); ++iter) { + if (num_initial_regions_selected + num_optional_regions_selected >= max_old_cset_length) { + // Added maximum number of old regions to the CSet. + print_finish_message("Maximum number of regions reached", true); + break; + } + G1HeapRegion* hr = (*iter)->_r; + // Skip evacuating pinned marking regions because we are not getting any free + // space from them (and we expect to get free space from marking candidates). + // Also prepare to move them to retained regions to be evacuated optionally later + // to not impact the mixed phase too much. + if (hr->has_pinned_objects()) { + num_pinned_regions++; + (*iter)->update_num_unreclaimed(); + log_trace(gc, ergo, cset)("Marking candidate %u can not be reclaimed currently. Skipping.", hr->hrm_index()); + pinned_old_regions->append(hr); + continue; + } + double predicted_time_ms = _policy->predict_region_total_time_ms(hr, false); + time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0); + // Add regions to old set until we reach the minimum amount + if (initial_old_regions->length() < min_old_cset_length) { + initial_old_regions->append(hr); + num_initial_regions_selected++; + predicted_initial_time_ms += predicted_time_ms; + // Record the number of regions added with no time remaining + if (time_remaining_ms == 0.0) { + num_expensive_regions++; + } + } else if (!check_time_remaining) { + // In the non-auto-tuning case, we'll finish adding regions + // to the CSet if we reach the minimum. + print_finish_message("Region amount reached min", true); + break; + } else { + // Keep adding regions to old set until we reach the optional threshold + if (time_remaining_ms > optional_threshold_ms) { + predicted_initial_time_ms += predicted_time_ms; + initial_old_regions->append(hr); + num_initial_regions_selected++; + } else if (time_remaining_ms > 0) { + // Keep adding optional regions until time is up. + assert(_optional_old_regions.length() < max_optional_regions, "Should not be possible."); + predicted_optional_time_ms += predicted_time_ms; + _optional_old_regions.append(hr); + num_optional_regions_selected++; + } else { + print_finish_message("Predicted time too high", true); + break; + } + } + } + if (iter == marking_list->end()) { + log_debug(gc, ergo, cset)("Marking candidates exhausted."); + } + + if (num_expensive_regions > 0) { + log_debug(gc, ergo, cset)("Added %u marking candidates to collection set although the predicted time was too high.", + num_expensive_regions); + } + + log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u, optional: %u, pinned: %u, " + "predicted initial time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2fms", + num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, + predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms); + + assert(initial_old_regions->length() == num_initial_regions_selected, "must be"); + assert(_optional_old_regions.length() == num_optional_regions_selected, "must be"); + return time_remaining_ms; +} + +void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions) { + uint num_initial_regions_selected = 0; + uint num_optional_regions_selected = 0; + uint num_expensive_regions_selected = 0; + uint num_pinned_regions = 0; + + double predicted_initial_time_ms = 0.0; + double predicted_optional_time_ms = 0.0; + + uint const min_regions = _policy->min_retained_old_cset_length(); + // We want to make sure that on the one hand we process the retained regions asap, + // but on the other hand do not take too many of them as optional regions. + // So we split the time budget into budget we will unconditionally take into the + // initial old regions, and budget for taking optional regions from the retained + // list. + double optional_time_remaining_ms = _policy->max_time_for_retaining(); + time_remaining_ms = MIN2(time_remaining_ms, optional_time_remaining_ms); + + G1CollectionCandidateList* retained_list = &candidates()->retained_regions(); + + log_debug(gc, ergo, cset)("Start adding retained candidates to collection set. " + "Min %u regions, available %u, " + "time remaining %1.2fms, optional remaining %1.2fms", + min_regions, retained_list->length(), time_remaining_ms, optional_time_remaining_ms); + + for (G1CollectionSetCandidateInfo* ci : *retained_list) { + G1HeapRegion* r = ci->_r; + double predicted_time_ms = _policy->predict_region_total_time_ms(r, collector_state()->in_young_only_phase()); + bool fits_in_remaining_time = predicted_time_ms <= time_remaining_ms; + // If we can't reclaim that region ignore it for now. + if (r->has_pinned_objects()) { + num_pinned_regions++; + if (ci->update_num_unreclaimed()) { + log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Skipping.", r->hrm_index()); + } else { + log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Dropping.", r->hrm_index()); + pinned_old_regions->append(r); + } + continue; + } + + if (fits_in_remaining_time || (num_expensive_regions_selected < min_regions)) { + predicted_initial_time_ms += predicted_time_ms; + if (!fits_in_remaining_time) { + num_expensive_regions_selected++; + } + initial_old_regions->append(r); + num_initial_regions_selected++; + } else if (predicted_time_ms <= optional_time_remaining_ms) { + predicted_optional_time_ms += predicted_time_ms; + _optional_old_regions.append(r); + num_optional_regions_selected++; + } else { + // Fits neither initial nor optional time limit. Exit. + break; + } + time_remaining_ms = MAX2(0.0, time_remaining_ms - predicted_time_ms); + optional_time_remaining_ms = MAX2(0.0, optional_time_remaining_ms - predicted_time_ms); + } + + uint num_regions_selected = num_initial_regions_selected + num_optional_regions_selected; + if (num_regions_selected == retained_list->length()) { + log_debug(gc, ergo, cset)("Retained candidates exhausted."); + } + if (num_expensive_regions_selected > 0) { + log_debug(gc, ergo, cset)("Added %u retained candidates to collection set although the predicted time was too high.", + num_expensive_regions_selected); + } + + log_debug(gc, ergo, cset)("Finish adding retained candidates to collection set. Initial: %u, optional: %u, pinned: %u, " + "predicted initial time: %1.2fms, predicted optional time: %1.2fms, " + "time remaining: %1.2fms optional time remaining %1.2fms", + num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, + predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms, optional_time_remaining_ms); +} + +void G1CollectionSet::select_candidates_from_optional_regions(double time_remaining_ms, + G1CollectionCandidateRegionList* selected_regions) { + assert(optional_region_length() > 0, + "Should only be called when there are optional regions"); + + double total_prediction_ms = 0.0; + + for (G1HeapRegion* r : _optional_old_regions) { + double prediction_ms = _policy->predict_region_total_time_ms(r, false); + + if (prediction_ms > time_remaining_ms) { + log_debug(gc, ergo, cset)("Prediction %.3fms for region %u does not fit remaining time: %.3fms.", + prediction_ms, r->hrm_index(), time_remaining_ms); + break; + } + // This region will be included in the next optional evacuation. + + total_prediction_ms += prediction_ms; + time_remaining_ms -= prediction_ms; + + selected_regions->append(r); + } + + log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", + selected_regions->length(), _optional_old_regions.length(), total_prediction_ms); +} + void G1CollectionSet::prepare_optional_regions(G1CollectionCandidateRegionList* regions){ uint cur_index = 0; for (G1HeapRegion* r : *regions) { @@ -441,9 +646,8 @@ bool G1CollectionSet::finalize_optional_for_evacuation(double remaining_pause_ti update_incremental_marker(); G1CollectionCandidateRegionList selected_regions; - _policy->calculate_optional_collection_set_regions(&_optional_old_regions, - remaining_pause_time, - &selected_regions); + select_candidates_from_optional_regions(remaining_pause_time, + &selected_regions); move_candidates_to_collection_set(&selected_regions); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index e569d3ee966c3..5280ba7d0fd6c 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -196,6 +196,22 @@ class G1CollectionSet { // and retained collection set candidates. void finalize_old_part(double time_remaining_ms); + // Calculate and fill in the initial, optional and pinned old gen candidate regions from + // the given candidate list and the remaining time. + // Returns the remaining time. + double select_candidates_from_marking(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions); + + void select_candidates_from_retained(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions); + + // Calculate the number of optional regions from the given collection set candidates, + // the remaining time and the maximum number of these regions. + void select_candidates_from_optional_regions(double time_remaining_ms, + G1CollectionCandidateRegionList* selected); + // Iterate the part of the collection set given by the offset and length applying the given // G1HeapRegionClosure. The worker_id will determine where in the part to start the iteration // to allow for more efficient parallel iteration. diff --git a/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp b/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp index d7b1a6da92c17..577a8552091f6 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionPrinter.hpp @@ -35,8 +35,8 @@ class G1HeapRegionPrinter : public AllStatic { // Print an action event. static void print(const char* action, G1HeapRegion* hr) { - log_trace(gc, region)("G1HR %s(%s) [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT "]", - action, hr->get_type_str(), p2i(hr->bottom()), p2i(hr->top()), p2i(hr->end())); + log_trace(gc, region)("G1HR %4u %s(%s) [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT "]", + hr->hrm_index(), action, hr->get_type_str(), p2i(hr->bottom()), p2i(hr->top()), p2i(hr->end())); } public: diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp b/src/hotspot/share/gc/g1/g1MonotonicArena.cpp index 81748d277cff9..b2706d7a9463c 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.cpp @@ -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 @@ -29,22 +29,22 @@ #include "runtime/vmOperations.hpp" #include "utilities/globalCounter.inline.hpp" -G1MonotonicArena::Segment::Segment(uint slot_size, uint num_slots, Segment* next, MEMFLAGS flag) : +G1MonotonicArena::Segment::Segment(uint slot_size, uint num_slots, Segment* next, MemTag mem_tag) : _slot_size(slot_size), _num_slots(num_slots), _next(next), _next_allocate(0), - _mem_flag(flag) { + _mem_tag(mem_tag) { _bottom = ((char*) this) + header_size(); } G1MonotonicArena::Segment* G1MonotonicArena::Segment::create_segment(uint slot_size, uint num_slots, Segment* next, - MEMFLAGS mem_flag) { + MemTag mem_tag) { size_t block_size = size_in_bytes(slot_size, num_slots); - char* alloc_block = NEW_C_HEAP_ARRAY(char, block_size, mem_flag); - return new (alloc_block) Segment(slot_size, num_slots, next, mem_flag); + char* alloc_block = NEW_C_HEAP_ARRAY(char, block_size, mem_tag); + return new (alloc_block) Segment(slot_size, num_slots, next, mem_tag); } void G1MonotonicArena::Segment::delete_segment(Segment* segment) { @@ -54,7 +54,7 @@ void G1MonotonicArena::Segment::delete_segment(Segment* segment) { GlobalCounter::write_synchronize(); } segment->~Segment(); - FREE_C_HEAP_ARRAY(_mem_flag, segment); + FREE_C_HEAP_ARRAY(_mem_tag, segment); } void G1MonotonicArena::SegmentFreeList::bulk_add(Segment& first, @@ -108,7 +108,7 @@ G1MonotonicArena::Segment* G1MonotonicArena::new_segment(Segment* const prev) { uint prev_num_slots = (prev != nullptr) ? prev->num_slots() : 0; uint num_slots = _alloc_options->next_num_slots(prev_num_slots); - next = Segment::create_segment(slot_size(), num_slots, prev, _alloc_options->mem_flag()); + next = Segment::create_segment(slot_size(), num_slots, prev, _alloc_options->mem_tag()); } else { assert(slot_size() == next->slot_size() , "Mismatch %d != %d", slot_size(), next->slot_size()); diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp b/src/hotspot/share/gc/g1/g1MonotonicArena.hpp index bf46e4a33513a..b51f3e37db180 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.hpp @@ -27,7 +27,7 @@ #define SHARE_GC_G1_G1MONOTONICARENA_HPP #include "gc/shared/freeListAllocator.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/lockFreeStack.hpp" @@ -120,7 +120,7 @@ class G1MonotonicArena::Segment { // to _num_slots (can be larger because we atomically increment this value and // check only afterwards if the allocation has been successful). uint volatile _next_allocate; - const MEMFLAGS _mem_flag; + const MemTag _mem_tag; char* _bottom; // Actual data. // Do not add class member variables beyond this point @@ -136,7 +136,7 @@ class G1MonotonicArena::Segment { NONCOPYABLE(Segment); - Segment(uint slot_size, uint num_slots, Segment* next, MEMFLAGS flag); + Segment(uint slot_size, uint num_slots, Segment* next, MemTag mem_tag); ~Segment() = default; public: Segment* volatile* next_addr() { return &_next; } @@ -173,7 +173,7 @@ class G1MonotonicArena::Segment { return header_size() + payload_size(slot_size, num_slots); } - static Segment* create_segment(uint slot_size, uint num_slots, Segment* next, MEMFLAGS mem_flag); + static Segment* create_segment(uint slot_size, uint num_slots, Segment* next, MemTag mem_tag); static void delete_segment(Segment* segment); // Copies the contents of this segment into the destination. @@ -222,7 +222,7 @@ class G1MonotonicArena::SegmentFreeList { class G1MonotonicArena::AllocOptions { protected: - const MEMFLAGS _mem_flag; + const MemTag _mem_tag; const uint _slot_size; const uint _initial_num_slots; // Defines a limit to the number of slots in the segment @@ -230,8 +230,8 @@ class G1MonotonicArena::AllocOptions { const uint _slot_alignment; public: - AllocOptions(MEMFLAGS mem_flag, uint slot_size, uint initial_num_slots, uint max_num_slots, uint alignment) : - _mem_flag(mem_flag), + AllocOptions(MemTag mem_tag, uint slot_size, uint initial_num_slots, uint max_num_slots, uint alignment) : + _mem_tag(mem_tag), _slot_size(align_up(slot_size, alignment)), _initial_num_slots(initial_num_slots), _max_num_slots(max_num_slots), @@ -250,7 +250,7 @@ class G1MonotonicArena::AllocOptions { uint slot_alignment() const { return _slot_alignment; } - MEMFLAGS mem_flag() const {return _mem_flag; } + MemTag mem_tag() const {return _mem_tag; } }; #endif //SHARE_GC_G1_MONOTONICARENA_HPP diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 1cfd6fca08a6f..3f7fefd8a07a6 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -87,8 +87,6 @@ class G1ParScanThreadState : public CHeapObj { // Indicates whether in the last generation (old) there is no more space // available for allocation. bool _old_gen_is_full; - // Size (in elements) of a partial objArray task chunk. - size_t _partial_objarray_chunk_size; PartialArrayStateAllocator* _partial_array_state_allocator; PartialArrayTaskStepper _partial_array_stepper; StringDedup::Requests _string_dedup_requests; diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index e7e57c962c734..1b71901f0fe05 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -53,7 +53,7 @@ #include "gc/shared/gcTraceTime.inline.hpp" G1Policy::G1Policy(STWGCTimer* gc_timer) : - _predictor(G1ConfidencePercent / 100.0), + _predictor((100 - G1ConfidencePercent) / 100.0), _analytics(new G1Analytics(&_predictor)), _remset_tracker(), _mmu_tracker(new G1MMUTracker(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)), @@ -1467,219 +1467,6 @@ uint G1Policy::calc_max_old_cset_length() const { return (uint)ceil(result); } -static void print_finish_message(const char* reason, bool from_marking) { - log_debug(gc, ergo, cset)("Finish adding %s candidates to collection set (%s).", - from_marking ? "marking" : "retained", reason); -} - -double G1Policy::select_candidates_from_marking(G1CollectionCandidateList* marking_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions) { - assert(marking_list != nullptr, "must be"); - - uint num_expensive_regions = 0; - - uint num_initial_regions_selected = 0; - uint num_optional_regions_selected = 0; - uint num_pinned_regions = 0; - - double predicted_initial_time_ms = 0.0; - double predicted_optional_time_ms = 0.0; - - double optional_threshold_ms = time_remaining_ms * optional_prediction_fraction(); - - const uint min_old_cset_length = calc_min_old_cset_length(candidates()->last_marking_candidates_length()); - const uint max_old_cset_length = MAX2(min_old_cset_length, calc_max_old_cset_length()); - const uint max_optional_regions = max_old_cset_length - min_old_cset_length; - bool check_time_remaining = use_adaptive_young_list_length(); - - log_debug(gc, ergo, cset)("Start adding marking candidates to collection set. " - "Min %u regions, max %u regions, available %u regions" - "time remaining %1.2fms, optional threshold %1.2fms", - min_old_cset_length, max_old_cset_length, marking_list->length(), time_remaining_ms, optional_threshold_ms); - - G1CollectionCandidateListIterator iter = marking_list->begin(); - for (; iter != marking_list->end(); ++iter) { - if (num_initial_regions_selected + num_optional_regions_selected >= max_old_cset_length) { - // Added maximum number of old regions to the CSet. - print_finish_message("Maximum number of regions reached", true); - break; - } - G1HeapRegion* hr = (*iter)->_r; - // Skip evacuating pinned marking regions because we are not getting any free - // space from them (and we expect to get free space from marking candidates). - // Also prepare to move them to retained regions to be evacuated optionally later - // to not impact the mixed phase too much. - if (hr->has_pinned_objects()) { - num_pinned_regions++; - (*iter)->update_num_unreclaimed(); - log_trace(gc, ergo, cset)("Marking candidate %u can not be reclaimed currently. Skipping.", hr->hrm_index()); - pinned_old_regions->append(hr); - continue; - } - double predicted_time_ms = predict_region_total_time_ms(hr, false); - time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0); - // Add regions to old set until we reach the minimum amount - if (initial_old_regions->length() < min_old_cset_length) { - initial_old_regions->append(hr); - num_initial_regions_selected++; - predicted_initial_time_ms += predicted_time_ms; - // Record the number of regions added with no time remaining - if (time_remaining_ms == 0.0) { - num_expensive_regions++; - } - } else if (!check_time_remaining) { - // In the non-auto-tuning case, we'll finish adding regions - // to the CSet if we reach the minimum. - print_finish_message("Region amount reached min", true); - break; - } else { - // Keep adding regions to old set until we reach the optional threshold - if (time_remaining_ms > optional_threshold_ms) { - predicted_initial_time_ms += predicted_time_ms; - initial_old_regions->append(hr); - num_initial_regions_selected++; - } else if (time_remaining_ms > 0) { - // Keep adding optional regions until time is up. - assert(optional_old_regions->length() < max_optional_regions, "Should not be possible."); - predicted_optional_time_ms += predicted_time_ms; - optional_old_regions->append(hr); - num_optional_regions_selected++; - } else { - print_finish_message("Predicted time too high", true); - break; - } - } - } - if (iter == marking_list->end()) { - log_debug(gc, ergo, cset)("Marking candidates exhausted."); - } - - if (num_expensive_regions > 0) { - log_debug(gc, ergo, cset)("Added %u marking candidates to collection set although the predicted time was too high.", - num_expensive_regions); - } - - log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u, optional: %u, pinned: %u, " - "predicted initial time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, - predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms); - - assert(initial_old_regions->length() == num_initial_regions_selected, "must be"); - assert(optional_old_regions->length() == num_optional_regions_selected, "must be"); - return time_remaining_ms; -} - -void G1Policy::select_candidates_from_retained(G1CollectionCandidateList* retained_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions) { - - uint const min_regions = min_retained_old_cset_length(); - - uint num_initial_regions_selected = 0; - uint num_optional_regions_selected = 0; - uint num_expensive_regions_selected = 0; - uint num_pinned_regions = 0; - - double predicted_initial_time_ms = 0.0; - double predicted_optional_time_ms = 0.0; - - // We want to make sure that on the one hand we process the retained regions asap, - // but on the other hand do not take too many of them as optional regions. - // So we split the time budget into budget we will unconditionally take into the - // initial old regions, and budget for taking optional regions from the retained - // list. - double optional_time_remaining_ms = max_time_for_retaining(); - time_remaining_ms = MIN2(time_remaining_ms, optional_time_remaining_ms); - - log_debug(gc, ergo, cset)("Start adding retained candidates to collection set. " - "Min %u regions, available %u, " - "time remaining %1.2fms, optional remaining %1.2fms", - min_regions, retained_list->length(), time_remaining_ms, optional_time_remaining_ms); - - for (G1CollectionSetCandidateInfo* ci : *retained_list) { - G1HeapRegion* r = ci->_r; - double predicted_time_ms = predict_region_total_time_ms(r, collector_state()->in_young_only_phase()); - bool fits_in_remaining_time = predicted_time_ms <= time_remaining_ms; - // If we can't reclaim that region ignore it for now. - if (r->has_pinned_objects()) { - num_pinned_regions++; - if (ci->update_num_unreclaimed()) { - log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Skipping.", r->hrm_index()); - } else { - log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Dropping.", r->hrm_index()); - pinned_old_regions->append(r); - } - continue; - } - - if (fits_in_remaining_time || (num_expensive_regions_selected < min_regions)) { - predicted_initial_time_ms += predicted_time_ms; - if (!fits_in_remaining_time) { - num_expensive_regions_selected++; - } - initial_old_regions->append(r); - num_initial_regions_selected++; - } else if (predicted_time_ms <= optional_time_remaining_ms) { - predicted_optional_time_ms += predicted_time_ms; - optional_old_regions->append(r); - num_optional_regions_selected++; - } else { - // Fits neither initial nor optional time limit. Exit. - break; - } - time_remaining_ms = MAX2(0.0, time_remaining_ms - predicted_time_ms); - optional_time_remaining_ms = MAX2(0.0, optional_time_remaining_ms - predicted_time_ms); - } - - uint num_regions_selected = num_initial_regions_selected + num_optional_regions_selected; - if (num_regions_selected == retained_list->length()) { - log_debug(gc, ergo, cset)("Retained candidates exhausted."); - } - if (num_expensive_regions_selected > 0) { - log_debug(gc, ergo, cset)("Added %u retained candidates to collection set although the predicted time was too high.", - num_expensive_regions_selected); - } - - log_debug(gc, ergo, cset)("Finish adding retained candidates to collection set. Initial: %u, optional: %u, pinned: %u, " - "predicted initial time: %1.2fms, predicted optional time: %1.2fms, " - "time remaining: %1.2fms optional time remaining %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, - predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms, optional_time_remaining_ms); -} - -void G1Policy::calculate_optional_collection_set_regions(G1CollectionCandidateRegionList* optional_regions, - double time_remaining_ms, - G1CollectionCandidateRegionList* selected_regions) { - assert(_collection_set->optional_region_length() > 0, - "Should only be called when there are optional regions"); - - double total_prediction_ms = 0.0; - - for (G1HeapRegion* r : *optional_regions) { - double prediction_ms = predict_region_total_time_ms(r, false); - - if (prediction_ms > time_remaining_ms) { - log_debug(gc, ergo, cset)("Prediction %.3fms for region %u does not fit remaining time: %.3fms.", - prediction_ms, r->hrm_index(), time_remaining_ms); - break; - } - // This region will be included in the next optional evacuation. - - total_prediction_ms += prediction_ms; - time_remaining_ms -= prediction_ms; - - selected_regions->append(r); - } - - log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", - selected_regions->length(), optional_regions->length(), total_prediction_ms); -} - void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { start_adding_survivor_regions(); diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 98d444084678c..9a6ffb570be70 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -335,27 +335,7 @@ class G1Policy: public CHeapObj { // Amount of allowed waste in bytes in the collection set. size_t allowed_waste_in_collection_set() const; - // Calculate and fill in the initial, optional and pinned old gen candidate regions from - // the given candidate list and the remaining time. - // Returns the remaining time. - double select_candidates_from_marking(G1CollectionCandidateList* marking_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions); - - void select_candidates_from_retained(G1CollectionCandidateList* retained_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions); - - // Calculate the number of optional regions from the given collection set candidates, - // the remaining time and the maximum number of these regions and return the number - // of actually selected regions in num_optional_regions. - void calculate_optional_collection_set_regions(G1CollectionCandidateRegionList* optional_old_regions, - double time_remaining_ms, - G1CollectionCandidateRegionList* selected); + private: @@ -423,12 +403,12 @@ class G1Policy: public CHeapObj { size_t desired_survivor_size(uint max_regions) const; +public: // Fraction used when predicting how many optional regions to include in // the CSet. This fraction of the available time is used for optional regions, // the rest is used to add old regions to the normal CSet. double optional_prediction_fraction() const { return 0.2; } -public: // Fraction used when evacuating the optional regions. This fraction of the // remaining time is used to choose what regions to include in the evacuation. double optional_evacuation_fraction() const { return 0.75; } diff --git a/src/hotspot/share/gc/g1/g1Predictions.hpp b/src/hotspot/share/gc/g1/g1Predictions.hpp index 510f296a9f3ac..ae2a8f418802a 100644 --- a/src/hotspot/share/gc/g1/g1Predictions.hpp +++ b/src/hotspot/share/gc/g1/g1Predictions.hpp @@ -29,8 +29,9 @@ // Utility class containing various helper methods for prediction. class G1Predictions { - private: - double _sigma; +private: + // Scale factor indicating to which degree stddev should be taking into account in predictions. + double _stddev_scale; // This function is used to estimate the stddev of sample sets. There is some // special consideration of small sample sets: the actual stddev for them is @@ -46,16 +47,14 @@ class G1Predictions { } return estimate; } - public: - G1Predictions(double sigma) : _sigma(sigma) { - assert(sigma >= 0.0, "Confidence must be larger than or equal to zero"); - } - // Confidence factor. - double sigma() const { return _sigma; } +public: + G1Predictions(double stddev_scale) : _stddev_scale(stddev_scale) { + assert(stddev_scale >= 0.0, "must be"); + } double predict(TruncatedSeq const* seq) const { - return seq->davg() + _sigma * stddev_estimate(seq); + return seq->davg() + _stddev_scale * stddev_estimate(seq); } double predict_in_unit_interval(TruncatedSeq const* seq) const { diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp index 5f903960cce8c..4403b4c8dd981 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, 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 @@ -40,15 +40,15 @@ G1RegionToSpaceMapper::G1RegionToSpaceMapper(ReservedSpace rs, size_t page_size, size_t region_granularity, size_t commit_factor, - MEMFLAGS type) : + MemTag mem_tag) : _listener(nullptr), _storage(rs, used_size, page_size), _region_commit_map(rs.size() * commit_factor / region_granularity, mtGC), - _memory_type(type) { + _memory_tag(mem_tag) { guarantee(is_power_of_2(page_size), "must be"); guarantee(is_power_of_2(region_granularity), "must be"); - MemTracker::record_virtual_memory_type((address)rs.base(), type); + MemTracker::record_virtual_memory_tag((address)rs.base(), mem_tag); } // Used to manually signal a mapper to handle a set of regions as committed. @@ -72,8 +72,8 @@ class G1RegionsLargerThanCommitSizeMapper : public G1RegionToSpaceMapper { size_t page_size, size_t alloc_granularity, size_t commit_factor, - MEMFLAGS type) : - G1RegionToSpaceMapper(rs, actual_size, page_size, alloc_granularity, commit_factor, type), + MemTag mem_tag) : + G1RegionToSpaceMapper(rs, actual_size, page_size, alloc_granularity, commit_factor, mem_tag), _pages_per_region(alloc_granularity / (page_size * commit_factor)) { guarantee(alloc_granularity >= page_size, "allocation granularity smaller than commit granularity"); @@ -97,7 +97,7 @@ class G1RegionsLargerThanCommitSizeMapper : public G1RegionToSpaceMapper { const size_t start_page = (size_t)start_idx * _pages_per_region; const size_t size_in_pages = num_regions * _pages_per_region; bool zero_filled = _storage.commit(start_page, size_in_pages); - if (_memory_type == mtJavaHeap) { + if (_memory_tag == mtJavaHeap) { for (uint region_index = start_idx; region_index < start_idx + num_regions; region_index++ ) { void* address = _storage.page_start(region_index * _pages_per_region); size_t size_in_bytes = _storage.page_size() * _pages_per_region; @@ -150,7 +150,7 @@ class G1RegionsSmallerThanCommitSizeMapper : public G1RegionToSpaceMapper { } void numa_request_on_node(size_t page_idx) { - if (_memory_type == mtJavaHeap) { + if (_memory_tag == mtJavaHeap) { uint region = (uint)(page_idx * _regions_per_page); void* address = _storage.page_start(page_idx); size_t size_in_bytes = _storage.page_size(); @@ -164,8 +164,8 @@ class G1RegionsSmallerThanCommitSizeMapper : public G1RegionToSpaceMapper { size_t page_size, size_t alloc_granularity, size_t commit_factor, - MEMFLAGS type) : - G1RegionToSpaceMapper(rs, actual_size, page_size, alloc_granularity, commit_factor, type), + MemTag mem_tag) : + G1RegionToSpaceMapper(rs, actual_size, page_size, alloc_granularity, commit_factor, mem_tag), _regions_per_page((page_size * commit_factor) / alloc_granularity), _lock(Mutex::service-3, "G1Mapper_lock") { @@ -263,10 +263,10 @@ G1RegionToSpaceMapper* G1RegionToSpaceMapper::create_mapper(ReservedSpace rs, size_t page_size, size_t region_granularity, size_t commit_factor, - MEMFLAGS type) { + MemTag mem_tag) { if (region_granularity >= (page_size * commit_factor)) { - return new G1RegionsLargerThanCommitSizeMapper(rs, actual_size, page_size, region_granularity, commit_factor, type); + return new G1RegionsLargerThanCommitSizeMapper(rs, actual_size, page_size, region_granularity, commit_factor, mem_tag); } else { - return new G1RegionsSmallerThanCommitSizeMapper(rs, actual_size, page_size, region_granularity, commit_factor, type); + return new G1RegionsSmallerThanCommitSizeMapper(rs, actual_size, page_size, region_granularity, commit_factor, mem_tag); } } diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp index 02498b394b39f..5ef0f8ec5ab51 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp @@ -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 @@ -52,9 +52,9 @@ class G1RegionToSpaceMapper : public CHeapObj { // Mapping management CHeapBitMap _region_commit_map; - MEMFLAGS _memory_type; + MemTag _memory_tag; - G1RegionToSpaceMapper(ReservedSpace rs, size_t used_size, size_t page_size, size_t region_granularity, size_t commit_factor, MEMFLAGS type); + G1RegionToSpaceMapper(ReservedSpace rs, size_t used_size, size_t page_size, size_t region_granularity, size_t commit_factor, MemTag mem_tag); void fire_on_commit(uint start_idx, size_t num_regions, bool zero_filled); public: @@ -85,7 +85,7 @@ class G1RegionToSpaceMapper : public CHeapObj { size_t page_size, size_t region_granularity, size_t byte_translation_factor, - MEMFLAGS type); + MemTag mem_tag); }; #endif // SHARE_GC_G1_G1REGIONTOSPACEMAPPER_HPP diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index f5f65cf1c48aa..bb5ac5036fe47 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -967,6 +967,10 @@ class G1MergeHeapRootsTask : public WorkerTask { _merged[G1GCPhaseTimes::MergeRSCards] += increment; } + void dec_remset_cards(size_t decrement) { + _merged[G1GCPhaseTimes::MergeRSCards] -= decrement; + } + size_t merged(uint i) const { return _merged[i]; } }; @@ -1091,6 +1095,11 @@ class G1MergeHeapRootsTask : public WorkerTask { G1MergeCardSetStats stats() { _merge_card_set_cache.flush(); + // Compensation for the dummy cards that were initially pushed into the + // card cache. + // We do not need to compensate for the other counters because the dummy + // card mark will never update another counter because it is initally "dirty". + _stats.dec_remset_cards(G1MergeCardSetCache::CacheSize); return _stats; } }; diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index c8016ddc0ddf5..ed02ba2dc5cad 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -111,7 +111,8 @@ range(1, max_intx) \ \ product(uint, G1ConfidencePercent, 50, \ - "Confidence level for MMU/pause predictions") \ + "Confidence level for MMU/pause predictions. A higher value " \ + "means that G1 will use less safety margin for its predictions.") \ range(1, 100) \ \ product(uintx, G1SummarizeRSetStatsPeriod, 0, DIAGNOSTIC, \ diff --git a/src/hotspot/share/gc/parallel/objectStartArray.cpp b/src/hotspot/share/gc/parallel/objectStartArray.cpp index b1fc956a54a21..ef9de7abfd771 100644 --- a/src/hotspot/share/gc/parallel/objectStartArray.cpp +++ b/src/hotspot/share/gc/parallel/objectStartArray.cpp @@ -51,7 +51,7 @@ void ObjectStartArray::initialize(MemRegion reserved_region) { if (!backing_store.is_reserved()) { vm_exit_during_initialization("Could not reserve space for ObjectStartArray"); } - MemTracker::record_virtual_memory_type(backing_store.base(), mtGC); + MemTracker::record_virtual_memory_tag(backing_store.base(), mtGC); // We do not commit any memory initially _virtual_space.initialize(backing_store); diff --git a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp index 658c3ef106fa0..46a178500e576 100644 --- a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp +++ b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp @@ -51,7 +51,7 @@ ParMarkBitMap::initialize(MemRegion covered_region) os::trace_page_sizes("Mark Bitmap", raw_bytes, raw_bytes, rs.base(), rs.size(), used_page_sz); - MemTracker::record_virtual_memory_type((address)rs.base(), mtGC); + MemTracker::record_virtual_memory_tag((address)rs.base(), mtGC); _virtual_space = new PSVirtualSpace(rs, page_sz); if (_virtual_space != nullptr && _virtual_space->expand_by(_reserved_byte_size)) { diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index 4bff8f8a7d06a..1ab7b2af7ed74 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -235,7 +235,7 @@ ParallelCompactData::create_vspace(size_t count, size_t element_size) os::trace_page_sizes("Parallel Compact Data", raw_bytes, raw_bytes, rs.base(), rs.size(), page_sz); - MemTracker::record_virtual_memory_type((address)rs.base(), mtGC); + MemTracker::record_virtual_memory_tag((address)rs.base(), mtGC); PSVirtualSpace* vspace = new PSVirtualSpace(rs, page_sz); if (vspace != nullptr) { diff --git a/src/hotspot/share/gc/parallel/psScavenge.hpp b/src/hotspot/share/gc/parallel/psScavenge.hpp index 99d0487760b15..55abdfd3cf38e 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.hpp +++ b/src/hotspot/share/gc/parallel/psScavenge.hpp @@ -34,9 +34,7 @@ #include "oops/oop.hpp" #include "utilities/stack.hpp" -class ReferenceProcessor; class ParallelScavengeHeap; -class ParallelScavengeTracer; class PSIsAliveClosure; class STWGCTimer; diff --git a/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp b/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp index 59b7f130df30b..31f18652c63d6 100644 --- a/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp +++ b/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp @@ -42,7 +42,7 @@ SerialBlockOffsetTable::SerialBlockOffsetTable(MemRegion reserved, vm_exit_during_initialization("Could not reserve enough space for heap offset array"); } - MemTracker::record_virtual_memory_type((address)rs.base(), mtGC); + MemTracker::record_virtual_memory_tag((address)rs.base(), mtGC); if (!_vs.initialize(rs, 0)) { vm_exit_during_initialization("Could not reserve enough space for heap offset array"); diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index 59e0245204441..643a7936b9b17 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -109,6 +109,10 @@ Label* BarrierStubC2::continuation() { return &_continuation; } +uint8_t BarrierStubC2::barrier_data() const { + return _node->barrier_data(); +} + void BarrierStubC2::preserve(Register r) { const VMReg vm_reg = r->as_VMReg(); assert(vm_reg->is_Register(), "r must be a general-purpose register"); diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp index c1485c069c83c..00fbf1f2c9f8b 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp @@ -254,6 +254,8 @@ class BarrierStubC2 : public ArenaObj { Label* entry(); // Return point from the stub (typically end of barrier). Label* continuation(); + // High-level, GC-specific barrier flags. + uint8_t barrier_data() const; // Preserve the value in reg across runtime calls in this barrier. void preserve(Register reg); @@ -340,6 +342,8 @@ class BarrierSetC2: public CHeapObj { // Estimated size of the node barrier in number of C2 Ideal nodes. // This is used to guide heuristics in C2, e.g. whether to unroll a loop. virtual uint estimated_barrier_size(const Node* node) const { return 0; } + // Whether the given store can be used to initialize a newly allocated object. + virtual bool can_initialize_object(const StoreNode* store) const { return true; } enum CompilePhase { BeforeOptimize, diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp index 87bb9f3cd5170..11b742156a831 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp @@ -125,39 +125,10 @@ void CardTableBarrierSetC2::post_barrier(GraphKit* kit, kit->final_sync(ideal); } -void CardTableBarrierSetC2::clone(GraphKit* kit, Node* src, Node* dst, Node* size, bool is_array) const { - BarrierSetC2::clone(kit, src, dst, size, is_array); - const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM; - - // If necessary, emit some card marks afterwards. (Non-arrays only.) - bool card_mark = !is_array && !use_ReduceInitialCardMarks(); - if (card_mark) { - assert(!is_array, ""); - // Put in store barrier for any and all oops we are sticking - // into this object. (We could avoid this if we could prove - // that the object type contains no oop fields at all.) - Node* no_particular_value = nullptr; - Node* no_particular_field = nullptr; - int raw_adr_idx = Compile::AliasIdxRaw; - post_barrier(kit, kit->control(), - kit->memory(raw_adr_type), - dst, - no_particular_field, - raw_adr_idx, - no_particular_value, - T_OBJECT, - false); - } -} - bool CardTableBarrierSetC2::use_ReduceInitialCardMarks() const { return ReduceInitialCardMarks; } -bool CardTableBarrierSetC2::is_gc_barrier_node(Node* node) const { - return ModRefBarrierSetC2::is_gc_barrier_node(node) || node->Opcode() == Op_StoreCM; -} - void CardTableBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { assert(node->Opcode() == Op_CastP2X, "ConvP2XNode required"); Node *shift = node->unique_out(); diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp index 9512f09ff8a6d..3bbf14892d3ef 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.hpp @@ -42,8 +42,6 @@ class CardTableBarrierSetC2: public ModRefBarrierSetC2 { Node* byte_map_base_node(GraphKit* kit) const; public: - virtual void clone(GraphKit* kit, Node* src, Node* dst, Node* size, bool is_array) const; - virtual bool is_gc_barrier_node(Node* node) const; virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const; virtual bool array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, bool is_clone_instance, ArrayCopyPhase phase) const; diff --git a/src/hotspot/share/gc/shared/cardTable.cpp b/src/hotspot/share/gc/shared/cardTable.cpp index 95f7058f4e4ee..acd4bda6e1071 100644 --- a/src/hotspot/share/gc/shared/cardTable.cpp +++ b/src/hotspot/share/gc/shared/cardTable.cpp @@ -84,7 +84,7 @@ void CardTable::initialize(void* region0_start, void* region1_start) { MAX2(_page_size, os::vm_allocation_granularity()); ReservedSpace heap_rs(_byte_map_size, rs_align, _page_size); - MemTracker::record_virtual_memory_type((address)heap_rs.base(), mtGC); + MemTracker::record_virtual_memory_tag((address)heap_rs.base(), mtGC); os::trace_page_sizes("Card Table", num_bytes, num_bytes, heap_rs.base(), heap_rs.size(), _page_size); diff --git a/src/hotspot/share/gc/shared/gcArguments.cpp b/src/hotspot/share/gc/shared/gcArguments.cpp index 9736c0f7fdcab..2522925746be1 100644 --- a/src/hotspot/share/gc/shared/gcArguments.cpp +++ b/src/hotspot/share/gc/shared/gcArguments.cpp @@ -30,6 +30,7 @@ #include "runtime/arguments.hpp" #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" +#include "utilities/formatBuffer.hpp" #include "utilities/macros.hpp" size_t HeapAlignment = 0; @@ -166,6 +167,13 @@ void GCArguments::initialize_heap_flags_and_sizes() { FLAG_SET_ERGO(MinHeapDeltaBytes, align_up(MinHeapDeltaBytes, SpaceAlignment)); + if (checked_cast(ObjectAlignmentInBytes) > GCCardSizeInBytes) { + err_msg message("ObjectAlignmentInBytes %u is larger than GCCardSizeInBytes %u", + ObjectAlignmentInBytes, GCCardSizeInBytes); + vm_exit_during_initialization("Invalid combination of GCCardSizeInBytes and ObjectAlignmentInBytes", + message); + } + DEBUG_ONLY(assert_flags();) } diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index 7117b86b26403..2373d6b1d93a8 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -127,10 +127,10 @@ OopStorage::ActiveArray::~ActiveArray() { } OopStorage::ActiveArray* OopStorage::ActiveArray::create(size_t size, - MEMFLAGS memflags, + MemTag mem_tag, AllocFailType alloc_fail) { size_t size_in_bytes = blocks_offset() + sizeof(Block*) * size; - void* mem = NEW_C_HEAP_ARRAY3(char, size_in_bytes, memflags, CURRENT_PC, alloc_fail); + void* mem = NEW_C_HEAP_ARRAY3(char, size_in_bytes, mem_tag, CURRENT_PC, alloc_fail); if (mem == nullptr) return nullptr; return new (mem) ActiveArray(size); } @@ -300,7 +300,12 @@ void OopStorage::Block::set_active_index(size_t index) { size_t OopStorage::Block::active_index_safe(const Block* block) { STATIC_ASSERT(sizeof(intptr_t) == sizeof(block->_active_index)); - return SafeFetchN((intptr_t*)&block->_active_index, 0); + // Be careful, because block could be a false positive from block_for_ptr. + assert(block != nullptr, "precondition"); + uintptr_t block_addr = reinterpret_cast(block); + uintptr_t index_loc = block_addr + offset_of(Block, _active_index); + static_assert(sizeof(size_t) == sizeof(intptr_t), "assumption"); + return static_cast(SafeFetchN(reinterpret_cast(index_loc), 0)); } unsigned OopStorage::Block::get_index(const oop* ptr) const { @@ -343,7 +348,7 @@ OopStorage::Block* OopStorage::Block::new_block(const OopStorage* owner) { // _data must be first member: aligning block => aligning _data. STATIC_ASSERT(_data_pos == 0); size_t size_needed = allocation_size(); - void* memory = NEW_C_HEAP_ARRAY_RETURN_NULL(char, size_needed, owner->memflags()); + void* memory = NEW_C_HEAP_ARRAY_RETURN_NULL(char, size_needed, owner->mem_tag()); if (memory == nullptr) { return nullptr; } @@ -366,21 +371,23 @@ void OopStorage::Block::delete_block(const Block& block) { OopStorage::Block* OopStorage::Block::block_for_ptr(const OopStorage* owner, const oop* ptr) { STATIC_ASSERT(_data_pos == 0); - // Const-ness of ptr is not related to const-ness of containing block. + assert(ptr != nullptr, "precondition"); // Blocks are allocated section-aligned, so get the containing section. - oop* section_start = align_down(const_cast(ptr), block_alignment); + uintptr_t section_start = align_down(reinterpret_cast(ptr), block_alignment); // Start with a guess that the containing section is the last section, // so the block starts section_count-1 sections earlier. - oop* section = section_start - (section_size * (section_count - 1)); + size_t section_size_in_bytes = sizeof(oop) * section_size; + uintptr_t section = section_start - (section_size_in_bytes * (section_count - 1)); // Walk up through the potential block start positions, looking for // the owner in the expected location. If we're below the actual block // start position, the value at the owner position will be some oop // (possibly null), which can never match the owner. intptr_t owner_addr = reinterpret_cast(owner); - for (unsigned i = 0; i < section_count; ++i, section += section_size) { - Block* candidate = reinterpret_cast(section); - if (SafeFetchN(&candidate->_owner_address, 0) == owner_addr) { - return candidate; + for (unsigned i = 0; i < section_count; ++i, section += section_size_in_bytes) { + uintptr_t owner_loc = section + offset_of(Block, _owner_address); + static_assert(sizeof(OopStorage*) == sizeof(intptr_t), "assumption"); + if (SafeFetchN(reinterpret_cast(owner_loc), 0) == owner_addr) { + return reinterpret_cast(section); } } return nullptr; @@ -575,7 +582,7 @@ bool OopStorage::expand_active_array() { log_debug(oopstorage, blocks)("%s: expand active array " SIZE_FORMAT, name(), new_size); ActiveArray* new_array = ActiveArray::create(new_size, - memflags(), + mem_tag(), AllocFailStrategy::RETURN_NULL); if (new_array == nullptr) return false; new_array->copy_from(old_array); @@ -643,8 +650,7 @@ class OopStorage::WithActiveArray : public StackObj { } }; -OopStorage::Block* OopStorage::find_block_or_null(const oop* ptr) const { - assert(ptr != nullptr, "precondition"); +OopStorage::Block* OopStorage::block_for_ptr(const oop* ptr) const { return Block::block_for_ptr(this, ptr); } @@ -771,7 +777,7 @@ static inline void check_release_entry(const oop* entry) { void OopStorage::release(const oop* ptr) { check_release_entry(ptr); - Block* block = find_block_or_null(ptr); + Block* block = block_for_ptr(ptr); assert(block != nullptr, "%s: invalid release " PTR_FORMAT, name(), p2i(ptr)); log_trace(oopstorage, ref)("%s: releasing " PTR_FORMAT, name(), p2i(ptr)); block->release_entries(block->bitmask_for_entry(ptr), this); @@ -782,7 +788,7 @@ void OopStorage::release(const oop* const* ptrs, size_t size) { size_t i = 0; while (i < size) { check_release_entry(ptrs[i]); - Block* block = find_block_or_null(ptrs[i]); + Block* block = block_for_ptr(ptrs[i]); assert(block != nullptr, "%s: invalid release " PTR_FORMAT, name(), p2i(ptrs[i])); size_t count = 0; uintx releasing = 0; @@ -805,8 +811,8 @@ void OopStorage::release(const oop* const* ptrs, size_t size) { } } -OopStorage* OopStorage::create(const char* name, MEMFLAGS memflags) { - return new (memflags) OopStorage(name, memflags); +OopStorage* OopStorage::create(const char* name, MemTag mem_tag) { + return new (mem_tag) OopStorage(name, mem_tag); } const size_t initial_active_array_size = 8; @@ -819,9 +825,9 @@ static Mutex* make_oopstorage_mutex(const char* storage_name, return new PaddedMutex(rank, name); } -OopStorage::OopStorage(const char* name, MEMFLAGS memflags) : +OopStorage::OopStorage(const char* name, MemTag mem_tag) : _name(os::strdup(name)), - _active_array(ActiveArray::create(initial_active_array_size, memflags)), + _active_array(ActiveArray::create(initial_active_array_size, mem_tag)), _allocation_list(), _deferred_updates(nullptr), _allocation_mutex(make_oopstorage_mutex(name, "alloc", Mutex::oopstorage)), @@ -829,7 +835,7 @@ OopStorage::OopStorage(const char* name, MEMFLAGS memflags) : _num_dead_callback(nullptr), _allocation_count(0), _concurrent_iteration_count(0), - _memflags(memflags), + _mem_tag(mem_tag), _needs_cleanup(false) { _active_array->increment_refcount(); @@ -989,7 +995,8 @@ bool OopStorage::delete_empty_blocks() { } OopStorage::EntryStatus OopStorage::allocation_status(const oop* ptr) const { - const Block* block = find_block_or_null(ptr); + if (ptr == nullptr) return INVALID_ENTRY; + const Block* block = block_for_ptr(ptr); if (block != nullptr) { // Prevent block deletion and _active_array modification. MutexLocker ml(_allocation_mutex, Mutex::_no_safepoint_check_flag); @@ -1030,7 +1037,7 @@ size_t OopStorage::total_memory_usage() const { return total_size; } -MEMFLAGS OopStorage::memflags() const { return _memflags; } +MemTag OopStorage::mem_tag() const { return _mem_tag; } // Parallel iteration support @@ -1135,6 +1142,26 @@ void OopStorage::BasicParState::report_num_dead() const { const char* OopStorage::name() const { return _name; } +bool OopStorage::print_containing(const oop* addr, outputStream* st) { + if (addr != nullptr) { + Block* block = block_for_ptr(addr); + if (block != nullptr && block->print_containing(addr, st)) { + st->print(" in oop storage \"%s\"", name()); + return true; + } + } + return false; +} + +bool OopStorage::Block::print_containing(const oop* addr, outputStream* st) { + if (contains(addr)) { + st->print(PTR_FORMAT " is a pointer %u/%zu into block %zu", + p2i(addr), get_index(addr), ARRAY_SIZE(_data), _active_index); + return true; + } + return false; +} + #ifndef PRODUCT void OopStorage::print_on(outputStream* st) const { diff --git a/src/hotspot/share/gc/shared/oopStorage.hpp b/src/hotspot/share/gc/shared/oopStorage.hpp index dfc0f83fc1912..34c980a058659 100644 --- a/src/hotspot/share/gc/shared/oopStorage.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.hpp @@ -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 @@ -74,7 +74,7 @@ class outputStream; class OopStorage : public CHeapObjBase { public: - static OopStorage* create(const char* name, MEMFLAGS memflags); + static OopStorage* create(const char* name, MemTag mem_tag); ~OopStorage(); // These count and usage accessors are racy unless at a safepoint. @@ -89,8 +89,8 @@ class OopStorage : public CHeapObjBase { // bookkeeping overhead, including this storage object. size_t total_memory_usage() const; - // The memory type for allocations. - MEMFLAGS memflags() const; + // The memory tag for allocations. + MemTag mem_tag() const; enum EntryStatus { INVALID_ENTRY, @@ -213,6 +213,7 @@ class OopStorage : public CHeapObjBase { // Debugging and logging support. const char* name() const; void print_on(outputStream* st) const PRODUCT_RETURN; + bool print_containing(const oop* addr, outputStream* st); // Provides access to storage internals, for unit testing. // Declare, but not define, the public class OopStorage::TestAccess. @@ -273,21 +274,21 @@ class OopStorage : public CHeapObjBase { // mutable because this gets set even for const iteration. mutable int _concurrent_iteration_count; - // The memory type for allocations. - MEMFLAGS _memflags; + // The memory tag for allocations. + MemTag _mem_tag; // Flag indicating this storage object is a candidate for empty block deletion. volatile bool _needs_cleanup; // Clients construct via "create" factory function. - OopStorage(const char* name, MEMFLAGS memflags); + OopStorage(const char* name, MemTag mem_tag); NONCOPYABLE(OopStorage); bool try_add_block(); Block* block_for_allocation(); void log_block_transition(Block* block, const char* new_state) const; - Block* find_block_or_null(const oop* ptr) const; + Block* block_for_ptr(const oop* ptr) const; void delete_empty_block(const Block& block); bool reduce_deferred_updates(); void record_needs_cleanup(); diff --git a/src/hotspot/share/gc/shared/oopStorage.inline.hpp b/src/hotspot/share/gc/shared/oopStorage.inline.hpp index e1e815acd094e..da0926a20b6e2 100644 --- a/src/hotspot/share/gc/shared/oopStorage.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.inline.hpp @@ -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 @@ -60,7 +60,7 @@ class OopStorage::ActiveArray { public: static ActiveArray* create(size_t size, - MEMFLAGS memflags = mtGC, + MemTag mem_tag = mtGC, AllocFailType alloc_fail = AllocFailStrategy::EXIT_OOM); static void destroy(ActiveArray* ba); @@ -184,7 +184,10 @@ class OopStorage::Block /* No base class, to avoid messing up alignment. */ { void set_active_index(size_t index); static size_t active_index_safe(const Block* block); // Returns 0 if access fails. - // Returns null if ptr is not in a block or not allocated in that block. + // Return block of owner containing ptr, if ptr is a valid entry of owner. + // If ptr is not a valid entry of owner then returns either null or a "false + // positive" pointer; see allocation_status. + // precondition: ptr != nullptr static Block* block_for_ptr(const OopStorage* owner, const oop* ptr); oop* allocate(); @@ -196,6 +199,8 @@ class OopStorage::Block /* No base class, to avoid messing up alignment. */ { template bool iterate(F f); template bool iterate(F f) const; + + bool print_containing(const oop* addr, outputStream* st); }; // class Block inline OopStorage::Block* OopStorage::AllocationList::head() { diff --git a/src/hotspot/share/gc/shared/oopStorageSet.cpp b/src/hotspot/share/gc/shared/oopStorageSet.cpp index e119e570759a4..e3a9fccbad3dc 100644 --- a/src/hotspot/share/gc/shared/oopStorageSet.cpp +++ b/src/hotspot/share/gc/shared/oopStorageSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -31,18 +31,18 @@ OopStorage* OopStorageSet::_storages[all_count] = {}; -OopStorage* OopStorageSet::create_strong(const char* name, MEMFLAGS memflags) { +OopStorage* OopStorageSet::create_strong(const char* name, MemTag mem_tag) { static uint registered_strong = 0; assert(registered_strong < strong_count, "More registered strong storages than slots"); - OopStorage* storage = OopStorage::create(name, memflags); + OopStorage* storage = OopStorage::create(name, mem_tag); _storages[strong_start + registered_strong++] = storage; return storage; } -OopStorage* OopStorageSet::create_weak(const char* name, MEMFLAGS memflags) { +OopStorage* OopStorageSet::create_weak(const char* name, MemTag mem_tag) { static uint registered_weak = 0; assert(registered_weak < weak_count, "More registered strong storages than slots"); - OopStorage* storage = OopStorage::create(name, memflags); + OopStorage* storage = OopStorage::create(name, mem_tag); _storages[weak_start + registered_weak++] = storage; return storage; } @@ -82,6 +82,25 @@ template OopStorage* OopStorageSet::get_storage(StrongId); template OopStorage* OopStorageSet::get_storage(WeakId); template OopStorage* OopStorageSet::get_storage(Id); +bool OopStorageSet::print_containing(const void* addr, outputStream* st) { + if (addr != nullptr) { + const void* aligned_addr = align_down(addr, alignof(oop)); + for (OopStorage* storage : Range()) { + // Check for null for extra safety: might get here while handling error + // before storage initialization. + if ((storage != nullptr) && storage->print_containing((oop*) aligned_addr, st)) { + if (aligned_addr != addr) { + st->print_cr(" (unaligned)"); + } else { + st->cr(); + } + return true; + } + } + } + return false; +} + #ifdef ASSERT void OopStorageSet::verify_initialized(uint index) { diff --git a/src/hotspot/share/gc/shared/oopStorageSet.hpp b/src/hotspot/share/gc/shared/oopStorageSet.hpp index 26e0e9f5a7775..867172c41ad74 100644 --- a/src/hotspot/share/gc/shared/oopStorageSet.hpp +++ b/src/hotspot/share/gc/shared/oopStorageSet.hpp @@ -25,7 +25,8 @@ #ifndef SHARE_GC_SHARED_OOPSTORAGESET_HPP #define SHARE_GC_SHARED_OOPSTORAGESET_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" +#include "oops/oop.hpp" #include "utilities/debug.hpp" #include "utilities/enumIterator.hpp" #include "utilities/globalDefinitions.hpp" @@ -79,8 +80,8 @@ class OopStorageSet : public AllStatic { static OopStorage* storage(WeakId id) { return get_storage(id); } static OopStorage* storage(Id id) { return get_storage(id); } - static OopStorage* create_strong(const char* name, MEMFLAGS memflags); - static OopStorage* create_weak(const char* name, MEMFLAGS memflags); + static OopStorage* create_strong(const char* name, MemTag mem_tag); + static OopStorage* create_weak(const char* name, MemTag mem_tag); // Support iteration over the storage objects. template class Range; @@ -89,6 +90,8 @@ class OopStorageSet : public AllStatic { template static void strong_oops_do(Closure* cl); + // Debugging: print location info, if in storage. + static bool print_containing(const void* addr, outputStream* st); }; ENUMERATOR_VALUE_RANGE(OopStorageSet::StrongId, diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index fd23a32022208..48ef974ecfa53 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -26,7 +26,7 @@ #include "gc/shared/partialArrayState.hpp" #include "memory/allocation.inline.hpp" #include "memory/arena.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/atomic.hpp" #include "runtime/orderAccess.hpp" diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp index aab2f5d312399..ab85c293941df 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupProcessor.cpp @@ -35,7 +35,7 @@ #include "gc/shared/stringdedup/stringDedupTable.hpp" #include "logging/log.hpp" #include "memory/iterator.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "oops/access.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/cpuTimeCounters.hpp" diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp index f4a3731583bc9..efbc1882fbed8 100644 --- a/src/hotspot/share/gc/shared/taskqueue.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.hpp @@ -116,8 +116,8 @@ void TaskQueueStats::reset() { // TaskQueueSuper collects functionality common to all GenericTaskQueue instances. -template -class TaskQueueSuper: public CHeapObj { +template +class TaskQueueSuper: public CHeapObj { protected: // Internal type for indexing the queue; also used for the tag. typedef NOT_LP64(uint16_t) LP64_ONLY(uint32_t) idx_t; @@ -324,39 +324,39 @@ class TaskQueueSuper: public CHeapObj { // practice of parallel programming (PPoPP 2013), 69-80 // -template -class GenericTaskQueue: public TaskQueueSuper { +template +class GenericTaskQueue: public TaskQueueSuper { protected: - typedef typename TaskQueueSuper::Age Age; - typedef typename TaskQueueSuper::idx_t idx_t; + typedef typename TaskQueueSuper::Age Age; + typedef typename TaskQueueSuper::idx_t idx_t; - using TaskQueueSuper::MOD_N_MASK; + using TaskQueueSuper::MOD_N_MASK; - using TaskQueueSuper::bottom_relaxed; - using TaskQueueSuper::bottom_acquire; + using TaskQueueSuper::bottom_relaxed; + using TaskQueueSuper::bottom_acquire; - using TaskQueueSuper::set_bottom_relaxed; - using TaskQueueSuper::release_set_bottom; + using TaskQueueSuper::set_bottom_relaxed; + using TaskQueueSuper::release_set_bottom; - using TaskQueueSuper::age_relaxed; - using TaskQueueSuper::set_age_relaxed; - using TaskQueueSuper::cmpxchg_age; - using TaskQueueSuper::age_top_relaxed; + using TaskQueueSuper::age_relaxed; + using TaskQueueSuper::set_age_relaxed; + using TaskQueueSuper::cmpxchg_age; + using TaskQueueSuper::age_top_relaxed; - using TaskQueueSuper::increment_index; - using TaskQueueSuper::decrement_index; - using TaskQueueSuper::dirty_size; - using TaskQueueSuper::clean_size; - using TaskQueueSuper::assert_not_underflow; + using TaskQueueSuper::increment_index; + using TaskQueueSuper::decrement_index; + using TaskQueueSuper::dirty_size; + using TaskQueueSuper::clean_size; + using TaskQueueSuper::assert_not_underflow; public: - typedef typename TaskQueueSuper::PopResult PopResult; + typedef typename TaskQueueSuper::PopResult PopResult; - using TaskQueueSuper::max_elems; - using TaskQueueSuper::size; + using TaskQueueSuper::max_elems; + using TaskQueueSuper::size; #if TASKQUEUE_STATS - using TaskQueueSuper::stats; + using TaskQueueSuper::stats; #endif private: @@ -428,12 +428,12 @@ class GenericTaskQueue: public TaskQueueSuper { // Note that size() is not hidden--it returns the number of elements in the // TaskQueue, and does not include the size of the overflow stack. This // simplifies replacement of GenericTaskQueues with OverflowTaskQueues. -template -class OverflowTaskQueue: public GenericTaskQueue +template +class OverflowTaskQueue: public GenericTaskQueue { public: - typedef Stack overflow_t; - typedef GenericTaskQueue taskqueue_t; + typedef Stack overflow_t; + typedef GenericTaskQueue taskqueue_t; TASKQUEUE_STATS_ONLY(using taskqueue_t::stats;) @@ -467,11 +467,11 @@ class TaskQueueSetSuper { virtual uint tasks() const = 0; }; -template class TaskQueueSetSuperImpl: public CHeapObj, public TaskQueueSetSuper { +template class TaskQueueSetSuperImpl: public CHeapObj, public TaskQueueSetSuper { }; -template -class GenericTaskQueueSet: public TaskQueueSetSuperImpl { +template +class GenericTaskQueueSet: public TaskQueueSetSuperImpl { public: typedef typename T::element_type E; typedef typename T::PopResult PopResult; @@ -518,29 +518,29 @@ class GenericTaskQueueSet: public TaskQueueSetSuperImpl { #endif // TASKQUEUE_STATS }; -template void -GenericTaskQueueSet::register_queue(uint i, T* q) { +template void +GenericTaskQueueSet::register_queue(uint i, T* q) { assert(i < _n, "index out of range."); _queues[i] = q; } -template T* -GenericTaskQueueSet::queue(uint i) { +template T* +GenericTaskQueueSet::queue(uint i) { assert(i < _n, "index out of range."); return _queues[i]; } #ifdef ASSERT -template -void GenericTaskQueueSet::assert_empty() const { +template +void GenericTaskQueueSet::assert_empty() const { for (uint j = 0; j < _n; j++) { _queues[j]->assert_empty(); } } #endif // ASSERT -template -uint GenericTaskQueueSet::tasks() const { +template +uint GenericTaskQueueSet::tasks() const { uint n = 0; for (uint j = 0; j < _n; j++) { n += _queues[j]->size(); diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index f937ce8a2e993..8e65cfd704fda 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -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 @@ -38,30 +38,30 @@ #include "utilities/ostream.hpp" #include "utilities/stack.inline.hpp" -template -inline GenericTaskQueueSet::GenericTaskQueueSet(uint n) : _n(n) { +template +inline GenericTaskQueueSet::GenericTaskQueueSet(uint n) : _n(n) { typedef T* GenericTaskQueuePtr; - _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n, F); + _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n, MT); for (uint i = 0; i < n; i++) { _queues[i] = nullptr; } } -template -inline GenericTaskQueueSet::~GenericTaskQueueSet() { +template +inline GenericTaskQueueSet::~GenericTaskQueueSet() { FREE_C_HEAP_ARRAY(T*, _queues); } #if TASKQUEUE_STATS -template -void GenericTaskQueueSet::print_taskqueue_stats_hdr(outputStream* const st, const char* label) { +template +void GenericTaskQueueSet::print_taskqueue_stats_hdr(outputStream* const st, const char* label) { st->print_cr("GC Task Stats %s", label); st->print("thr "); TaskQueueStats::print_header(1, st); st->cr(); st->print("--- "); TaskQueueStats::print_header(2, st); st->cr(); } -template -void GenericTaskQueueSet::print_taskqueue_stats(outputStream* const st, const char* label) { +template +void GenericTaskQueueSet::print_taskqueue_stats(outputStream* const st, const char* label) { print_taskqueue_stats_hdr(st, label); TaskQueueStats totals; @@ -75,16 +75,16 @@ void GenericTaskQueueSet::print_taskqueue_stats(outputStream* const st, co DEBUG_ONLY(totals.verify()); } -template -void GenericTaskQueueSet::reset_taskqueue_stats() { +template +void GenericTaskQueueSet::reset_taskqueue_stats() { const uint n = size(); for (uint i = 0; i < n; ++i) { queue(i)->stats.reset(); } } -template -inline void GenericTaskQueueSet::print_and_reset_taskqueue_stats(const char* label) { +template +inline void GenericTaskQueueSet::print_and_reset_taskqueue_stats(const char* label) { if (!log_is_enabled(Trace, gc, task, stats)) { return; } @@ -97,19 +97,19 @@ inline void GenericTaskQueueSet::print_and_reset_taskqueue_stats(const cha } #endif // TASKQUEUE_STATS -template -inline GenericTaskQueue::GenericTaskQueue() : - _elems(MallocArrayAllocator::allocate(N, F)), +template +inline GenericTaskQueue::GenericTaskQueue() : + _elems(MallocArrayAllocator::allocate(N, MT)), _last_stolen_queue_id(InvalidQueueId), _seed(17 /* random number */) {} -template -inline GenericTaskQueue::~GenericTaskQueue() { +template +inline GenericTaskQueue::~GenericTaskQueue() { MallocArrayAllocator::free(_elems); } -template inline bool -GenericTaskQueue::push(E t) { +template inline bool +GenericTaskQueue::push(E t) { uint localBot = bottom_relaxed(); assert(localBot < N, "_bottom out of range."); idx_t top = age_top_relaxed(); @@ -134,8 +134,8 @@ GenericTaskQueue::push(E t) { return false; // Queue is full. } -template -inline bool OverflowTaskQueue::push(E t) { +template +inline bool OverflowTaskQueue::push(E t) { if (!taskqueue_t::push(t)) { overflow_stack()->push(t); TASKQUEUE_STATS_ONLY(stats.record_overflow(overflow_stack()->size())); @@ -143,8 +143,8 @@ inline bool OverflowTaskQueue::push(E t) { return true; } -template -inline bool OverflowTaskQueue::try_push_to_taskqueue(E t) { +template +inline bool OverflowTaskQueue::try_push_to_taskqueue(E t) { return taskqueue_t::push(t); } @@ -154,8 +154,8 @@ inline bool OverflowTaskQueue::try_push_to_taskqueue(E t) { // whenever the queue goes empty which it will do here if this thread // gets the last task or in pop_global() if the queue wraps (top == 0 // and pop_global() succeeds, see pop_global()). -template -bool GenericTaskQueue::pop_local_slow(uint localBot, Age oldAge) { +template +bool GenericTaskQueue::pop_local_slow(uint localBot, Age oldAge) { // This queue was observed to contain exactly one element; either this // thread will claim it, or a competing "pop_global". In either case, // the queue will be logically empty afterwards. Create a new Age value @@ -187,8 +187,8 @@ bool GenericTaskQueue::pop_local_slow(uint localBot, Age oldAge) { return false; } -template inline bool -GenericTaskQueue::pop_local(E& t, uint threshold) { +template inline bool +GenericTaskQueue::pop_local(E& t, uint threshold) { uint localBot = bottom_relaxed(); // This value cannot be N-1. That can only occur as a result of // the assignment to bottom in this method. If it does, this method @@ -224,8 +224,8 @@ GenericTaskQueue::pop_local(E& t, uint threshold) { } } -template -bool OverflowTaskQueue::pop_overflow(E& t) +template +bool OverflowTaskQueue::pop_overflow(E& t) { if (overflow_empty()) return false; t = overflow_stack()->pop(); @@ -253,8 +253,8 @@ bool OverflowTaskQueue::pop_overflow(E& t) // (3) Owner starts a push, writing elems[bottom]. At the same time, Thief // reads elems[oldAge.top]. The owner's bottom == the thief's oldAge.top. // (4) Thief will discard the read value, because its cmpxchg of age will fail. -template -typename GenericTaskQueue::PopResult GenericTaskQueue::pop_global(E& t) { +template +typename GenericTaskQueue::PopResult GenericTaskQueue::pop_global(E& t) { Age oldAge = age_relaxed(); // Architectures with non-multi-copy-atomic memory model require a @@ -311,13 +311,13 @@ inline int randomParkAndMiller(int *seed0) { return seed; } -template -int GenericTaskQueue::next_random_queue_id() { +template +int GenericTaskQueue::next_random_queue_id() { return randomParkAndMiller(&_seed); } -template -typename GenericTaskQueueSet::PopResult GenericTaskQueueSet::steal_best_of_2(uint queue_num, E& t) { +template +typename GenericTaskQueueSet::PopResult GenericTaskQueueSet::steal_best_of_2(uint queue_num, E& t) { T* const local_queue = queue(queue_num); if (_n > 2) { uint k1 = queue_num; @@ -372,8 +372,8 @@ typename GenericTaskQueueSet::PopResult GenericTaskQueueSet::steal_b } } -template -bool GenericTaskQueueSet::steal(uint queue_num, E& t) { +template +bool GenericTaskQueueSet::steal(uint queue_num, E& t) { uint const num_retries = 2 * _n; TASKQUEUE_STATS_ONLY(uint contended_in_a_row = 0;) @@ -394,9 +394,9 @@ bool GenericTaskQueueSet::steal(uint queue_num, E& t) { return false; } -template +template template -inline void GenericTaskQueue::iterate(Fn fn) { +inline void GenericTaskQueue::iterate(Fn fn) { uint iters = size(); uint index = bottom_relaxed(); for (uint i = 0; i < iters; ++i) { diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 368d76696058c..691c78cd02486 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -249,8 +249,9 @@ void ShenandoahBarrierSetC2::satb_write_barrier_pre(GraphKit* kit, } __ else_(); { // logging buffer is full, call the runtime - const TypeFunc *tf = ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type(); - __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), "shenandoah_wb_pre", pre_val, tls); + const TypeFunc *tf = ShenandoahBarrierSetC2::write_ref_field_pre_Type(); + __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), "shenandoah_wb_pre", + pre_val, tls); } __ end_if(); // (!index) } __ end_if(); // (pre_val != nullptr) } __ end_if(); // (!marking) @@ -268,7 +269,12 @@ void ShenandoahBarrierSetC2::satb_write_barrier_pre(GraphKit* kit, bool ShenandoahBarrierSetC2::is_shenandoah_wb_pre_call(Node* call) { return call->is_CallLeaf() && - call->as_CallLeaf()->entry_point() == CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry); + call->as_CallLeaf()->entry_point() == CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre); +} + +bool ShenandoahBarrierSetC2::is_shenandoah_clone_call(Node* call) { + return call->is_CallLeaf() && + call->as_CallLeaf()->entry_point() == CAST_FROM_FN_PTR(address, ShenandoahRuntime::clone_barrier); } bool ShenandoahBarrierSetC2::is_shenandoah_lrb_call(Node* call) { @@ -428,7 +434,7 @@ void ShenandoahBarrierSetC2::insert_pre_barrier(GraphKit* kit, Node* base_oop, N #undef __ -const TypeFunc* ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type() { +const TypeFunc* ShenandoahBarrierSetC2::write_ref_field_pre_Type() { const Type **fields = TypeTuple::fields(2); fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // original field value fields[TypeFunc::Parms+1] = TypeRawPtr::NOTNULL; // thread @@ -441,7 +447,7 @@ const TypeFunc* ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type() { return TypeFunc::make(domain, range); } -const TypeFunc* ShenandoahBarrierSetC2::shenandoah_clone_barrier_Type() { +const TypeFunc* ShenandoahBarrierSetC2::clone_barrier_Type() { const Type **fields = TypeTuple::fields(1); fields[TypeFunc::Parms+0] = TypeOopPtr::NOTNULL; // src oop const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); @@ -453,7 +459,7 @@ const TypeFunc* ShenandoahBarrierSetC2::shenandoah_clone_barrier_Type() { return TypeFunc::make(domain, range); } -const TypeFunc* ShenandoahBarrierSetC2::shenandoah_load_reference_barrier_Type() { +const TypeFunc* ShenandoahBarrierSetC2::load_reference_barrier_Type() { const Type **fields = TypeTuple::fields(2); fields[TypeFunc::Parms+0] = TypeOopPtr::BOTTOM; // original field value fields[TypeFunc::Parms+1] = TypeRawPtr::BOTTOM; // original load address @@ -474,10 +480,17 @@ Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& const TypePtr* adr_type = access.addr().type(); Node* adr = access.addr().node(); + bool no_keepalive = (decorators & AS_NO_KEEPALIVE) != 0; + if (!access.is_oop()) { return BarrierSetC2::store_at_resolved(access, val); } + if (no_keepalive) { + // No keep-alive means no need for the pre-barrier. + return BarrierSetC2::store_at_resolved(access, val); + } + if (access.is_parse_access()) { C2ParseAccess& parse_access = static_cast(access); GraphKit* kit = parse_access.kit(); @@ -675,20 +688,11 @@ bool ShenandoahBarrierSetC2::is_gc_pre_barrier_node(Node* node) const { return is_shenandoah_wb_pre_call(node); } -// Support for GC barriers emitted during parsing bool ShenandoahBarrierSetC2::is_gc_barrier_node(Node* node) const { - if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) return true; - if (node->Opcode() != Op_CallLeaf && node->Opcode() != Op_CallLeafNoFP) { - return false; - } - CallLeafNode *call = node->as_CallLeaf(); - if (call->_name == nullptr) { - return false; - } - - return strcmp(call->_name, "shenandoah_clone_barrier") == 0 || - strcmp(call->_name, "shenandoah_cas_obj") == 0 || - strcmp(call->_name, "shenandoah_wb_pre") == 0; + return (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) || + is_shenandoah_lrb_call(node) || + is_shenandoah_wb_pre_call(node) || + is_shenandoah_clone_call(node); } Node* ShenandoahBarrierSetC2::step_over_gc_barrier(Node* c) const { @@ -802,11 +806,11 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo // Heap is unstable, call into clone barrier stub Node* call = phase->make_leaf_call(unstable_ctrl, mem, - ShenandoahBarrierSetC2::shenandoah_clone_barrier_Type(), - CAST_FROM_FN_PTR(address, ShenandoahRuntime::shenandoah_clone_barrier), - "shenandoah_clone", - TypeRawPtr::BOTTOM, - src_base); + ShenandoahBarrierSetC2::clone_barrier_Type(), + CAST_FROM_FN_PTR(address, ShenandoahRuntime::clone_barrier), + "shenandoah_clone", + TypeRawPtr::BOTTOM, + src_base); call = phase->transform_later(call); ctrl = phase->transform_later(new ProjNode(call, TypeFunc::Control)); @@ -981,7 +985,7 @@ void ShenandoahBarrierSetC2::verify_gc_barriers(Compile* compile, CompilePhase p Node* ShenandoahBarrierSetC2::ideal_node(PhaseGVN* phase, Node* n, bool can_reshape) const { if (is_shenandoah_wb_pre_call(n)) { - uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type()->domain()->cnt(); + uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_Type()->domain()->cnt(); if (n->req() > cnt) { Node* addp = n->in(cnt); if (has_only_shenandoah_wb_pre_uses(addp)) { @@ -1067,7 +1071,7 @@ bool ShenandoahBarrierSetC2::final_graph_reshaping(Compile* compile, Node* n, ui assert (n->is_Call(), ""); CallNode *call = n->as_Call(); if (ShenandoahBarrierSetC2::is_shenandoah_wb_pre_call(call)) { - uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type()->domain()->cnt(); + uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_Type()->domain()->cnt(); if (call->req() > cnt) { assert(call->req() == cnt + 1, "only one extra input"); Node *addp = call->in(cnt); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index 4619b217e96c6..6e241b39ce967 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -85,6 +85,7 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { static ShenandoahBarrierSetC2* bsc2(); static bool is_shenandoah_wb_pre_call(Node* call); + static bool is_shenandoah_clone_call(Node* call); static bool is_shenandoah_lrb_call(Node* call); static bool is_shenandoah_marking_if(PhaseValues* phase, Node* n); static bool is_shenandoah_state_load(Node* n); @@ -92,9 +93,9 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { ShenandoahBarrierSetC2State* state() const; - static const TypeFunc* write_ref_field_pre_entry_Type(); - static const TypeFunc* shenandoah_clone_barrier_Type(); - static const TypeFunc* shenandoah_load_reference_barrier_Type(); + static const TypeFunc* write_ref_field_pre_Type(); + static const TypeFunc* clone_barrier_Type(); + static const TypeFunc* load_reference_barrier_Type(); virtual bool has_load_barrier_nodes() const { return true; } // This is the entry-point for the backend to perform accesses through the Access API. diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 0a51f74299545..efa0ced603cda 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -414,7 +414,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { "cipherBlockChaining_decryptAESCrypt", { { TypeFunc::Parms, ShenandoahLoad }, { TypeFunc::Parms+1, ShenandoahStore }, { TypeFunc::Parms+2, ShenandoahLoad }, { TypeFunc::Parms+3, ShenandoahLoad }, { -1, ShenandoahNone}, { -1, ShenandoahNone} }, - "shenandoah_clone_barrier", + "shenandoah_clone", { { TypeFunc::Parms, ShenandoahLoad }, { -1, ShenandoahNone}, { -1, ShenandoahNone}, { -1, ShenandoahNone}, { -1, ShenandoahNone}, { -1, ShenandoahNone} }, "ghash_processBlocks", @@ -995,7 +995,7 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo name = "load_reference_barrier_phantom"; } } - Node* call = new CallLeafNode(ShenandoahBarrierSetC2::shenandoah_load_reference_barrier_Type(), calladdr, name, TypeRawPtr::BOTTOM); + Node* call = new CallLeafNode(ShenandoahBarrierSetC2::load_reference_barrier_Type(), calladdr, name, TypeRawPtr::BOTTOM); call->init_req(TypeFunc::Control, ctrl); call->init_req(TypeFunc::I_O, phase->C->top()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp index 25062c5317dcb..70401b4246189 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2023, Red Hat, Inc. All rights reserved. + * 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 @@ -57,7 +58,7 @@ ShenandoahCollectionSet::ShenandoahCollectionSet(ShenandoahHeap* heap, ReservedS // subsystem for mapping not-yet-written-to pages to a single physical backing page, // but this is not guaranteed, and would confuse NMT and other memory accounting tools. - MemTracker::record_virtual_memory_type(_map_space.base(), mtGC); + MemTracker::record_virtual_memory_tag(_map_space.base(), mtGC); size_t page_size = os::vm_page_size(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 6ed75a9d96106..75cdb99e177d1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -225,8 +225,7 @@ void ShenandoahConcurrentMark::finish_mark() { assert(Thread::current()->is_VM_thread(), "Must by VM Thread"); finish_mark_work(); assert(task_queues()->is_empty(), "Should be empty"); - TASKQUEUE_STATS_ONLY(task_queues()->print_taskqueue_stats()); - TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats()); + TASKQUEUE_STATS_ONLY(task_queues()->print_and_reset_taskqueue_stats("")); ShenandoahHeap* const heap = ShenandoahHeap::heap(); heap->set_concurrent_mark_in_progress(false); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 95a70de5790e9..df2d6d092e630 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -177,10 +177,13 @@ void ShenandoahControlThread::run_service() { // it is a normal completion, or the abort. heap->free_set()->log_status_under_lock(); - // Notify Universe about new heap usage. This has implications for - // global soft refs policy, and we better report it every time heap - // usage goes down. - heap->update_capacity_and_used_at_gc(); + { + // Notify Universe about new heap usage. This has implications for + // global soft refs policy, and we better report it every time heap + // usage goes down. + ShenandoahHeapLocker locker(heap->lock()); + heap->update_capacity_and_used_at_gc(); + } // Signal that we have completed a visit to all live objects. heap->record_whole_heap_examined_timestamp(); @@ -371,6 +374,16 @@ void ShenandoahControlThread::request_gc(GCCause::Cause cause) { } void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) { + // For normal requested GCs (System.gc) we want to block the caller. However, + // for whitebox requested GC, we want to initiate the GC and return immediately. + // The whitebox caller thread will arrange for itself to wait until the GC notifies + // it that has reached the requested breakpoint (phase in the GC). + if (cause == GCCause::_wb_breakpoint) { + _requested_gc_cause = cause; + _gc_requested.set(); + return; + } + // Make sure we have at least one complete GC cycle before unblocking // from the explicit GC request. // @@ -390,9 +403,7 @@ void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) { _requested_gc_cause = cause; _gc_requested.set(); - if (cause != GCCause::_wb_breakpoint) { - ml.wait(); - } + ml.wait(); current_gc_id = get_gc_id(); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 03e19a3af5e30..310cd5b8061eb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -910,7 +910,6 @@ void ShenandoahFreeSet::try_recycle_trashed(ShenandoahHeapRegion* r) { void ShenandoahFreeSet::recycle_trash() { // lock is not reentrable, check we don't have it shenandoah_assert_not_heaplocked(); - size_t count = 0; for (size_t i = 0; i < _heap->num_regions(); i++) { ShenandoahHeapRegion* r = _heap->get_region(i); @@ -919,16 +918,45 @@ void ShenandoahFreeSet::recycle_trash() { } } - // Relinquish the lock after this much time passed. - static constexpr jlong deadline_ns = 30000; // 30 us + size_t total_batches = 0; + jlong batch_start_time = 0; + jlong recycle_trash_start_time = os::javaTimeNanos(); // This value will be treated as the initial batch_start_time + jlong batch_end_time = recycle_trash_start_time; + // Process as many batches as can be processed within 10 us. + static constexpr jlong deadline_ns = 10000; // 10 us size_t idx = 0; + jlong predicted_next_batch_end_time; + jlong batch_process_time_estimate = 0; while (idx < count) { - os::naked_yield(); // Yield to allow allocators to take the lock - ShenandoahHeapLocker locker(_heap->lock()); - const jlong deadline = os::javaTimeNanos() + deadline_ns; - while (idx < count && os::javaTimeNanos() < deadline) { - try_recycle_trashed(_trash_regions[idx++]); + if (idx > 0) { + os::naked_yield(); // Yield to allow allocators to take the lock, except on the first iteration } + // Avoid another call to javaTimeNanos() if we already know time at which last batch ended + batch_start_time = batch_end_time; + const jlong deadline = batch_start_time + deadline_ns; + + ShenandoahHeapLocker locker(_heap->lock()); + do { + // Measurements on typical 2024 hardware suggest it typically requires between 1400 and 2000 ns to process a batch of + // 32 regions, assuming low contention with other threads. Sometimes this goes higher, when mutator threads + // are contending for CPU cores and/or the heap lock. On this hardware with a 10 us deadline, we expect 3-6 batches + // to be processed between yields most of the time. + // + // Note that deadline is enforced since the end of previous batch. In the case that yield() or acquisition of heap lock + // takes a "long time", we will have less time to process regions, but we will always process at least one batch between + // yields. Yielding more frequently when there is heavy contention for the heap lock or for CPU cores is considered the + // right thing to do. + const size_t REGIONS_PER_BATCH = 32; + size_t max_idx = MIN2(count, idx + REGIONS_PER_BATCH); + while (idx < max_idx) { + try_recycle_trashed(_trash_regions[idx++]); + } + total_batches++; + batch_end_time = os::javaTimeNanos(); + // Estimate includes historic combination of yield times and heap lock acquisition times. + batch_process_time_estimate = (batch_end_time - recycle_trash_start_time) / total_batches; + predicted_next_batch_end_time = batch_end_time + batch_process_time_estimate; + } while ((idx < count) && (predicted_next_batch_end_time < deadline)); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index a587cc417e319..7ae4a1cf8b3dc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -252,7 +252,7 @@ jint ShenandoahHeap::initialize() { bitmap_size_orig, bitmap_page_size, bitmap.base(), bitmap.size(), bitmap.page_size()); - MemTracker::record_virtual_memory_type(bitmap.base(), mtGC); + MemTracker::record_virtual_memory_tag(bitmap.base(), mtGC); _bitmap_region = MemRegion((HeapWord*) bitmap.base(), bitmap.size() / HeapWordSize); _bitmap_region_special = bitmap.special(); @@ -276,7 +276,7 @@ jint ShenandoahHeap::initialize() { os::commit_memory_or_exit(verify_bitmap.base(), verify_bitmap.size(), bitmap_page_size, false, "Cannot commit verification bitmap memory"); } - MemTracker::record_virtual_memory_type(verify_bitmap.base(), mtGC); + MemTracker::record_virtual_memory_tag(verify_bitmap.base(), mtGC); MemRegion verify_bitmap_region = MemRegion((HeapWord *) verify_bitmap.base(), verify_bitmap.size() / HeapWordSize); _verification_bit_map.initialize(_heap_region, verify_bitmap_region); _verifier = new ShenandoahVerifier(this, &_verification_bit_map); @@ -290,7 +290,7 @@ jint ShenandoahHeap::initialize() { bitmap_size_orig, aux_bitmap_page_size, aux_bitmap.base(), aux_bitmap.size(), aux_bitmap.page_size()); - MemTracker::record_virtual_memory_type(aux_bitmap.base(), mtGC); + MemTracker::record_virtual_memory_tag(aux_bitmap.base(), mtGC); _aux_bitmap_region = MemRegion((HeapWord*) aux_bitmap.base(), aux_bitmap.size() / HeapWordSize); _aux_bitmap_region_special = aux_bitmap.special(); _aux_bit_map.initialize(_heap_region, _aux_bitmap_region); @@ -308,7 +308,7 @@ jint ShenandoahHeap::initialize() { region_storage_size_orig, region_page_size, region_storage.base(), region_storage.size(), region_storage.page_size()); - MemTracker::record_virtual_memory_type(region_storage.base(), mtGC); + MemTracker::record_virtual_memory_tag(region_storage.base(), mtGC); if (!region_storage.special()) { os::commit_memory_or_exit(region_storage.base(), region_storage_size, region_page_size, false, "Cannot commit region memory"); @@ -693,7 +693,7 @@ void ShenandoahHeap::notify_mutator_alloc_words(size_t words, bool waste) { if (ShenandoahPacing) { control_thread()->pacing_notify_alloc(words); if (waste) { - pacer()->claim_for_alloc(words, true); + pacer()->claim_for_alloc(words); } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp index e433c2910c8d8..e6baa4096f0a1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp @@ -61,33 +61,23 @@ class ShenandoahMarkRefsSuperClosure : public MetadataVisitingOopIterateClosure } }; -class ShenandoahMarkUpdateRefsSuperClosure : public ShenandoahMarkRefsSuperClosure { -protected: +template +class ShenandoahMarkUpdateRefsClosure : public ShenandoahMarkRefsSuperClosure { +private: ShenandoahHeap* const _heap; - template + template inline void work(T* p); public: - ShenandoahMarkUpdateRefsSuperClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : + ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : ShenandoahMarkRefsSuperClosure(q, rp), _heap(ShenandoahHeap::heap()) { assert(_heap->is_stw_gc_in_progress(), "Can only be used for STW GC"); - }; -}; - -template -class ShenandoahMarkUpdateRefsClosure : public ShenandoahMarkUpdateRefsSuperClosure { -private: - template - inline void do_oop_work(T* p) { work(p); } - -public: - ShenandoahMarkUpdateRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp) : - ShenandoahMarkUpdateRefsSuperClosure(q, rp) {} + } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } + virtual void do_oop(narrowOop* p) { work(p); } + virtual void do_oop(oop* p) { work(p); } }; template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp index 70d7e94fb503f..e0662c24462f8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp @@ -35,8 +35,9 @@ inline void ShenandoahMarkRefsSuperClosure::work(T* p) { ShenandoahMark::mark_through_ref(p, _queue, _mark_context, _weak); } -template -inline void ShenandoahMarkUpdateRefsSuperClosure::work(T* p) { +template +template +inline void ShenandoahMarkUpdateRefsClosure::work(T* p) { // Update the location _heap->update_with_forwarded(p); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp index 0fc6744c15ae7..8d10b7cbfcfaf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp @@ -65,6 +65,7 @@ void ShenandoahPacer::setup_for_mark() { size_t non_taxable = free * ShenandoahPacingCycleSlack / 100; size_t taxable = free - non_taxable; + taxable = MAX2(1, taxable); double tax = 1.0 * live / taxable; // base tax for available free space tax *= 1; // mark can succeed with immediate garbage, claim all available space @@ -88,6 +89,7 @@ void ShenandoahPacer::setup_for_evac() { size_t non_taxable = free * ShenandoahPacingCycleSlack / 100; size_t taxable = free - non_taxable; + taxable = MAX2(1, taxable); double tax = 1.0 * used / taxable; // base tax for available free space tax *= 2; // evac is followed by update-refs, claim 1/2 of remaining free @@ -112,6 +114,7 @@ void ShenandoahPacer::setup_for_updaterefs() { size_t non_taxable = free * ShenandoahPacingCycleSlack / 100; size_t taxable = free - non_taxable; + taxable = MAX2(1, taxable); double tax = 1.0 * used / taxable; // base tax for available free space tax *= 1; // update-refs is the last phase, claim the remaining free @@ -189,7 +192,8 @@ void ShenandoahPacer::restart_with(size_t non_taxable_bytes, double tax_rate) { _need_notify_waiters.try_set(); } -bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) { +template +bool ShenandoahPacer::claim_for_alloc(size_t words) { assert(ShenandoahPacing, "Only be here when pacing is enabled"); intptr_t tax = MAX2(1, words * Atomic::load(&_tax_rate)); @@ -198,7 +202,7 @@ bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) { intptr_t new_val = 0; do { cur = Atomic::load(&_budget); - if (cur < tax && !force) { + if (cur < tax && !FORCE) { // Progress depleted, alas. return false; } @@ -207,6 +211,9 @@ bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) { return true; } +template bool ShenandoahPacer::claim_for_alloc(size_t words); +template bool ShenandoahPacer::claim_for_alloc(size_t words); + void ShenandoahPacer::unpace_for_alloc(intptr_t epoch, size_t words) { assert(ShenandoahPacing, "Only be here when pacing is enabled"); @@ -227,18 +234,11 @@ void ShenandoahPacer::pace_for_alloc(size_t words) { assert(ShenandoahPacing, "Only be here when pacing is enabled"); // Fast path: try to allocate right away - bool claimed = claim_for_alloc(words, false); + bool claimed = claim_for_alloc(words); if (claimed) { return; } - // Forcefully claim the budget: it may go negative at this point, and - // GC should replenish for this and subsequent allocations. After this claim, - // we would wait a bit until our claim is matched by additional progress, - // or the time budget depletes. - claimed = claim_for_alloc(words, true); - assert(claimed, "Should always succeed"); - // Threads that are attaching should not block at all: they are not // fully initialized yet. Blocking them would be awkward. // This is probably the path that allocates the thread oop itself. @@ -249,32 +249,25 @@ void ShenandoahPacer::pace_for_alloc(size_t words) { JavaThread* current = JavaThread::current(); if (current->is_attaching_via_jni() || !current->is_active_Java_thread()) { + claim_for_alloc(words); return; } - double start = os::elapsedTime(); - - size_t max_ms = ShenandoahPacingMaxDelay; - size_t total_ms = 0; - - while (true) { + jlong const max_delay = ShenandoahPacingMaxDelay * NANOSECS_PER_MILLISEC; + jlong const start_time = os::elapsed_counter(); + while (!claimed && (os::elapsed_counter() - start_time) < max_delay) { // We could instead assist GC, but this would suffice for now. - size_t cur_ms = (max_ms > total_ms) ? (max_ms - total_ms) : 1; - wait(cur_ms); - - double end = os::elapsedTime(); - total_ms = (size_t)((end - start) * 1000); - - if (total_ms > max_ms || Atomic::load(&_budget) >= 0) { - // Exiting if either: - // a) Spent local time budget to wait for enough GC progress. - // Breaking out and allocating anyway, which may mean we outpace GC, - // and start Degenerated GC cycle. - // b) The budget had been replenished, which means our claim is satisfied. - ShenandoahThreadLocalData::add_paced_time(JavaThread::current(), end - start); - break; - } + wait(1); + claimed = claim_for_alloc(words); + } + if (!claimed) { + // Spent local time budget to wait for enough GC progress. + // Force allocating anyway, which may mean we outpace GC, + // and start Degenerated GC cycle. + claimed = claim_for_alloc(words); + assert(claimed, "Should always succeed"); } + ShenandoahThreadLocalData::add_paced_time(current, (double)(os::elapsed_counter() - start_time) / NANOSECS_PER_SEC); } void ShenandoahPacer::wait(size_t time_ms) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp index 1c2bf00eb56ba..44ad2700f8704 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPacer.hpp @@ -107,7 +107,9 @@ class ShenandoahPacer : public CHeapObj { inline void report_alloc(size_t words); - bool claim_for_alloc(size_t words, bool force); + template + bool claim_for_alloc(size_t words); + void pace_for_alloc(size_t words); void unpace_for_alloc(intptr_t epoch, size_t words); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp index 2c727de585799..b217c641824c2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp @@ -31,22 +31,19 @@ #include "oops/oop.inline.hpp" #include "utilities/copy.hpp" -void ShenandoahRuntime::arraycopy_barrier_oop_entry(oop* src, oop* dst, size_t length) { - ShenandoahBarrierSet *bs = ShenandoahBarrierSet::barrier_set(); - bs->arraycopy_barrier(src, dst, length); -} +JRT_LEAF(void, ShenandoahRuntime::arraycopy_barrier_oop(oop* src, oop* dst, size_t length)) + ShenandoahBarrierSet::barrier_set()->arraycopy_barrier(src, dst, length); +JRT_END -void ShenandoahRuntime::arraycopy_barrier_narrow_oop_entry(narrowOop* src, narrowOop* dst, size_t length) { - ShenandoahBarrierSet *bs = ShenandoahBarrierSet::barrier_set(); - bs->arraycopy_barrier(src, dst, length); -} +JRT_LEAF(void, ShenandoahRuntime::arraycopy_barrier_narrow_oop(narrowOop* src, narrowOop* dst, size_t length)) + ShenandoahBarrierSet::barrier_set()->arraycopy_barrier(src, dst, length); +JRT_END -// Shenandoah pre write barrier slowpath -JRT_LEAF(void, ShenandoahRuntime::write_ref_field_pre_entry(oopDesc* orig, JavaThread *thread)) +JRT_LEAF(void, ShenandoahRuntime::write_ref_field_pre(oopDesc * orig, JavaThread * thread)) assert(thread == JavaThread::current(), "pre-condition"); assert(orig != nullptr, "should be optimized out"); shenandoah_assert_correct(nullptr, orig); - // store the original value that was in the field reference + // Capture the original value that was in the field reference. assert(ShenandoahThreadLocalData::satb_mark_queue(thread).is_active(), "Shouldn't be here otherwise"); SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); ShenandoahBarrierSet::satb_mark_queue_set().enqueue_known_active(queue, orig); @@ -60,26 +57,24 @@ JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_strong_narrow(oopDe return ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(src, load_addr); JRT_END -// Shenandoah clone barrier: makes sure that references point to to-space -// in cloned objects. -JRT_LEAF(void, ShenandoahRuntime::shenandoah_clone_barrier(oopDesc* src)) - oop s = oop(src); - shenandoah_assert_correct(nullptr, s); - ShenandoahBarrierSet::barrier_set()->clone_barrier(s); -JRT_END - -JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak(oopDesc * src, oop* load_addr)) +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak(oopDesc* src, oop* load_addr)) return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(ON_WEAK_OOP_REF, oop(src), load_addr); JRT_END -JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak_narrow(oopDesc * src, narrowOop* load_addr)) +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak_narrow(oopDesc* src, narrowOop* load_addr)) return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(ON_WEAK_OOP_REF, oop(src), load_addr); JRT_END -JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_phantom(oopDesc * src, oop* load_addr)) +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_phantom(oopDesc* src, oop* load_addr)) return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(ON_PHANTOM_OOP_REF, oop(src), load_addr); JRT_END -JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_phantom_narrow(oopDesc * src, narrowOop* load_addr)) +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_phantom_narrow(oopDesc* src, narrowOop* load_addr)) return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(ON_PHANTOM_OOP_REF, oop(src), load_addr); JRT_END + +JRT_LEAF(void, ShenandoahRuntime::clone_barrier(oopDesc* src)) + oop s = oop(src); + shenandoah_assert_correct(nullptr, s); + ShenandoahBarrierSet::barrier_set()->clone_barrier(s); +JRT_END diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp index e187e4360b16b..4ad8fc997ea76 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp @@ -33,10 +33,10 @@ class oopDesc; class ShenandoahRuntime : public AllStatic { public: - static void arraycopy_barrier_oop_entry(oop* src, oop* dst, size_t length); - static void arraycopy_barrier_narrow_oop_entry(narrowOop* src, narrowOop* dst, size_t length); + static void arraycopy_barrier_oop(oop* src, oop* dst, size_t length); + static void arraycopy_barrier_narrow_oop(narrowOop* src, narrowOop* dst, size_t length); - static void write_ref_field_pre_entry(oopDesc* orig, JavaThread* thread); + static void write_ref_field_pre(oopDesc* orig, JavaThread* thread); static oopDesc* load_reference_barrier_strong(oopDesc* src, oop* load_addr); static oopDesc* load_reference_barrier_strong_narrow(oopDesc* src, narrowOop* load_addr); @@ -47,7 +47,7 @@ class ShenandoahRuntime : public AllStatic { static oopDesc* load_reference_barrier_phantom(oopDesc* src, oop* load_addr); static oopDesc* load_reference_barrier_phantom_narrow(oopDesc* src, narrowOop* load_addr); - static void shenandoah_clone_barrier(oopDesc* src); + static void clone_barrier(oopDesc* src); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHRUNTIME_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index 05cd8ef66b9d1..9a30b1fed8724 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -133,8 +133,7 @@ void ShenandoahSTWMark::mark() { ShenandoahCodeRoots::disarm_nmethods(); assert(task_queues()->is_empty(), "Should be empty"); - TASKQUEUE_STATS_ONLY(task_queues()->print_taskqueue_stats()); - TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats()); + TASKQUEUE_STATS_ONLY(task_queues()->print_and_reset_taskqueue_stats("")); } void ShenandoahSTWMark::mark_roots(uint worker_id) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp index c3e8108752fed..127e6324fb01e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.cpp @@ -23,7 +23,7 @@ */ #include "precompiled.hpp" -#include "gc/shenandoah/shenandoahSimpleBitMap.hpp" +#include "gc/shenandoah/shenandoahSimpleBitMap.inline.hpp" ShenandoahSimpleBitMap::ShenandoahSimpleBitMap(size_t num_bits) : _num_bits(num_bits), @@ -43,8 +43,8 @@ size_t ShenandoahSimpleBitMap::count_leading_ones(idx_t start_idx) const { assert((start_idx >= 0) && (start_idx < _num_bits), "precondition"); size_t array_idx = start_idx >> LogBitsPerWord; uintx element_bits = _bitmap[array_idx]; - uintx bit_number = start_idx & right_n_bits(LogBitsPerWord); - uintx mask = ~right_n_bits(bit_number); + uintx bit_number = start_idx & (BitsPerWord - 1); + uintx mask = ~tail_mask(bit_number); size_t counted_ones = 0; while ((element_bits & mask) == mask) { // All bits numbered >= bit_number are set @@ -54,7 +54,7 @@ size_t ShenandoahSimpleBitMap::count_leading_ones(idx_t start_idx) const { // Strength reduction: array_idx = (start_idx >> LogBitsPerWord) array_idx++; element_bits = _bitmap[array_idx]; - // Constant folding: bit_number = start_idx & right_n_bits(LogBitsPerWord); + // Constant folding: bit_number = start_idx & (BitsPerWord - 1); bit_number = 0; // Constant folding: mask = ~right_n_bits(bit_number); mask = ~0; @@ -70,9 +70,9 @@ size_t ShenandoahSimpleBitMap::count_trailing_ones(idx_t last_idx) const { assert((last_idx >= 0) && (last_idx < _num_bits), "precondition"); size_t array_idx = last_idx >> LogBitsPerWord; uintx element_bits = _bitmap[array_idx]; - uintx bit_number = last_idx & right_n_bits(LogBitsPerWord); + uintx bit_number = last_idx & (BitsPerWord - 1); // All ones from bit 0 to the_bit - uintx mask = right_n_bits(bit_number + 1); + uintx mask = tail_mask(bit_number + 1); size_t counted_ones = 0; while ((element_bits & mask) == mask) { // All bits numbered <= bit_number are set @@ -81,7 +81,7 @@ size_t ShenandoahSimpleBitMap::count_trailing_ones(idx_t last_idx) const { // Dead code: do not need to compute: last_idx -= found_ones; array_idx--; element_bits = _bitmap[array_idx]; - // Constant folding: bit_number = last_idx & right_n_bits(LogBitsPerWord); + // Constant folding: bit_number = last_idx & (BitsPerWord - 1); bit_number = BitsPerWord - 1; // Constant folding: mask = right_n_bits(bit_number + 1); mask = ~0; @@ -99,7 +99,7 @@ bool ShenandoahSimpleBitMap::is_forward_consecutive_ones(idx_t start_idx, idx_t start_idx, count); assert(start_idx + count <= (idx_t) _num_bits, "precondition"); size_t array_idx = start_idx >> LogBitsPerWord; - uintx bit_number = start_idx & right_n_bits(LogBitsPerWord); + uintx bit_number = start_idx & (BitsPerWord - 1); uintx element_bits = _bitmap[array_idx]; uintx bits_to_examine = BitsPerWord - bit_number; element_bits >>= bit_number; @@ -128,7 +128,7 @@ bool ShenandoahSimpleBitMap::is_backward_consecutive_ones(idx_t last_idx, idx_t assert((last_idx >= 0) && (last_idx < _num_bits), "precondition"); assert(last_idx - count >= -1, "precondition"); size_t array_idx = last_idx >> LogBitsPerWord; - uintx bit_number = last_idx & right_n_bits(LogBitsPerWord); + uintx bit_number = last_idx & (BitsPerWord - 1); uintx element_bits = _bitmap[array_idx]; uintx bits_to_examine = bit_number + 1; element_bits <<= (BitsPerWord - bits_to_examine); @@ -161,10 +161,10 @@ idx_t ShenandoahSimpleBitMap::find_first_consecutive_set_bits(idx_t beg, idx_t e return end; } uintx array_idx = beg >> LogBitsPerWord; - uintx bit_number = beg & right_n_bits(LogBitsPerWord); + uintx bit_number = beg & (BitsPerWord - 1); uintx element_bits = _bitmap[array_idx]; if (bit_number > 0) { - uintx mask_out = right_n_bits(bit_number); + uintx mask_out = tail_mask(bit_number); element_bits &= ~mask_out; } @@ -222,9 +222,9 @@ idx_t ShenandoahSimpleBitMap::find_first_consecutive_set_bits(idx_t beg, idx_t e } array_idx = beg >> LogBitsPerWord; element_bits = _bitmap[array_idx]; - bit_number = beg & right_n_bits(LogBitsPerWord); + bit_number = beg & (BitsPerWord - 1); if (bit_number > 0) { - size_t mask_out = right_n_bits(bit_number); + size_t mask_out = tail_mask(bit_number); element_bits &= ~mask_out; } } @@ -242,10 +242,10 @@ idx_t ShenandoahSimpleBitMap::find_last_consecutive_set_bits(const idx_t beg, id } size_t array_idx = end >> LogBitsPerWord; - uintx bit_number = end & right_n_bits(LogBitsPerWord); + uintx bit_number = end & (BitsPerWord - 1); uintx element_bits = _bitmap[array_idx]; if (bit_number < BitsPerWord - 1) { - uintx mask_in = right_n_bits(bit_number + 1); + uintx mask_in = tail_mask(bit_number + 1); element_bits &= mask_in; } @@ -280,10 +280,10 @@ idx_t ShenandoahSimpleBitMap::find_last_consecutive_set_bits(const idx_t beg, id return beg; } array_idx = end >> LogBitsPerWord; - bit_number = end & right_n_bits(LogBitsPerWord); + bit_number = end & (BitsPerWord - 1); element_bits = _bitmap[array_idx]; if (bit_number < BitsPerWord - 1){ - size_t mask_in = right_n_bits(bit_number + 1); + size_t mask_in = tail_mask(bit_number + 1); element_bits &= mask_in; } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp index c22e952700204..55d21b06e4bbd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp @@ -50,7 +50,7 @@ typedef ssize_t idx_t; // ShenandoahSimpleBitMap resembles CHeapBitMap but adds missing support for find_first_consecutive_set_bits() and // find_last_consecutive_set_bits. An alternative refactoring of code would subclass CHeapBitMap, but this might // break abstraction rules, because efficient implementation requires assumptions about superclass internals that -// might be violatee through future software maintenance. +// might be violated through future software maintenance. class ShenandoahSimpleBitMap { const idx_t _num_bits; const size_t _num_words; @@ -80,11 +80,13 @@ class ShenandoahSimpleBitMap { bool is_forward_consecutive_ones(idx_t start_idx, idx_t count) const; bool is_backward_consecutive_ones(idx_t last_idx, idx_t count) const; + static inline uintx tail_mask(uintx bit_number); + public: inline idx_t aligned_index(idx_t idx) const { assert((idx >= 0) && (idx < _num_bits), "precondition"); - idx_t array_idx = idx & ~right_n_bits(LogBitsPerWord); + idx_t array_idx = idx & ~(BitsPerWord - 1); return array_idx; } @@ -107,7 +109,7 @@ class ShenandoahSimpleBitMap { inline void set_bit(idx_t idx) { assert((idx >= 0) && (idx < _num_bits), "precondition"); size_t array_idx = idx >> LogBitsPerWord; - uintx bit_number = idx & right_n_bits(LogBitsPerWord); + uintx bit_number = idx & (BitsPerWord - 1); uintx the_bit = nth_bit(bit_number); _bitmap[array_idx] |= the_bit; } @@ -116,7 +118,7 @@ class ShenandoahSimpleBitMap { assert((idx >= 0) && (idx < _num_bits), "precondition"); assert(idx >= 0, "precondition"); size_t array_idx = idx >> LogBitsPerWord; - uintx bit_number = idx & right_n_bits(LogBitsPerWord); + uintx bit_number = idx & (BitsPerWord - 1); uintx the_bit = nth_bit(bit_number); _bitmap[array_idx] &= ~the_bit; } @@ -125,9 +127,9 @@ class ShenandoahSimpleBitMap { assert((idx >= 0) && (idx < _num_bits), "precondition"); assert(idx >= 0, "precondition"); size_t array_idx = idx >> LogBitsPerWord; - uintx bit_number = idx & right_n_bits(LogBitsPerWord); + uintx bit_number = idx & (BitsPerWord - 1); uintx the_bit = nth_bit(bit_number); - return (_bitmap[array_idx] & the_bit)? true: false; + return (_bitmap[array_idx] & the_bit) != 0; } // Return the index of the first set bit in the range [beg, size()), or size() if none found. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp index 3e602ed11e0c0..4582ab9a781dd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp @@ -27,15 +27,22 @@ #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" +inline uintx ShenandoahSimpleBitMap::tail_mask(uintx bit_number) { + if (bit_number >= BitsPerWord) { + return -1; + } + return (uintx(1) << bit_number) - 1; +} + inline idx_t ShenandoahSimpleBitMap::find_first_set_bit(idx_t beg, idx_t end) const { assert((beg >= 0) && (beg < _num_bits), "precondition"); assert((end > beg) && (end <= _num_bits), "precondition"); do { size_t array_idx = beg >> LogBitsPerWord; - uintx bit_number = beg & right_n_bits(LogBitsPerWord); + uintx bit_number = beg & (BitsPerWord - 1); uintx element_bits = _bitmap[array_idx]; if (bit_number > 0) { - uintx mask_out = right_n_bits(bit_number); + uintx mask_out = tail_mask(bit_number); element_bits &= ~mask_out; } if (element_bits) { @@ -62,10 +69,10 @@ inline idx_t ShenandoahSimpleBitMap::find_last_set_bit(idx_t beg, idx_t end) con assert((beg >= -1) && (beg < end), "precondition"); do { idx_t array_idx = end >> LogBitsPerWord; - uintx bit_number = end & right_n_bits(LogBitsPerWord); + uint8_t bit_number = end & (BitsPerWord - 1); uintx element_bits = _bitmap[array_idx]; if (bit_number < BitsPerWord - 1){ - uintx mask_in = right_n_bits(bit_number + 1); + uintx mask_in = tail_mask(bit_number + 1); element_bits &= mask_in; } if (element_bits) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.cpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.cpp index 3cddc0c6c0a83..eb185c197bd5d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.cpp @@ -51,45 +51,6 @@ bool ShenandoahObjToScanQueueSet::is_empty() { return true; } -#if TASKQUEUE_STATS -void ShenandoahObjToScanQueueSet::print_taskqueue_stats_hdr(outputStream* const st) { - st->print_raw_cr("GC Task Stats"); - st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr(); - st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr(); -} - -void ShenandoahObjToScanQueueSet::print_taskqueue_stats() const { - if (!log_develop_is_enabled(Trace, gc, task, stats)) { - return; - } - Log(gc, task, stats) log; - ResourceMark rm; - LogStream ls(log.trace()); - outputStream* st = &ls; - print_taskqueue_stats_hdr(st); - - ShenandoahObjToScanQueueSet* queues = const_cast(this); - TaskQueueStats totals; - const uint n = size(); - for (uint i = 0; i < n; ++i) { - st->print(UINT32_FORMAT_W(3), i); - queues->queue(i)->stats.print(st); - st->cr(); - totals += queues->queue(i)->stats; - } - st->print("tot "); totals.print(st); st->cr(); - DEBUG_ONLY(totals.verify()); - -} - -void ShenandoahObjToScanQueueSet::reset_taskqueue_stats() { - const uint n = size(); - for (uint i = 0; i < n; ++i) { - queue(i)->stats.reset(); - } -} -#endif // TASKQUEUE_STATS - bool ShenandoahTerminatorTerminator::should_exit_termination() { return _heap->cancelled_gc(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index 50f18a8c73f92..10887ad8c19d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2016, 2024, Red Hat, Inc. All rights reserved. + * 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 @@ -28,7 +29,7 @@ #include "gc/shared/taskTerminator.hpp" #include "gc/shared/taskqueue.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutex.hpp" @@ -36,11 +37,11 @@ class ShenandoahHeap; -template -class BufferedOverflowTaskQueue: public OverflowTaskQueue +template +class BufferedOverflowTaskQueue: public OverflowTaskQueue { public: - typedef OverflowTaskQueue taskqueue_t; + typedef OverflowTaskQueue taskqueue_t; BufferedOverflowTaskQueue() : _buf_empty(true) {}; @@ -301,8 +302,8 @@ class ShenandoahMarkTask typedef BufferedOverflowTaskQueue ShenandoahBufferedOverflowTaskQueue; typedef Padded ShenandoahObjToScanQueue; -template -class ParallelClaimableQueueSet: public GenericTaskQueueSet { +template +class ParallelClaimableQueueSet: public GenericTaskQueueSet { private: shenandoah_padding(0); volatile jint _claimed_index; @@ -311,10 +312,10 @@ class ParallelClaimableQueueSet: public GenericTaskQueueSet { debug_only(uint _reserved; ) public: - using GenericTaskQueueSet::size; + using GenericTaskQueueSet::size; public: - ParallelClaimableQueueSet(int n) : GenericTaskQueueSet(n), _claimed_index(0) { + ParallelClaimableQueueSet(int n) : GenericTaskQueueSet(n), _claimed_index(0) { debug_only(_reserved = 0; ) } @@ -331,9 +332,9 @@ class ParallelClaimableQueueSet: public GenericTaskQueueSet { debug_only(uint get_reserved() const { return (uint)_reserved; }) }; -template -T* ParallelClaimableQueueSet::claim_next() { - jint size = (jint)GenericTaskQueueSet::size(); +template +T* ParallelClaimableQueueSet::claim_next() { + jint size = (jint)GenericTaskQueueSet::size(); if (_claimed_index >= size) { return nullptr; @@ -342,7 +343,7 @@ T* ParallelClaimableQueueSet::claim_next() { jint index = Atomic::add(&_claimed_index, 1, memory_order_relaxed); if (index <= size) { - return GenericTaskQueueSet::queue((uint)index - 1); + return GenericTaskQueueSet::queue((uint)index - 1); } else { return nullptr; } @@ -354,12 +355,6 @@ class ShenandoahObjToScanQueueSet: public ParallelClaimableQueueSet -bool BufferedOverflowTaskQueue::pop(E &t) { +template +bool BufferedOverflowTaskQueue::pop(E &t) { if (!_buf_empty) { t = _elem; _buf_empty = true; @@ -45,8 +46,8 @@ bool BufferedOverflowTaskQueue::pop(E &t) { return taskqueue_t::pop_overflow(t); } -template -inline bool BufferedOverflowTaskQueue::push(E t) { +template +inline bool BufferedOverflowTaskQueue::push(E t) { if (_buf_empty) { _elem = t; _buf_empty = false; @@ -58,8 +59,8 @@ inline bool BufferedOverflowTaskQueue::push(E t) { return true; } -template -void BufferedOverflowTaskQueue::clear() { +template +void BufferedOverflowTaskQueue::clear() { _buf_empty = true; taskqueue_t::set_empty(); taskqueue_t::overflow_stack()->clear(); diff --git a/src/hotspot/share/gc/x/xVirtualMemory.cpp b/src/hotspot/share/gc/x/xVirtualMemory.cpp index 1d66cdd069ef7..63cb789d8de12 100644 --- a/src/hotspot/share/gc/x/xVirtualMemory.cpp +++ b/src/hotspot/share/gc/x/xVirtualMemory.cpp @@ -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 @@ -182,7 +182,7 @@ bool XVirtualMemoryManager::reserve(size_t max_capacity) { void XVirtualMemoryManager::nmt_reserve(uintptr_t start, size_t size) { MemTracker::record_virtual_memory_reserve((void*)start, size, CALLER_PC); - MemTracker::record_virtual_memory_type((void*)start, mtJavaHeap); + MemTracker::record_virtual_memory_tag((void*)start, mtJavaHeap); } bool XVirtualMemoryManager::is_initialized() const { diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index f72e84eaf5935..12ee400eb2da2 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -46,7 +46,7 @@ #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" -template +template class ZArenaHashtable : public ResourceObj { class ZArenaHashtableEntry : public ResourceObj { public: @@ -55,10 +55,10 @@ class ZArenaHashtable : public ResourceObj { V _value; }; - static const size_t _table_mask = _table_size - 1; + static const size_t TableMask = TableSize - 1; Arena* _arena; - ZArenaHashtableEntry* _table[_table_size]; + ZArenaHashtableEntry* _table[TableSize]; public: class Iterator { @@ -84,7 +84,7 @@ class ZArenaHashtable : public ResourceObj { if (_current_entry != nullptr) { _current_entry = _current_entry->_next; } - while (_current_entry == nullptr && ++_current_index < _table_size) { + while (_current_entry == nullptr && ++_current_index < TableSize) { _current_entry = _table->_table[_current_index]; } } @@ -100,12 +100,12 @@ class ZArenaHashtable : public ResourceObj { ZArenaHashtableEntry* entry = new (_arena) ZArenaHashtableEntry(); entry->_key = key; entry->_value = value; - entry->_next = _table[key & _table_mask]; - _table[key & _table_mask] = entry; + entry->_next = _table[key & TableMask]; + _table[key & TableMask] = entry; } V* get(K key) const { - for (ZArenaHashtableEntry* e = _table[key & _table_mask]; e != nullptr; e = e->_next) { + for (ZArenaHashtableEntry* e = _table[key & TableMask]; e != nullptr; e = e->_next) { if (e->_key == key) { return &(e->_value); } @@ -239,21 +239,23 @@ void ZLoadBarrierStubC2::emit_code(MacroAssembler& masm) { ZBarrierSet::assembler()->generate_c2_load_barrier_stub(&masm, static_cast(this)); } -ZStoreBarrierStubC2* ZStoreBarrierStubC2::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) { +ZStoreBarrierStubC2* ZStoreBarrierStubC2::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) { AARCH64_ONLY(fatal("Should use ZStoreBarrierStubC2Aarch64::create")); - ZStoreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic); + ZStoreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive); register_stub(stub); return stub; } -ZStoreBarrierStubC2::ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) +ZStoreBarrierStubC2::ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, + bool is_native, bool is_atomic, bool is_nokeepalive) : ZBarrierStubC2(node), _ref_addr(ref_addr), _new_zaddress(new_zaddress), _new_zpointer(new_zpointer), _is_native(is_native), - _is_atomic(is_atomic) {} + _is_atomic(is_atomic), + _is_nokeepalive(is_nokeepalive) {} Address ZStoreBarrierStubC2::ref_addr() const { return _ref_addr; @@ -275,6 +277,10 @@ bool ZStoreBarrierStubC2::is_atomic() const { return _is_atomic; } +bool ZStoreBarrierStubC2::is_nokeepalive() const { + return _is_nokeepalive; +} + void ZStoreBarrierStubC2::emit_code(MacroAssembler& masm) { ZBarrierSet::assembler()->generate_c2_store_barrier_stub(&masm, static_cast(this)); } diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp index bf46780226eea..58f75441b910c 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp @@ -79,18 +79,20 @@ class ZStoreBarrierStubC2 : public ZBarrierStubC2 { const Register _new_zpointer; const bool _is_native; const bool _is_atomic; + const bool _is_nokeepalive; protected: - ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); public: - static ZStoreBarrierStubC2* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + static ZStoreBarrierStubC2* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); Address ref_addr() const; Register new_zaddress() const; Register new_zpointer() const; bool is_native() const; bool is_atomic() const; + bool is_nokeepalive() const; virtual void emit_code(MacroAssembler& masm); }; diff --git a/src/hotspot/share/gc/z/zBarrierSet.hpp b/src/hotspot/share/gc/z/zBarrierSet.hpp index bf233df683afb..9c20211e2285d 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.hpp +++ b/src/hotspot/share/gc/z/zBarrierSet.hpp @@ -155,7 +155,7 @@ class ZBarrierSet : public BarrierSet { }; template<> struct BarrierSet::GetName { - static const BarrierSet::Name value = BarrierSet::ZBarrierSet; + static const BarrierSet::Name Value = BarrierSet::ZBarrierSet; }; template<> struct BarrierSet::GetType { diff --git a/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp b/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp index b41fec3d0a552..c267d9bb24aa6 100644 --- a/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp @@ -59,6 +59,10 @@ JRT_LEAF(void, ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing(oo ZBarrier::store_barrier_on_heap_oop_field((zpointer*)p, false /* heal */); JRT_END +JRT_LEAF(void, ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing(oop* p)) + ZBarrier::no_keep_alive_store_barrier_on_heap_oop_field((zpointer*)p); +JRT_END + JRT_LEAF(void, ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing(oop* p)) ZBarrier::store_barrier_on_native_oop_field((zpointer*)p, false /* heal */); JRT_END @@ -126,6 +130,10 @@ address ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr() { return reinterpret_cast
(store_barrier_on_oop_field_without_healing); } +address ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr() { + return reinterpret_cast
(no_keepalive_store_barrier_on_oop_field_without_healing); +} + address ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr() { return reinterpret_cast
(store_barrier_on_native_oop_field_without_healing); } diff --git a/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp b/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp index 8a81f162bf1ef..8d59be02e68c9 100644 --- a/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp +++ b/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp @@ -40,6 +40,7 @@ class ZBarrierSetRuntime : public AllStatic { static oopDesc* no_keepalive_load_barrier_on_phantom_oop_field_preloaded(oopDesc* o, oop* p); static void store_barrier_on_oop_field_with_healing(oop* p); static void store_barrier_on_oop_field_without_healing(oop* p); + static void no_keepalive_store_barrier_on_oop_field_without_healing(oop* p); static void store_barrier_on_native_oop_field_without_healing(oop* p); static void load_barrier_on_oop_array(oop* p, size_t length); static void clone(oopDesc* src, oopDesc* dst, size_t size); @@ -54,6 +55,7 @@ class ZBarrierSetRuntime : public AllStatic { static address no_keepalive_load_barrier_on_phantom_oop_field_preloaded_addr(); static address store_barrier_on_oop_field_with_healing_addr(); static address store_barrier_on_oop_field_without_healing_addr(); + static address no_keepalive_store_barrier_on_oop_field_without_healing_addr(); static address store_barrier_on_native_oop_field_without_healing_addr(); static address load_barrier_on_oop_array_addr(); static address clone_addr(); diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp index ccfa7af6b7d72..8afefd5a7cc1c 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.cpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp @@ -49,6 +49,7 @@ #include "memory/universe.hpp" #include "oops/stackChunkOop.hpp" #include "runtime/continuationJavaClasses.hpp" +#include "runtime/java.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/stackWatermarkSet.hpp" #include "services/memoryUsage.hpp" @@ -60,7 +61,7 @@ ZCollectedHeap* ZCollectedHeap::heap() { ZCollectedHeap::ZCollectedHeap() : _barrier_set(), - _initialize(&_barrier_set), + _initializer(&_barrier_set), _heap(), _driver_minor(new ZDriverMinor()), _driver_major(new ZDriverMajor()), @@ -78,11 +79,14 @@ const char* ZCollectedHeap::name() const { jint ZCollectedHeap::initialize() { if (!_heap.is_initialized()) { + vm_shutdown_during_initialization(ZInitialize::error_message()); return JNI_ENOMEM; } Universe::set_verify_data(~(ZAddressHeapBase - 1) | 0x7, ZAddressHeapBase); + ZInitialize::finish(); + return JNI_OK; } diff --git a/src/hotspot/share/gc/z/zCollectedHeap.hpp b/src/hotspot/share/gc/z/zCollectedHeap.hpp index 528bacd8df82b..434204e16b80d 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.hpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp @@ -43,7 +43,7 @@ class ZCollectedHeap : public CollectedHeap { private: ZBarrierSet _barrier_set; - ZInitialize _initialize; + ZInitializer _initializer; ZHeap _heap; ZDriverMinor* _driver_minor; ZDriverMajor* _driver_major; diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index 8901a9fbc6500..3c0cb660206a2 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -535,6 +535,19 @@ static double calculate_young_to_old_worker_ratio(const ZDirectorStats& stats) { const size_t reclaimed_per_old_gc = stats._old_stats._stat_heap._reclaimed_avg; const double current_young_bytes_freed_per_gc_time = double(reclaimed_per_young_gc) / double(young_gc_time); const double current_old_bytes_freed_per_gc_time = double(reclaimed_per_old_gc) / double(old_gc_time); + + if (current_young_bytes_freed_per_gc_time == 0.0) { + if (current_old_bytes_freed_per_gc_time == 0.0) { + // Neither young nor old collections have reclaimed any memory. + // Give them equal priority. + return 1.0; + } + + // Only old collections have reclaimed memory. + // Prioritize old. + return ZOldGCThreads; + } + const double old_vs_young_efficiency_ratio = current_old_bytes_freed_per_gc_time / current_young_bytes_freed_per_gc_time; return old_vs_young_efficiency_ratio; @@ -839,7 +852,7 @@ void ZDirector::evaluate_rules() { } bool ZDirector::wait_for_tick() { - const uint64_t interval_ms = MILLIUNITS / decision_hz; + const uint64_t interval_ms = MILLIUNITS / DecisionHz; ZLocker locker(&_monitor); diff --git a/src/hotspot/share/gc/z/zDirector.hpp b/src/hotspot/share/gc/z/zDirector.hpp index 73c556a2fbd46..929ec6c2c566b 100644 --- a/src/hotspot/share/gc/z/zDirector.hpp +++ b/src/hotspot/share/gc/z/zDirector.hpp @@ -29,7 +29,7 @@ class ZDirector : public ZThread { private: - static const uint64_t decision_hz = 100; + static const uint64_t DecisionHz = 100; static ZDirector* _director; ZConditionLock _monitor; diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 21ff703796219..1e917bb5ee39d 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -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 @@ -34,6 +34,7 @@ #include "gc/z/zHeap.inline.hpp" #include "gc/z/zHeapIterator.hpp" #include "gc/z/zHeuristics.hpp" +#include "gc/z/zInitialize.hpp" #include "gc/z/zPage.inline.hpp" #include "gc/z/zPageTable.inline.hpp" #include "gc/z/zResurrection.hpp" @@ -74,7 +75,7 @@ ZHeap::ZHeap() // Prime cache if (!_page_allocator.prime_cache(_old.workers(), InitialHeapSize)) { - log_error_p(gc)("Failed to allocate initial Java heap (" SIZE_FORMAT "M)", InitialHeapSize / M); + ZInitialize::error("Failed to allocate initial Java heap (" SIZE_FORMAT "M)", InitialHeapSize / M); return; } @@ -240,20 +241,19 @@ void ZHeap::undo_alloc_page(ZPage* page) { log_trace(gc)("Undo page allocation, thread: " PTR_FORMAT " (%s), page: " PTR_FORMAT ", size: " SIZE_FORMAT, p2i(Thread::current()), ZUtils::thread_name(), p2i(page), page->size()); - free_page(page); + free_page(page, false /* allow_defragment */); } -void ZHeap::free_page(ZPage* page) { +void ZHeap::free_page(ZPage* page, bool allow_defragment) { // Remove page table entry _page_table.remove(page); if (page->is_old()) { - page->verify_remset_cleared_current(); - page->verify_remset_cleared_previous(); + page->remset_delete(); } // Free page - _page_allocator.free_page(page); + _page_allocator.free_page(page, allow_defragment); } size_t ZHeap::free_empty_pages(const ZArray* pages) { @@ -261,12 +261,10 @@ size_t ZHeap::free_empty_pages(const ZArray* pages) { // Remove page table entries ZArrayIterator iter(pages); for (ZPage* page; iter.next(&page);) { + _page_table.remove(page); if (page->is_old()) { - // The remset of pages should be clean when installed into the page - // cache. - page->remset_clear(); + page->remset_delete(); } - _page_table.remove(page); freed += page->size(); } diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index 18fa0d6349bea..7b75c63cf8ce0 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -104,7 +104,7 @@ class ZHeap { // Page allocation ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age); void undo_alloc_page(ZPage* page); - void free_page(ZPage* page); + void free_page(ZPage* page, bool allow_defragment); size_t free_empty_pages(const ZArray* pages); // Object allocation diff --git a/src/hotspot/share/gc/z/zInitialize.cpp b/src/hotspot/share/gc/z/zInitialize.cpp index 52229bf283097..e37fc550bfe2a 100644 --- a/src/hotspot/share/gc/z/zInitialize.cpp +++ b/src/hotspot/share/gc/z/zInitialize.cpp @@ -22,6 +22,7 @@ */ #include "precompiled.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "gc/z/zAddress.hpp" #include "gc/z/zBarrierSet.hpp" #include "gc/z/zCPU.hpp" @@ -38,9 +39,19 @@ #include "gc/z/zThreadLocalAllocBuffer.hpp" #include "gc/z/zTracer.hpp" #include "logging/log.hpp" +#include "nmt/memTag.hpp" #include "runtime/vm_version.hpp" +#include "utilities/formatBuffer.hpp" -ZInitialize::ZInitialize(ZBarrierSet* barrier_set) { +char ZInitialize::_error_message[ErrorMessageLength] = {}; +bool ZInitialize::_had_error = false; +bool ZInitialize::_finished = false; + +ZInitializer::ZInitializer(ZBarrierSet* barrier_set) { + ZInitialize::initialize(barrier_set); +} + +void ZInitialize::initialize(ZBarrierSet* barrier_set) { log_info(gc, init)("Initializing %s", ZName); log_info(gc, init)("Version: %s (%s)", VM_Version::vm_release(), @@ -62,3 +73,51 @@ ZInitialize::ZInitialize(ZBarrierSet* barrier_set) { pd_initialize(); } + +void ZInitialize::register_error(bool debug, const char *error_msg) { + guarantee(!_finished, "Only register errors during initialization"); + + if (!_had_error) { + strncpy(_error_message, error_msg, ErrorMessageLength - 1); + _had_error = true; + } + + if (debug) { + log_error_pd(gc)("%s", error_msg); + } else { + log_error_p(gc)("%s", error_msg); + } +} + +void ZInitialize::error(const char* msg_format, ...) { + va_list argp; + va_start(argp, msg_format); + const FormatBuffer error_msg(FormatBufferDummy(), msg_format, argp); + va_end(argp); + register_error(false /* debug */, error_msg); +} + +void ZInitialize::error_d(const char* msg_format, ...) { + va_list argp; + va_start(argp, msg_format); + const FormatBuffer error_msg(FormatBufferDummy(), msg_format, argp); + va_end(argp); + register_error(true /* debug */, error_msg); +} + +bool ZInitialize::had_error() { + return _had_error; +} + +const char* ZInitialize::error_message() { + assert(had_error(), "Should have registered an error"); + if (had_error()) { + return _error_message; + } + return "Unknown error, check error GC logs"; +} + +void ZInitialize::finish() { + guarantee(!_finished, "Only finish initialization once"); + _finished = true; +} diff --git a/src/hotspot/share/gc/z/zInitialize.hpp b/src/hotspot/share/gc/z/zInitialize.hpp index 599b656623448..3c551b4c62260 100644 --- a/src/hotspot/share/gc/z/zInitialize.hpp +++ b/src/hotspot/share/gc/z/zInitialize.hpp @@ -24,16 +24,39 @@ #ifndef SHARE_GC_Z_ZINITIALIZE_HPP #define SHARE_GC_Z_ZINITIALIZE_HPP -#include "memory/allocation.hpp" +#include "memory/allStatic.hpp" +#include "utilities/compilerWarnings.hpp" + +#include class ZBarrierSet; -class ZInitialize { +class ZInitializer { +public: + ZInitializer(ZBarrierSet* barrier_set); +}; + +class ZInitialize : public AllStatic { private: - void pd_initialize(); + static constexpr size_t ErrorMessageLength = 256; + + static char _error_message[ErrorMessageLength]; + static bool _had_error; + static bool _finished; + + static void register_error(bool debug, const char *error_msg); + + static void pd_initialize(); public: - ZInitialize(ZBarrierSet* barrier_set); + static void error(const char* msg_format, ...) ATTRIBUTE_PRINTF(1, 2); + static void error_d(const char* msg_format, ...) ATTRIBUTE_PRINTF(1, 2); + + static bool had_error(); + static const char* error_message(); + + static void initialize(ZBarrierSet* barrier_set); + static void finish(); }; #endif // SHARE_GC_Z_ZINITIALIZE_HPP diff --git a/src/hotspot/share/gc/z/zLiveMap.cpp b/src/hotspot/share/gc/z/zLiveMap.cpp index 4123620f8b7e0..715ebc6291724 100644 --- a/src/hotspot/share/gc/z/zLiveMap.cpp +++ b/src/hotspot/share/gc/z/zLiveMap.cpp @@ -35,9 +35,9 @@ static const ZStatCounter ZCounterMarkSeqNumResetContention("Contention", "Mark SeqNum Reset Contention", ZStatUnitOpsPerSecond); static const ZStatCounter ZCounterMarkSegmentResetContention("Contention", "Mark Segment Reset Contention", ZStatUnitOpsPerSecond); -static size_t bitmap_size(uint32_t size, size_t nsegments) { +static size_t bitmap_size(uint32_t size, size_t NumSegments) { // We need at least one bit per segment - return MAX2(size, nsegments) * 2; + return MAX2(size, NumSegments) * 2; } ZLiveMap::ZLiveMap(uint32_t size) @@ -46,7 +46,7 @@ ZLiveMap::ZLiveMap(uint32_t size) _live_bytes(0), _segment_live_bits(0), _segment_claim_bits(0), - _bitmap(bitmap_size(size, nsegments)), + _bitmap(bitmap_size(size, NumSegments)), _segment_shift(log2i_exact(segment_size())) {} void ZLiveMap::reset(ZGenerationId id) { @@ -127,7 +127,7 @@ void ZLiveMap::reset_segment(BitMap::idx_t segment) { } void ZLiveMap::resize(uint32_t size) { - const size_t new_bitmap_size = bitmap_size(size, nsegments); + const size_t new_bitmap_size = bitmap_size(size, NumSegments); if (_bitmap.size() != new_bitmap_size) { _bitmap.reinitialize(new_bitmap_size, false /* clear */); _segment_shift = log2i_exact(segment_size()); diff --git a/src/hotspot/share/gc/z/zLiveMap.hpp b/src/hotspot/share/gc/z/zLiveMap.hpp index f8b16d06dc52c..7c18e1db06096 100644 --- a/src/hotspot/share/gc/z/zLiveMap.hpp +++ b/src/hotspot/share/gc/z/zLiveMap.hpp @@ -35,7 +35,7 @@ class ZLiveMap { friend class ZLiveMapTest; private: - static const size_t nsegments = 64; + static const size_t NumSegments = 64; volatile uint32_t _seqnum; volatile uint32_t _live_objects; diff --git a/src/hotspot/share/gc/z/zLiveMap.inline.hpp b/src/hotspot/share/gc/z/zLiveMap.inline.hpp index 28390b72a89ca..a9382522480c0 100644 --- a/src/hotspot/share/gc/z/zLiveMap.inline.hpp +++ b/src/hotspot/share/gc/z/zLiveMap.inline.hpp @@ -52,19 +52,19 @@ inline size_t ZLiveMap::live_bytes() const { } inline const BitMapView ZLiveMap::segment_live_bits() const { - return BitMapView(const_cast(&_segment_live_bits), nsegments); + return BitMapView(const_cast(&_segment_live_bits), NumSegments); } inline const BitMapView ZLiveMap::segment_claim_bits() const { - return BitMapView(const_cast(&_segment_claim_bits), nsegments); + return BitMapView(const_cast(&_segment_claim_bits), NumSegments); } inline BitMapView ZLiveMap::segment_live_bits() { - return BitMapView(&_segment_live_bits, nsegments); + return BitMapView(&_segment_live_bits, NumSegments); } inline BitMapView ZLiveMap::segment_claim_bits() { - return BitMapView(&_segment_claim_bits, nsegments); + return BitMapView(&_segment_claim_bits, NumSegments); } inline bool ZLiveMap::is_segment_live(BitMap::idx_t segment) const { @@ -80,15 +80,15 @@ inline bool ZLiveMap::claim_segment(BitMap::idx_t segment) { } inline BitMap::idx_t ZLiveMap::first_live_segment() const { - return segment_live_bits().find_first_set_bit(0, nsegments); + return segment_live_bits().find_first_set_bit(0, NumSegments); } inline BitMap::idx_t ZLiveMap::next_live_segment(BitMap::idx_t segment) const { - return segment_live_bits().find_first_set_bit(segment + 1, nsegments); + return segment_live_bits().find_first_set_bit(segment + 1, NumSegments); } inline BitMap::idx_t ZLiveMap::segment_size() const { - return _bitmap.size() / nsegments; + return _bitmap.size() / NumSegments; } inline BitMap::idx_t ZLiveMap::index_to_segment(BitMap::idx_t index) const { @@ -167,7 +167,7 @@ inline void ZLiveMap::iterate(ZGenerationId id, Function function) { return true; }; - for (BitMap::idx_t segment = first_live_segment(); segment < nsegments; segment = next_live_segment(segment)) { + for (BitMap::idx_t segment = first_live_segment(); segment < NumSegments; segment = next_live_segment(segment)) { // For each live segment iterate_segment(segment, live_only); } diff --git a/src/hotspot/share/gc/z/zMarkStackAllocator.cpp b/src/hotspot/share/gc/z/zMarkStackAllocator.cpp index a9e404a0f55c1..100036dc3fe53 100644 --- a/src/hotspot/share/gc/z/zMarkStackAllocator.cpp +++ b/src/hotspot/share/gc/z/zMarkStackAllocator.cpp @@ -22,8 +22,8 @@ */ #include "precompiled.hpp" -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/z/zInitialize.hpp" #include "gc/z/zLock.inline.hpp" #include "gc/z/zMarkStack.inline.hpp" #include "gc/z/zMarkStackAllocator.hpp" @@ -43,7 +43,7 @@ ZMarkStackSpace::ZMarkStackSpace() const size_t size = ZMarkStackSpaceLimit; const uintptr_t addr = (uintptr_t)os::reserve_memory(size, !ExecMem, mtGC); if (addr == 0) { - log_error_pd(gc, marking)("Failed to reserve address space for mark stacks"); + ZInitialize::error_d("Failed to reserve address space for mark stacks"); return; } diff --git a/src/hotspot/share/gc/z/zNMT.cpp b/src/hotspot/share/gc/z/zNMT.cpp index 41d99b102bf08..b23452eb15647 100644 --- a/src/hotspot/share/gc/z/zNMT.cpp +++ b/src/hotspot/share/gc/z/zNMT.cpp @@ -26,7 +26,7 @@ #include "gc/z/zGlobals.hpp" #include "gc/z/zNMT.hpp" #include "gc/z/zVirtualMemory.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "nmt/memTracker.hpp" #include "nmt/memoryFileTracker.hpp" #include "utilities/nativeCallStack.hpp" diff --git a/src/hotspot/share/gc/z/zPage.cpp b/src/hotspot/share/gc/z/zPage.cpp index dc40c5367c121..ff56768e9ad99 100644 --- a/src/hotspot/share/gc/z/zPage.cpp +++ b/src/hotspot/share/gc/z/zPage.cpp @@ -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 @@ -78,27 +78,16 @@ void ZPage::reset_seqnum() { Atomic::store(&_seqnum_other, ZGeneration::generation(_generation_id == ZGenerationId::young ? ZGenerationId::old : ZGenerationId::young)->seqnum()); } -void ZPage::remset_initialize() { - // Remsets should only be initialized once and only for old pages. +void ZPage::remset_alloc() { + // Remsets should only be allocated/initialized once and only for old pages. assert(!_remembered_set.is_initialized(), "Should not be initialized"); assert(is_old(), "Only old pages need a remset"); _remembered_set.initialize(size()); } -void ZPage::remset_initialize_or_verify_cleared() { - assert(is_old(), "Only old pages need a remset"); - - if (_remembered_set.is_initialized()) { - verify_remset_cleared_current(); - verify_remset_cleared_previous(); - } else { - remset_initialize(); - } -} - -void ZPage::remset_clear() { - _remembered_set.clear_all(); +void ZPage::remset_delete() { + _remembered_set.delete_all(); } void ZPage::reset(ZPageAge age) { @@ -123,7 +112,6 @@ void ZPage::reset_top_for_allocation() { void ZPage::reset_type_and_size(ZPageType type) { _type = type; _livemap.resize(object_max_count()); - _remembered_set.resize(size()); } ZPage* ZPage::retype(ZPageType type) { @@ -216,10 +204,6 @@ void ZPage::verify_remset_cleared_previous() const { } } -void ZPage::clear_remset_current() { - _remembered_set.clear_current(); -} - void ZPage::clear_remset_previous() { _remembered_set.clear_previous(); } diff --git a/src/hotspot/share/gc/z/zPage.hpp b/src/hotspot/share/gc/z/zPage.hpp index 42e14f904bc77..9b6c155f77d9f 100644 --- a/src/hotspot/share/gc/z/zPage.hpp +++ b/src/hotspot/share/gc/z/zPage.hpp @@ -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 @@ -155,9 +155,8 @@ class ZPage : public CHeapObj { void clear_remset_range_non_par_current(uintptr_t l_offset, size_t size); void swap_remset_bitmaps(); - void remset_initialize(); - void remset_initialize_or_verify_cleared(); - void remset_clear(); + void remset_alloc(); + void remset_delete(); ZBitMap::ReverseIterator remset_reverse_iterator_previous(); BitMap::Iterator remset_iterator_limited_current(uintptr_t l_offset, size_t size); @@ -182,7 +181,6 @@ class ZPage : public CHeapObj { void verify_remset_cleared_current() const; void verify_remset_cleared_previous() const; - void clear_remset_current(); void clear_remset_previous(); void* remset_current(); diff --git a/src/hotspot/share/gc/z/zPageAllocator.cpp b/src/hotspot/share/gc/z/zPageAllocator.cpp index f5d8ae6e3d160..010241294a701 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.cpp +++ b/src/hotspot/share/gc/z/zPageAllocator.cpp @@ -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 @@ -275,7 +275,7 @@ bool ZPageAllocator::prime_cache(ZWorkers* workers, size_t size) { workers->run_all(&task); } - free_page(page); + free_page(page, false /* allow_defragment */); return true; } @@ -462,6 +462,38 @@ void ZPageAllocator::destroy_page(ZPage* page) { safe_destroy_page(page); } +bool ZPageAllocator::should_defragment(const ZPage* page) const { + // A small page can end up at a high address (second half of the address space) + // if we've split a larger page or we have a constrained address space. To help + // fight address space fragmentation we remap such pages to a lower address, if + // a lower address is available. + return page->type() == ZPageType::small && + page->start() >= to_zoffset(_virtual.reserved() / 2) && + page->start() > _virtual.lowest_available_address(); +} + +ZPage* ZPageAllocator::defragment_page(ZPage* page) { + // Harvest the physical memory (which is committed) + ZPhysicalMemory pmem; + ZPhysicalMemory& old_pmem = page->physical_memory(); + pmem.add_segments(old_pmem); + old_pmem.remove_segments(); + + _unmapper->unmap_and_destroy_page(page); + + // Allocate new virtual memory at a low address + const ZVirtualMemory vmem = _virtual.alloc(pmem.size(), true /* force_low_address */); + + // Create the new page and map it + ZPage* new_page = new ZPage(ZPageType::small, vmem, pmem); + map_page(new_page); + + // Update statistics + ZStatInc(ZCounterDefragment); + + return new_page; +} + bool ZPageAllocator::is_alloc_allowed(size_t size) const { const size_t available = _current_max_capacity - _used - _claimed; return available >= size; @@ -623,16 +655,6 @@ ZPage* ZPageAllocator::alloc_page_create(ZPageAllocation* allocation) { return new ZPage(allocation->type(), vmem, pmem); } -bool ZPageAllocator::should_defragment(const ZPage* page) const { - // A small page can end up at a high address (second half of the address space) - // if we've split a larger page or we have a constrained address space. To help - // fight address space fragmentation we remap such pages to a lower address, if - // a lower address is available. - return page->type() == ZPageType::small && - page->start() >= to_zoffset(_virtual.reserved() / 2) && - page->start() > _virtual.lowest_available_address(); -} - bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const { // The allocation is immediately satisfied if the list of pages contains // exactly one page, with the type and size that was requested. However, @@ -652,12 +674,6 @@ bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const { return false; } - if (should_defragment(page)) { - // Defragment address space - ZStatInc(ZCounterDefragment); - return false; - } - // Allocation immediately satisfied return true; } @@ -733,7 +749,7 @@ ZPage* ZPageAllocator::alloc_page(ZPageType type, size_t size, ZAllocationFlags page->reset_top_for_allocation(); page->reset_livemap(); if (age == ZPageAge::old) { - page->remset_initialize_or_verify_cleared(); + page->remset_alloc(); } // Update allocation statistics. Exclude gc relocations to avoid @@ -773,6 +789,18 @@ void ZPageAllocator::satisfy_stalled() { } } +ZPage* ZPageAllocator::prepare_to_recycle(ZPage* page, bool allow_defragment) { + // Make sure we have a page that is safe to recycle + ZPage* const to_recycle = _safe_recycle.register_and_clone_if_activated(page); + + // Defragment the page before recycle if allowed and needed + if (allow_defragment && should_defragment(to_recycle)) { + return defragment_page(to_recycle); + } + + return to_recycle; +} + void ZPageAllocator::recycle_page(ZPage* page) { // Set time when last used page->set_last_used(); @@ -781,9 +809,11 @@ void ZPageAllocator::recycle_page(ZPage* page) { _cache.free_page(page); } -void ZPageAllocator::free_page(ZPage* page) { +void ZPageAllocator::free_page(ZPage* page, bool allow_defragment) { const ZGenerationId generation_id = page->generation_id(); - ZPage* const to_recycle = _safe_recycle.register_and_clone_if_activated(page); + + // Prepare page for recycling before taking the lock + ZPage* const to_recycle = prepare_to_recycle(page, allow_defragment); ZLocker locker(&_lock); @@ -800,11 +830,12 @@ void ZPageAllocator::free_page(ZPage* page) { } void ZPageAllocator::free_pages(const ZArray* pages) { - ZArray to_recycle; + ZArray to_recycle_pages; size_t young_size = 0; size_t old_size = 0; + // Prepare pages for recycling before taking the lock ZArrayIterator pages_iter(pages); for (ZPage* page; pages_iter.next(&page);) { if (page->is_young()) { @@ -812,7 +843,12 @@ void ZPageAllocator::free_pages(const ZArray* pages) { } else { old_size += page->size(); } - to_recycle.push(_safe_recycle.register_and_clone_if_activated(page)); + + // Prepare to recycle + ZPage* const to_recycle = prepare_to_recycle(page, true /* allow_defragment */); + + // Register for recycling + to_recycle_pages.push(to_recycle); } ZLocker locker(&_lock); @@ -823,7 +859,7 @@ void ZPageAllocator::free_pages(const ZArray* pages) { decrease_used_generation(ZGenerationId::old, old_size); // Free pages - ZArrayIterator iter(&to_recycle); + ZArrayIterator iter(&to_recycle_pages); for (ZPage* page; iter.next(&page);) { recycle_page(page); } @@ -833,11 +869,16 @@ void ZPageAllocator::free_pages(const ZArray* pages) { } void ZPageAllocator::free_pages_alloc_failed(ZPageAllocation* allocation) { - ZArray to_recycle; + ZArray to_recycle_pages; + // Prepare pages for recycling before taking the lock ZListRemoveIterator allocation_pages_iter(allocation->pages()); for (ZPage* page; allocation_pages_iter.next(&page);) { - to_recycle.push(_safe_recycle.register_and_clone_if_activated(page)); + // Prepare to recycle + ZPage* const to_recycle = prepare_to_recycle(page, false /* allow_defragment */); + + // Register for recycling + to_recycle_pages.push(to_recycle); } ZLocker locker(&_lock); @@ -849,7 +890,7 @@ void ZPageAllocator::free_pages_alloc_failed(ZPageAllocation* allocation) { size_t freed = 0; // Free any allocated/flushed pages - ZArrayIterator iter(&to_recycle); + ZArrayIterator iter(&to_recycle_pages); for (ZPage* page; iter.next(&page);) { freed += page->size(); recycle_page(page); diff --git a/src/hotspot/share/gc/z/zPageAllocator.hpp b/src/hotspot/share/gc/z/zPageAllocator.hpp index 5d3d59a416344..7df83a10eaf5a 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.hpp +++ b/src/hotspot/share/gc/z/zPageAllocator.hpp @@ -104,13 +104,15 @@ class ZPageAllocator { void destroy_page(ZPage* page); + bool should_defragment(const ZPage* page) const; + ZPage* defragment_page(ZPage* page); + bool is_alloc_allowed(size_t size) const; bool alloc_page_common_inner(ZPageType type, size_t size, ZList* pages); bool alloc_page_common(ZPageAllocation* allocation); bool alloc_page_stall(ZPageAllocation* allocation); bool alloc_page_or_stall(ZPageAllocation* allocation); - bool should_defragment(const ZPage* page) const; bool is_alloc_satisfied(ZPageAllocation* allocation) const; ZPage* alloc_page_create(ZPageAllocation* allocation); ZPage* alloc_page_finalize(ZPageAllocation* allocation); @@ -149,9 +151,10 @@ class ZPageAllocator { void reset_statistics(ZGenerationId id); ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age); + ZPage* prepare_to_recycle(ZPage* page, bool allow_defragment); void recycle_page(ZPage* page); void safe_destroy_page(ZPage* page); - void free_page(ZPage* page); + void free_page(ZPage* page, bool allow_defragment); void free_pages(const ZArray* pages); void enable_safe_destroy() const; diff --git a/src/hotspot/share/gc/z/zReferenceProcessor.hpp b/src/hotspot/share/gc/z/zReferenceProcessor.hpp index f8e924ed99fc1..31c789ee859e5 100644 --- a/src/hotspot/share/gc/z/zReferenceProcessor.hpp +++ b/src/hotspot/share/gc/z/zReferenceProcessor.hpp @@ -36,8 +36,8 @@ class ZReferenceProcessor : public ReferenceDiscoverer { friend class ZReferenceProcessorTask; private: - static const size_t reference_type_count = REF_PHANTOM + 1; - typedef size_t Counters[reference_type_count]; + static const size_t ReferenceTypeCount = REF_PHANTOM + 1; + typedef size_t Counters[ReferenceTypeCount]; ZWorkers* const _workers; ReferencePolicy* _soft_reference_policy; diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index 90209e4c62253..7f69c0752bc5a 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -411,7 +411,7 @@ static void retire_target_page(ZGeneration* generation, ZPage* page) { // relocate the remaining objects, leaving the target page empty when // relocation completed. if (page->used() == 0) { - ZHeap::heap()->free_page(page); + ZHeap::heap()->free_page(page, true /* allow_defragment */); } } @@ -848,7 +848,7 @@ class ZRelocateWork : public StackObj { to_page->reset(to_age); to_page->reset_top_for_allocation(); if (promotion) { - to_page->remset_initialize(); + to_page->remset_alloc(); } // Verify that the inactive remset is clear when resetting the page for @@ -943,35 +943,15 @@ class ZRelocateWork : public StackObj { return ZGeneration::old()->active_remset_is_current(); } - void clear_remset_before_reuse(ZPage* page, bool in_place) { + void clear_remset_before_in_place_reuse(ZPage* page) { if (_forwarding->from_age() != ZPageAge::old) { // No remset bits return; } - if (in_place) { - // Clear 'previous' remset bits. For in-place relocated pages, the previous - // remset bits are always used, even when active_remset_is_current(). - page->clear_remset_previous(); - - return; - } - - // Normal relocate - - // Clear active remset bits - if (active_remset_is_current()) { - page->clear_remset_current(); - } else { - page->clear_remset_previous(); - } - - // Verify that inactive remset bits are all cleared - if (active_remset_is_current()) { - page->verify_remset_cleared_previous(); - } else { - page->verify_remset_cleared_current(); - } + // Clear 'previous' remset bits. For in-place relocated pages, the previous + // remset bits are always used, even when active_remset_is_current(). + page->clear_remset_previous(); } void finish_in_place_relocation() { @@ -1017,7 +997,7 @@ class ZRelocateWork : public StackObj { ZPage* const page = _forwarding->detach_page(); // Ensure that previous remset bits are cleared - clear_remset_before_reuse(page, true /* in_place */); + clear_remset_before_in_place_reuse(page); page->log_msg(" (relocate page done in-place)"); @@ -1029,15 +1009,10 @@ class ZRelocateWork : public StackObj { // Wait for all other threads to call release_page ZPage* const page = _forwarding->detach_page(); - // Ensure that all remset bits are cleared - // Note: cleared after detach_page, when we know that - // the young generation isn't scanning the remset. - clear_remset_before_reuse(page, false /* in_place */); - page->log_msg(" (relocate page done normal)"); // Free page - ZHeap::heap()->free_page(page); + ZHeap::heap()->free_page(page, true /* allow_defragment */); } } }; @@ -1292,7 +1267,7 @@ class ZFlipAgePagesTask : public ZTask { new_page->reset(to_age); new_page->reset_livemap(); if (promotion) { - new_page->remset_initialize(); + new_page->remset_alloc(); } if (promotion) { diff --git a/src/hotspot/share/gc/z/zRememberedSet.cpp b/src/hotspot/share/gc/z/zRememberedSet.cpp index ed1dcfaf14d55..f605401648dfb 100644 --- a/src/hotspot/share/gc/z/zRememberedSet.cpp +++ b/src/hotspot/share/gc/z/zRememberedSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,18 +55,10 @@ void ZRememberedSet::initialize(size_t page_size) { _bitmap[1].initialize(size_in_bits, true /* clear */); } -void ZRememberedSet::resize(size_t page_size) { - // The bitmaps only need to be resized if remset has been - // initialized, and hence the bitmaps have been initialized. - if (is_initialized()) { - const BitMap::idx_t size_in_bits = to_bit_size(page_size); - - // The bitmaps need to be cleared when free, but since this function is - // only used for shrinking the clear argument is correct but not crucial. - assert(size_in_bits <= _bitmap[0].size(), "Only used for shrinking"); - _bitmap[0].resize(size_in_bits, true /* clear */); - _bitmap[1].resize(size_in_bits, true /* clear */); - } +void ZRememberedSet::delete_all() { + assert(is_initialized(), "precondition"); + _bitmap[0].resize(0); + _bitmap[1].resize(0); } bool ZRememberedSet::is_cleared_current() const { @@ -77,15 +69,6 @@ bool ZRememberedSet::is_cleared_previous() const { return previous()->is_empty(); } -void ZRememberedSet::clear_all() { - _bitmap[0].clear_large(); - _bitmap[1].clear_large(); -} - -void ZRememberedSet::clear_current() { - current()->clear_large(); -} - void ZRememberedSet::clear_previous() { previous()->clear_large(); } diff --git a/src/hotspot/share/gc/z/zRememberedSet.hpp b/src/hotspot/share/gc/z/zRememberedSet.hpp index d18c357f0ddbe..c3c6e8bc31317 100644 --- a/src/hotspot/share/gc/z/zRememberedSet.hpp +++ b/src/hotspot/share/gc/z/zRememberedSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -114,8 +114,7 @@ class ZRememberedSet { bool is_initialized() const; void initialize(size_t page_size); - - void resize(size_t page_size); + void delete_all(); bool at_current(uintptr_t offset) const; bool at_previous(uintptr_t offset) const; @@ -133,8 +132,6 @@ class ZRememberedSet { bool is_cleared_current() const; bool is_cleared_previous() const; - void clear_all(); - void clear_current(); void clear_previous(); void swap_remset_bitmaps(); diff --git a/src/hotspot/share/gc/z/zStackWatermark.cpp b/src/hotspot/share/gc/z/zStackWatermark.cpp index ead5300e9c318..0c7ec553e5c0c 100644 --- a/src/hotspot/share/gc/z/zStackWatermark.cpp +++ b/src/hotspot/share/gc/z/zStackWatermark.cpp @@ -130,7 +130,7 @@ void ZStackWatermark::save_old_watermark() { } else { // Found none too replace - push it to the top _old_watermarks_newest++; - assert(_old_watermarks_newest < _old_watermarks_max, "Unexpected amount of old watermarks"); + assert(_old_watermarks_newest < OldWatermarksMax, "Unexpected amount of old watermarks"); } // Install old watermark diff --git a/src/hotspot/share/gc/z/zStackWatermark.hpp b/src/hotspot/share/gc/z/zStackWatermark.hpp index fd4efeef76131..f2cf59d3b4a80 100644 --- a/src/hotspot/share/gc/z/zStackWatermark.hpp +++ b/src/hotspot/share/gc/z/zStackWatermark.hpp @@ -59,8 +59,8 @@ class ZStackWatermark : public StackWatermark { private: // Stores old watermarks, which describes the // colors of the non-processed part of the stack. - const static int _old_watermarks_max = 3; - ZColorWatermark _old_watermarks[_old_watermarks_max]; + static const int OldWatermarksMax = 3; + ZColorWatermark _old_watermarks[OldWatermarksMax]; int _old_watermarks_newest; ThreadLocalAllocStats _stats; diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp index c2a7a23c04f53..56b3590960f7c 100644 --- a/src/hotspot/share/gc/z/zStat.cpp +++ b/src/hotspot/share/gc/z/zStat.cpp @@ -1019,7 +1019,7 @@ ZStatMutatorAllocRateStats ZStatMutatorAllocRate::stats() { // Stat thread // ZStat::ZStat() - : _metronome(sample_hz) { + : _metronome(SampleHz) { set_name("ZStat"); create_and_start(); ZStatMutatorAllocRate::initialize(); @@ -1098,11 +1098,11 @@ void ZStat::terminate() { // class ZStatTablePrinter { private: - static const size_t _buffer_size = 256; + static const size_t BufferSize = 256; const size_t _column0_width; const size_t _columnN_width; - char _buffer[_buffer_size]; + char _buffer[BufferSize]; public: class ZColumn { @@ -1119,7 +1119,7 @@ class ZStatTablePrinter { } size_t print(size_t position, const char* fmt, va_list va) { - const int res = jio_vsnprintf(_buffer + position, _buffer_size - position, fmt, va); + const int res = jio_vsnprintf(_buffer + position, BufferSize - position, fmt, va); if (res < 0) { return 0; } diff --git a/src/hotspot/share/gc/z/zStat.hpp b/src/hotspot/share/gc/z/zStat.hpp index d7482dbe6aaaf..d7fff6d7b8e13 100644 --- a/src/hotspot/share/gc/z/zStat.hpp +++ b/src/hotspot/share/gc/z/zStat.hpp @@ -384,7 +384,7 @@ class ZStatMutatorAllocRate : public AllStatic { // class ZStat : public ZThread { private: - static const uint64_t sample_hz = 1; + static const uint64_t SampleHz = 1; ZMetronome _metronome; diff --git a/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp b/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp index c94551dc62d20..78106aad729b7 100644 --- a/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp +++ b/src/hotspot/share/gc/z/zStoreBarrierBuffer.cpp @@ -55,7 +55,7 @@ ZStoreBarrierBuffer::ZStoreBarrierBuffer() _last_installed_color(), _base_pointer_lock(), _base_pointers(), - _current(ZBufferStoreBarriers ? _buffer_size_bytes : 0) {} + _current(ZBufferStoreBarriers ? BufferSizeBytes : 0) {} void ZStoreBarrierBuffer::initialize() { _last_processed_color = ZPointerStoreGoodMask; @@ -63,11 +63,11 @@ void ZStoreBarrierBuffer::initialize() { } void ZStoreBarrierBuffer::clear() { - _current = _buffer_size_bytes; + _current = BufferSizeBytes; } bool ZStoreBarrierBuffer::is_empty() const { - return _current == _buffer_size_bytes; + return _current == BufferSizeBytes; } void ZStoreBarrierBuffer::install_base_pointers_inner() { @@ -79,7 +79,7 @@ void ZStoreBarrierBuffer::install_base_pointers_inner() { (ZPointer::remap_bits(_last_processed_color) & ZPointerRemappedOldMask) == 0, "Should not have double bit errors"); - for (size_t i = current(); i < _buffer_length; ++i) { + for (size_t i = current(); i < BufferLength; ++i) { const ZStoreBarrierEntry& entry = _buffer[i]; volatile zpointer* const p = entry._p; const zaddress_unsafe p_unsafe = to_zaddress_unsafe((uintptr_t)p); @@ -229,7 +229,7 @@ void ZStoreBarrierBuffer::on_new_phase() { // Install all base pointers for relocation install_base_pointers(); - for (size_t i = current(); i < _buffer_length; ++i) { + for (size_t i = current(); i < BufferLength; ++i) { on_new_phase_relocate(i); on_new_phase_remember(i); on_new_phase_mark(i); @@ -259,7 +259,7 @@ void ZStoreBarrierBuffer::on_error(outputStream* st) { st->print_cr(" _last_processed_color: " PTR_FORMAT, _last_processed_color); st->print_cr(" _last_installed_color: " PTR_FORMAT, _last_installed_color); - for (size_t i = current(); i < _buffer_length; ++i) { + for (size_t i = current(); i < BufferLength; ++i) { st->print_cr(" [%2zu]: base: " PTR_FORMAT " p: " PTR_FORMAT " prev: " PTR_FORMAT, i, untype(_base_pointers[i]), @@ -276,7 +276,7 @@ void ZStoreBarrierBuffer::flush() { OnError on_error(this); VMErrorCallbackMark mark(&on_error); - for (size_t i = current(); i < _buffer_length; ++i) { + for (size_t i = current(); i < BufferLength; ++i) { const ZStoreBarrierEntry& entry = _buffer[i]; const zaddress addr = ZBarrier::make_load_good(entry._prev); ZBarrier::mark_and_remember(entry._p, addr); @@ -296,7 +296,7 @@ bool ZStoreBarrierBuffer::is_in(volatile zpointer* p) { const uintptr_t last_remap_bits = ZPointer::remap_bits(buffer->_last_processed_color) & ZPointerRemappedMask; const bool needs_remap = last_remap_bits != ZPointerRemapped; - for (size_t i = buffer->current(); i < _buffer_length; ++i) { + for (size_t i = buffer->current(); i < BufferLength; ++i) { const ZStoreBarrierEntry& entry = buffer->_buffer[i]; volatile zpointer* entry_p = entry._p; diff --git a/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp b/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp index 5903edb6ad409..68984a15a1ee9 100644 --- a/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp +++ b/src/hotspot/share/gc/z/zStoreBarrierBuffer.hpp @@ -42,10 +42,10 @@ class ZStoreBarrierBuffer : public CHeapObj { friend class ZVerify; private: - static const size_t _buffer_length = 32; - static const size_t _buffer_size_bytes = _buffer_length * sizeof(ZStoreBarrierEntry); + static const size_t BufferLength = 32; + static const size_t BufferSizeBytes = BufferLength * sizeof(ZStoreBarrierEntry); - ZStoreBarrierEntry _buffer[_buffer_length]; + ZStoreBarrierEntry _buffer[BufferLength]; // Color from previous phase this buffer was processed uintptr_t _last_processed_color; @@ -54,7 +54,7 @@ class ZStoreBarrierBuffer : public CHeapObj { uintptr_t _last_installed_color; ZLock _base_pointer_lock; - zaddress_unsafe _base_pointers[_buffer_length]; + zaddress_unsafe _base_pointers[BufferLength]; // sizeof(ZStoreBarrierEntry) scaled index growing downwards size_t _current; diff --git a/src/hotspot/share/gc/z/zValue.hpp b/src/hotspot/share/gc/z/zValue.hpp index 29f1b707e7c6b..4953978297f52 100644 --- a/src/hotspot/share/gc/z/zValue.hpp +++ b/src/hotspot/share/gc/z/zValue.hpp @@ -39,7 +39,7 @@ class ZValueStorage : public AllStatic { static uintptr_t _end; public: - static const size_t offset = 4 * K; + static const size_t Offset = 4 * K; static uintptr_t alloc(size_t size); }; diff --git a/src/hotspot/share/gc/z/zValue.inline.hpp b/src/hotspot/share/gc/z/zValue.inline.hpp index 2367bac0f0aab..c2aa8bbbb4004 100644 --- a/src/hotspot/share/gc/z/zValue.inline.hpp +++ b/src/hotspot/share/gc/z/zValue.inline.hpp @@ -44,7 +44,7 @@ template uintptr_t ZValueStorage::_top = 0; template uintptr_t ZValueStorage::alloc(size_t size) { - assert(size <= offset, "Allocation too large"); + assert(size <= Offset, "Allocation too large"); // Allocate entry in existing memory block const uintptr_t addr = align_up(_top, S::alignment()); @@ -56,10 +56,10 @@ uintptr_t ZValueStorage::alloc(size_t size) { } // Allocate new block of memory - const size_t block_alignment = offset; - const size_t block_size = offset * S::count(); + const size_t block_alignment = Offset; + const size_t block_size = Offset * S::count(); _top = ZUtils::alloc_aligned_unfreeable(block_alignment, block_size); - _end = _top + offset; + _end = _top + Offset; // Retry allocation return alloc(size); @@ -119,7 +119,7 @@ inline uint32_t ZPerWorkerStorage::id() { template inline uintptr_t ZValue::value_addr(uint32_t value_id) const { - return _addr + (value_id * S::offset); + return _addr + (value_id * S::Offset); } template diff --git a/src/hotspot/share/gc/z/zVerify.cpp b/src/hotspot/share/gc/z/zVerify.cpp index b735965e9d49b..03b50e110e4c4 100644 --- a/src/hotspot/share/gc/z/zVerify.cpp +++ b/src/hotspot/share/gc/z/zVerify.cpp @@ -589,7 +589,7 @@ void ZVerify::on_color_flip() { for (JavaThreadIteratorWithHandle jtiwh; JavaThread* const jt = jtiwh.next(); ) { const ZStoreBarrierBuffer* const buffer = ZThreadLocalData::store_barrier_buffer(jt); - for (size_t i = buffer->current(); i < ZStoreBarrierBuffer::_buffer_length; ++i) { + for (size_t i = buffer->current(); i < ZStoreBarrierBuffer::BufferLength; ++i) { volatile zpointer* const p = buffer->_buffer[i]._p; bool created = false; z_verify_store_barrier_buffer_table->put_if_absent(p, true, &created); diff --git a/src/hotspot/share/gc/z/zVirtualMemory.cpp b/src/hotspot/share/gc/z/zVirtualMemory.cpp index 6b53b2ba7c82b..2160aa3894802 100644 --- a/src/hotspot/share/gc/z/zVirtualMemory.cpp +++ b/src/hotspot/share/gc/z/zVirtualMemory.cpp @@ -27,6 +27,7 @@ #include "gc/z/zAddress.inline.hpp" #include "gc/z/zAddressSpaceLimit.hpp" #include "gc/z/zGlobals.hpp" +#include "gc/z/zInitialize.hpp" #include "gc/z/zNMT.hpp" #include "gc/z/zVirtualMemory.inline.hpp" #include "utilities/align.hpp" @@ -44,7 +45,7 @@ ZVirtualMemoryManager::ZVirtualMemoryManager(size_t max_capacity) // Reserve address space if (!reserve(max_capacity)) { - log_error_pd(gc)("Failed to reserve enough address space for Java heap"); + ZInitialize::error_d("Failed to reserve enough address space for Java heap"); return; } diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 2fad5ba39ef5c..616ba29c62b33 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -74,7 +74,9 @@ void AbstractInterpreter::print() { tty->print_cr("avg codelet size = %6d bytes", _code->used_space() / _code->number_of_stubs()); tty->cr(); } + _should_print_instructions = PrintInterpreter; _code->print(); + _should_print_instructions = false; tty->print_cr("----------------------------------------------------------------------"); tty->cr(); } @@ -91,6 +93,8 @@ address AbstractInterpreter::_slow_signature_handler; address AbstractInterpreter::_entry_table [AbstractInterpreter::number_of_method_entries]; address AbstractInterpreter::_native_abi_to_tosca [AbstractInterpreter::number_of_result_handlers]; +bool AbstractInterpreter::_should_print_instructions = false; + //------------------------------------------------------------------------------------------------------------------------ // Generation of complete interpreter @@ -138,6 +142,7 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(const methodHan case vmIntrinsics::_dsin: return java_lang_math_sin; case vmIntrinsics::_dcos: return java_lang_math_cos; case vmIntrinsics::_dtan: return java_lang_math_tan; + case vmIntrinsics::_dtanh: return java_lang_math_tanh; case vmIntrinsics::_dabs: return java_lang_math_abs; case vmIntrinsics::_dlog: return java_lang_math_log; case vmIntrinsics::_dlog10: return java_lang_math_log10; @@ -198,6 +203,7 @@ vmIntrinsics::ID AbstractInterpreter::method_intrinsic(MethodKind kind) { case java_lang_math_sin : return vmIntrinsics::_dsin; case java_lang_math_cos : return vmIntrinsics::_dcos; case java_lang_math_tan : return vmIntrinsics::_dtan; + case java_lang_math_tanh : return vmIntrinsics::_dtanh; case java_lang_math_abs : return vmIntrinsics::_dabs; case java_lang_math_log : return vmIntrinsics::_dlog; case java_lang_math_log10 : return vmIntrinsics::_dlog10; @@ -309,6 +315,7 @@ void AbstractInterpreter::print_method_kind(MethodKind kind) { case java_lang_math_sin : tty->print("java_lang_math_sin" ); break; case java_lang_math_cos : tty->print("java_lang_math_cos" ); break; case java_lang_math_tan : tty->print("java_lang_math_tan" ); break; + case java_lang_math_tanh : tty->print("java_lang_math_tanh" ); break; case java_lang_math_abs : tty->print("java_lang_math_abs" ); break; case java_lang_math_log : tty->print("java_lang_math_log" ); break; case java_lang_math_log10 : tty->print("java_lang_math_log10" ); break; diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp index e487b152b76ea..55fb58021a0d4 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.hpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,6 +72,7 @@ class AbstractInterpreter: AllStatic { java_lang_math_sin, // implementation of java.lang.Math.sin (x) java_lang_math_cos, // implementation of java.lang.Math.cos (x) java_lang_math_tan, // implementation of java.lang.Math.tan (x) + java_lang_math_tanh, // implementation of java.lang.Math.tanh (x) java_lang_math_abs, // implementation of java.lang.Math.abs (x) java_lang_math_sqrt, // implementation of java.lang.Math.sqrt (x) java_lang_math_sqrt_strict, // implementation of java.lang.StrictMath.sqrt(x) @@ -125,6 +126,8 @@ class AbstractInterpreter: AllStatic { static address _rethrow_exception_entry; // rethrows an activation in previous frame + static bool _should_print_instructions; // only with PrintInterpreter and when printing all InterpreterCodelet + friend class AbstractInterpreterGenerator; friend class InterpreterMacroAssembler; @@ -132,6 +135,7 @@ class AbstractInterpreter: AllStatic { // Initialization/debugging static void initialize(); static StubQueue* code() { return _code; } + static bool should_print_instructions() { return _should_print_instructions; } // Method activation @@ -151,6 +155,7 @@ class AbstractInterpreter: AllStatic { case vmIntrinsics::_dsin : // fall thru case vmIntrinsics::_dcos : // fall thru case vmIntrinsics::_dtan : // fall thru + case vmIntrinsics::_dtanh : // fall thru case vmIntrinsics::_dabs : // fall thru case vmIntrinsics::_dsqrt : // fall thru case vmIntrinsics::_dsqrt_strict : // fall thru diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index e5a3e9c16f4a1..cdb53b62f8c40 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -105,7 +105,7 @@ class BytecodePrinter { // the incoming method. We could lose a line of trace output. // This is acceptable in a debug-only feature. st->cr(); - st->print("[%ld] ", (long) Thread::current()->osthread()->thread_id()); + st->print("[" UINTX_FORMAT "] ", Thread::current()->osthread()->thread_id_for_printing()); method->print_name(st); st->cr(); _current_method = method(); @@ -128,7 +128,7 @@ class BytecodePrinter { code == Bytecodes::_return_register_finalizer || (code >= Bytecodes::_ireturn && code <= Bytecodes::_return)) { int bci = (int)(bcp - method->code_base()); - st->print("[%ld] ", (long) Thread::current()->osthread()->thread_id()); + st->print("[" UINTX_FORMAT "] ", Thread::current()->osthread()->thread_id_for_printing()); if (Verbose) { st->print("%8d %4d " INTPTR_FORMAT " " INTPTR_FORMAT " %s", BytecodeCounter::counter_value(), bci, tos, tos2, Bytecodes::name(code)); diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index 3c4ff4c1749e9..cba26f5aa6a6d 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,7 +66,7 @@ void InterpreterCodelet::verify() {} void InterpreterCodelet::print_on(outputStream* st) const { ttyLocker ttyl; - if (PrintInterpreter) { + if (AbstractInterpreter::should_print_instructions()) { st->cr(); st->print_cr("----------------------------------------------------------------------"); } @@ -76,7 +76,7 @@ void InterpreterCodelet::print_on(outputStream* st) const { st->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", p2i(code_begin()), p2i(code_end()), code_size()); - if (PrintInterpreter) { + if (AbstractInterpreter::should_print_instructions()) { st->cr(); Disassembler::decode(code_begin(), code_end(), st NOT_PRODUCT(COMMA &_asm_remarks)); } diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp index cadc3e8a2e802..36847580d9c63 100644 --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -87,7 +87,7 @@ void CallInfo::set_interface(Klass* resolved_klass, // we should pick the vtable index from the resolved method. // In that case, the caller must call set_virtual instead of set_interface. assert(resolved_method->method_holder()->is_interface(), ""); - assert(itable_index == resolved_method()->itable_index(), ""); + assert(itable_index == resolved_method->itable_index(), ""); set_common(resolved_klass, resolved_method, selected_method, CallInfo::itable_call, itable_index, CHECK); } @@ -1541,7 +1541,7 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result, } // resolve the method in the receiver class, unless it is private - if (!is_abstract_interpretation && !resolved_method()->is_private()) { + if (!is_abstract_interpretation && !resolved_method->is_private()) { // do lookup based on receiver klass // This search must match the linktime preparation search for itable initialization // to correctly enforce loader constraints for interface method inheritance. @@ -1590,17 +1590,17 @@ void LinkResolver::runtime_resolve_interface_method(CallInfo& result, assert(is_abstract_interpretation || vtable_index == selected_method->vtable_index(), "sanity check"); result.set_virtual(resolved_klass, resolved_method, selected_method, vtable_index, CHECK); } else if (resolved_method->has_itable_index()) { - int itable_index = resolved_method()->itable_index(); + int itable_index = resolved_method->itable_index(); log_develop_trace(itables)(" -- itable index: %d", itable_index); result.set_interface(resolved_klass, resolved_method, selected_method, itable_index, CHECK); } else { int index = resolved_method->vtable_index(); log_develop_trace(itables)(" -- non itable/vtable index: %d", index); assert(index == Method::nonvirtual_vtable_index, "Oops hit another case!"); - assert(resolved_method()->is_private() || - (resolved_method()->is_final() && resolved_method->method_holder() == vmClasses::Object_klass()), + assert(resolved_method->is_private() || + (resolved_method->is_final() && resolved_method->method_holder() == vmClasses::Object_klass()), "Should only have non-virtual invokeinterface for private or final-Object methods!"); - assert(resolved_method()->can_be_statically_bound(), "Should only have non-virtual invokeinterface for statically bound methods!"); + assert(resolved_method->can_be_statically_bound(), "Should only have non-virtual invokeinterface for statically bound methods!"); // This sets up the nonvirtual form of "virtual" call (as needed for final and private methods) result.set_virtual(resolved_klass, resolved_method, resolved_method, index, CHECK); } diff --git a/src/hotspot/share/interpreter/linkResolver.hpp b/src/hotspot/share/interpreter/linkResolver.hpp index 340c7d412d599..69bdf56137d41 100644 --- a/src/hotspot/share/interpreter/linkResolver.hpp +++ b/src/hotspot/share/interpreter/linkResolver.hpp @@ -99,7 +99,6 @@ class CallInfo : public StackObj { // Materialize a java.lang.invoke.ResolvedMethodName for this resolved_method void set_resolved_method_name(TRAPS); - BasicType result_type() const { return selected_method()->result_type(); } CallKind call_kind() const { return _call_kind; } int vtable_index() const { // Even for interface calls the vtable index could be non-negative. diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp index 9cd6f5ceffbe9..3f497c3360b7e 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -192,6 +192,7 @@ void TemplateInterpreterGenerator::generate_all() { method_entry(java_lang_math_sin ) method_entry(java_lang_math_cos ) method_entry(java_lang_math_tan ) + method_entry(java_lang_math_tanh ) method_entry(java_lang_math_abs ) method_entry(java_lang_math_sqrt ) method_entry(java_lang_math_sqrt_strict) @@ -457,6 +458,7 @@ address TemplateInterpreterGenerator::generate_intrinsic_entry(AbstractInterpret case Interpreter::java_lang_math_sin : // fall thru case Interpreter::java_lang_math_cos : // fall thru case Interpreter::java_lang_math_tan : // fall thru + case Interpreter::java_lang_math_tanh : // fall thru case Interpreter::java_lang_math_abs : // fall thru case Interpreter::java_lang_math_log : // fall thru case Interpreter::java_lang_math_log10 : // fall thru diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp index bcccff2fe82ec..b0afcb5279522 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,7 +74,7 @@ class TemplateInterpreterGenerator: public AbstractInterpreterGenerator { void set_safepoints_for_all_bytes(); // Helpers for generate_and_dispatch - address generate_trace_code(TosState state) PRODUCT_RETURN0; + address generate_trace_code(TosState state) PRODUCT_RETURN_NULL; void count_bytecode() PRODUCT_RETURN; void histogram_bytecode(Template* t) PRODUCT_RETURN; void histogram_bytecode_pair(Template* t) PRODUCT_RETURN; diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp index e08d9553c3e07..27ea1b9706719 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp @@ -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 2007, 2008, 2009, 2010, 2011 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -54,6 +54,7 @@ void ZeroInterpreterGenerator::generate_all() { method_entry(java_lang_math_sin ); method_entry(java_lang_math_cos ); method_entry(java_lang_math_tan ); + method_entry(java_lang_math_tanh ); method_entry(java_lang_math_abs ); method_entry(java_lang_math_sqrt ); method_entry(java_lang_math_sqrt_strict); @@ -95,6 +96,7 @@ address ZeroInterpreterGenerator::generate_method_entry( case Interpreter::java_lang_math_sin : // fall thru case Interpreter::java_lang_math_cos : // fall thru case Interpreter::java_lang_math_tan : // fall thru + case Interpreter::java_lang_math_tanh : // fall thru case Interpreter::java_lang_math_abs : // fall thru case Interpreter::java_lang_math_log : // fall thru case Interpreter::java_lang_math_log10 : // fall thru diff --git a/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp b/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp index 8f1d2b4d5b120..9a123a916299f 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_JFR_LEAKPROFILER_JFRBITSET_HPP #define SHARE_JFR_LEAKPROFILER_JFRBITSET_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/objectBitSet.inline.hpp" typedef ObjectBitSet JFRBitSet; diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 0782f94a9034d..babb24f28ea1d 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -735,7 +735,7 @@ + description="Native memory usage for a given memory tag in the JVM" period="everyChunk"> diff --git a/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.cpp b/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.cpp index 2b86108121586..6c0584f50ff31 100644 --- a/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.cpp +++ b/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.cpp @@ -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,10 +63,10 @@ void JfrNativeMemoryEvent::send_total_event(const Ticks& timestamp) { event.commit(); } -void JfrNativeMemoryEvent::send_type_event(const Ticks& starttime, MEMFLAGS flag, size_t reserved, size_t committed) { +void JfrNativeMemoryEvent::send_type_event(const Ticks& starttime, MemTag mem_tag, size_t reserved, size_t committed) { EventNativeMemoryUsage event(UNTIMED); event.set_starttime(starttime); - event.set_type(NMTUtil::flag_to_index(flag)); + event.set_type(NMTUtil::tag_to_index(mem_tag)); event.set_reserved(reserved); event.set_committed(committed); event.commit(); @@ -79,12 +79,12 @@ void JfrNativeMemoryEvent::send_type_events(const Ticks& timestamp) { NMTUsage* usage = get_usage(timestamp); - for (int index = 0; index < mt_number_of_types; index ++) { - MEMFLAGS flag = NMTUtil::index_to_flag(index); - if (flag == mtNone) { + for (int index = 0; index < mt_number_of_tags; index ++) { + MemTag mem_tag = NMTUtil::index_to_tag(index); + if (mem_tag == mtNone) { // Skip mtNone since it is not really used. continue; } - send_type_event(timestamp, flag, usage->reserved(flag), usage->committed(flag)); + send_type_event(timestamp, mem_tag, usage->reserved(mem_tag), usage->committed(mem_tag)); } } diff --git a/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.hpp b/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.hpp index c5a476c78a89c..f2628bc1287ab 100644 --- a/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.hpp +++ b/src/hotspot/share/jfr/periodic/jfrNativeMemoryEvent.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_JFR_PERIODIC_JFRNATIVEMEMORYEVENT_HPP #define SHARE_JFR_PERIODIC_JFRNATIVEMEMORYEVENT_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "nmt/nmtUsage.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ticks.hpp" @@ -35,7 +35,7 @@ // so no more synchronization is needed. class JfrNativeMemoryEvent : public AllStatic { private: - static void send_type_event(const Ticks& starttime, MEMFLAGS flag, size_t reserved, size_t committed); + static void send_type_event(const Ticks& starttime, MemTag mem_tag, size_t reserved, size_t committed); public: static void send_total_event(const Ticks& timestamp); static void send_type_events(const Ticks& timestamp); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp index 5cd9e6b253b1e..63c7a027373be 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, 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 @@ -332,10 +332,10 @@ void CompilerTypeConstant::serialize(JfrCheckpointWriter& writer) { } void NMTTypeConstant::serialize(JfrCheckpointWriter& writer) { - writer.write_count(mt_number_of_types); - for (int i = 0; i < mt_number_of_types; ++i) { + writer.write_count(mt_number_of_tags); + for (int i = 0; i < mt_number_of_tags; ++i) { writer.write_key(i); - MEMFLAGS flag = NMTUtil::index_to_flag(i); - writer.write(NMTUtil::flag_to_name(flag)); + MemTag mem_tag = NMTUtil::index_to_tag(i); + writer.write(NMTUtil::tag_to_name(mem_tag)); } } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 279b871d8181a..a53eaa474f3f0 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -484,6 +484,9 @@ static void do_primitives() { static void do_unloading_klass(Klass* klass) { assert(klass != nullptr, "invariant"); assert(_subsystem_callback != nullptr, "invariant"); + if (klass->is_instance_klass() && InstanceKlass::cast(klass)->is_scratch_class()) { + return; + } if (JfrKlassUnloading::on_unload(klass)) { _subsystem_callback->do_artifact(klass); } diff --git a/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp b/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp index 595fcc9c65daa..4982e24e8987d 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -208,7 +208,7 @@ static DCmdArgument _dcmd_globalbuffersize( static DCmdArgument _dcmd_numglobalbuffers( "numglobalbuffers", "Number of global buffers", - "JULONG", + "INT", false, default_num_global_buffers); @@ -222,7 +222,7 @@ static DCmdArgument _dcmd_maxchunksize( static DCmdArgument _dcmd_old_object_queue_size ( "old-object-queue-size", "Maximum number of old objects to track", - "JINT", + "INT", false, default_old_object_queue_size); @@ -245,7 +245,7 @@ static DCmdArgument _dcmd_sample_protection( static DCmdArgument _dcmd_stackdepth( "stackdepth", "Stack depth for stacktraces (minimum 1, maximum 2048)", - "JULONG", + "INT", false, default_stack_depth); diff --git a/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp b/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp index 78309f00913cc..aaae65ed10dce 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, 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 @@ -117,7 +117,7 @@ bool JfrVirtualMemorySegment::initialize(size_t reservation_size_request_bytes) _rs.base(), _rs.size(), os::vm_page_size()); - MemTracker::record_virtual_memory_type((address)_rs.base(), mtTracing); + MemTracker::record_virtual_memory_tag((address)_rs.base(), mtTracing); assert(is_aligned(_rs.base(), os::vm_page_size()), "invariant"); assert(is_aligned(_rs.size(), os::vm_page_size()), "invariant"); diff --git a/src/hotspot/share/jfr/support/jfrNativeLibraryLoadEvent.cpp b/src/hotspot/share/jfr/support/jfrNativeLibraryLoadEvent.cpp index 920e07eca454b..5e80e31a9987a 100644 --- a/src/hotspot/share/jfr/support/jfrNativeLibraryLoadEvent.cpp +++ b/src/hotspot/share/jfr/support/jfrNativeLibraryLoadEvent.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023, 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 @@ -117,6 +117,7 @@ static void commit(const HelperType& helper) { JavaThread* jt = JavaThread::cast(thread); if (jt->thread_state() == _thread_in_native) { // For a JavaThread to take a JFR stacktrace, it must be in _thread_in_vm. Can safepoint here. + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); ThreadInVMfromNative transition(jt); event.commit(); return; diff --git a/src/hotspot/share/jvmci/jvmciCompiler.cpp b/src/hotspot/share/jvmci/jvmciCompiler.cpp index 2b8684f7ab85b..04cdb4b4b4fda 100644 --- a/src/hotspot/share/jvmci/jvmciCompiler.cpp +++ b/src/hotspot/share/jvmci/jvmciCompiler.cpp @@ -87,7 +87,7 @@ void JVMCICompiler::bootstrap(TRAPS) { int len = objectMethods->length(); for (int i = 0; i < len; i++) { methodHandle mh(THREAD, objectMethods->at(i)); - if (!mh->is_native() && !mh->is_static() && !mh->is_initializer()) { + if (!mh->is_native() && !mh->is_static() && !mh->is_object_initializer() && !mh->is_static_initializer()) { ResourceMark rm; int hot_count = 10; // TODO: what's the appropriate value? CompileBroker::compile_method(mh, InvocationEntryBci, CompLevel_full_optimization, mh, hot_count, CompileTask::Reason_Bootstrap, CHECK); diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index d231fbe8a6a0e..ba1155e66946b 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -2178,7 +2178,7 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredConstructors, (JNIEnv* env, jobject, A GrowableArray constructors_array; for (int i = 0; i < iklass->methods()->length(); i++) { Method* m = iklass->methods()->at(i); - if (m->is_initializer() && !m->is_static()) { + if (m->is_object_initializer()) { constructors_array.append(m); } } @@ -2205,7 +2205,7 @@ C2V_VMENTRY_NULL(jobjectArray, getDeclaredMethods, (JNIEnv* env, jobject, ARGUME GrowableArray methods_array; for (int i = 0; i < iklass->methods()->length(); i++) { Method* m = iklass->methods()->at(i); - if (!m->is_initializer() && !m->is_overpass()) { + if (!m->is_object_initializer() && !m->is_static_initializer() && !m->is_overpass()) { methods_array.append(m); } } @@ -2921,12 +2921,11 @@ C2V_VMENTRY_NULL(jobject, asReflectionExecutable, (JNIEnv* env, jobject, ARGUMEN requireInHotSpot("asReflectionExecutable", JVMCI_CHECK_NULL); methodHandle m(THREAD, UNPACK_PAIR(Method, method)); oop executable; - if (m->is_initializer()) { - if (m->is_static_initializer()) { - JVMCI_THROW_MSG_NULL(IllegalArgumentException, - "Cannot create java.lang.reflect.Method for class initializer"); - } + if (m->is_object_initializer()) { executable = Reflection::new_constructor(m, CHECK_NULL); + } else if (m->is_static_initializer()) { + JVMCI_THROW_MSG_NULL(IllegalArgumentException, + "Cannot create java.lang.reflect.Method for class initializer"); } else { executable = Reflection::new_method(m, false, CHECK_NULL); } diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp index fa4b1c75c0573..0773de6ddbaa0 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp @@ -116,6 +116,7 @@ class CompilerToVM { static address dsin; static address dcos; static address dtan; + static address dtanh; static address dexp; static address dlog; static address dlog10; diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 26c88abec0f18..1612038008a32 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -135,6 +135,7 @@ int CompilerToVM::Data::sizeof_ZStoreBarrierEntry = sizeof(ZStoreBarrierEntry); address CompilerToVM::Data::dsin; address CompilerToVM::Data::dcos; address CompilerToVM::Data::dtan; +address CompilerToVM::Data::dtanh; address CompilerToVM::Data::dexp; address CompilerToVM::Data::dlog; address CompilerToVM::Data::dlog10; @@ -268,6 +269,19 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { SET_TRIGFUNC(dpow); #undef SET_TRIGFUNC + +#define SET_TRIGFUNC_OR_NULL(name) \ + if (StubRoutines::name() != nullptr) { \ + name = StubRoutines::name(); \ + } else { \ + name = nullptr; \ + } + + SET_TRIGFUNC_OR_NULL(dtanh); + +#undef SET_TRIGFUNC_OR_NULL + + } static jboolean is_c1_supported(vmIntrinsics::ID id){ diff --git a/src/hotspot/share/jvmci/jvmci_globals.cpp b/src/hotspot/share/jvmci/jvmci_globals.cpp index 86d8491b73303..2ae38044df0ff 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.cpp +++ b/src/hotspot/share/jvmci/jvmci_globals.cpp @@ -26,6 +26,7 @@ #include "compiler/compilerDefinitions.hpp" #include "gc/shared/gcConfig.hpp" #include "jvm.h" +#include "jvmci/jvmci.hpp" #include "jvmci/jvmci_globals.hpp" #include "logging/log.hpp" #include "runtime/arguments.hpp" @@ -80,20 +81,25 @@ bool JVMCIGlobals::check_jvmci_flags_are_consistent() { CHECK_NOT_SET(LibJVMCICompilerThreadHidden, UseJVMCICompiler) if (UseJVMCICompiler) { - if (FLAG_IS_DEFAULT(UseJVMCINativeLibrary) && !UseJVMCINativeLibrary) { - char path[JVM_MAXPATHLEN]; - if (os::dll_locate_lib(path, sizeof(path), Arguments::get_dll_dir(), JVMCI_SHARED_LIBRARY_NAME)) { - // If a JVMCI native library is present, - // we enable UseJVMCINativeLibrary by default. - FLAG_SET_DEFAULT(UseJVMCINativeLibrary, true); - } - } if (!FLAG_IS_DEFAULT(EnableJVMCI) && !EnableJVMCI) { jio_fprintf(defaultStream::error_stream(), "Improperly specified VM option UseJVMCICompiler: EnableJVMCI cannot be disabled\n"); return false; } FLAG_SET_DEFAULT(EnableJVMCI, true); + } + + if (EnableJVMCI) { + if (FLAG_IS_DEFAULT(UseJVMCINativeLibrary) && !UseJVMCINativeLibrary) { + if (JVMCI::shared_library_exists()) { + // If a JVMCI native library is present, + // we enable UseJVMCINativeLibrary by default. + FLAG_SET_DEFAULT(UseJVMCINativeLibrary, true); + } + } + } + + if (UseJVMCICompiler) { if (BootstrapJVMCI && UseJVMCINativeLibrary) { jio_fprintf(defaultStream::error_stream(), "-XX:+BootstrapJVMCI is not compatible with -XX:+UseJVMCINativeLibrary\n"); return false; diff --git a/src/hotspot/share/jvmci/jvmci_globals.hpp b/src/hotspot/share/jvmci/jvmci_globals.hpp index 1f2c0c647ab1e..4da49b24e6ef9 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.hpp +++ b/src/hotspot/share/jvmci/jvmci_globals.hpp @@ -45,7 +45,7 @@ class fileStream; constraint) \ \ product(bool, EnableJVMCI, false, EXPERIMENTAL, \ - "Enable JVMCI") \ + "Enable JVMCI. Defaults to true if UseJVMCICompiler is true.") \ \ product(bool, UseGraalJIT, false, EXPERIMENTAL, \ "Select the Graal JVMCI compiler. This is an alias for: " \ @@ -140,12 +140,14 @@ class fileStream; product(bool, UseJVMCINativeLibrary, false, EXPERIMENTAL, \ "Execute JVMCI Java code from a shared library (\"libjvmci\") " \ "instead of loading it from class files and executing it " \ - "on the HotSpot heap. Defaults to true if EnableJVMCIProduct is " \ - "true and a JVMCI native library is available.") \ + "on the HotSpot heap. Defaults to true if UseJVMCICompiler or " \ + "EnableJVMCI is true and a JVMCI native library is available.") \ \ - product(double, JVMCINativeLibraryThreadFraction, 0.33, EXPERIMENTAL, \ + product(double, JVMCINativeLibraryThreadFraction, 0.66, EXPERIMENTAL, \ "The fraction of compiler threads used by libjvmci. " \ - "The remaining compiler threads are used by C1.") \ + "The remaining compiler threads are used by C1. " \ + "Reducing this value could reduce the max RSS but " \ + "also increase the warmup time.") \ range(0.0, 1.0) \ \ product(ccstr, JVMCINativeLibraryErrorFile, nullptr, EXPERIMENTAL, \ diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index df77e8a2882ee..a26cd5efe23b1 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -38,6 +38,7 @@ #include "runtime/continuationEntry.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/jvmFlag.hpp" +#include "runtime/objectMonitor.hpp" #include "runtime/osThread.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" @@ -240,12 +241,12 @@ nonstatic_field(JavaThread, _jvmci_reserved_oop0, oop) \ nonstatic_field(JavaThread, _should_post_on_exceptions_flag, int) \ nonstatic_field(JavaThread, _jni_environment, JNIEnv) \ - nonstatic_field(JavaThread, _poll_data, SafepointMechanism::ThreadData) \ nonstatic_field(JavaThread, _stack_overflow_state._reserved_stack_activation, address) \ nonstatic_field(JavaThread, _held_monitor_count, intx) \ nonstatic_field(JavaThread, _lock_stack, LockStack) \ nonstatic_field(JavaThread, _om_cache, OMCache) \ nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \ + nonstatic_field(JavaThread, _unlocked_inflated_monitor, ObjectMonitor*) \ JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_VTMS_transition, bool)) \ JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_tmp_VTMS_transition, bool)) \ JVMTI_ONLY(nonstatic_field(JavaThread, _is_disable_suspend, bool)) \ @@ -407,6 +408,7 @@ static_field(StubRoutines, _cont_thaw, address) \ static_field(StubRoutines, _lookup_secondary_supers_table_slow_path_stub, address) \ \ + nonstatic_field(Thread, _poll_data, SafepointMechanism::ThreadData) \ nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \ nonstatic_field(Thread, _allocated_bytes, jlong) \ JFR_ONLY(nonstatic_field(Thread, _jfr_thread_local, JfrThreadLocal)) \ @@ -845,6 +847,7 @@ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_store_good)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_load_barrier_on_weak_oop_field_preloaded)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_load_barrier_on_phantom_oop_field_preloaded)) \ + ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing)) \ diff --git a/src/hotspot/share/logging/logSelection.cpp b/src/hotspot/share/logging/logSelection.cpp index aea5719b36d4f..1e7ba3a887848 100644 --- a/src/hotspot/share/logging/logSelection.cpp +++ b/src/hotspot/share/logging/logSelection.cpp @@ -33,11 +33,11 @@ const LogSelection LogSelection::Invalid; -LogSelection::LogSelection() : _ntags(0), _wildcard(false), _level(LogLevel::Invalid), _tag_sets_selected(0) { +LogSelection::LogSelection() : _ntags(0), _tags(), _wildcard(false), _level(LogLevel::Invalid), _tag_sets_selected(0) { } LogSelection::LogSelection(const LogTagType tags[LogTag::MaxTags], bool wildcard, LogLevelType level) - : _ntags(0), _wildcard(wildcard), _level(level), _tag_sets_selected(0) { + : _ntags(0), _tags(), _wildcard(wildcard), _level(level), _tag_sets_selected(0) { while (_ntags < LogTag::MaxTags && tags[_ntags] != LogTag::__NO_TAG) { _tags[_ntags] = tags[_ntags]; _ntags++; diff --git a/src/hotspot/share/memory/allocation.cpp b/src/hotspot/share/memory/allocation.cpp index 73bc9d4ad2a80..096ee6964210e 100644 --- a/src/hotspot/share/memory/allocation.cpp +++ b/src/hotspot/share/memory/allocation.cpp @@ -36,10 +36,10 @@ // allocate using malloc; will fail if no memory available char* AllocateHeap(size_t size, - MEMFLAGS flags, + MemTag mem_tag, const NativeCallStack& stack, AllocFailType alloc_failmode /* = AllocFailStrategy::EXIT_OOM*/) { - char* p = (char*) os::malloc(size, flags, stack); + char* p = (char*) os::malloc(size, mem_tag, stack); if (p == nullptr && alloc_failmode == AllocFailStrategy::EXIT_OOM) { vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "AllocateHeap"); } @@ -47,16 +47,16 @@ char* AllocateHeap(size_t size, } char* AllocateHeap(size_t size, - MEMFLAGS flags, + MemTag mem_tag, AllocFailType alloc_failmode /* = AllocFailStrategy::EXIT_OOM*/) { - return AllocateHeap(size, flags, CALLER_PC, alloc_failmode); + return AllocateHeap(size, mem_tag, CALLER_PC, alloc_failmode); } char* ReallocateHeap(char *old, size_t size, - MEMFLAGS flag, + MemTag mem_tag, AllocFailType alloc_failmode) { - char* p = (char*) os::realloc(old, size, flag, CALLER_PC); + char* p = (char*) os::realloc(old, size, mem_tag, CALLER_PC); if (p == nullptr && alloc_failmode == AllocFailStrategy::EXIT_OOM) { vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "ReallocateHeap"); } @@ -119,16 +119,16 @@ void* AnyObj::operator new(size_t size, Arena *arena) { return res; } -void* AnyObj::operator new(size_t size, MEMFLAGS flags) throw() { - address res = (address)AllocateHeap(size, flags, CALLER_PC); +void* AnyObj::operator new(size_t size, MemTag mem_tag) throw() { + address res = (address)AllocateHeap(size, mem_tag, CALLER_PC); DEBUG_ONLY(set_allocation_type(res, C_HEAP);) return res; } void* AnyObj::operator new(size_t size, const std::nothrow_t& nothrow_constant, - MEMFLAGS flags) throw() { + MemTag mem_tag) throw() { // should only call this with std::nothrow, use other operator new() otherwise - address res = (address)AllocateHeap(size, flags, CALLER_PC, AllocFailStrategy::RETURN_NULL); + address res = (address)AllocateHeap(size, mem_tag, CALLER_PC, AllocFailStrategy::RETURN_NULL); DEBUG_ONLY(if (res!= nullptr) set_allocation_type(res, C_HEAP);) return res; } diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index da5f7b1983002..9841ce3183caa 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -26,7 +26,7 @@ #define SHARE_MEMORY_ALLOCATION_HPP #include "memory/allStatic.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -94,9 +94,9 @@ typedef AllocFailStrategy::AllocFailEnum AllocFailType; // NEW_C_HEAP_OBJ* // FREE_C_HEAP_OBJ // -// char* AllocateHeap(size_t size, MEMFLAGS flags, const NativeCallStack& stack, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); -// char* AllocateHeap(size_t size, MEMFLAGS flags, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); -// char* ReallocateHeap(char *old, size_t size, MEMFLAGS flag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); +// char* AllocateHeap(size_t size, MemTag mem_tag, const NativeCallStack& stack, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); +// char* AllocateHeap(size_t size, MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); +// char* ReallocateHeap(char *old, size_t size, MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); // void FreeHeap(void* p); // @@ -106,16 +106,16 @@ class NativeCallStack; char* AllocateHeap(size_t size, - MEMFLAGS flags, + MemTag mem_tag, const NativeCallStack& stack, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); char* AllocateHeap(size_t size, - MEMFLAGS flags, + MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); char* ReallocateHeap(char *old, size_t size, - MEMFLAGS flag, + MemTag mem_tag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); // handles null pointers @@ -123,50 +123,50 @@ void FreeHeap(void* p); class CHeapObjBase { public: - ALWAYSINLINE void* operator new(size_t size, MEMFLAGS f) { - return AllocateHeap(size, f); + ALWAYSINLINE void* operator new(size_t size, MemTag mem_tag) { + return AllocateHeap(size, mem_tag); } ALWAYSINLINE void* operator new(size_t size, - MEMFLAGS f, + MemTag mem_tag, const NativeCallStack& stack) { - return AllocateHeap(size, f, stack); + return AllocateHeap(size, mem_tag, stack); } ALWAYSINLINE void* operator new(size_t size, - MEMFLAGS f, + MemTag mem_tag, const std::nothrow_t&, const NativeCallStack& stack) throw() { - return AllocateHeap(size, f, stack, AllocFailStrategy::RETURN_NULL); + return AllocateHeap(size, mem_tag, stack, AllocFailStrategy::RETURN_NULL); } ALWAYSINLINE void* operator new(size_t size, - MEMFLAGS f, + MemTag mem_tag, const std::nothrow_t&) throw() { - return AllocateHeap(size, f, AllocFailStrategy::RETURN_NULL); + return AllocateHeap(size, mem_tag, AllocFailStrategy::RETURN_NULL); } - ALWAYSINLINE void* operator new[](size_t size, MEMFLAGS f) { - return AllocateHeap(size, f); + ALWAYSINLINE void* operator new[](size_t size, MemTag mem_tag) { + return AllocateHeap(size, mem_tag); } ALWAYSINLINE void* operator new[](size_t size, - MEMFLAGS f, + MemTag mem_tag, const NativeCallStack& stack) { - return AllocateHeap(size, f, stack); + return AllocateHeap(size, mem_tag, stack); } ALWAYSINLINE void* operator new[](size_t size, - MEMFLAGS f, + MemTag mem_tag, const std::nothrow_t&, const NativeCallStack& stack) throw() { - return AllocateHeap(size, f, stack, AllocFailStrategy::RETURN_NULL); + return AllocateHeap(size, mem_tag, stack, AllocFailStrategy::RETURN_NULL); } ALWAYSINLINE void* operator new[](size_t size, - MEMFLAGS f, + MemTag mem_tag, const std::nothrow_t&) throw() { - return AllocateHeap(size, f, AllocFailStrategy::RETURN_NULL); + return AllocateHeap(size, mem_tag, AllocFailStrategy::RETURN_NULL); } void operator delete(void* p) { FreeHeap(p); } @@ -174,43 +174,43 @@ class CHeapObjBase { }; // Uses the implicitly static new and delete operators of CHeapObjBase -template +template class CHeapObj { public: ALWAYSINLINE void* operator new(size_t size) { - return CHeapObjBase::operator new(size, F); + return CHeapObjBase::operator new(size, MT); } ALWAYSINLINE void* operator new(size_t size, const NativeCallStack& stack) { - return CHeapObjBase::operator new(size, F, stack); + return CHeapObjBase::operator new(size, MT, stack); } ALWAYSINLINE void* operator new(size_t size, const std::nothrow_t& nt, const NativeCallStack& stack) throw() { - return CHeapObjBase::operator new(size, F, nt, stack); + return CHeapObjBase::operator new(size, MT, nt, stack); } ALWAYSINLINE void* operator new(size_t size, const std::nothrow_t& nt) throw() { - return CHeapObjBase::operator new(size, F, nt); + return CHeapObjBase::operator new(size, MT, nt); } ALWAYSINLINE void* operator new[](size_t size) { - return CHeapObjBase::operator new[](size, F); + return CHeapObjBase::operator new[](size, MT); } ALWAYSINLINE void* operator new[](size_t size, const NativeCallStack& stack) { - return CHeapObjBase::operator new[](size, F, stack); + return CHeapObjBase::operator new[](size, MT, stack); } ALWAYSINLINE void* operator new[](size_t size, const std::nothrow_t& nt, const NativeCallStack& stack) throw() { - return CHeapObjBase::operator new[](size, F, nt, stack); + return CHeapObjBase::operator new[](size, MT, nt, stack); } ALWAYSINLINE void* operator new[](size_t size, const std::nothrow_t& nt) throw() { - return CHeapObjBase::operator new[](size, F, nt); + return CHeapObjBase::operator new[](size, MT, nt); } void operator delete(void* p) { @@ -439,10 +439,10 @@ class AnyObj { public: // CHeap allocations - void* operator new(size_t size, MEMFLAGS flags) throw(); - void* operator new [](size_t size, MEMFLAGS flags) throw() = delete; - void* operator new(size_t size, const std::nothrow_t& nothrow_constant, MEMFLAGS flags) throw(); - void* operator new [](size_t size, const std::nothrow_t& nothrow_constant, MEMFLAGS flags) throw() = delete; + void* operator new(size_t size, MemTag mem_tag) throw(); + void* operator new [](size_t size, MemTag mem_tag) throw() = delete; + void* operator new(size_t size, const std::nothrow_t& nothrow_constant, MemTag mem_tag) throw(); + void* operator new [](size_t size, const std::nothrow_t& nothrow_constant, MemTag mem_tag) throw() = delete; // Arena allocations void* operator new(size_t size, Arena *arena); @@ -510,36 +510,36 @@ class AnyObj { #define NEW_RESOURCE_OBJ_RETURN_NULL(type)\ NEW_RESOURCE_ARRAY_RETURN_NULL(type, 1) -#define NEW_C_HEAP_ARRAY3(type, size, memflags, pc, allocfail)\ - (type*) AllocateHeap((size) * sizeof(type), memflags, pc, allocfail) +#define NEW_C_HEAP_ARRAY3(type, size, mem_tag, pc, allocfail)\ + (type*) AllocateHeap((size) * sizeof(type), mem_tag, pc, allocfail) -#define NEW_C_HEAP_ARRAY2(type, size, memflags, pc)\ - (type*) (AllocateHeap((size) * sizeof(type), memflags, pc)) +#define NEW_C_HEAP_ARRAY2(type, size, mem_tag, pc)\ + (type*) (AllocateHeap((size) * sizeof(type), mem_tag, pc)) -#define NEW_C_HEAP_ARRAY(type, size, memflags)\ - (type*) (AllocateHeap((size) * sizeof(type), memflags)) +#define NEW_C_HEAP_ARRAY(type, size, mem_tag)\ + (type*) (AllocateHeap((size) * sizeof(type), mem_tag)) -#define NEW_C_HEAP_ARRAY2_RETURN_NULL(type, size, memflags, pc)\ - NEW_C_HEAP_ARRAY3(type, (size), memflags, pc, AllocFailStrategy::RETURN_NULL) +#define NEW_C_HEAP_ARRAY2_RETURN_NULL(type, size, mem_tag, pc)\ + NEW_C_HEAP_ARRAY3(type, (size), mem_tag, pc, AllocFailStrategy::RETURN_NULL) -#define NEW_C_HEAP_ARRAY_RETURN_NULL(type, size, memflags)\ - NEW_C_HEAP_ARRAY2(type, (size), memflags, AllocFailStrategy::RETURN_NULL) +#define NEW_C_HEAP_ARRAY_RETURN_NULL(type, size, mem_tag)\ + NEW_C_HEAP_ARRAY2(type, (size), mem_tag, AllocFailStrategy::RETURN_NULL) -#define REALLOC_C_HEAP_ARRAY(type, old, size, memflags)\ - (type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), memflags)) +#define REALLOC_C_HEAP_ARRAY(type, old, size, mem_tag)\ + (type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), mem_tag)) -#define REALLOC_C_HEAP_ARRAY_RETURN_NULL(type, old, size, memflags)\ - (type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), memflags, AllocFailStrategy::RETURN_NULL)) +#define REALLOC_C_HEAP_ARRAY_RETURN_NULL(type, old, size, mem_tag)\ + (type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), mem_tag, AllocFailStrategy::RETURN_NULL)) #define FREE_C_HEAP_ARRAY(type, old) \ FreeHeap((char*)(old)) // allocate type in heap without calling ctor -#define NEW_C_HEAP_OBJ(type, memflags)\ - NEW_C_HEAP_ARRAY(type, 1, memflags) +#define NEW_C_HEAP_OBJ(type, mem_tag)\ + NEW_C_HEAP_ARRAY(type, 1, mem_tag) -#define NEW_C_HEAP_OBJ_RETURN_NULL(type, memflags)\ - NEW_C_HEAP_ARRAY_RETURN_NULL(type, 1, memflags) +#define NEW_C_HEAP_OBJ_RETURN_NULL(type, mem_tag)\ + NEW_C_HEAP_ARRAY_RETURN_NULL(type, 1, mem_tag) // deallocate obj of type in heap without calling dtor #define FREE_C_HEAP_OBJ(objname)\ @@ -568,8 +568,8 @@ class MmapArrayAllocator : public AllStatic { static size_t size_for(size_t length); public: - static E* allocate_or_null(size_t length, MEMFLAGS flags); - static E* allocate(size_t length, MEMFLAGS flags); + static E* allocate_or_null(size_t length, MemTag mem_tag); + static E* allocate(size_t length, MemTag mem_tag); static void free(E* addr, size_t length); }; @@ -579,8 +579,8 @@ class MallocArrayAllocator : public AllStatic { public: static size_t size_for(size_t length); - static E* allocate(size_t length, MEMFLAGS flags); - static E* reallocate(E* addr, size_t new_length, MEMFLAGS flags); + static E* allocate(size_t length, MemTag mem_tag); + static E* reallocate(E* addr, size_t new_length, MemTag mem_tag); static void free(E* addr); }; diff --git a/src/hotspot/share/memory/allocation.inline.hpp b/src/hotspot/share/memory/allocation.inline.hpp index 4a1b0b0c5971b..8d531c6dd2318 100644 --- a/src/hotspot/share/memory/allocation.inline.hpp +++ b/src/hotspot/share/memory/allocation.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,10 +55,10 @@ size_t MmapArrayAllocator::size_for(size_t length) { } template -E* MmapArrayAllocator::allocate_or_null(size_t length, MEMFLAGS flags) { +E* MmapArrayAllocator::allocate_or_null(size_t length, MemTag mem_tag) { size_t size = size_for(length); - char* addr = os::reserve_memory(size, !ExecMem, flags); + char* addr = os::reserve_memory(size, !ExecMem, mem_tag); if (addr == nullptr) { return nullptr; } @@ -72,10 +72,10 @@ E* MmapArrayAllocator::allocate_or_null(size_t length, MEMFLAGS flags) { } template -E* MmapArrayAllocator::allocate(size_t length, MEMFLAGS flags) { +E* MmapArrayAllocator::allocate(size_t length, MemTag mem_tag) { size_t size = size_for(length); - char* addr = os::reserve_memory(size, !ExecMem, flags); + char* addr = os::reserve_memory(size, !ExecMem, mem_tag); if (addr == nullptr) { vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "Allocator (reserve)"); } @@ -97,13 +97,13 @@ size_t MallocArrayAllocator::size_for(size_t length) { } template -E* MallocArrayAllocator::allocate(size_t length, MEMFLAGS flags) { - return (E*)AllocateHeap(size_for(length), flags); +E* MallocArrayAllocator::allocate(size_t length, MemTag mem_tag) { + return (E*)AllocateHeap(size_for(length), mem_tag); } template -E* MallocArrayAllocator::reallocate(E* addr, size_t new_length, MEMFLAGS flags) { - return (E*)ReallocateHeap((char*)addr, size_for(new_length), flags); +E* MallocArrayAllocator::reallocate(E* addr, size_t new_length, MemTag mem_tag) { + return (E*)ReallocateHeap((char*)addr, size_for(new_length), mem_tag); } template diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index d1f3f3de7b2ab..51d7cda9c61ec 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -222,8 +222,8 @@ void Chunk::next_chop(Chunk* k) { k->_next = nullptr; } -Arena::Arena(MEMFLAGS flag, Tag tag, size_t init_size) : - _flags(flag), _tag(tag), +Arena::Arena(MemTag mem_tag, Tag tag, size_t init_size) : + _mem_tag(mem_tag), _tag(tag), _size_in_bytes(0), _first(nullptr), _chunk(nullptr), _hwm(nullptr), _max(nullptr) @@ -233,13 +233,13 @@ Arena::Arena(MEMFLAGS flag, Tag tag, size_t init_size) : _first = _chunk; _hwm = _chunk->bottom(); // Save the cached hwm, max _max = _chunk->top(); - MemTracker::record_new_arena(flag); + MemTracker::record_new_arena(mem_tag); set_size_in_bytes(init_size); } Arena::~Arena() { destruct_contents(); - MemTracker::record_arena_free(_flags); + MemTracker::record_arena_free(_mem_tag); } // Destroy this arenas contents and reset to empty @@ -259,8 +259,8 @@ void Arena::set_size_in_bytes(size_t size) { if (_size_in_bytes != size) { ssize_t delta = size - size_in_bytes(); _size_in_bytes = size; - MemTracker::record_arena_size_change(delta, _flags); - if (CompilationMemoryStatistic::enabled() && _flags == mtCompiler) { + MemTracker::record_arena_size_change(delta, _mem_tag); + if (CompilationMemoryStatistic::enabled() && _mem_tag == mtCompiler) { Thread* const t = Thread::current(); if (t != nullptr && t->is_Compiler_thread()) { CompilationMemoryStatistic::on_arena_change(delta, this); @@ -286,7 +286,7 @@ void* Arena::grow(size_t x, AllocFailType alloc_failmode) { // (Note: all chunk sizes have to be 64-bit aligned) size_t len = MAX2(ARENA_ALIGN(x), (size_t) Chunk::size); - if (MemTracker::check_exceeds_limit(x, _flags)) { + if (MemTracker::check_exceeds_limit(x, _mem_tag)) { return nullptr; } diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index 1f3c5eb4e8bac..5f0def2a6556d 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -107,7 +107,7 @@ class Arena : public CHeapObjBase { static const char* tag_desc[static_cast(Arena::Tag::tag_count)]; private: - const MEMFLAGS _flags; // Memory tracking flags + const MemTag _mem_tag; // Native Memory Tracking tag const Tag _tag; size_t _size_in_bytes; // Size of arena (used for native memory tracking) @@ -138,7 +138,7 @@ class Arena : public CHeapObjBase { public: // Start the chunk_pool cleaner task static void start_chunk_pool_cleaner_task(); - Arena(MEMFLAGS memflag, Tag tag = Tag::tag_other, size_t init_size = Chunk::init_size); + Arena(MemTag mem_tag, Tag tag = Tag::tag_other, size_t init_size = Chunk::init_size); ~Arena(); void destruct_contents(); char* hwm() const { return _hwm; } diff --git a/src/hotspot/share/memory/guardedMemory.cpp b/src/hotspot/share/memory/guardedMemory.cpp index 7b676d725882a..12ffde3cc1b94 100644 --- a/src/hotspot/share/memory/guardedMemory.cpp +++ b/src/hotspot/share/memory/guardedMemory.cpp @@ -23,7 +23,7 @@ */ #include "precompiled.hpp" #include "memory/guardedMemory.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "runtime/os.hpp" void* GuardedMemory::wrap_copy(const void* ptr, const size_t len, const void* tag) { diff --git a/src/hotspot/share/memory/heap.cpp b/src/hotspot/share/memory/heap.cpp index 98e59f5818432..658ec3e8de773 100644 --- a/src/hotspot/share/memory/heap.cpp +++ b/src/hotspot/share/memory/heap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -232,7 +232,7 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s return false; } - MemTracker::record_virtual_memory_type((address)_segmap.low_boundary(), mtCode); + MemTracker::record_virtual_memory_tag((address)_segmap.low_boundary(), mtCode); assert(_segmap.committed_size() >= (size_t) _number_of_committed_segments, "could not commit enough space for segment map"); assert(_segmap.reserved_size() >= (size_t) _number_of_reserved_segments , "could not reserve enough space for segment map"); diff --git a/src/hotspot/share/memory/memRegion.cpp b/src/hotspot/share/memory/memRegion.cpp index 7dd7e1be4ab05..d6565b0032442 100644 --- a/src/hotspot/share/memory/memRegion.cpp +++ b/src/hotspot/share/memory/memRegion.cpp @@ -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 @@ -102,8 +102,8 @@ MemRegion MemRegion::minus(const MemRegion mr2) const { return MemRegion(); } -MemRegion* MemRegion::create_array(size_t length, MEMFLAGS flags) { - MemRegion* result = NEW_C_HEAP_ARRAY(MemRegion, length, flags); +MemRegion* MemRegion::create_array(size_t length, MemTag mem_tag) { + MemRegion* result = NEW_C_HEAP_ARRAY(MemRegion, length, mem_tag); for (size_t i = 0; i < length; i++) { ::new (&result[i]) MemRegion(); } diff --git a/src/hotspot/share/memory/memRegion.hpp b/src/hotspot/share/memory/memRegion.hpp index 5d3d635c650cd..920efe7528844 100644 --- a/src/hotspot/share/memory/memRegion.hpp +++ b/src/hotspot/share/memory/memRegion.hpp @@ -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 @@ -90,7 +90,7 @@ class MemRegion { bool is_empty() const { return word_size() == 0; } // Creates and initializes an array of MemRegions of the given length. - static MemRegion* create_array(size_t length, MEMFLAGS flags); + static MemRegion* create_array(size_t length, MemTag mem_tag); static void destroy_array(MemRegion* array, size_t length); }; diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 2674278ec9939..f86be4774d5ab 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -772,7 +772,7 @@ void Metaspace::global_initialize() { } // Mark class space as such - MemTracker::record_virtual_memory_type((address)rs.base(), mtClass); + MemTracker::record_virtual_memory_tag((address)rs.base(), mtClass); // Initialize space Metaspace::initialize_class_space(rs); diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index a8afbc130c968..83a591e4cad8a 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -259,7 +259,7 @@ VirtualSpaceNode* VirtualSpaceNode::create_node(size_t word_size, if (!rs.is_reserved()) { vm_exit_out_of_memory(word_size * BytesPerWord, OOM_MMAP_ERROR, "Failed to reserve memory for metaspace"); } - MemTracker::record_virtual_memory_type(rs.base(), mtMetaspace); + MemTracker::record_virtual_memory_tag(rs.base(), mtMetaspace); assert_is_aligned(rs.base(), chunklevel::MAX_CHUNK_BYTE_SIZE); InternalStats::inc_num_vsnodes_births(); return new VirtualSpaceNode(rs, true, limiter, reserve_words_counter, commit_words_counter); diff --git a/src/hotspot/share/memory/padded.hpp b/src/hotspot/share/memory/padded.hpp index bca1d168cb5ae..7e0e06152081e 100644 --- a/src/hotspot/share/memory/padded.hpp +++ b/src/hotspot/share/memory/padded.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_MEMORY_PADDED_HPP #define SHARE_MEMORY_PADDED_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" @@ -89,7 +89,7 @@ class PaddedEnd : public PaddedEndImpl { // Helper class to create an array of PaddedEnd objects. All elements will // start at a multiple of alignment and the size will be aligned to alignment. -template +template class PaddedArray { public: // Creates an aligned padded array. @@ -100,7 +100,7 @@ class PaddedArray { // Helper class to create an array of references to arrays of primitive types // Both the array of references and the data arrays are aligned to the given // alignment. The allocated memory is zero-filled. -template +template class Padded2DArray { public: // Creates an aligned padded 2D array. @@ -112,7 +112,7 @@ class Padded2DArray { // Helper class to create an array of T objects. The array as a whole will // start at a multiple of alignment and its size will be aligned to alignment. -template +template class PaddedPrimitiveArray { public: static T* create_unfreeable(size_t length); diff --git a/src/hotspot/share/memory/padded.inline.hpp b/src/hotspot/share/memory/padded.inline.hpp index 72001e3aad6d2..ba477bfe88f04 100644 --- a/src/hotspot/share/memory/padded.inline.hpp +++ b/src/hotspot/share/memory/padded.inline.hpp @@ -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 @@ -34,13 +34,13 @@ // Creates an aligned padded array. // The memory can't be deleted since the raw memory chunk is not returned. -template -PaddedEnd* PaddedArray::create_unfreeable(uint length) { +template +PaddedEnd* PaddedArray::create_unfreeable(uint length) { // Check that the PaddedEnd class works as intended. STATIC_ASSERT(is_aligned(sizeof(PaddedEnd), alignment)); // Allocate a chunk of memory large enough to allow for some alignment. - void* chunk = AllocateHeap(length * sizeof(PaddedEnd) + alignment, flags); + void* chunk = AllocateHeap(length * sizeof(PaddedEnd) + alignment, MT); // Make the initial alignment. PaddedEnd* aligned_padded_array = (PaddedEnd*)align_up(chunk, alignment); @@ -53,8 +53,8 @@ PaddedEnd* PaddedArray::create_unfreeable(uint length) { return aligned_padded_array; } -template -T** Padded2DArray::create_unfreeable(uint rows, uint columns, size_t* allocation_size) { +template +T** Padded2DArray::create_unfreeable(uint rows, uint columns, size_t* allocation_size) { // Calculate and align the size of the first dimension's table. size_t table_size = align_up(rows * sizeof(T*), alignment); // The size of the separate rows. @@ -63,7 +63,7 @@ T** Padded2DArray::create_unfreeable(uint rows, uint column size_t total_size = table_size + rows * row_size + alignment; // Allocate a chunk of memory large enough to allow alignment of the chunk. - void* chunk = MmapArrayAllocator::allocate(total_size, flags); + void* chunk = MmapArrayAllocator::allocate(total_size, MT); // Clear the allocated memory. // Align the chunk of memory. T** result = (T**)align_up(chunk, alignment); @@ -81,16 +81,16 @@ T** Padded2DArray::create_unfreeable(uint rows, uint column return result; } -template -T* PaddedPrimitiveArray::create_unfreeable(size_t length) { +template +T* PaddedPrimitiveArray::create_unfreeable(size_t length) { void* temp; return create(length, &temp); } -template -T* PaddedPrimitiveArray::create(size_t length, void** alloc_base) { +template +T* PaddedPrimitiveArray::create(size_t length, void** alloc_base) { // Allocate a chunk of memory large enough to allow for some alignment. - void* chunk = AllocateHeap(length * sizeof(T) + alignment, flags); + void* chunk = AllocateHeap(length * sizeof(T) + alignment, MT); memset(chunk, 0, length * sizeof(T) + alignment); diff --git a/src/hotspot/share/memory/resourceArea.hpp b/src/hotspot/share/memory/resourceArea.hpp index 5fd376068c5b3..b9a1904b5078c 100644 --- a/src/hotspot/share/memory/resourceArea.hpp +++ b/src/hotspot/share/memory/resourceArea.hpp @@ -51,11 +51,11 @@ class ResourceArea: public Arena { #endif // ASSERT public: - ResourceArea(MEMFLAGS flags = mtThread) : - Arena(flags, Arena::Tag::tag_ra) DEBUG_ONLY(COMMA _nesting(0)) {} + ResourceArea(MemTag mem_tag = mtThread) : + Arena(mem_tag, Arena::Tag::tag_ra) DEBUG_ONLY(COMMA _nesting(0)) {} - ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : - Arena(flags, Arena::Tag::tag_ra, init_size) DEBUG_ONLY(COMMA _nesting(0)) { + ResourceArea(size_t init_size, MemTag mem_tag = mtThread) : + Arena(mem_tag, Arena::Tag::tag_ra, init_size) DEBUG_ONLY(COMMA _nesting(0)) { } char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); diff --git a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp index c27e607353af1..0cc81ce85a617 100644 --- a/src/hotspot/share/memory/virtualspace.cpp +++ b/src/hotspot/share/memory/virtualspace.cpp @@ -653,7 +653,7 @@ ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, size_t page_ "area must be distinguishable from marks for mark-sweep"); if (base() != nullptr) { - MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap); + MemTracker::record_virtual_memory_tag((address)base(), mtJavaHeap); } if (_fd_for_heap != -1) { @@ -671,7 +671,7 @@ ReservedCodeSpace::ReservedCodeSpace(size_t r_size, size_t rs_align, size_t rs_page_size) : ReservedSpace() { initialize(r_size, rs_align, rs_page_size, /*requested address*/ nullptr, /*executable*/ true); - MemTracker::record_virtual_memory_type((address)base(), mtCode); + MemTracker::record_virtual_memory_tag((address)base(), mtCode); } // VirtualSpace diff --git a/src/hotspot/share/nmt/allocationSite.hpp b/src/hotspot/share/nmt/allocationSite.hpp index 022fb6f4390d8..a7bc2e962501c 100644 --- a/src/hotspot/share/nmt/allocationSite.hpp +++ b/src/hotspot/share/nmt/allocationSite.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_NMT_ALLOCATIONSITE_HPP #define SHARE_NMT_ALLOCATIONSITE_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/nativeCallStack.hpp" // Allocation site represents a code path that makes a memory @@ -33,9 +33,9 @@ class AllocationSite { private: const NativeCallStack _call_stack; - const MEMFLAGS _flag; + const MemTag _mem_tag; public: - AllocationSite(const NativeCallStack& stack, MEMFLAGS flag) : _call_stack(stack), _flag(flag) { } + AllocationSite(const NativeCallStack& stack, MemTag mem_tag) : _call_stack(stack), _mem_tag(mem_tag) { } bool equals(const NativeCallStack& stack) const { return _call_stack.equals(stack); @@ -49,7 +49,7 @@ class AllocationSite { return &_call_stack; } - MEMFLAGS flag() const { return _flag; } + MemTag mem_tag() const { return _mem_tag; } }; #endif // SHARE_NMT_ALLOCATIONSITE_HPP diff --git a/src/hotspot/share/nmt/arrayWithFreeList.hpp b/src/hotspot/share/nmt/arrayWithFreeList.hpp index 13aa1045fe7cb..2c1812dcc5283 100644 --- a/src/hotspot/share/nmt/arrayWithFreeList.hpp +++ b/src/hotspot/share/nmt/arrayWithFreeList.hpp @@ -31,7 +31,7 @@ // A flat array of elements E, backed by C-heap, growing on-demand. It allows for // returning arbitrary elements and keeps them in a freelist. Elements can be uniquely // identified via array index. -template +template class ArrayWithFreeList { // An E must be trivially copyable and destructible, but it may be constructed @@ -52,7 +52,7 @@ class ArrayWithFreeList { E e; }; - GrowableArrayCHeap _backing_storage; + GrowableArrayCHeap _backing_storage; I _free_start; bool is_in_bounds(I i) { diff --git a/src/hotspot/share/nmt/mallocHeader.cpp b/src/hotspot/share/nmt/mallocHeader.cpp index d5a7b689c2a48..defe2fc045d41 100644 --- a/src/hotspot/share/nmt/mallocHeader.cpp +++ b/src/hotspot/share/nmt/mallocHeader.cpp @@ -26,16 +26,16 @@ #include "nmt/mallocHeader.inline.hpp" #include "nmt/mallocSiteTable.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/nativeCallStack.hpp" #include "utilities/ostream.hpp" -// The malloc header, as well as the coming VMATree implementation, rely on MEMFLAGS +// The malloc header, as well as the coming VMATree implementation, rely on MemTag // fitting into eight bits. -STATIC_ASSERT(sizeof(MEMFLAGS) == sizeof(uint8_t)); +STATIC_ASSERT(sizeof(MemTag) == sizeof(uint8_t)); void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const { assert(bad_address >= (address)this, "sanity"); diff --git a/src/hotspot/share/nmt/mallocHeader.hpp b/src/hotspot/share/nmt/mallocHeader.hpp index 9f9f7f97ea7f5..6711c2b993e6f 100644 --- a/src/hotspot/share/nmt/mallocHeader.hpp +++ b/src/hotspot/share/nmt/mallocHeader.hpp @@ -26,7 +26,7 @@ #ifndef SHARE_NMT_MALLOCHEADER_HPP #define SHARE_NMT_MALLOCHEADER_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/nativeCallStack.hpp" @@ -92,7 +92,7 @@ class MallocHeader { NOT_LP64(uint32_t _alt_canary); const size_t _size; const uint32_t _mst_marker; - const MEMFLAGS _flags; + const MemTag _mem_tag; const uint8_t _unused; uint16_t _canary; @@ -121,19 +121,20 @@ class MallocHeader { // Contains all of the necessary data to to deaccount block with NMT. struct FreeInfo { const size_t size; - const MEMFLAGS flags; + const MemTag mem_tag; const uint32_t mst_marker; }; - inline MallocHeader(size_t size, MEMFLAGS flags, uint32_t mst_marker); + inline MallocHeader(size_t size, MemTag mem_tag, uint32_t mst_marker); - inline size_t size() const { return _size; } - inline MEMFLAGS flags() const { return _flags; } + inline static size_t malloc_overhead() { return sizeof(MallocHeader) + sizeof(uint16_t); } + inline size_t size() const { return _size; } + inline MemTag mem_tag() const { return _mem_tag; } inline uint32_t mst_marker() const { return _mst_marker; } // Return the necessary data to deaccount the block with NMT. FreeInfo free_info() { - return FreeInfo{this->size(), this->flags(), this->mst_marker()}; + return FreeInfo{this->size(), this->mem_tag(), this->mst_marker()}; } inline void mark_block_as_dead(); inline void revive(); diff --git a/src/hotspot/share/nmt/mallocHeader.inline.hpp b/src/hotspot/share/nmt/mallocHeader.inline.hpp index d763241b36d8c..34ec891d33f78 100644 --- a/src/hotspot/share/nmt/mallocHeader.inline.hpp +++ b/src/hotspot/share/nmt/mallocHeader.inline.hpp @@ -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. * Copyright (c) 2021, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -34,8 +34,8 @@ #include "utilities/macros.hpp" #include "utilities/nativeCallStack.hpp" -inline MallocHeader::MallocHeader(size_t size, MEMFLAGS flags, uint32_t mst_marker) - : _size(size), _mst_marker(mst_marker), _flags(flags), +inline MallocHeader::MallocHeader(size_t size, MemTag mem_tag, uint32_t mst_marker) + : _size(size), _mst_marker(mst_marker), _mem_tag(mem_tag), _unused(0), _canary(_header_canary_live_mark) { assert(size < max_reasonable_malloc_size, "Too large allocation size?"); diff --git a/src/hotspot/share/nmt/mallocLimit.cpp b/src/hotspot/share/nmt/mallocLimit.cpp index 746c3b9201bd9..5e16a406821ea 100644 --- a/src/hotspot/share/nmt/mallocLimit.cpp +++ b/src/hotspot/share/nmt/mallocLimit.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "nmt/mallocLimit.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "nmt/nmtCommon.hpp" #include "runtime/java.hpp" #include "runtime/globals.hpp" @@ -80,7 +80,7 @@ class ParserHelper { // Check if string at position matches a category name. // Advances position on match. - bool match_category(MEMFLAGS* out) { + bool match_category(MemTag* out) { if (eof()) { return false; } @@ -90,9 +90,9 @@ class ParserHelper { } stringStream ss; ss.print("%.*s", (int)(end - _p), _p); - MEMFLAGS f = NMTUtil::string_to_flag(ss.base()); - if (f != mtNone) { - *out = f; + MemTag mem_tag = NMTUtil::string_to_mem_tag(ss.base()); + if (mem_tag != mtNone) { + *out = mem_tag; _p = end; return true; } @@ -131,16 +131,16 @@ void MallocLimitSet::set_global_limit(size_t s, MallocLimitMode flag) { _glob.sz = s; _glob.mode = flag; } -void MallocLimitSet::set_category_limit(MEMFLAGS f, size_t s, MallocLimitMode flag) { - const int i = NMTUtil::flag_to_index(f); +void MallocLimitSet::set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode flag) { + const int i = NMTUtil::tag_to_index(mem_tag); _cat[i].sz = s; _cat[i].mode = flag; } void MallocLimitSet::reset() { set_global_limit(0, MallocLimitMode::trigger_fatal); _glob.sz = 0; _glob.mode = MallocLimitMode::trigger_fatal; - for (int i = 0; i < mt_number_of_types; i++) { - set_category_limit(NMTUtil::index_to_flag(i), 0, MallocLimitMode::trigger_fatal); + for (int i = 0; i < mt_number_of_tags; i++) { + set_category_limit(NMTUtil::index_to_tag(i), 0, MallocLimitMode::trigger_fatal); } } @@ -150,10 +150,10 @@ void MallocLimitSet::print_on(outputStream* st) const { st->print_cr("MallocLimit: total limit: " PROPERFMT " (%s)", PROPERFMTARGS(_glob.sz), mode_to_name(_glob.mode)); } else { - for (int i = 0; i < mt_number_of_types; i++) { + for (int i = 0; i < mt_number_of_tags; i++) { if (_cat[i].sz > 0) { st->print_cr("MallocLimit: category \"%s\" limit: " PROPERFMT " (%s)", - NMTUtil::flag_to_enum_name(NMTUtil::index_to_flag(i)), + NMTUtil::tag_to_enum_name(NMTUtil::index_to_tag(i)), PROPERFMTARGS(_cat[i].sz), mode_to_name(_cat[i].mode)); } } @@ -187,13 +187,13 @@ bool MallocLimitSet::parse_malloclimit_option(const char* v, const char** err) { // Category-specific form? else { while (!sst.eof()) { - MEMFLAGS f; + MemTag mem_tag; // Match category, followed by : - BAIL_UNLESS(sst.match_category(&f), "Expected category name"); + BAIL_UNLESS(sst.match_category(&mem_tag), "Expected category name"); BAIL_UNLESS(sst.match_char(':'), "Expected colon following category"); - malloclimit* const modified_limit = &_cat[NMTUtil::flag_to_index(f)]; + malloclimit* const modified_limit = &_cat[NMTUtil::tag_to_index(mem_tag)]; // Match size BAIL_UNLESS(sst.match_size(&modified_limit->sz), "Expected size"); diff --git a/src/hotspot/share/nmt/mallocLimit.hpp b/src/hotspot/share/nmt/mallocLimit.hpp index 2034e3ce24b77..ec6799b41a392 100644 --- a/src/hotspot/share/nmt/mallocLimit.hpp +++ b/src/hotspot/share/nmt/mallocLimit.hpp @@ -27,7 +27,7 @@ #define SHARE_SERVICES_MALLOCLIMIT_HPP #include "memory/allStatic.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -46,18 +46,18 @@ class outputStream; class MallocLimitSet { malloclimit _glob; // global limit - malloclimit _cat[mt_number_of_types]; // per-category limit + malloclimit _cat[mt_number_of_tags]; // per-category limit public: MallocLimitSet(); void reset(); bool parse_malloclimit_option(const char* optionstring, const char** err); - void set_global_limit(size_t s, MallocLimitMode flag); - void set_category_limit(MEMFLAGS f, size_t s, MallocLimitMode flag); + void set_global_limit(size_t s, MallocLimitMode type); + void set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode mode); const malloclimit* global_limit() const { return &_glob; } - const malloclimit* category_limit(MEMFLAGS f) const { return &_cat[(int)f]; } + const malloclimit* category_limit(MemTag mem_tag) const { return &_cat[(int)mem_tag]; } void print_on(outputStream* st) const; }; @@ -69,7 +69,7 @@ class MallocLimitHandler : public AllStatic { public: static const malloclimit* global_limit() { return _limits.global_limit(); } - static const malloclimit* category_limit(MEMFLAGS f) { return _limits.category_limit(f); } + static const malloclimit* category_limit(MemTag mem_tag) { return _limits.category_limit(mem_tag); } static void initialize(const char* options); static void print_on(outputStream* st); diff --git a/src/hotspot/share/nmt/mallocSiteTable.cpp b/src/hotspot/share/nmt/mallocSiteTable.cpp index 0fdf9e0f83dcc..9411f76c491ac 100644 --- a/src/hotspot/share/nmt/mallocSiteTable.cpp +++ b/src/hotspot/share/nmt/mallocSiteTable.cpp @@ -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 @@ -111,15 +111,15 @@ bool MallocSiteTable::walk(MallocSiteWalker* walker) { * 2. Overflow hash bucket. * Under any of above circumstances, caller should handle the situation. */ -MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* marker, MEMFLAGS flags) { - assert(flags != mtNone, "Should have a real memory type"); +MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* marker, MemTag mem_tag) { + assert(mem_tag != mtNone, "Should have a real memory tag"); const unsigned int hash = key.calculate_hash(); const unsigned int index = hash_to_index(hash); *marker = 0; // First entry for this hash bucket if (_table[index] == nullptr) { - MallocSiteHashtableEntry* entry = new_entry(key, flags); + MallocSiteHashtableEntry* entry = new_entry(key, mem_tag); // OOM check if (entry == nullptr) return nullptr; @@ -137,14 +137,14 @@ MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* while (head != nullptr && pos_idx < MAX_BUCKET_LENGTH) { if (head->hash() == hash) { MallocSite* site = head->data(); - if (site->flag() == flags && site->equals(key)) { + if (site->mem_tag() == mem_tag && site->equals(key)) { *marker = build_marker(index, pos_idx); return head->data(); } } if (head->next() == nullptr && pos_idx < (MAX_BUCKET_LENGTH - 1)) { - MallocSiteHashtableEntry* entry = new_entry(key, flags); + MallocSiteHashtableEntry* entry = new_entry(key, mem_tag); // OOM check if (entry == nullptr) return nullptr; if (head->atomic_insert(entry)) { @@ -177,10 +177,10 @@ MallocSite* MallocSiteTable::malloc_site(uint32_t marker) { // Allocates MallocSiteHashtableEntry object. Special call stack // (pre-installed allocation site) has to be used to avoid infinite // recursion. -MallocSiteHashtableEntry* MallocSiteTable::new_entry(const NativeCallStack& key, MEMFLAGS flags) { +MallocSiteHashtableEntry* MallocSiteTable::new_entry(const NativeCallStack& key, MemTag mem_tag) { void* p = AllocateHeap(sizeof(MallocSiteHashtableEntry), mtNMT, *hash_entry_allocation_stack(), AllocFailStrategy::RETURN_NULL); - return ::new (p) MallocSiteHashtableEntry(key, flags); + return ::new (p) MallocSiteHashtableEntry(key, mem_tag); } bool MallocSiteTable::walk_malloc_site(MallocSiteWalker* walker) { diff --git a/src/hotspot/share/nmt/mallocSiteTable.hpp b/src/hotspot/share/nmt/mallocSiteTable.hpp index ae9266f5369a0..472bc397dd49e 100644 --- a/src/hotspot/share/nmt/mallocSiteTable.hpp +++ b/src/hotspot/share/nmt/mallocSiteTable.hpp @@ -38,8 +38,8 @@ class MallocSite : public AllocationSite { MemoryCounter _c; public: - MallocSite(const NativeCallStack& stack, MEMFLAGS flags) : - AllocationSite(stack, flags) {} + MallocSite(const NativeCallStack& stack, MemTag mem_tag) : + AllocationSite(stack, mem_tag) {} void allocate(size_t size) { _c.allocate(size); } void deallocate(size_t size) { _c.deallocate(size); } @@ -63,9 +63,9 @@ class MallocSiteHashtableEntry : public CHeapObj { public: - MallocSiteHashtableEntry(NativeCallStack stack, MEMFLAGS flags): - _malloc_site(stack, flags), _hash(stack.calculate_hash()), _next(nullptr) { - assert(flags != mtNone, "Expect a real memory type"); + MallocSiteHashtableEntry(NativeCallStack stack, MemTag mem_tag): + _malloc_site(stack, mem_tag), _hash(stack.calculate_hash()), _next(nullptr) { + assert(mem_tag != mtNone, "Expect a real memory tag"); } inline const MallocSiteHashtableEntry* next() const { @@ -147,8 +147,8 @@ class MallocSiteTable : AllStatic { // 1. out of memory // 2. overflow hash bucket static inline bool allocation_at(const NativeCallStack& stack, size_t size, - uint32_t* marker, MEMFLAGS flags) { - MallocSite* site = lookup_or_add(stack, marker, flags); + uint32_t* marker, MemTag mem_tag) { + MallocSite* site = lookup_or_add(stack, marker, mem_tag); if (site != nullptr) site->allocate(size); return site != nullptr; } @@ -170,9 +170,9 @@ class MallocSiteTable : AllStatic { static void print_tuning_statistics(outputStream* st); private: - static MallocSiteHashtableEntry* new_entry(const NativeCallStack& key, MEMFLAGS flags); + static MallocSiteHashtableEntry* new_entry(const NativeCallStack& key, MemTag mem_tag); - static MallocSite* lookup_or_add(const NativeCallStack& key, uint32_t* marker, MEMFLAGS flags); + static MallocSite* lookup_or_add(const NativeCallStack& key, uint32_t* marker, MemTag mem_tag); static MallocSite* malloc_site(uint32_t marker); static bool walk(MallocSiteWalker* walker); diff --git a/src/hotspot/share/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp index 021ce5d1332b1..6829db90b4bc1 100644 --- a/src/hotspot/share/nmt/mallocTracker.cpp +++ b/src/hotspot/share/nmt/mallocTracker.cpp @@ -69,7 +69,7 @@ void MallocMemorySnapshot::copy_to(MallocMemorySnapshot* s) { s->_all_mallocs = _all_mallocs; size_t total_size = 0; size_t total_count = 0; - for (int index = 0; index < mt_number_of_types; index ++) { + for (int index = 0; index < mt_number_of_tags; index ++) { s->_malloc[index] = _malloc[index]; total_size += s->_malloc[index].malloc_size(); total_count += s->_malloc[index].malloc_count(); @@ -81,7 +81,7 @@ void MallocMemorySnapshot::copy_to(MallocMemorySnapshot* s) { // Total malloc'd memory used by arenas size_t MallocMemorySnapshot::total_arena() const { size_t amount = 0; - for (int index = 0; index < mt_number_of_types; index ++) { + for (int index = 0; index < mt_number_of_tags; index ++) { amount += _malloc[index].arena_size(); } return amount; @@ -91,7 +91,7 @@ size_t MallocMemorySnapshot::total_arena() const { // from total chunks to get total free chunk size void MallocMemorySnapshot::make_adjustment() { size_t arena_size = total_arena(); - int chunk_idx = NMTUtil::flag_to_index(mtChunk); + int chunk_idx = NMTUtil::tag_to_index(mtChunk); _malloc[chunk_idx].record_free(arena_size); _all_mallocs.deallocate(arena_size); } @@ -128,11 +128,11 @@ bool MallocMemorySummary::total_limit_reached(size_t s, size_t so_far, const mal return true; } -bool MallocMemorySummary::category_limit_reached(MEMFLAGS f, size_t s, size_t so_far, const malloclimit* limit) { +bool MallocMemorySummary::category_limit_reached(MemTag mem_tag, size_t s, size_t so_far, const malloclimit* limit) { #define FORMATTED \ "MallocLimit: reached category \"%s\" limit (triggering allocation size: " PROPERFMT ", allocated so far: " PROPERFMT ", limit: " PROPERFMT ") ", \ - NMTUtil::flag_to_enum_name(f), PROPERFMTARGS(s), PROPERFMTARGS(so_far), PROPERFMTARGS(limit->sz) + NMTUtil::tag_to_enum_name(mem_tag), PROPERFMTARGS(s), PROPERFMTARGS(so_far), PROPERFMTARGS(limit->sz) // If we hit the limit during error reporting, we print a short warning but otherwise ignore it. // We don't want to risk recursive assertion or torn hs-err logs. @@ -167,20 +167,20 @@ bool MallocTracker::initialize(NMT_TrackingLevel level) { } // Record a malloc memory allocation -void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flags, +void* MallocTracker::record_malloc(void* malloc_base, size_t size, MemTag mem_tag, const NativeCallStack& stack) { assert(MemTracker::enabled(), "precondition"); assert(malloc_base != nullptr, "precondition"); - MallocMemorySummary::record_malloc(size, flags); + MallocMemorySummary::record_malloc(size, mem_tag); uint32_t mst_marker = 0; if (MemTracker::tracking_level() == NMT_detail) { - MallocSiteTable::allocation_at(stack, size, &mst_marker, flags); + MallocSiteTable::allocation_at(stack, size, &mst_marker, mem_tag); } // Uses placement global new operator to initialize malloc header - MallocHeader* const header = ::new (malloc_base)MallocHeader(size, flags, mst_marker); + MallocHeader* const header = ::new (malloc_base)MallocHeader(size, mem_tag, mst_marker); void* const memblock = (void*)((char*)malloc_base + sizeof(MallocHeader)); // The alignment check: 8 bytes alignment for 32 bit systems. @@ -192,7 +192,7 @@ void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flag { const MallocHeader* header2 = MallocHeader::resolve_checked(memblock); assert(header2->size() == size, "Wrong size"); - assert(header2->flags() == flags, "Wrong flags"); + assert(header2->mem_tag() == mem_tag, "Wrong memory tag"); } #endif @@ -213,7 +213,7 @@ void* MallocTracker::record_free_block(void* memblock) { } void MallocTracker::deaccount(MallocHeader::FreeInfo free_info) { - MallocMemorySummary::record_free(free_info.size, free_info.flags); + MallocMemorySummary::record_free(free_info.size, free_info.mem_tag); if (MemTracker::tracking_level() == NMT_detail) { MallocSiteTable::deallocation_at(free_info.size, free_info.mst_marker); } @@ -296,7 +296,7 @@ bool MallocTracker::print_pointer_information(const void* p, outputStream* st) { p2i(p), where, (block->is_dead() ? "dead" : "live"), p2i(block + 1), // lets print the payload start, not the header - block->size(), NMTUtil::flag_to_enum_name(block->flags())); + block->size(), NMTUtil::tag_to_enum_name(block->mem_tag())); if (MemTracker::tracking_level() == NMT_detail) { NativeCallStack ncs; if (MallocSiteTable::access_stack(ncs, *block)) { diff --git a/src/hotspot/share/nmt/mallocTracker.hpp b/src/hotspot/share/nmt/mallocTracker.hpp index 9c14ea04bf0f2..de30f32373edf 100644 --- a/src/hotspot/share/nmt/mallocTracker.hpp +++ b/src/hotspot/share/nmt/mallocTracker.hpp @@ -27,7 +27,7 @@ #define SHARE_NMT_MALLOCTRACKER_HPP #include "nmt/mallocHeader.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "nmt/nmtCommon.hpp" #include "runtime/atomic.hpp" #include "runtime/threadCritical.hpp" @@ -150,23 +150,23 @@ class MallocMemorySnapshot { friend class MallocMemorySummary; private: - MallocMemory _malloc[mt_number_of_types]; + MallocMemory _malloc[mt_number_of_tags]; MemoryCounter _all_mallocs; public: - inline MallocMemory* by_type(MEMFLAGS flags) { - int index = NMTUtil::flag_to_index(flags); + inline MallocMemory* by_type(MemTag mem_tag) { + int index = NMTUtil::tag_to_index(mem_tag); return &_malloc[index]; } - inline const MallocMemory* by_type(MEMFLAGS flags) const { - int index = NMTUtil::flag_to_index(flags); + inline const MallocMemory* by_type(MemTag mem_tag) const { + int index = NMTUtil::tag_to_index(mem_tag); return &_malloc[index]; } inline size_t malloc_overhead() const { - return _all_mallocs.count() * sizeof(MallocHeader); + return _all_mallocs.count() * MallocHeader::malloc_overhead(); } // Total malloc invocation count @@ -214,31 +214,31 @@ class MallocMemorySummary : AllStatic { // Called when a total limit break was detected. // Will return true if the limit was handled, false if it was ignored. - static bool category_limit_reached(MEMFLAGS f, size_t s, size_t so_far, const malloclimit* limit); + static bool category_limit_reached(MemTag mem_tag, size_t s, size_t so_far, const malloclimit* limit); public: static void initialize(); - static inline void record_malloc(size_t size, MEMFLAGS flag) { - as_snapshot()->by_type(flag)->record_malloc(size); + static inline void record_malloc(size_t size, MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->record_malloc(size); as_snapshot()->_all_mallocs.allocate(size); } - static inline void record_free(size_t size, MEMFLAGS flag) { - as_snapshot()->by_type(flag)->record_free(size); + static inline void record_free(size_t size, MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->record_free(size); as_snapshot()->_all_mallocs.deallocate(size); } - static inline void record_new_arena(MEMFLAGS flag) { - as_snapshot()->by_type(flag)->record_new_arena(); + static inline void record_new_arena(MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->record_new_arena(); } - static inline void record_arena_free(MEMFLAGS flag) { - as_snapshot()->by_type(flag)->record_arena_free(); + static inline void record_arena_free(MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->record_arena_free(); } - static inline void record_arena_size_change(ssize_t size, MEMFLAGS flag) { - as_snapshot()->by_type(flag)->record_arena_size_change(size); + static inline void record_arena_size_change(ssize_t size, MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->record_arena_size_change(size); } static void snapshot(MallocMemorySnapshot* s) { @@ -257,7 +257,7 @@ class MallocMemorySummary : AllStatic { // MallocLimit: returns true if allocating s bytes on f would trigger // either global or the category limit - static inline bool check_exceeds_limit(size_t s, MEMFLAGS f); + static inline bool check_exceeds_limit(size_t s, MemTag mem_tag); }; @@ -269,7 +269,7 @@ class MallocTracker : AllStatic { // The overhead that is incurred by switching on NMT (we need, per malloc allocation, // space for header and 16-bit footer) - static const size_t overhead_per_malloc = sizeof(MallocHeader) + sizeof(uint16_t); + static inline size_t overhead_per_malloc() { return MallocHeader::malloc_overhead(); } // Parameter name convention: // memblock : the beginning address for user data @@ -280,7 +280,7 @@ class MallocTracker : AllStatic { // // Record malloc on specified memory block - static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags, + static void* record_malloc(void* malloc_base, size_t size, MemTag mem_tag, const NativeCallStack& stack); // Given a block returned by os::malloc() or os::realloc(): @@ -289,21 +289,21 @@ class MallocTracker : AllStatic { // Given the free info from a block, de-account block from NMT. static void deaccount(MallocHeader::FreeInfo free_info); - static inline void record_new_arena(MEMFLAGS flags) { - MallocMemorySummary::record_new_arena(flags); + static inline void record_new_arena(MemTag mem_tag) { + MallocMemorySummary::record_new_arena(mem_tag); } - static inline void record_arena_free(MEMFLAGS flags) { - MallocMemorySummary::record_arena_free(flags); + static inline void record_arena_free(MemTag mem_tag) { + MallocMemorySummary::record_arena_free(mem_tag); } - static inline void record_arena_size_change(ssize_t size, MEMFLAGS flags) { - MallocMemorySummary::record_arena_size_change(size, flags); + static inline void record_arena_size_change(ssize_t size, MemTag mem_tag) { + MallocMemorySummary::record_arena_size_change(size, mem_tag); } // MallocLimt: Given an allocation size s, check if mallocing this much - // under category f would hit either the global limit or the limit for category f. - static inline bool check_exceeds_limit(size_t s, MEMFLAGS f); + // for MemTag would hit either the global limit or the limit for MemTag. + static inline bool check_exceeds_limit(size_t s, MemTag mem_tag); // Given a pointer, look for the containing malloc block. // Print the block. Note that since there is very low risk of memory looking diff --git a/src/hotspot/share/nmt/mallocTracker.inline.hpp b/src/hotspot/share/nmt/mallocTracker.inline.hpp index 243f965a3829a..19d3775ed778e 100644 --- a/src/hotspot/share/nmt/mallocTracker.inline.hpp +++ b/src/hotspot/share/nmt/mallocTracker.inline.hpp @@ -32,7 +32,7 @@ #include "utilities/globalDefinitions.hpp" // Returns true if allocating s bytes on f would trigger either global or the category limit -inline bool MallocMemorySummary::check_exceeds_limit(size_t s, MEMFLAGS f) { +inline bool MallocMemorySummary::check_exceeds_limit(size_t s, MemTag mem_tag) { // Note: checks are ordered to have as little impact as possible on the standard code path, // when MallocLimit is unset, resp. it is set but we have reached no limit yet. @@ -50,12 +50,12 @@ inline bool MallocMemorySummary::check_exceeds_limit(size_t s, MEMFLAGS f) { } } else { // Category Limit? - l = MallocLimitHandler::category_limit(f); + l = MallocLimitHandler::category_limit(mem_tag); if (l->sz > 0) { - const MallocMemory* mm = as_snapshot()->by_type(f); + const MallocMemory* mm = as_snapshot()->by_type(mem_tag); size_t so_far = mm->malloc_size() + mm->arena_size(); if ((so_far + s) > l->sz) { - return category_limit_reached(f, s, so_far, l); + return category_limit_reached(mem_tag, s, so_far, l); } } } @@ -64,8 +64,8 @@ inline bool MallocMemorySummary::check_exceeds_limit(size_t s, MEMFLAGS f) { return false; } -inline bool MallocTracker::check_exceeds_limit(size_t s, MEMFLAGS f) { - return MallocMemorySummary::check_exceeds_limit(s, f); +inline bool MallocTracker::check_exceeds_limit(size_t s, MemTag mem_tag) { + return MallocMemorySummary::check_exceeds_limit(s, mem_tag); } diff --git a/src/hotspot/share/nmt/memBaseline.cpp b/src/hotspot/share/nmt/memBaseline.cpp index 7c7dd3ec24e5f..6f82b2de9f106 100644 --- a/src/hotspot/share/nmt/memBaseline.cpp +++ b/src/hotspot/share/nmt/memBaseline.cpp @@ -61,11 +61,11 @@ int compare_malloc_site(const MallocSite& s1, const MallocSite& s2) { return s1.call_stack()->compare(*s2.call_stack()); } -// Sort into allocation site addresses and memory type order for baseline comparison +// Sort into allocation site addresses and memory tag order for baseline comparison int compare_malloc_site_and_type(const MallocSite& s1, const MallocSite& s2) { int res = compare_malloc_site(s1, s2); if (res == 0) { - res = (int)(NMTUtil::flag_to_index(s1.flag()) - NMTUtil::flag_to_index(s2.flag())); + res = (int)(NMTUtil::tag_to_index(s1.mem_tag()) - NMTUtil::tag_to_index(s2.mem_tag())); } return res; @@ -207,7 +207,7 @@ bool MemBaseline::aggregate_virtual_memory_allocation_sites() { const ReservedMemoryRegion* rgn; VirtualMemoryAllocationSite* site; while ((rgn = itr.next()) != nullptr) { - VirtualMemoryAllocationSite tmp(*rgn->call_stack(), rgn->flag()); + VirtualMemoryAllocationSite tmp(*rgn->call_stack(), rgn->mem_tag()); site = allocation_sites.find(tmp); if (site == nullptr) { LinkedListNode* node = diff --git a/src/hotspot/share/nmt/memBaseline.hpp b/src/hotspot/share/nmt/memBaseline.hpp index 903f558051103..be389e375e342 100644 --- a/src/hotspot/share/nmt/memBaseline.hpp +++ b/src/hotspot/share/nmt/memBaseline.hpp @@ -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 @@ -53,7 +53,7 @@ class MemBaseline { by_address, // by memory address by_size, // by memory size by_site, // by call site where the memory is allocated from - by_site_and_type // by call site and memory type + by_site_and_type // by call site and memory tag }; private: @@ -144,14 +144,14 @@ class MemBaseline { return bl->_malloc_memory_snapshot.malloc_overhead(); } - MallocMemory* malloc_memory(MEMFLAGS flag) { + MallocMemory* malloc_memory(MemTag mem_tag) { assert(baseline_type() != Not_baselined, "Not yet baselined"); - return _malloc_memory_snapshot.by_type(flag); + return _malloc_memory_snapshot.by_type(mem_tag); } - VirtualMemory* virtual_memory(MEMFLAGS flag) { + VirtualMemory* virtual_memory(MemTag mem_tag) { assert(baseline_type() != Not_baselined, "Not yet baselined"); - return _virtual_memory_snapshot.by_type(flag); + return _virtual_memory_snapshot.by_type(mem_tag); } @@ -203,7 +203,7 @@ class MemBaseline { void malloc_sites_to_size_order(); // Sort allocation sites in call site address order void malloc_sites_to_allocation_site_order(); - // Sort allocation sites in call site address and memory type order + // Sort allocation sites in call site address and memory tag order void malloc_sites_to_allocation_site_and_type_order(); // Sort allocation sites in reserved size order diff --git a/src/hotspot/share/nmt/memMapPrinter.cpp b/src/hotspot/share/nmt/memMapPrinter.cpp index 5f920b102a977..34ddb7a871336 100644 --- a/src/hotspot/share/nmt/memMapPrinter.cpp +++ b/src/hotspot/share/nmt/memMapPrinter.cpp @@ -25,15 +25,15 @@ #include "precompiled.hpp" -#ifdef LINUX +#if defined(LINUX) || defined(_WIN64) #include "gc/shared/collectedHeap.hpp" #include "logging/logAsyncWriter.hpp" #include "memory/allocation.hpp" #include "memory/universe.hpp" #include "memory/resourceArea.hpp" -#include "nmt/memflags.hpp" -#include "nmt/memFlagBitmap.hpp" +#include "nmt/memTag.hpp" +#include "nmt/memTagBitmap.hpp" #include "nmt/memMapPrinter.hpp" #include "nmt/memTracker.hpp" #include "nmt/virtualMemoryTracker.hpp" @@ -50,9 +50,9 @@ /// NMT mechanics // Short, clear, descriptive names for all possible markers. Note that we only expect to see -// those that have been used with mmap. Flags left out are printed with their nmt flag name. +// those that have been used with mmap. Flags left out are printed with their nmt tags name. #define NMT_FLAGS_DO(f) \ - /* flag, short, description */ \ + /* mem_tag, short, description */ \ f(mtGCCardSet, "CARDTBL", "GC Card table") \ f(mtClassShared, "CDS", "CDS archives") \ f(mtClass, "CLASS", "Class Space") \ @@ -67,11 +67,11 @@ f(mtTest, "TEST", "JVM internal test mappings") //end -static const char* get_shortname_for_nmt_flag(MEMFLAGS f) { -#define DO(flag, shortname, text) if (flag == f) return shortname; +static const char* get_shortname_for_mem_tag(MemTag mem_tag) { +#define DO(t, shortname, text) if (t == mem_tag) return shortname; NMT_FLAGS_DO(DO) #undef DO - return NMTUtil::flag_to_enum_name(f); + return NMTUtil::tag_to_enum_name(mem_tag); } /// NMT virtual memory @@ -80,7 +80,7 @@ static bool range_intersects(const void* from1, const void* to1, const void* fro return MAX2(from1, from2) < MIN2(to1, to2); } -// A Cache that correlates range with MEMFLAG, optimized to be iterated quickly +// A Cache that correlates range with MemTag, optimized to be iterated quickly // (cache friendly). class CachedNMTInformation : public VirtualMemoryWalker { struct Range { const void* from; const void* to; }; @@ -88,24 +88,24 @@ class CachedNMTInformation : public VirtualMemoryWalker { // structure would have, and it allows for faster iteration of ranges since more // of them fit into a cache line. Range* _ranges; - MEMFLAGS* _flags; + MemTag* _mem_tags; size_t _count, _capacity; mutable size_t _last; public: - CachedNMTInformation() : _ranges(nullptr), _flags(nullptr), + CachedNMTInformation() : _ranges(nullptr), _mem_tags(nullptr), _count(0), _capacity(0), _last(0) {} ~CachedNMTInformation() { ALLOW_C_FUNCTION(free, ::free(_ranges);) - ALLOW_C_FUNCTION(free, ::free(_flags);) + ALLOW_C_FUNCTION(free, ::free(_mem_tags);) } - bool add(const void* from, const void* to, MEMFLAGS f) { + bool add(const void* from, const void* to, MemTag mem_tag) { // We rely on NMT regions being sorted by base assert(_count == 0 || (from >= _ranges[_count - 1].to), "NMT regions unordered?"); - // we can just fold two regions if they are adjacent and have the same flag. - if (_count > 0 && from == _ranges[_count - 1].to && f == _flags[_count - 1]) { + // we can just fold two regions if they are adjacent and have the same mem_tag. + if (_count > 0 && from == _ranges[_count - 1].to && mem_tag == _mem_tags[_count - 1]) { _ranges[_count - 1].to = to; return true; } @@ -114,8 +114,8 @@ class CachedNMTInformation : public VirtualMemoryWalker { const size_t new_capacity = MAX2((size_t)4096, 2 * _capacity); // Unfortunately, we need to allocate manually, raw, since we must prevent NMT deadlocks (ThreadCritical). ALLOW_C_FUNCTION(realloc, _ranges = (Range*)::realloc(_ranges, new_capacity * sizeof(Range));) - ALLOW_C_FUNCTION(realloc, _flags = (MEMFLAGS*)::realloc(_flags, new_capacity * sizeof(MEMFLAGS));) - if (_ranges == nullptr || _flags == nullptr) { + ALLOW_C_FUNCTION(realloc, _mem_tags = (MemTag*)::realloc(_mem_tags, new_capacity * sizeof(MemTag));) + if (_ranges == nullptr || _mem_tags == nullptr) { // In case of OOM lets make no fuss. Just return. return false; } @@ -123,14 +123,14 @@ class CachedNMTInformation : public VirtualMemoryWalker { } assert(_capacity > _count, "Sanity"); _ranges[_count] = Range { from, to }; - _flags[_count] = f; + _mem_tags[_count] = mem_tag; _count++; return true; } // Given a vma [from, to), find all regions that intersect with this vma and // return their collective flags. - MemFlagBitmap lookup(const void* from, const void* to) const { + MemTagBitmap lookup(const void* from, const void* to) const { assert(from <= to, "Sanity"); // We optimize for sequential lookups. Since this class is used when a list // of OS mappings is scanned (VirtualQuery, /proc/pid/maps), and these lists @@ -139,10 +139,10 @@ class CachedNMTInformation : public VirtualMemoryWalker { // the range is to the right of the given section, we need to re-start the search _last = 0; } - MemFlagBitmap bm; + MemTagBitmap bm; for(uintx i = _last; i < _count; i++) { if (range_intersects(from, to, _ranges[i].from, _ranges[i].to)) { - bm.set_flag(_flags[i]); + bm.set_tag(_mem_tags[i]); } else if (to <= _ranges[i].from) { _last = i; break; @@ -153,7 +153,7 @@ class CachedNMTInformation : public VirtualMemoryWalker { bool do_allocation_site(const ReservedMemoryRegion* rgn) override { // Cancel iteration if we run out of memory (add returns false); - return add(rgn->base(), rgn->end(), rgn->flag()); + return add(rgn->base(), rgn->end(), rgn->mem_tag()); } // Iterate all NMT virtual memory regions and fill this cache. @@ -247,16 +247,16 @@ bool MappingPrintSession::print_nmt_info_for_region(const void* vma_from, const // print NMT information, if available if (MemTracker::enabled()) { // Correlate vma region (from, to) with NMT region(s) we collected previously. - const MemFlagBitmap flags = _nmt_info.lookup(vma_from, vma_to); + const MemTagBitmap flags = _nmt_info.lookup(vma_from, vma_to); if (flags.has_any()) { - for (int i = 0; i < mt_number_of_types; i++) { - const MEMFLAGS flag = (MEMFLAGS)i; - if (flags.has_flag(flag)) { + for (int i = 0; i < mt_number_of_tags; i++) { + const MemTag mem_tag = (MemTag)i; + if (flags.has_tag(mem_tag)) { if (num_printed > 0) { _out->put(','); } - _out->print("%s", get_shortname_for_nmt_flag(flag)); - if (flag == mtThreadStack) { + _out->print("%s", get_shortname_for_mem_tag(mem_tag)); + if (mem_tag == mtThreadStack) { print_thread_details_for_supposed_stack_address(vma_from, vma_to, _out); } num_printed++; diff --git a/src/hotspot/share/nmt/memMapPrinter.hpp b/src/hotspot/share/nmt/memMapPrinter.hpp index aa35a830001e3..7f3ef9abca893 100644 --- a/src/hotspot/share/nmt/memMapPrinter.hpp +++ b/src/hotspot/share/nmt/memMapPrinter.hpp @@ -27,10 +27,10 @@ #define SHARE_SERVICES_MEMMAPPRINTER_HPP #include "memory/allStatic.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/globalDefinitions.hpp" -#ifdef LINUX +#if defined(LINUX) || defined(_WIN64) class outputStream; class CachedNMTInformation; diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index d53782dfdaa15..6ce6206ebcc2a 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -26,7 +26,7 @@ #include "memory/metaspace.hpp" #include "memory/metaspaceUtils.hpp" #include "nmt/mallocTracker.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "nmt/memReporter.hpp" #include "nmt/memoryFileTracker.hpp" #include "nmt/threadStackTracker.hpp" @@ -70,17 +70,17 @@ void MemReporterBase::print_total(size_t reserved, size_t committed, size_t peak } } -void MemReporterBase::print_malloc(const MemoryCounter* c, MEMFLAGS flag) const { +void MemReporterBase::print_malloc(const MemoryCounter* c, MemTag mem_tag) const { const char* scale = current_scale(); outputStream* out = output(); - const char* alloc_type = (flag == mtThreadStack) ? "" : "malloc="; + const char* alloc_type = (mem_tag == mtThreadStack) ? "" : "malloc="; const size_t amount = c->size(); const size_t count = c->count(); - if (flag != mtNone) { + if (mem_tag != mtNone) { out->print("(%s" SIZE_FORMAT "%s type=%s", alloc_type, - amount_in_current_scale(amount), scale, NMTUtil::flag_to_name(flag)); + amount_in_current_scale(amount), scale, NMTUtil::tag_to_name(mem_tag)); } else { out->print("(%s" SIZE_FORMAT "%s", alloc_type, amount_in_current_scale(amount), scale); @@ -176,31 +176,31 @@ void MemSummaryReporter::report() { out->cr(); out->cr(); - // Summary by memory type - for (int index = 0; index < mt_number_of_types; index ++) { - MEMFLAGS flag = NMTUtil::index_to_flag(index); + // Summary by memory tag + for (int index = 0; index < mt_number_of_tags; index ++) { + MemTag mem_tag = NMTUtil::index_to_tag(index); // thread stack is reported as part of thread category - if (flag == mtThreadStack) continue; - MallocMemory* malloc_memory = _malloc_snapshot->by_type(flag); - VirtualMemory* virtual_memory = _vm_snapshot->by_type(flag); + if (mem_tag == mtThreadStack) continue; + MallocMemory* malloc_memory = _malloc_snapshot->by_type(mem_tag); + VirtualMemory* virtual_memory = _vm_snapshot->by_type(mem_tag); - report_summary_of_type(flag, malloc_memory, virtual_memory); + report_summary_of_type(mem_tag, malloc_memory, virtual_memory); } } -void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, +void MemSummaryReporter::report_summary_of_type(MemTag mem_tag, MallocMemory* malloc_memory, VirtualMemory* virtual_memory) { size_t reserved_amount = reserved_total (malloc_memory, virtual_memory); size_t committed_amount = committed_total(malloc_memory, virtual_memory); // Count thread's native stack in "Thread" category - if (flag == mtThread) { + if (mem_tag == mtThread) { const VirtualMemory* thread_stack_usage = (const VirtualMemory*)_vm_snapshot->by_type(mtThreadStack); reserved_amount += thread_stack_usage->reserved(); committed_amount += thread_stack_usage->committed(); - } else if (flag == mtNMT) { + } else if (mem_tag == mtNMT) { // Count malloc headers in "NMT" category reserved_amount += _malloc_snapshot->malloc_overhead(); committed_amount += _malloc_snapshot->malloc_overhead(); @@ -219,10 +219,10 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, outputStream* out = output(); const char* scale = current_scale(); constexpr int indent = 28; - out->print("-%*s (", indent - 2, NMTUtil::flag_to_name(flag)); + out->print("-%*s (", indent - 2, NMTUtil::tag_to_name(mem_tag)); print_total(reserved_amount, committed_amount); #if INCLUDE_CDS - if (flag == mtClassShared) { + if (mem_tag == mtClassShared) { size_t read_only_bytes = FileMapInfo::readonly_total(); output()->print(", readonly=" SIZE_FORMAT "%s", amount_in_current_scale(read_only_bytes), scale); @@ -232,12 +232,12 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, streamIndentor si(out, indent); - if (flag == mtClass) { + if (mem_tag == mtClass) { // report class count out->print_cr("(classes #" SIZE_FORMAT ")", (_instance_class_count + _array_class_count)); out->print_cr("( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")", _instance_class_count, _array_class_count); - } else if (flag == mtThread) { + } else if (mem_tag == mtThread) { const VirtualMemory* thread_stack_usage = _vm_snapshot->by_type(mtThreadStack); // report thread count @@ -263,11 +263,11 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, out->cr(); } - if (flag == mtNMT && + if (mem_tag == mtNMT && amount_in_current_scale(_malloc_snapshot->malloc_overhead()) > 0) { out->print_cr("(tracking overhead=" SIZE_FORMAT "%s)", amount_in_current_scale(_malloc_snapshot->malloc_overhead()), scale); - } else if (flag == mtClass) { + } else if (mem_tag == mtClass) { // Metadata information report_metadata(Metaspace::NonClassType); if (Metaspace::using_class_space()) { @@ -338,12 +338,12 @@ int MemDetailReporter::report_malloc_sites() { } const NativeCallStack* stack = malloc_site->call_stack(); _stackprinter.print_stack(stack); - MEMFLAGS flag = malloc_site->flag(); - assert(NMTUtil::flag_is_valid(flag) && flag != mtNone, - "Must have a valid memory type"); + MemTag mem_tag = malloc_site->mem_tag(); + assert(NMTUtil::tag_is_valid(mem_tag) && mem_tag != mtNone, + "Must have a valid memory tag"); INDENT_BY(29, out->print("("); - print_malloc(malloc_site->counter(), flag); + print_malloc(malloc_site->counter(), mem_tag); out->print_cr(")"); ) out->cr(); @@ -378,9 +378,9 @@ int MemDetailReporter::report_virtual_memory_allocation_sites() { INDENT_BY(29, out->print("("); print_total(virtual_memory_site->reserved(), virtual_memory_site->committed()); - const MEMFLAGS flag = virtual_memory_site->flag(); - if (flag != mtNone) { - out->print(" Type=%s", NMTUtil::flag_to_name(flag)); + const MemTag mem_tag = virtual_memory_site->mem_tag(); + if (mem_tag != mtNone) { + out->print(" Type=%s", NMTUtil::tag_to_name(mem_tag)); } out->print_cr(")"); ) @@ -423,7 +423,7 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* const char* region_type = (all_committed ? "reserved and committed" : "reserved"); out->cr(); print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size()); - out->print(" for %s", NMTUtil::flag_to_name(reserved_rgn->flag())); + out->print(" for %s", NMTUtil::tag_to_name(reserved_rgn->mem_tag())); if (stack->is_empty()) { out->cr(); } else { @@ -519,31 +519,31 @@ void MemSummaryDiffReporter::report_diff() { out->cr(); out->cr(); - // Summary diff by memory type - for (int index = 0; index < mt_number_of_types; index ++) { - MEMFLAGS flag = NMTUtil::index_to_flag(index); + // Summary diff by memory tag + for (int index = 0; index < mt_number_of_tags; index ++) { + MemTag mem_tag = NMTUtil::index_to_tag(index); // thread stack is reported as part of thread category - if (flag == mtThreadStack) continue; - diff_summary_of_type(flag, - _early_baseline.malloc_memory(flag), - _early_baseline.virtual_memory(flag), + if (mem_tag == mtThreadStack) continue; + diff_summary_of_type(mem_tag, + _early_baseline.malloc_memory(mem_tag), + _early_baseline.virtual_memory(mem_tag), _early_baseline.metaspace_stats(), - _current_baseline.malloc_memory(flag), - _current_baseline.virtual_memory(flag), + _current_baseline.malloc_memory(mem_tag), + _current_baseline.virtual_memory(mem_tag), _current_baseline.metaspace_stats()); } } void MemSummaryDiffReporter::print_malloc_diff(size_t current_amount, size_t current_count, - size_t early_amount, size_t early_count, MEMFLAGS flags) const { + size_t early_amount, size_t early_count, MemTag mem_tag) const { const char* scale = current_scale(); outputStream* out = output(); - const char* alloc_type = (flags == mtThread) ? "" : "malloc="; + const char* alloc_tag = (mem_tag == mtThread) ? "" : "malloc="; - out->print("%s" SIZE_FORMAT "%s", alloc_type, amount_in_current_scale(current_amount), scale); + out->print("%s" SIZE_FORMAT "%s", alloc_tag, amount_in_current_scale(current_amount), scale); // Report type only if it is valid and not under "thread" category - if (flags != mtNone && flags != mtThread) { - out->print(" type=%s", NMTUtil::flag_to_name(flags)); + if (mem_tag != mtNone && mem_tag != mtThread) { + out->print(" type=%s", NMTUtil::tag_to_name(mem_tag)); } int64_t amount_diff = diff_in_current_scale(current_amount, early_amount); @@ -594,7 +594,7 @@ void MemSummaryDiffReporter::print_virtual_memory_diff(size_t current_reserved, } -void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, +void MemSummaryDiffReporter::diff_summary_of_type(MemTag mem_tag, const MallocMemory* early_malloc, const VirtualMemory* early_vm, const MetaspaceCombinedStats& early_ms, const MallocMemory* current_malloc, const VirtualMemory* current_vm, @@ -613,7 +613,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, size_t early_committed_amount = committed_total(early_malloc, early_vm); // Adjust virtual memory total - if (flag == mtThread) { + if (mem_tag == mtThread) { const VirtualMemory* early_thread_stack_usage = _early_baseline.virtual_memory(mtThreadStack); const VirtualMemory* current_thread_stack_usage = @@ -624,7 +624,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, current_reserved_amount += current_thread_stack_usage->reserved(); current_committed_amount += current_thread_stack_usage->committed(); - } else if (flag == mtNMT) { + } else if (mem_tag == mtNMT) { early_reserved_amount += _early_baseline.malloc_tracking_overhead(); early_committed_amount += _early_baseline.malloc_tracking_overhead(); @@ -636,7 +636,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, diff_in_current_scale(current_reserved_amount, early_reserved_amount) != 0) { // print summary line - out->print("-%*s (", indent - 2, NMTUtil::flag_to_name(flag)); + out->print("-%*s (", indent - 2, NMTUtil::tag_to_name(mem_tag)); print_virtual_memory_diff(current_reserved_amount, current_committed_amount, early_reserved_amount, early_committed_amount); out->print_cr(")"); @@ -644,7 +644,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, streamIndentor si(out, indent); // detail lines - if (flag == mtClass) { + if (mem_tag == mtClass) { // report class count out->print("(classes #" SIZE_FORMAT, _current_baseline.class_count()); const ssize_t class_count_diff = @@ -668,7 +668,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, } out->print_cr(")"); - } else if (flag == mtThread) { + } else if (mem_tag == mtThread) { // report thread count out->print("(threads #" SIZE_FORMAT, _current_baseline.thread_count()); const ssize_t thread_count_diff = counter_diff(_current_baseline.thread_count(), _early_baseline.thread_count()); @@ -696,7 +696,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, if (amount_in_current_scale(current_malloc_amount) > 0 || diff_in_current_scale(current_malloc_amount, early_malloc_amount) != 0) { out->print("("); - print_malloc_diff(current_malloc_amount, (flag == mtChunk) ? 0 : current_malloc->malloc_count(), + print_malloc_diff(current_malloc_amount, (mem_tag == mtChunk) ? 0 : current_malloc->malloc_count(), early_malloc_amount, early_malloc->malloc_count(), mtNone); out->print_cr(")"); } @@ -720,7 +720,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, } // Report native memory tracking overhead - if (flag == mtNMT) { + if (mem_tag == mtNMT) { size_t current_tracking_overhead = amount_in_current_scale(_current_baseline.malloc_tracking_overhead()); size_t early_tracking_overhead = amount_in_current_scale(_early_baseline.malloc_tracking_overhead()); @@ -733,7 +733,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, out->print(" " INT64_PLUS_FORMAT "%s", overhead_diff, scale); } out->print_cr(")"); - } else if (flag == mtClass) { + } else if (mem_tag == mtClass) { print_metaspace_diff(current_ms, early_ms); } out->cr(); @@ -847,9 +847,9 @@ void MemDetailDiffReporter::diff_virtual_memory_sites() const { } else if (compVal > 0) { old_virtual_memory_site(early_site); early_site = early_itr.next(); - } else if (early_site->flag() != current_site->flag()) { - // This site was originally allocated with one flag, then released, - // then re-allocated at the same site (as far as we can tell) with a different flag. + } else if (early_site->mem_tag() != current_site->mem_tag()) { + // This site was originally allocated with one memory tag, then released, + // then re-allocated at the same site (as far as we can tell) with a different memory tag. old_virtual_memory_site(early_site); early_site = early_itr.next(); new_virtual_memory_site(current_site); @@ -866,29 +866,29 @@ void MemDetailDiffReporter::diff_virtual_memory_sites() const { void MemDetailDiffReporter::new_malloc_site(const MallocSite* malloc_site) const { diff_malloc_site(malloc_site->call_stack(), malloc_site->size(), malloc_site->count(), - 0, 0, malloc_site->flag()); + 0, 0, malloc_site->mem_tag()); } void MemDetailDiffReporter::old_malloc_site(const MallocSite* malloc_site) const { diff_malloc_site(malloc_site->call_stack(), 0, 0, malloc_site->size(), - malloc_site->count(), malloc_site->flag()); + malloc_site->count(), malloc_site->mem_tag()); } void MemDetailDiffReporter::diff_malloc_site(const MallocSite* early, const MallocSite* current) const { - if (early->flag() != current->flag()) { + if (early->mem_tag() != current->mem_tag()) { // If malloc site type changed, treat it as deallocation of old type and // allocation of new type. old_malloc_site(early); new_malloc_site(current); } else { diff_malloc_site(current->call_stack(), current->size(), current->count(), - early->size(), early->count(), early->flag()); + early->size(), early->count(), early->mem_tag()); } } void MemDetailDiffReporter::diff_malloc_site(const NativeCallStack* stack, size_t current_size, - size_t current_count, size_t early_size, size_t early_count, MEMFLAGS flags) const { + size_t current_count, size_t early_size, size_t early_count, MemTag mem_tag) const { outputStream* out = output(); assert(stack != nullptr, "null stack"); @@ -900,7 +900,7 @@ void MemDetailDiffReporter::diff_malloc_site(const NativeCallStack* stack, size_ _stackprinter.print_stack(stack); INDENT_BY(28, out->print("("); - print_malloc_diff(current_size, current_count, early_size, early_count, flags); + print_malloc_diff(current_size, current_count, early_size, early_count, mem_tag); out->print_cr(")"); ) out->cr(); @@ -909,21 +909,21 @@ void MemDetailDiffReporter::diff_malloc_site(const NativeCallStack* stack, size_ void MemDetailDiffReporter::new_virtual_memory_site(const VirtualMemoryAllocationSite* site) const { - diff_virtual_memory_site(site->call_stack(), site->reserved(), site->committed(), 0, 0, site->flag()); + diff_virtual_memory_site(site->call_stack(), site->reserved(), site->committed(), 0, 0, site->mem_tag()); } void MemDetailDiffReporter::old_virtual_memory_site(const VirtualMemoryAllocationSite* site) const { - diff_virtual_memory_site(site->call_stack(), 0, 0, site->reserved(), site->committed(), site->flag()); + diff_virtual_memory_site(site->call_stack(), 0, 0, site->reserved(), site->committed(), site->mem_tag()); } void MemDetailDiffReporter::diff_virtual_memory_site(const VirtualMemoryAllocationSite* early, const VirtualMemoryAllocationSite* current) const { diff_virtual_memory_site(current->call_stack(), current->reserved(), current->committed(), - early->reserved(), early->committed(), current->flag()); + early->reserved(), early->committed(), current->mem_tag()); } void MemDetailDiffReporter::diff_virtual_memory_site(const NativeCallStack* stack, size_t current_reserved, - size_t current_committed, size_t early_reserved, size_t early_committed, MEMFLAGS flag) const { + size_t current_committed, size_t early_reserved, size_t early_committed, MemTag mem_tag) const { outputStream* out = output(); // no change @@ -936,8 +936,8 @@ void MemDetailDiffReporter::diff_virtual_memory_site(const NativeCallStack* stac INDENT_BY(28, out->print("(mmap: "); print_virtual_memory_diff(current_reserved, current_committed, early_reserved, early_committed); - if (flag != mtNone) { - out->print(" Type=%s", NMTUtil::flag_to_name(flag)); + if (mem_tag != mtNone) { + out->print(" Type=%s", NMTUtil::tag_to_name(mem_tag)); } out->print_cr(")"); ) diff --git a/src/hotspot/share/nmt/memReporter.hpp b/src/hotspot/share/nmt/memReporter.hpp index 095c05509391b..773377b5e20c7 100644 --- a/src/hotspot/share/nmt/memReporter.hpp +++ b/src/hotspot/share/nmt/memReporter.hpp @@ -108,7 +108,7 @@ class MemReporterBase : public StackObj { // Print summary total, malloc and virtual memory void print_total(size_t reserved, size_t committed, size_t peak = 0) const; - void print_malloc(const MemoryCounter* c, MEMFLAGS flag = mtNone) const; + void print_malloc(const MemoryCounter* c, MemTag mem_tag = mtNone) const; void print_virtual_memory(size_t reserved, size_t committed, size_t peak) const; void print_arena(const MemoryCounter* c) const; @@ -138,8 +138,8 @@ class MemSummaryReporter : public MemReporterBase { // Generate summary report virtual void report(); private: - // Report summary for each memory type - void report_summary_of_type(MEMFLAGS type, MallocMemory* malloc_memory, + // Report summary for each memory tag + void report_summary_of_type(MemTag mem_tag, MallocMemory* malloc_memory, VirtualMemory* virtual_memory); void report_metadata(Metaspace::MetadataType type) const; @@ -203,8 +203,8 @@ class MemSummaryDiffReporter : public MemReporterBase { virtual void report_diff(); private: - // report the comparison of each memory type - void diff_summary_of_type(MEMFLAGS type, + // report the comparison of each mem_tag + void diff_summary_of_type(MemTag mem_tag, const MallocMemory* early_malloc, const VirtualMemory* early_vm, const MetaspaceCombinedStats& early_ms, const MallocMemory* current_malloc, const VirtualMemory* current_vm, @@ -212,7 +212,7 @@ class MemSummaryDiffReporter : public MemReporterBase { protected: void print_malloc_diff(size_t current_amount, size_t current_count, - size_t early_amount, size_t early_count, MEMFLAGS flags) const; + size_t early_amount, size_t early_count, MemTag mem_tag) const; void print_virtual_memory_diff(size_t current_reserved, size_t current_committed, size_t early_reserved, size_t early_committed) const; void print_arena_diff(size_t current_amount, size_t current_count, @@ -262,9 +262,9 @@ class MemDetailDiffReporter : public MemSummaryDiffReporter { const VirtualMemoryAllocationSite* current) const; void diff_malloc_site(const NativeCallStack* stack, size_t current_size, - size_t currrent_count, size_t early_size, size_t early_count, MEMFLAGS flags) const; + size_t currrent_count, size_t early_size, size_t early_count, MemTag mem_tag) const; void diff_virtual_memory_site(const NativeCallStack* stack, size_t current_reserved, - size_t current_committed, size_t early_reserved, size_t early_committed, MEMFLAGS flag) const; + size_t current_committed, size_t early_reserved, size_t early_committed, MemTag mem_tag) const; }; #endif // SHARE_NMT_MEMREPORTER_HPP diff --git a/src/hotspot/share/nmt/memflags.hpp b/src/hotspot/share/nmt/memTag.hpp similarity index 84% rename from src/hotspot/share/nmt/memflags.hpp rename to src/hotspot/share/nmt/memTag.hpp index 530c9ae9d9528..9255645638d83 100644 --- a/src/hotspot/share/nmt/memflags.hpp +++ b/src/hotspot/share/nmt/memTag.hpp @@ -22,13 +22,13 @@ * */ -#ifndef SHARE_NMT_MEMFLAGS_HPP -#define SHARE_NMT_MEMFLAGS_HPP +#ifndef SHARE_NMT_MEM_TAG_HPP +#define SHARE_NMT_MEM_TAG_HPP #include "utilities/globalDefinitions.hpp" -#define MEMORY_TYPES_DO(f) \ - /* Memory type by sub systems. It occupies lower byte. */ \ +#define MEMORY_TAG_DO(f) \ + /* Memory tag by sub systems. It occupies lower byte. */ \ f(mtJavaHeap, "Java Heap") /* Java heap */ \ f(mtClass, "Class") /* Java classes */ \ f(mtThread, "Thread") /* thread objects */ \ @@ -61,22 +61,22 @@ f(mtNone, "Unknown") \ //end -#define MEMORY_TYPE_DECLARE_ENUM(type, human_readable) \ - type, +#define MEMORY_TAG_DECLARE_ENUM(mem_tag, human_readable) \ +mem_tag, -enum class MEMFLAGS : uint8_t { - MEMORY_TYPES_DO(MEMORY_TYPE_DECLARE_ENUM) - mt_number_of_types // number of memory types (mtDontTrack - // is not included as validate type) +enum class MemTag : uint8_t { + MEMORY_TAG_DO(MEMORY_TAG_DECLARE_ENUM) + mt_number_of_tags // number of memory tags (mtDontTrack + // is not included as validate tag) }; -#define MEMORY_TYPE_SHORTNAME(type, human_readable) \ - constexpr MEMFLAGS type = MEMFLAGS::type; +#define MEMORY_TAG_SHORTNAME(mem_tag, human_readable) \ + constexpr MemTag mem_tag = MemTag::mem_tag; -// Generate short aliases for the enum values. E.g. mtGC instead of MEMFLAGS::mtGC. -MEMORY_TYPES_DO(MEMORY_TYPE_SHORTNAME) +// Generate short aliases for the enum values. E.g. mtGC instead of MemTag::mtGC. +MEMORY_TAG_DO(MEMORY_TAG_SHORTNAME) // Make an int version of the sentinel end value. -constexpr int mt_number_of_types = static_cast(MEMFLAGS::mt_number_of_types); +constexpr int mt_number_of_tags = static_cast(MemTag::mt_number_of_tags); -#endif // SHARE_NMT_MEMFLAGS_HPP +#endif // SHARE_NMT_MEM_TAG_HPP diff --git a/src/hotspot/share/nmt/memFlagBitmap.hpp b/src/hotspot/share/nmt/memTagBitmap.hpp similarity index 75% rename from src/hotspot/share/nmt/memFlagBitmap.hpp rename to src/hotspot/share/nmt/memTagBitmap.hpp index 0464179948b54..f65dce60fa62b 100644 --- a/src/hotspot/share/nmt/memFlagBitmap.hpp +++ b/src/hotspot/share/nmt/memTagBitmap.hpp @@ -23,34 +23,34 @@ * */ -#ifndef SHARE_NMT_MEMFLAGBITMAP_HPP -#define SHARE_NMT_MEMFLAGBITMAP_HPP +#ifndef SHARE_NMT_MEMTAGBITMAP_HPP +#define SHARE_NMT_MEMTAGBITMAP_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -class MemFlagBitmap { +class MemTagBitmap { uint32_t _v; - STATIC_ASSERT(sizeof(_v) * BitsPerByte >= mt_number_of_types); + STATIC_ASSERT(sizeof(_v) * BitsPerByte >= mt_number_of_tags); public: - MemFlagBitmap(uint32_t v = 0) : _v(v) {} - MemFlagBitmap(const MemFlagBitmap& o) : _v(o._v) {} + MemTagBitmap(uint32_t v = 0) : _v(v) {} + MemTagBitmap(const MemTagBitmap& o) : _v(o._v) {} uint32_t raw_value() const { return _v; } - void set_flag(MEMFLAGS f) { - const int bitno = (int)f; + void set_tag(MemTag mem_tag) { + const int bitno = (int)mem_tag; _v |= nth_bit(bitno); } - bool has_flag(MEMFLAGS f) const { - const int bitno = (int)f; + bool has_tag(MemTag mem_tag) const { + const int bitno = (int)mem_tag; return _v & nth_bit(bitno); } bool has_any() const { return _v > 0; } }; -#endif // SHARE_NMT_NMTUSAGE_HPP +#endif // SHARE_NMT_MEMTAGBITMAP_HPP diff --git a/src/hotspot/share/nmt/memTracker.cpp b/src/hotspot/share/nmt/memTracker.cpp index f40f9428443c1..fb9c9a50db1f7 100644 --- a/src/hotspot/share/nmt/memTracker.cpp +++ b/src/hotspot/share/nmt/memTracker.cpp @@ -63,7 +63,7 @@ void MemTracker::initialize() { // Memory type is encoded into tracking header as a byte field, // make sure that we don't overflow it. - STATIC_ASSERT(mt_number_of_types <= max_jubyte); + STATIC_ASSERT(mt_number_of_tags <= max_jubyte); if (level > NMT_off) { if (!MallocTracker::initialize(level) || diff --git a/src/hotspot/share/nmt/memTracker.hpp b/src/hotspot/share/nmt/memTracker.hpp index 74aa9f803b1bf..6ba1db2e7ffe6 100644 --- a/src/hotspot/share/nmt/memTracker.hpp +++ b/src/hotspot/share/nmt/memTracker.hpp @@ -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 @@ -72,14 +72,14 @@ class MemTracker : AllStatic { // Per-malloc overhead incurred by NMT, depending on the current NMT level static size_t overhead_per_malloc() { - return enabled() ? MallocTracker::overhead_per_malloc : 0; + return enabled() ? MallocTracker::overhead_per_malloc() : 0; } - static inline void* record_malloc(void* mem_base, size_t size, MEMFLAGS flag, + static inline void* record_malloc(void* mem_base, size_t size, MemTag mem_tag, const NativeCallStack& stack) { assert(mem_base != nullptr, "caller should handle null"); if (enabled()) { - return MallocTracker::record_malloc(mem_base, size, flag, stack); + return MallocTracker::record_malloc(mem_base, size, mem_tag, stack); } return mem_base; } @@ -99,34 +99,34 @@ class MemTracker : AllStatic { } // Record creation of an arena - static inline void record_new_arena(MEMFLAGS flag) { + static inline void record_new_arena(MemTag mem_tag) { if (!enabled()) return; - MallocTracker::record_new_arena(flag); + MallocTracker::record_new_arena(mem_tag); } // Record destruction of an arena - static inline void record_arena_free(MEMFLAGS flag) { + static inline void record_arena_free(MemTag mem_tag) { if (!enabled()) return; - MallocTracker::record_arena_free(flag); + MallocTracker::record_arena_free(mem_tag); } // Record arena size change. Arena size is the size of all arena // chunks that are backing up the arena. - static inline void record_arena_size_change(ssize_t diff, MEMFLAGS flag) { + static inline void record_arena_size_change(ssize_t diff, MemTag mem_tag) { if (!enabled()) return; - MallocTracker::record_arena_size_change(diff, flag); + MallocTracker::record_arena_size_change(diff, mem_tag); } // Note: virtual memory operations should only ever be called after NMT initialization // (we do not do any reservations before that). static inline void record_virtual_memory_reserve(void* addr, size_t size, const NativeCallStack& stack, - MEMFLAGS flag = mtNone) { + MemTag mem_tag = mtNone) { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { ThreadCritical tc; - VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, flag); + VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, mem_tag); } } @@ -147,12 +147,12 @@ class MemTracker : AllStatic { } static inline void record_virtual_memory_reserve_and_commit(void* addr, size_t size, - const NativeCallStack& stack, MEMFLAGS flag = mtNone) { + const NativeCallStack& stack, MemTag mem_tag = mtNone) { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { ThreadCritical tc; - VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, flag); + VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, mem_tag); VirtualMemoryTracker::add_committed_region((address)addr, size, stack); } } @@ -183,12 +183,12 @@ class MemTracker : AllStatic { } static inline void allocate_memory_in(MemoryFileTracker::MemoryFile* file, size_t offset, size_t size, - const NativeCallStack& stack, MEMFLAGS flag) { + const NativeCallStack& stack, MemTag mem_tag) { assert_post_init(); if (!enabled()) return; assert(file != nullptr, "must be"); MemoryFileTracker::Instance::Locker lock; - MemoryFileTracker::Instance::allocate_memory(file, offset, size, stack, flag); + MemoryFileTracker::Instance::allocate_memory(file, offset, size, stack, mem_tag); } static inline void free_memory_in(MemoryFileTracker::MemoryFile* file, @@ -206,21 +206,21 @@ class MemTracker : AllStatic { // // The two new memory regions will be both registered under stack and // memory flags of the original region. - static inline void record_virtual_memory_split_reserved(void* addr, size_t size, size_t split, MEMFLAGS flag, MEMFLAGS split_flag) { + static inline void record_virtual_memory_split_reserved(void* addr, size_t size, size_t split, MemTag mem_tag, MemTag split_tag) { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { ThreadCritical tc; - VirtualMemoryTracker::split_reserved_region((address)addr, size, split, flag, split_flag); + VirtualMemoryTracker::split_reserved_region((address)addr, size, split, mem_tag, split_tag); } } - static inline void record_virtual_memory_type(void* addr, MEMFLAGS flag) { + static inline void record_virtual_memory_tag(void* addr, MemTag mem_tag) { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { ThreadCritical tc; - VirtualMemoryTracker::set_reserved_region_type((address)addr, flag); + VirtualMemoryTracker::set_reserved_region_type((address)addr, mem_tag); } } @@ -262,8 +262,8 @@ class MemTracker : AllStatic { static void tuning_statistics(outputStream* out); // MallocLimt: Given an allocation size s, check if mallocing this much - // under category f would hit either the global limit or the limit for category f. - static inline bool check_exceeds_limit(size_t s, MEMFLAGS f); + // for MemTag would hit either the global limit or the limit for MemTag. + static inline bool check_exceeds_limit(size_t s, MemTag mem_tag); // Given an unknown pointer, check if it points into a known region; print region if found // and return true; false if not found. diff --git a/src/hotspot/share/nmt/memTracker.inline.hpp b/src/hotspot/share/nmt/memTracker.inline.hpp index 500f2a75d8cf7..a850c6b07fdce 100644 --- a/src/hotspot/share/nmt/memTracker.inline.hpp +++ b/src/hotspot/share/nmt/memTracker.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,11 +30,11 @@ #include "nmt/mallocTracker.inline.hpp" -inline bool MemTracker::check_exceeds_limit(size_t s, MEMFLAGS f) { +inline bool MemTracker::check_exceeds_limit(size_t s, MemTag mem_tag) { if (!enabled()) { return false; } - return MallocTracker::check_exceeds_limit(s, f); + return MallocTracker::check_exceeds_limit(s, mem_tag); } #endif // SHARE_NMT_MEMTRACKER_INLINE_HPP diff --git a/src/hotspot/share/nmt/memoryFileTracker.cpp b/src/hotspot/share/nmt/memoryFileTracker.cpp index 25f2667e5c387..ede483ed33727 100644 --- a/src/hotspot/share/nmt/memoryFileTracker.cpp +++ b/src/hotspot/share/nmt/memoryFileTracker.cpp @@ -42,23 +42,23 @@ MemoryFileTracker::MemoryFileTracker(bool is_detailed_mode) void MemoryFileTracker::allocate_memory(MemoryFile* file, size_t offset, size_t size, const NativeCallStack& stack, - MEMFLAGS flag) { + MemTag mem_tag) { NativeCallStackStorage::StackIndex sidx = _stack_storage.push(stack); - VMATree::RegionData regiondata(sidx, flag); + VMATree::RegionData regiondata(sidx, mem_tag); VMATree::SummaryDiff diff = file->_tree.commit_mapping(offset, size, regiondata); - for (int i = 0; i < mt_number_of_types; i++) { - VirtualMemory* summary = file->_summary.by_type(NMTUtil::index_to_flag(i)); - summary->reserve_memory(diff.flag[i].commit); - summary->commit_memory(diff.flag[i].commit); + for (int i = 0; i < mt_number_of_tags; i++) { + VirtualMemory* summary = file->_summary.by_type(NMTUtil::index_to_tag(i)); + summary->reserve_memory(diff.tag[i].commit); + summary->commit_memory(diff.tag[i].commit); } } void MemoryFileTracker::free_memory(MemoryFile* file, size_t offset, size_t size) { VMATree::SummaryDiff diff = file->_tree.release_mapping(offset, size); - for (int i = 0; i < mt_number_of_types; i++) { - VirtualMemory* summary = file->_summary.by_type(NMTUtil::index_to_flag(i)); - summary->reserve_memory(diff.flag[i].commit); - summary->commit_memory(diff.flag[i].commit); + for (int i = 0; i < mt_number_of_tags; i++) { + VirtualMemory* summary = file->_summary.by_type(NMTUtil::index_to_tag(i)); + summary->reserve_memory(diff.tag[i].commit); + summary->commit_memory(diff.tag[i].commit); } } @@ -79,7 +79,7 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st return; } #ifdef ASSERT - if (broken_start != nullptr && prev->val().out.type() != current->val().in.type()) { + if (broken_start != nullptr && prev->val().out.mem_tag() != current->val().in.mem_tag()) { broken_start = prev; broken_end = current; } @@ -91,7 +91,7 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st start_addr, end_addr, NMTUtil::amount_in_scale(end_addr - start_addr, scale), NMTUtil::scale_name(scale), - NMTUtil::flag_to_name(prev->val().out.flag())); + NMTUtil::tag_to_name(prev->val().out.mem_tag())); { streamIndentor si(stream, 4); _stack_storage.get(prev->val().out.stack()).print_on(stream); @@ -138,8 +138,8 @@ bool MemoryFileTracker::Instance::initialize(NMT_TrackingLevel tracking_level) { void MemoryFileTracker::Instance::allocate_memory(MemoryFile* file, size_t offset, size_t size, const NativeCallStack& stack, - MEMFLAGS flag) { - _tracker->allocate_memory(file, offset, size, stack, flag); + MemTag mem_tag) { + _tracker->allocate_memory(file, offset, size, stack, mem_tag); } void MemoryFileTracker::Instance::free_memory(MemoryFile* file, size_t offset, size_t size) { @@ -181,9 +181,9 @@ const GrowableArrayCHeap& MemoryFileTrack void MemoryFileTracker::summary_snapshot(VirtualMemorySnapshot* snapshot) const { for (int d = 0; d < _files.length(); d++) { const MemoryFile* file = _files.at(d); - for (int i = 0; i < mt_number_of_types; i++) { - VirtualMemory* snap = snapshot->by_type(NMTUtil::index_to_flag(i)); - const VirtualMemory* current = file->_summary.by_type(NMTUtil::index_to_flag(i)); + for (int i = 0; i < mt_number_of_tags; i++) { + VirtualMemory* snap = snapshot->by_type(NMTUtil::index_to_tag(i)); + const VirtualMemory* current = file->_summary.by_type(NMTUtil::index_to_tag(i)); // Only account the committed memory. snap->commit_memory(current->committed()); } diff --git a/src/hotspot/share/nmt/memoryFileTracker.hpp b/src/hotspot/share/nmt/memoryFileTracker.hpp index 432b6f9d99e1e..42902701a16df 100644 --- a/src/hotspot/share/nmt/memoryFileTracker.hpp +++ b/src/hotspot/share/nmt/memoryFileTracker.hpp @@ -66,7 +66,7 @@ class MemoryFileTracker { MemoryFileTracker(bool is_detailed_mode); void allocate_memory(MemoryFile* file, size_t offset, size_t size, const NativeCallStack& stack, - MEMFLAGS flag); + MemTag mem_tag); void free_memory(MemoryFile* file, size_t offset, size_t size); MemoryFile* make_file(const char* descriptive_name); @@ -96,7 +96,7 @@ class MemoryFileTracker { static void free_file(MemoryFile* device); static void allocate_memory(MemoryFile* device, size_t offset, size_t size, - const NativeCallStack& stack, MEMFLAGS flag); + const NativeCallStack& stack, MemTag mem_tag); static void free_memory(MemoryFile* device, size_t offset, size_t size); static void summary_snapshot(VirtualMemorySnapshot* snapshot); diff --git a/src/hotspot/share/nmt/nativeCallStackPrinter.hpp b/src/hotspot/share/nmt/nativeCallStackPrinter.hpp index deebb338626eb..78fd541fc980f 100644 --- a/src/hotspot/share/nmt/nativeCallStackPrinter.hpp +++ b/src/hotspot/share/nmt/nativeCallStackPrinter.hpp @@ -27,7 +27,7 @@ #define SHARE_NMT_NATIVECALLSTACKPRINTER_HPP #include "memory/arena.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/resourceHash.hpp" diff --git a/src/hotspot/share/nmt/nmtCommon.cpp b/src/hotspot/share/nmt/nmtCommon.cpp index dadb830f29182..24a4cb1105adf 100644 --- a/src/hotspot/share/nmt/nmtCommon.cpp +++ b/src/hotspot/share/nmt/nmtCommon.cpp @@ -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 @@ -29,11 +29,11 @@ STATIC_ASSERT(NMT_off > NMT_unknown); STATIC_ASSERT(NMT_summary > NMT_off); STATIC_ASSERT(NMT_detail > NMT_summary); -#define MEMORY_TYPE_DECLARE_NAME(type, human_readable) \ +#define MEMORY_TAG_DECLARE_NAME(type, human_readable) \ { #type, human_readable }, NMTUtil::S NMTUtil::_strings[] = { - MEMORY_TYPES_DO(MEMORY_TYPE_DECLARE_NAME) + MEMORY_TAG_DO(MEMORY_TAG_DECLARE_NAME) }; const char* NMTUtil::scale_name(size_t scale) { @@ -87,14 +87,14 @@ NMT_TrackingLevel NMTUtil::parse_tracking_level(const char* s) { return NMT_unknown; } -MEMFLAGS NMTUtil::string_to_flag(const char* s) { - for (int i = 0; i < mt_number_of_types; i ++) { +MemTag NMTUtil::string_to_mem_tag(const char* s) { + for (int i = 0; i < mt_number_of_tags; i ++) { assert(::strlen(_strings[i].enum_s) > 2, "Sanity"); // should always start with "mt" if (::strcasecmp(_strings[i].human_readable, s) == 0 || ::strcasecmp(_strings[i].enum_s, s) == 0 || ::strcasecmp(_strings[i].enum_s + 2, s) == 0) // "mtXXX" -> match also "XXX" or "xxx" { - return (MEMFLAGS)i; + return (MemTag)i; } } return mtNone; diff --git a/src/hotspot/share/nmt/nmtCommon.hpp b/src/hotspot/share/nmt/nmtCommon.hpp index 8ca0965b3d35b..3f72960f21fde 100644 --- a/src/hotspot/share/nmt/nmtCommon.hpp +++ b/src/hotspot/share/nmt/nmtCommon.hpp @@ -28,7 +28,7 @@ #define SHARE_NMT_NMTCOMMON_HPP #include "memory/allStatic.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" @@ -75,37 +75,37 @@ const int NMT_TrackingStackDepth = 4; // A few common utilities for native memory tracking class NMTUtil : AllStatic { public: - // Check if index is a valid MEMFLAGS enum value (including mtNone) - static inline bool flag_index_is_valid(int index) { - return index >= 0 && index < mt_number_of_types; + // Check if index is a valid MemTag enum value (including mtNone) + static inline bool tag_index_is_valid(int index) { + return index >= 0 && index < mt_number_of_tags; } - // Check if flag value is a valid MEMFLAGS enum value (including mtNone) - static inline bool flag_is_valid(MEMFLAGS flag) { - const int index = static_cast(flag); - return flag_index_is_valid(index); + // Check if tag value is a valid MemTag enum value (including mtNone) + static inline bool tag_is_valid(MemTag mem_tag) { + const int index = static_cast(mem_tag); + return tag_index_is_valid(index); } - // Map memory type to index - static inline int flag_to_index(MEMFLAGS flag) { - assert(flag_is_valid(flag), "Invalid flag (%u)", (unsigned)flag); - return static_cast(flag); + // Map memory tag to index + static inline int tag_to_index(MemTag mem_tag) { + assert(tag_is_valid(mem_tag), "Invalid type (%u)", (unsigned)mem_tag); + return static_cast(mem_tag); } - // Map memory type to human readable name - static const char* flag_to_name(MEMFLAGS flag) { - return _strings[flag_to_index(flag)].human_readable; + // Map memory tag to human readable name + static const char* tag_to_name(MemTag mem_tag) { + return _strings[tag_to_index(mem_tag)].human_readable; } - // Map memory type to literalized enum name (e.g. "mtTest") - static const char* flag_to_enum_name(MEMFLAGS flag) { - return _strings[flag_to_index(flag)].enum_s; + // Map memory tag to literalized enum name (e.g. "mtTest") + static const char* tag_to_enum_name(MemTag mem_tag) { + return _strings[tag_to_index(mem_tag)].enum_s; } - // Map an index to memory type - static MEMFLAGS index_to_flag(int index) { - assert(flag_index_is_valid(index), "Invalid flag index (%d)", index); - return static_cast(index); + // Map an index to memory tag + static MemTag index_to_tag(int index) { + assert(tag_index_is_valid(index), "Invalid type index (%d)", index); + return static_cast(index); } // Memory size scale @@ -121,10 +121,10 @@ class NMTUtil : AllStatic { // string is not a valid level. static NMT_TrackingLevel parse_tracking_level(const char* s); - // Given a string, return associated flag. mtNone if name is invalid. + // Given a string, return associated mem_tag. mtNone if name is invalid. // String can be either the human readable name or the // stringified enum (with or without leading "mt". In all cases, case is ignored. - static MEMFLAGS string_to_flag(const char* name); + static MemTag string_to_mem_tag(const char* name); // Returns textual representation of a tracking level. static const char* tracking_level_to_string(NMT_TrackingLevel level); @@ -134,7 +134,7 @@ class NMTUtil : AllStatic { const char* enum_s; // e.g. "mtNMT" const char* human_readable; // e.g. "Native Memory Tracking" }; - static S _strings[mt_number_of_types]; + static S _strings[mt_number_of_tags]; }; diff --git a/src/hotspot/share/nmt/nmtPreInit.cpp b/src/hotspot/share/nmt/nmtPreInit.cpp index a8ff18f3b62b2..0aa74566f42cb 100644 --- a/src/hotspot/share/nmt/nmtPreInit.cpp +++ b/src/hotspot/share/nmt/nmtPreInit.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023 SAP SE. All rights reserved. - * 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 @@ -198,8 +198,8 @@ void NMTPreInit::create_table() { } // Allocate with os::malloc (hidden to prevent having to include os.hpp) -void* NMTPreInit::do_os_malloc(size_t size, MEMFLAGS memflags) { - return os::malloc(size, memflags); +void* NMTPreInit::do_os_malloc(size_t size, MemTag mem_tag) { + return os::malloc(size, mem_tag); } // Switches from NMT pre-init state to NMT post-init state; diff --git a/src/hotspot/share/nmt/nmtPreInit.hpp b/src/hotspot/share/nmt/nmtPreInit.hpp index 38a34616b3f38..1524c2bd7dc69 100644 --- a/src/hotspot/share/nmt/nmtPreInit.hpp +++ b/src/hotspot/share/nmt/nmtPreInit.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023 SAP SE. All rights reserved. - * 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 @@ -255,7 +255,7 @@ class NMTPreInit : public AllStatic { } // Just a wrapper for os::malloc to avoid including os.hpp here. - static void* do_os_malloc(size_t size, MEMFLAGS memflags); + static void* do_os_malloc(size_t size, MemTag mem_tag); public: @@ -283,7 +283,7 @@ class NMTPreInit : public AllStatic { // Called from os::realloc. // Returns true if reallocation was handled here; in that case, // *rc contains the return address. - static bool handle_realloc(void** rc, void* old_p, size_t new_size, MEMFLAGS memflags) { + static bool handle_realloc(void** rc, void* old_p, size_t new_size, MemTag mem_tag) { if (old_p == nullptr) { // realloc(null, n) return handle_malloc(rc, new_size); } @@ -322,7 +322,7 @@ class NMTPreInit : public AllStatic { // and confusing us. const NMTPreInitAllocation* a = find_in_map(old_p); if (a != nullptr) { // this was originally a pre-init allocation - void* p_new = do_os_malloc(new_size, memflags); + void* p_new = do_os_malloc(new_size, mem_tag); ::memcpy(p_new, a->payload, MIN2(a->size, new_size)); (*rc) = p_new; return true; diff --git a/src/hotspot/share/nmt/nmtUsage.cpp b/src/hotspot/share/nmt/nmtUsage.cpp index 62c4122544171..a854f001593c1 100644 --- a/src/hotspot/share/nmt/nmtUsage.cpp +++ b/src/hotspot/share/nmt/nmtUsage.cpp @@ -57,9 +57,9 @@ void NMTUsage::update_malloc_usage() { const MallocMemorySnapshot* ms = MallocMemorySummary::as_snapshot(); size_t total_arena_size = 0; - for (int i = 0; i < mt_number_of_types; i++) { - MEMFLAGS flag = NMTUtil::index_to_flag(i); - const MallocMemory* mm = ms->by_type(flag); + for (int i = 0; i < mt_number_of_tags; i++) { + MemTag mem_tag = NMTUtil::index_to_tag(i); + const MallocMemory* mm = ms->by_type(mem_tag); _malloc_by_type[i] = mm->malloc_size() + mm->arena_size(); total_arena_size += mm->arena_size(); } @@ -68,11 +68,11 @@ void NMTUsage::update_malloc_usage() { _malloc_total = ms->total(); // Adjustment due to mtChunk double counting. - _malloc_by_type[NMTUtil::flag_to_index(mtChunk)] -= total_arena_size; + _malloc_by_type[NMTUtil::tag_to_index(mtChunk)] -= total_arena_size; _malloc_total -= total_arena_size; // Adjust mtNMT to include malloc overhead. - _malloc_by_type[NMTUtil::flag_to_index(mtNMT)] += ms->malloc_overhead(); + _malloc_by_type[NMTUtil::tag_to_index(mtNMT)] += ms->malloc_overhead(); } void NMTUsage::update_vm_usage() { @@ -81,9 +81,9 @@ void NMTUsage::update_vm_usage() { // Reset total to allow recalculation. _vm_total.committed = 0; _vm_total.reserved = 0; - for (int i = 0; i < mt_number_of_types; i++) { - MEMFLAGS flag = NMTUtil::index_to_flag(i); - const VirtualMemory* vm = vms->by_type(flag); + for (int i = 0; i < mt_number_of_tags; i++) { + MemTag mem_tag = NMTUtil::index_to_tag(i); + const VirtualMemory* vm = vms->by_type(mem_tag); _vm_by_type[i].reserved = vm->reserved(); _vm_by_type[i].committed = vm->committed(); @@ -116,12 +116,12 @@ size_t NMTUsage::total_committed() const { return _malloc_total + _vm_total.committed; } -size_t NMTUsage::reserved(MEMFLAGS flag) const { - int index = NMTUtil::flag_to_index(flag); +size_t NMTUsage::reserved(MemTag mem_tag) const { + int index = NMTUtil::tag_to_index(mem_tag); return _malloc_by_type[index] + _vm_by_type[index].reserved; } -size_t NMTUsage::committed(MEMFLAGS flag) const { - int index = NMTUtil::flag_to_index(flag); +size_t NMTUsage::committed(MemTag mem_tag) const { + int index = NMTUtil::tag_to_index(mem_tag); return _malloc_by_type[index] + _vm_by_type[index].committed; } diff --git a/src/hotspot/share/nmt/nmtUsage.hpp b/src/hotspot/share/nmt/nmtUsage.hpp index cfff59db9af85..390d207250cb0 100644 --- a/src/hotspot/share/nmt/nmtUsage.hpp +++ b/src/hotspot/share/nmt/nmtUsage.hpp @@ -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 @@ -41,9 +41,9 @@ struct NMTUsageOptions { class NMTUsage : public CHeapObj { private: - size_t _malloc_by_type[mt_number_of_types]; + size_t _malloc_by_type[mt_number_of_tags]; size_t _malloc_total; - NMTUsagePair _vm_by_type[mt_number_of_types]; + NMTUsagePair _vm_by_type[mt_number_of_tags]; NMTUsagePair _vm_total; NMTUsageOptions _usage_options; @@ -61,8 +61,8 @@ class NMTUsage : public CHeapObj { size_t total_reserved() const; size_t total_committed() const; - size_t reserved(MEMFLAGS flag) const; - size_t committed(MEMFLAGS flag) const; + size_t reserved(MemTag mem_tag) const; + size_t committed(MemTag mem_tag) const; }; #endif // SHARE_NMT_NMTUSAGE_HPP diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index d25f3689a4268..d298381f1038f 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -142,7 +142,7 @@ bool ReservedMemoryRegion::add_committed_region(address addr, size_t size, const // At this point the previous overlapping regions have been // cleared, and the full region is guaranteed to be inserted. - VirtualMemorySummary::record_committed_memory(size, flag()); + VirtualMemorySummary::record_committed_memory(size, mem_tag()); // Try to merge with prev and possibly next. if (try_merge_with(prev, addr, size, stack)) { @@ -212,14 +212,14 @@ bool ReservedMemoryRegion::remove_uncommitted_region(address addr, size_t sz) { crgn = head->data(); if (crgn->same_region(addr, sz)) { - VirtualMemorySummary::record_uncommitted_memory(crgn->size(), flag()); + VirtualMemorySummary::record_uncommitted_memory(crgn->size(), mem_tag()); _committed_regions.remove_after(prev); return true; } // del_rgn contains crgn if (del_rgn.contain_region(crgn->base(), crgn->size())) { - VirtualMemorySummary::record_uncommitted_memory(crgn->size(), flag()); + VirtualMemorySummary::record_uncommitted_memory(crgn->size(), mem_tag()); head = head->next(); _committed_regions.remove_after(prev); continue; // don't update head or prev @@ -230,20 +230,20 @@ bool ReservedMemoryRegion::remove_uncommitted_region(address addr, size_t sz) { // (1) Found addr+size in current crgn as well. (del_rgn is contained in crgn) if (crgn->contain_address(end - 1)) { - VirtualMemorySummary::record_uncommitted_memory(sz, flag()); + VirtualMemorySummary::record_uncommitted_memory(sz, mem_tag()); return remove_uncommitted_region(head, addr, sz); // done! } else { // (2) Did not find del_rgn's end in crgn. size_t size = crgn->end() - del_rgn.base(); crgn->exclude_region(addr, size); - VirtualMemorySummary::record_uncommitted_memory(size, flag()); + VirtualMemorySummary::record_uncommitted_memory(size, mem_tag()); } } else if (crgn->contain_address(end - 1)) { // Found del_rgn's end, but not its base addr. size_t size = del_rgn.end() - crgn->base(); crgn->exclude_region(crgn->base(), size); - VirtualMemorySummary::record_uncommitted_memory(size, flag()); + VirtualMemorySummary::record_uncommitted_memory(size, mem_tag()); return true; // should be done if the list is sorted properly! } @@ -292,19 +292,19 @@ size_t ReservedMemoryRegion::committed_size() const { return committed; } -void ReservedMemoryRegion::set_flag(MEMFLAGS f) { - assert((flag() == mtNone || flag() == f), - "Overwrite memory type for region [" INTPTR_FORMAT "-" INTPTR_FORMAT "), %u->%u.", - p2i(base()), p2i(end()), (unsigned)flag(), (unsigned)f); - if (flag() != f) { - VirtualMemorySummary::move_reserved_memory(flag(), f, size()); - VirtualMemorySummary::move_committed_memory(flag(), f, committed_size()); - _flag = f; +void ReservedMemoryRegion::set_mem_tag(MemTag new_mem_tag) { + assert((mem_tag() == mtNone || mem_tag() == new_mem_tag), + "Overwrite memory tag for region [" INTPTR_FORMAT "-" INTPTR_FORMAT "), %u->%u.", + p2i(base()), p2i(end()), (unsigned)mem_tag(), (unsigned)new_mem_tag); + if (mem_tag() != new_mem_tag) { + VirtualMemorySummary::move_reserved_memory(mem_tag(), new_mem_tag, size()); + VirtualMemorySummary::move_committed_memory(mem_tag(), new_mem_tag, committed_size()); + _mem_tag = new_mem_tag; } } address ReservedMemoryRegion::thread_stack_uncommitted_bottom() const { - assert(flag() == mtThreadStack, "Only for thread stack"); + assert(mem_tag() == mtThreadStack, "Only for thread stack"); LinkedListNode* head = _committed_regions.head(); address bottom = base(); address top = base() + size(); @@ -334,26 +334,26 @@ bool VirtualMemoryTracker::initialize(NMT_TrackingLevel level) { } bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, - const NativeCallStack& stack, MEMFLAGS flag) { + const NativeCallStack& stack, MemTag mem_tag) { assert(base_addr != nullptr, "Invalid address"); assert(size > 0, "Invalid size"); assert(_reserved_regions != nullptr, "Sanity check"); - ReservedMemoryRegion rgn(base_addr, size, stack, flag); + ReservedMemoryRegion rgn(base_addr, size, stack, mem_tag); ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); log_debug(nmt)("Add reserved region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ")", - rgn.flag_name(), p2i(rgn.base()), rgn.size()); + rgn.mem_tag_name(), p2i(rgn.base()), rgn.size()); if (reserved_rgn == nullptr) { - VirtualMemorySummary::record_reserved_memory(size, flag); + VirtualMemorySummary::record_reserved_memory(size, mem_tag); return _reserved_regions->add(rgn) != nullptr; } else { // Deal with recursive reservation // os::reserve_memory() -> pd_reserve_memory() -> os::reserve_memory() // See JDK-8198226. if (reserved_rgn->same_region(base_addr, size) && - (reserved_rgn->flag() == flag || reserved_rgn->flag() == mtNone)) { + (reserved_rgn->mem_tag() == mem_tag || reserved_rgn->mem_tag() == mtNone)) { reserved_rgn->set_call_stack(stack); - reserved_rgn->set_flag(flag); + reserved_rgn->set_mem_tag(mem_tag); return true; } else { assert(reserved_rgn->overlap_region(base_addr, size), "Must be"); @@ -362,16 +362,16 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, // It can happen when the regions are thread stacks, as JNI // thread does not detach from VM before exits, and leads to // leak JavaThread object - if (reserved_rgn->flag() == mtThreadStack) { + if (reserved_rgn->mem_tag() == mtThreadStack) { guarantee(!CheckJNICalls, "Attached JNI thread exited without being detached"); // Overwrite with new region // Release old region - VirtualMemorySummary::record_uncommitted_memory(reserved_rgn->committed_size(), reserved_rgn->flag()); - VirtualMemorySummary::record_released_memory(reserved_rgn->size(), reserved_rgn->flag()); + VirtualMemorySummary::record_uncommitted_memory(reserved_rgn->committed_size(), reserved_rgn->mem_tag()); + VirtualMemorySummary::record_released_memory(reserved_rgn->size(), reserved_rgn->mem_tag()); // Add new region - VirtualMemorySummary::record_reserved_memory(rgn.size(), flag); + VirtualMemorySummary::record_reserved_memory(rgn.size(), mem_tag); *reserved_rgn = rgn; return true; @@ -380,27 +380,27 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, // CDS mapping region. // CDS reserves the whole region for mapping CDS archive, then maps each section into the region. // NMT reports CDS as a whole. - if (reserved_rgn->flag() == mtClassShared) { + if (reserved_rgn->mem_tag() == mtClassShared) { log_debug(nmt)("CDS reserved region \'%s\' as a whole (" INTPTR_FORMAT ", " SIZE_FORMAT ")", - reserved_rgn->flag_name(), p2i(reserved_rgn->base()), reserved_rgn->size()); + reserved_rgn->mem_tag_name(), p2i(reserved_rgn->base()), reserved_rgn->size()); assert(reserved_rgn->contain_region(base_addr, size), "Reserved CDS region should contain this mapping region"); return true; } // Mapped CDS string region. // The string region(s) is part of the java heap. - if (reserved_rgn->flag() == mtJavaHeap) { + if (reserved_rgn->mem_tag() == mtJavaHeap) { log_debug(nmt)("CDS reserved region \'%s\' as a whole (" INTPTR_FORMAT ", " SIZE_FORMAT ")", - reserved_rgn->flag_name(), p2i(reserved_rgn->base()), reserved_rgn->size()); + reserved_rgn->mem_tag_name(), p2i(reserved_rgn->base()), reserved_rgn->size()); assert(reserved_rgn->contain_region(base_addr, size), "Reserved heap region should contain this mapping region"); return true; } // Print some more details. Don't use UL here to avoid circularities. - tty->print_cr("Error: existing region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), flag %u.\n" - " new region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), flag %u.", - p2i(reserved_rgn->base()), p2i(reserved_rgn->end()), (unsigned)reserved_rgn->flag(), - p2i(base_addr), p2i(base_addr + size), (unsigned)flag); + tty->print_cr("Error: existing region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %u.\n" + " new region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %u.", + p2i(reserved_rgn->base()), p2i(reserved_rgn->end()), (unsigned)reserved_rgn->mem_tag(), + p2i(base_addr), p2i(base_addr + size), (unsigned)mem_tag); if (MemTracker::tracking_level() == NMT_detail) { tty->print_cr("Existing region allocated from:"); reserved_rgn->call_stack()->print_on(tty); @@ -413,7 +413,7 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, } } -void VirtualMemoryTracker::set_reserved_region_type(address addr, MEMFLAGS flag) { +void VirtualMemoryTracker::set_reserved_region_type(address addr, MemTag mem_tag) { assert(addr != nullptr, "Invalid address"); assert(_reserved_regions != nullptr, "Sanity check"); @@ -421,10 +421,10 @@ void VirtualMemoryTracker::set_reserved_region_type(address addr, MEMFLAGS flag) ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); if (reserved_rgn != nullptr) { assert(reserved_rgn->contain_address(addr), "Containment"); - if (reserved_rgn->flag() != flag) { - assert(reserved_rgn->flag() == mtNone, "Overwrite memory type (should be mtNone, is: \"%s\")", - NMTUtil::flag_to_name(reserved_rgn->flag())); - reserved_rgn->set_flag(flag); + if (reserved_rgn->mem_tag() != mem_tag) { + assert(reserved_rgn->mem_tag() == mtNone, "Overwrite memory tag (should be mtNone, is: \"%s\")", + NMTUtil::tag_to_name(reserved_rgn->mem_tag())); + reserved_rgn->set_mem_tag(mem_tag); } } } @@ -440,13 +440,13 @@ bool VirtualMemoryTracker::add_committed_region(address addr, size_t size, if (reserved_rgn == nullptr) { log_debug(nmt)("Add committed region \'%s\', No reserved region found for (" INTPTR_FORMAT ", " SIZE_FORMAT ")", - rgn.flag_name(), p2i(rgn.base()), rgn.size()); + rgn.mem_tag_name(), p2i(rgn.base()), rgn.size()); } assert(reserved_rgn != nullptr, "Add committed region, No reserved region found"); assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); bool result = reserved_rgn->add_committed_region(addr, size, stack); log_debug(nmt)("Add committed region \'%s\'(" INTPTR_FORMAT ", " SIZE_FORMAT ") %s", - reserved_rgn->flag_name(), p2i(rgn.base()), rgn.size(), (result ? "Succeeded" : "Failed")); + reserved_rgn->mem_tag_name(), p2i(rgn.base()), rgn.size(), (result ? "Succeeded" : "Failed")); return result; } @@ -459,10 +459,10 @@ bool VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); assert(reserved_rgn != nullptr, "No reserved region (" INTPTR_FORMAT ", " SIZE_FORMAT ")", p2i(addr), size); assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); - const char* flag_name = reserved_rgn->flag_name(); // after remove, info is not complete + const char* type_name = reserved_rgn->mem_tag_name(); // after remove, info is not complete bool result = reserved_rgn->remove_uncommitted_region(addr, size); log_debug(nmt)("Removed uncommitted region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") %s", - flag_name, p2i(addr), size, (result ? " Succeeded" : "Failed")); + type_name, p2i(addr), size, (result ? " Succeeded" : "Failed")); return result; } @@ -474,15 +474,15 @@ bool VirtualMemoryTracker::remove_released_region(ReservedMemoryRegion* rgn) { ReservedMemoryRegion backup(*rgn); bool result = rgn->remove_uncommitted_region(rgn->base(), rgn->size()); log_debug(nmt)("Remove uncommitted region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") %s", - backup.flag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed")); + backup.mem_tag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed")); if (!result) { return false; } - VirtualMemorySummary::record_released_memory(rgn->size(), rgn->flag()); + VirtualMemorySummary::record_released_memory(rgn->size(), rgn->mem_tag()); result = _reserved_regions->remove(*rgn); log_debug(nmt)("Removed region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") from _reserved_regions %s" , - backup.flag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed")); + backup.mem_tag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed")); return result; } @@ -508,7 +508,7 @@ bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { return false; } - if (reserved_rgn->flag() == mtClassShared) { + if (reserved_rgn->mem_tag() == mtClassShared) { if (reserved_rgn->contain_region(addr, size)) { // This is an unmapped CDS region, which is part of the reserved shared // memory region. @@ -523,14 +523,14 @@ bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { (size - reserved_rgn->size())); ReservedMemoryRegion* cls_rgn = _reserved_regions->find(class_rgn); assert(cls_rgn != nullptr, "Class space region not recorded?"); - assert(cls_rgn->flag() == mtClass, "Must be class type"); + assert(cls_rgn->mem_tag() == mtClass, "Must be class mem tag"); remove_released_region(reserved_rgn); remove_released_region(cls_rgn); return true; } } - VirtualMemorySummary::record_released_memory(size, reserved_rgn->flag()); + VirtualMemorySummary::record_released_memory(size, reserved_rgn->mem_tag()); assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); if (reserved_rgn->base() == addr || @@ -541,7 +541,7 @@ bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { address top = reserved_rgn->end(); address high_base = addr + size; ReservedMemoryRegion high_rgn(high_base, top - high_base, - *reserved_rgn->call_stack(), reserved_rgn->flag()); + *reserved_rgn->call_stack(), reserved_rgn->mem_tag()); // use original region for lower region reserved_rgn->exclude_region(addr, top - addr); @@ -557,8 +557,8 @@ bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { // Given an existing memory mapping registered with NMT, split the mapping in // two. The newly created two mappings will be registered under the call -// stack and the memory flags of the original section. -bool VirtualMemoryTracker::split_reserved_region(address addr, size_t size, size_t split, MEMFLAGS flag, MEMFLAGS split_flag) { +// stack and the memory tags of the original section. +bool VirtualMemoryTracker::split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_tag) { ReservedMemoryRegion rgn(addr, size); ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); @@ -567,15 +567,15 @@ bool VirtualMemoryTracker::split_reserved_region(address addr, size_t size, size assert(reserved_rgn->committed_size() == 0, "Splitting committed region?"); NativeCallStack original_stack = *reserved_rgn->call_stack(); - MEMFLAGS original_flags = reserved_rgn->flag(); + MemTag original_tag = reserved_rgn->mem_tag(); - const char* name = reserved_rgn->flag_name(); + const char* name = reserved_rgn->mem_tag_name(); remove_released_region(reserved_rgn); log_debug(nmt)("Split region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") with size " SIZE_FORMAT, name, p2i(rgn.base()), rgn.size(), split); // Now, create two new regions. - add_reserved_region(addr, split, original_stack, flag); - add_reserved_region(addr + split, size - split, original_stack, split_flag); + add_reserved_region(addr, split, original_stack, mem_tag); + add_reserved_region(addr + split, size - split, original_stack, split_tag); return true; } @@ -621,7 +621,7 @@ class SnapshotThreadStackWalker : public VirtualMemoryWalker { SnapshotThreadStackWalker() {} bool do_allocation_site(const ReservedMemoryRegion* rgn) { - if (rgn->flag() == mtThreadStack) { + if (rgn->mem_tag() == mtThreadStack) { address stack_bottom = rgn->thread_stack_uncommitted_bottom(); address committed_start; size_t committed_size; @@ -688,7 +688,7 @@ class PrintRegionWalker : public VirtualMemoryWalker { bool do_allocation_site(const ReservedMemoryRegion* rgn) { if (rgn->contain_address(_p)) { _st->print_cr(PTR_FORMAT " in mmap'd memory region [" PTR_FORMAT " - " PTR_FORMAT "], tag %s", - p2i(_p), p2i(rgn->base()), p2i(rgn->base() + rgn->size()), NMTUtil::flag_to_enum_name(rgn->flag())); + p2i(_p), p2i(rgn->base()), p2i(rgn->base() + rgn->size()), NMTUtil::tag_to_enum_name(rgn->mem_tag())); if (MemTracker::tracking_level() == NMT_detail) { _stackprinter.print_stack(rgn->call_stack()); _st->cr(); diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index e84245ce1f8db..6e36d3a858ad5 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp @@ -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 @@ -77,8 +77,8 @@ class VirtualMemory { class VirtualMemoryAllocationSite : public AllocationSite { VirtualMemory _c; public: - VirtualMemoryAllocationSite(const NativeCallStack& stack, MEMFLAGS flag) : - AllocationSite(stack, flag) { } + VirtualMemoryAllocationSite(const NativeCallStack& stack, MemTag mem_tag) : + AllocationSite(stack, mem_tag) { } inline void reserve_memory(size_t sz) { _c.reserve_memory(sz); } inline void commit_memory (size_t sz) { _c.commit_memory(sz); } @@ -95,22 +95,22 @@ class VirtualMemorySnapshot : public ResourceObj { friend class VirtualMemorySummary; private: - VirtualMemory _virtual_memory[mt_number_of_types]; + VirtualMemory _virtual_memory[mt_number_of_tags]; public: - inline VirtualMemory* by_type(MEMFLAGS flag) { - int index = NMTUtil::flag_to_index(flag); + inline VirtualMemory* by_type(MemTag mem_tag) { + int index = NMTUtil::tag_to_index(mem_tag); return &_virtual_memory[index]; } - inline const VirtualMemory* by_type(MEMFLAGS flag) const { - int index = NMTUtil::flag_to_index(flag); + inline const VirtualMemory* by_type(MemTag mem_tag) const { + int index = NMTUtil::tag_to_index(mem_tag); return &_virtual_memory[index]; } inline size_t total_reserved() const { size_t amount = 0; - for (int index = 0; index < mt_number_of_types; index ++) { + for (int index = 0; index < mt_number_of_tags; index ++) { amount += _virtual_memory[index].reserved(); } return amount; @@ -118,14 +118,14 @@ class VirtualMemorySnapshot : public ResourceObj { inline size_t total_committed() const { size_t amount = 0; - for (int index = 0; index < mt_number_of_types; index ++) { + for (int index = 0; index < mt_number_of_tags; index ++) { amount += _virtual_memory[index].committed(); } return amount; } void copy_to(VirtualMemorySnapshot* s) { - for (int index = 0; index < mt_number_of_types; index ++) { + for (int index = 0; index < mt_number_of_tags; index ++) { s->_virtual_memory[index] = _virtual_memory[index]; } } @@ -134,32 +134,32 @@ class VirtualMemorySnapshot : public ResourceObj { class VirtualMemorySummary : AllStatic { public: - static inline void record_reserved_memory(size_t size, MEMFLAGS flag) { - as_snapshot()->by_type(flag)->reserve_memory(size); + static inline void record_reserved_memory(size_t size, MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->reserve_memory(size); } - static inline void record_committed_memory(size_t size, MEMFLAGS flag) { - as_snapshot()->by_type(flag)->commit_memory(size); + static inline void record_committed_memory(size_t size, MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->commit_memory(size); } - static inline void record_uncommitted_memory(size_t size, MEMFLAGS flag) { - as_snapshot()->by_type(flag)->uncommit_memory(size); + static inline void record_uncommitted_memory(size_t size, MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->uncommit_memory(size); } - static inline void record_released_memory(size_t size, MEMFLAGS flag) { - as_snapshot()->by_type(flag)->release_memory(size); + static inline void record_released_memory(size_t size, MemTag mem_tag) { + as_snapshot()->by_type(mem_tag)->release_memory(size); } - // Move virtual memory from one memory type to another. - // Virtual memory can be reserved before it is associated with a memory type, and tagged + // Move virtual memory from one memory tag to another. + // Virtual memory can be reserved before it is associated with a memory tag, and tagged // as 'unknown'. Once the memory is tagged, the virtual memory will be moved from 'unknown' - // type to specified memory type. - static inline void move_reserved_memory(MEMFLAGS from, MEMFLAGS to, size_t size) { + // type to specified memory tag. + static inline void move_reserved_memory(MemTag from, MemTag to, size_t size) { as_snapshot()->by_type(from)->release_memory(size); as_snapshot()->by_type(to)->reserve_memory(size); } - static inline void move_committed_memory(MEMFLAGS from, MEMFLAGS to, size_t size) { + static inline void move_committed_memory(MemTag from, MemTag to, size_t size) { as_snapshot()->by_type(from)->uncommit_memory(size); as_snapshot()->by_type(to)->commit_memory(size); } @@ -293,16 +293,16 @@ class ReservedMemoryRegion : public VirtualMemoryRegion { _committed_regions; NativeCallStack _stack; - MEMFLAGS _flag; + MemTag _mem_tag; public: ReservedMemoryRegion(address base, size_t size, const NativeCallStack& stack, - MEMFLAGS flag = mtNone) : - VirtualMemoryRegion(base, size), _stack(stack), _flag(flag) { } + MemTag mem_tag = mtNone) : + VirtualMemoryRegion(base, size), _stack(stack), _mem_tag(mem_tag) { } ReservedMemoryRegion(address base, size_t size) : - VirtualMemoryRegion(base, size), _stack(NativeCallStack::empty_stack()), _flag(mtNone) { } + VirtualMemoryRegion(base, size), _stack(NativeCallStack::empty_stack()), _mem_tag(mtNone) { } // Copy constructor ReservedMemoryRegion(const ReservedMemoryRegion& rr) : @@ -313,8 +313,8 @@ class ReservedMemoryRegion : public VirtualMemoryRegion { inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; } inline const NativeCallStack* call_stack() const { return &_stack; } - void set_flag(MEMFLAGS flag); - inline MEMFLAGS flag() const { return _flag; } + void set_mem_tag(MemTag mem_tag); + inline MemTag mem_tag() const { return _mem_tag; } // uncommitted thread stack bottom, above guard pages if there is any. address thread_stack_uncommitted_bottom() const; @@ -336,8 +336,8 @@ class ReservedMemoryRegion : public VirtualMemoryRegion { set_base(other.base()); set_size(other.size()); - _stack = *other.call_stack(); - _flag = other.flag(); + _stack = *other.call_stack(); + _mem_tag = other.mem_tag(); _committed_regions.clear(); CommittedRegionIterator itr = other.iterate_committed_regions(); @@ -350,7 +350,7 @@ class ReservedMemoryRegion : public VirtualMemoryRegion { return *this; } - const char* flag_name() const { return NMTUtil::flag_to_name(_flag); } + const char* mem_tag_name() const { return NMTUtil::tag_to_name(_mem_tag); } private: // The committed region contains the uncommitted region, subtract the uncommitted @@ -380,18 +380,18 @@ class VirtualMemoryTracker : AllStatic { public: static bool initialize(NMT_TrackingLevel level); - static bool add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, MEMFLAGS flag = mtNone); + static bool add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, MemTag mem_tag = mtNone); static bool add_committed_region (address base_addr, size_t size, const NativeCallStack& stack); static bool remove_uncommitted_region (address base_addr, size_t size); static bool remove_released_region (address base_addr, size_t size); static bool remove_released_region (ReservedMemoryRegion* rgn); - static void set_reserved_region_type (address addr, MEMFLAGS flag); + static void set_reserved_region_type (address addr, MemTag mem_tag); // Given an existing memory mapping registered with NMT, split the mapping in // two. The newly created two mappings will be registered under the call - // stack and the memory flags of the original section. - static bool split_reserved_region(address addr, size_t size, size_t split, MEMFLAGS flag, MEMFLAGS split_flag); + // stack and the memory tag of the original section. + static bool split_reserved_region(address addr, size_t size, size_t split, MemTag mem_tag, MemTag split_type); // Walk virtual memory data structure for creating baseline, etc. static bool walk_virtual_memory(VirtualMemoryWalker* walker); diff --git a/src/hotspot/share/nmt/vmatree.cpp b/src/hotspot/share/nmt/vmatree.cpp index cef2d48e8165b..65a5bdb94aef0 100644 --- a/src/hotspot/share/nmt/vmatree.cpp +++ b/src/hotspot/share/nmt/vmatree.cpp @@ -24,6 +24,7 @@ */ #include "precompiled.hpp" +#include "logging/log.hpp" #include "nmt/vmatree.hpp" #include "utilities/growableArray.hpp" @@ -34,7 +35,9 @@ const char* VMATree::statetype_strings[3] = { }; VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType state, - const RegionData& metadata) { + const RegionData& metadata, bool use_tag_inplace) { + assert(!use_tag_inplace || metadata.mem_tag == mtNone, + "If using use_tag_inplace, then the supplied tag should be mtNone, was instead: %s", NMTUtil::tag_to_name(metadata.mem_tag)); if (A == B) { // A 0-sized mapping isn't worth recording. return SummaryDiff(); @@ -55,6 +58,10 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType AddressState LEQ_A; TreapNode* leqA_n = _tree.closest_leq(A); if (leqA_n == nullptr) { + assert(!use_tag_inplace, "Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B); + if (use_tag_inplace) { + log_debug(nmt)("Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B); + } // No match. We add the A node directly, unless it would have no effect. if (!stA.is_noop()) { _tree.upsert(A, stA); @@ -62,6 +69,17 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType } else { LEQ_A_found = true; LEQ_A = AddressState{leqA_n->key(), leqA_n->val()}; + StateType leqA_state = leqA_n->val().out.type(); + StateType new_state = stA.out.type(); + // If we specify use_tag_inplace then the new region takes over the current tag instead of the tag in metadata. + // This is important because the VirtualMemoryTracker API doesn't require supplying the tag for some operations. + if (use_tag_inplace) { + assert(leqA_n->val().out.type() != StateType::Released, "Should not use inplace the tag of a released region"); + MemTag tag = leqA_n->val().out.mem_tag(); + stA.out.set_tag(tag); + stB.in.set_tag(tag); + } + // Unless we know better, let B's outgoing state be the outgoing state of the node at or preceding A. // Consider the case where the found node is the start of a region enclosing [A,B) stB.out = leqA_n->val().out; @@ -83,8 +101,8 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType } else { // If the state is not matching then we have different operations, such as: // reserve [x1, A); ... commit [A, x2); or - // reserve [x1, A), flag1; ... reserve [A, x2), flag2; or - // reserve [A, x1), flag1; ... reserve [A, x2), flag2; + // reserve [x1, A), mem_tag1; ... reserve [A, x2), mem_tag2; or + // reserve [A, x1), mem_tag1; ... reserve [A, x2), mem_tag2; // then we re-use the existing out node, overwriting its old metadata. leqA_n->val() = stA; } @@ -147,7 +165,7 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType if (to_be_deleted_inbetween_a_b.length() == 0 && LEQ_A_found) { // We must have smashed a hole in an existing region (or replaced it entirely). // LEQ_A < A < B <= C - SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(LEQ_A.out().flag())]; + SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(LEQ_A.out().mem_tag())]; if (LEQ_A.out().type() == StateType::Reserved) { rescom.reserve -= B - A; } else if (LEQ_A.out().type() == StateType::Committed) { @@ -163,7 +181,7 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType _tree.remove(delete_me.address); // Perform summary accounting - SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(delete_me.in().flag())]; + SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(delete_me.in().mem_tag())]; if (delete_me.in().type() == StateType::Reserved) { rescom.reserve -= delete_me.address - prev.address; } else if (delete_me.in().type() == StateType::Committed) { @@ -178,17 +196,17 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType // A - prev - B - (some node >= B) // It might be that prev.address == B == (some node >= B), this is fine. if (prev.out().type() == StateType::Reserved) { - SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(prev.out().flag())]; + SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())]; rescom.reserve -= B - prev.address; } else if (prev.out().type() == StateType::Committed) { - SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(prev.out().flag())]; + SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())]; rescom.commit -= B - prev.address; rescom.reserve -= B - prev.address; } } // Finally, we can register the new region [A, B)'s summary data. - SingleDiff& rescom = diff.flag[NMTUtil::flag_to_index(metadata.flag)]; + SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(metadata.mem_tag)]; if (state == StateType::Reserved) { rescom.reserve += B - A; } else if (state == StateType::Committed) { diff --git a/src/hotspot/share/nmt/vmatree.hpp b/src/hotspot/share/nmt/vmatree.hpp index 3316219a1d340..cfb3c8ab5246d 100644 --- a/src/hotspot/share/nmt/vmatree.hpp +++ b/src/hotspot/share/nmt/vmatree.hpp @@ -35,7 +35,7 @@ // A VMATree stores a sequence of points on the natural number line. // Each of these points stores information about a state change. // For example, the state may go from released memory to committed memory, -// or from committed memory of a certain MEMFLAGS to committed memory of a different MEMFLAGS. +// or from committed memory of a certain MemTag to committed memory of a different MemTag. // The set of points is stored in a balanced binary tree for efficient querying and updating. class VMATree { friend class NMTVMATreeTest; @@ -66,18 +66,18 @@ class VMATree { return statetype_strings[static_cast(type)]; } - // Each point has some stack and a flag associated with it. + // Each point has some stack and a tag associated with it. struct RegionData { const NativeCallStackStorage::StackIndex stack_idx; - const MEMFLAGS flag; + const MemTag mem_tag; - RegionData() : stack_idx(), flag(mtNone) {} + RegionData() : stack_idx(), mem_tag(mtNone) {} - RegionData(NativeCallStackStorage::StackIndex stack_idx, MEMFLAGS flag) - : stack_idx(stack_idx), flag(flag) {} + RegionData(NativeCallStackStorage::StackIndex stack_idx, MemTag mem_tag) + : stack_idx(stack_idx), mem_tag(mem_tag) {} static bool equals(const RegionData& a, const RegionData& b) { - return a.flag == b.flag && + return a.mem_tag == b.mem_tag && NativeCallStackStorage::equals(a.stack_idx, b.stack_idx); } }; @@ -87,29 +87,33 @@ class VMATree { private: struct IntervalState { private: - // Store the type and flag as two bytes - uint8_t type_flag[2]; + // Store the type and mem_tag as two bytes + uint8_t type_tag[2]; NativeCallStackStorage::StackIndex sidx; public: - IntervalState() : type_flag{0,0}, sidx() {} + IntervalState() : type_tag{0,0}, sidx() {} IntervalState(const StateType type, const RegionData data) { - assert(!(type == StateType::Released) || data.flag == mtNone, "Released type must have flag mtNone"); - type_flag[0] = static_cast(type); - type_flag[1] = static_cast(data.flag); + assert(!(type == StateType::Released) || data.mem_tag == mtNone, "Released type must have memory tag mtNone"); + type_tag[0] = static_cast(type); + type_tag[1] = static_cast(data.mem_tag); sidx = data.stack_idx; } StateType type() const { - return static_cast(type_flag[0]); + return static_cast(type_tag[0]); } - MEMFLAGS flag() const { - return static_cast(type_flag[1]); + MemTag mem_tag() const { + return static_cast(type_tag[1]); } RegionData regiondata() const { - return RegionData{sidx, flag()}; + return RegionData{sidx, mem_tag()}; + } + + void set_tag(MemTag tag) { + type_tag[1] = static_cast(tag); } NativeCallStackStorage::StackIndex stack() const { @@ -159,22 +163,28 @@ class VMATree { delta commit; }; struct SummaryDiff { - SingleDiff flag[mt_number_of_types]; + SingleDiff tag[mt_number_of_tags]; SummaryDiff() { - for (int i = 0; i < mt_number_of_types; i++) { - flag[i] = SingleDiff{0, 0}; + for (int i = 0; i < mt_number_of_tags; i++) { + tag[i] = SingleDiff{0, 0}; } } }; - SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata); + private: + SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata, bool use_tag_inplace = false); + public: SummaryDiff reserve_mapping(position from, position sz, const RegionData& metadata) { - return register_mapping(from, from + sz, StateType::Reserved, metadata); + return register_mapping(from, from + sz, StateType::Reserved, metadata, false); + } + + SummaryDiff commit_mapping(position from, position sz, const RegionData& metadata, bool use_tag_inplace = false) { + return register_mapping(from, from + sz, StateType::Committed, metadata, use_tag_inplace); } - SummaryDiff commit_mapping(position from, position sz, const RegionData& metadata) { - return register_mapping(from, from + sz, StateType::Committed, metadata); + SummaryDiff uncommit_mapping(position from, position sz, const RegionData& metadata) { + return register_mapping(from, from + sz, StateType::Reserved, metadata, true); } SummaryDiff release_mapping(position from, position sz) { diff --git a/src/hotspot/share/oops/compressedOops.cpp b/src/hotspot/share/oops/compressedOops.cpp index 08f78b1d7734c..ec41dd8521918 100644 --- a/src/hotspot/share/oops/compressedOops.cpp +++ b/src/hotspot/share/oops/compressedOops.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -35,8 +35,10 @@ #include "runtime/globals.hpp" // For UseCompressedOops. -NarrowPtrStruct CompressedOops::_narrow_oop = { nullptr, 0, true }; -MemRegion CompressedOops::_heap_address_range; +address CompressedOops::_base = nullptr; +int CompressedOops::_shift = 0; +bool CompressedOops::_use_implicit_null_checks = true; +MemRegion CompressedOops::_heap_address_range; // Choose the heap base address and oop encoding mode // when compressed oops are used: @@ -59,7 +61,7 @@ void CompressedOops::initialize(const ReservedHeapSpace& heap_space) { } if ((uint64_t)heap_space.end() <= OopEncodingHeapMax) { // Did reserve heap below 32Gb. Can use base == 0; - set_base(0); + set_base(nullptr); } else { set_base((address)heap_space.compressed_oop_base()); } @@ -88,16 +90,16 @@ void CompressedOops::initialize(const ReservedHeapSpace& heap_space) { void CompressedOops::set_base(address base) { assert(UseCompressedOops, "no compressed oops?"); - _narrow_oop._base = base; + _base = base; } void CompressedOops::set_shift(int shift) { - _narrow_oop._shift = shift; + _shift = shift; } void CompressedOops::set_use_implicit_null_checks(bool use) { assert(UseCompressedOops, "no compressed ptrs?"); - _narrow_oop._use_implicit_null_checks = use; + _use_implicit_null_checks = use; } bool CompressedOops::is_in(void* addr) { @@ -113,7 +115,7 @@ CompressedOops::Mode CompressedOops::mode() { return DisjointBaseNarrowOop; } - if (base() != 0) { + if (base() != nullptr) { return HeapBasedNarrowOop; } @@ -148,14 +150,14 @@ bool CompressedOops::is_disjoint_heap_base_address(address addr) { // Check for disjoint base compressed oops. bool CompressedOops::base_disjoint() { - return _narrow_oop._base != nullptr && is_disjoint_heap_base_address(_narrow_oop._base); + return _base != nullptr && is_disjoint_heap_base_address(_base); } // Check for real heapbased compressed oops. // We must subtract the base as the bits overlap. // If we negate above function, we also get unscaled and zerobased. bool CompressedOops::base_overlaps() { - return _narrow_oop._base != nullptr && !is_disjoint_heap_base_address(_narrow_oop._base); + return _base != nullptr && !is_disjoint_heap_base_address(_base); } void CompressedOops::print_mode(outputStream* st) { @@ -164,7 +166,7 @@ void CompressedOops::print_mode(outputStream* st) { st->print(", Compressed Oops mode: %s", mode_to_string(mode())); - if (base() != 0) { + if (base() != nullptr) { st->print(": " PTR_FORMAT, p2i(base())); } diff --git a/src/hotspot/share/oops/compressedOops.hpp b/src/hotspot/share/oops/compressedOops.hpp index cd3f00393ca44..33af420305cba 100644 --- a/src/hotspot/share/oops/compressedOops.hpp +++ b/src/hotspot/share/oops/compressedOops.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -34,23 +34,18 @@ class outputStream; class ReservedHeapSpace; -struct NarrowPtrStruct { +class CompressedOops : public AllStatic { + friend class VMStructs; + // Base address for oop-within-java-object materialization. // null if using wide oops or zero based narrow oops. - address _base; + static address _base; // Number of shift bits for encoding/decoding narrow ptrs. - // 0 if using wide ptrs or zero based unscaled narrow ptrs, + // 0 if using wide oops or zero based unscaled narrow oops, // LogMinObjAlignmentInBytes otherwise. - int _shift; - // Generate code with implicit null checks for narrow ptrs. - bool _use_implicit_null_checks; -}; - -class CompressedOops : public AllStatic { - friend class VMStructs; - - // For UseCompressedOops. - static NarrowPtrStruct _narrow_oop; + static int _shift; + // Generate code with implicit null checks for narrow oops. + static bool _use_implicit_null_checks; // The address range of the heap static MemRegion _heap_address_range; @@ -73,8 +68,7 @@ class CompressedOops : public AllStatic { UnscaledNarrowOop = 0, ZeroBasedNarrowOop = 1, DisjointBaseNarrowOop = 2, - HeapBasedNarrowOop = 3, - AnyNarrowOopMode = 4 + HeapBasedNarrowOop = 3 }; // The representation type for narrowOop is assumed to be uint32_t. @@ -87,15 +81,13 @@ class CompressedOops : public AllStatic { static void set_shift(int shift); static void set_use_implicit_null_checks(bool use); - static address base() { return _narrow_oop._base; } + static address base() { return _base; } + static address base_addr() { return (address)&_base; } static address begin() { return (address)_heap_address_range.start(); } static address end() { return (address)_heap_address_range.end(); } static bool is_base(void* addr) { return (base() == (address)addr); } - static int shift() { return _narrow_oop._shift; } - static bool use_implicit_null_checks() { return _narrow_oop._use_implicit_null_checks; } - - static address ptrs_base_addr() { return (address)&_narrow_oop._base; } - static address ptrs_base() { return _narrow_oop._base; } + static int shift() { return _shift; } + static bool use_implicit_null_checks() { return _use_implicit_null_checks; } static bool is_in(void* addr); static bool is_in(MemRegion mr); diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 7a17c62ddaf99..bcc9a08dd6ca0 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -37,6 +37,7 @@ #include "utilities/align.hpp" #include "utilities/bytes.hpp" #include "utilities/constantTag.hpp" +#include "utilities/macros.hpp" #include "utilities/resourceHash.hpp" // A ConstantPool is an array containing class constants as described in the @@ -781,7 +782,7 @@ class ConstantPool : public Metadata { int pre_resolve_shared_klasses(TRAPS); // Debugging - const char* printable_name_at(int cp_index) PRODUCT_RETURN0; + const char* printable_name_at(int cp_index) PRODUCT_RETURN_NULL; private: diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 817a35959f348..321d8add75594 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -604,6 +604,7 @@ void ConstantPoolCache::adjust_method_entries(bool * trace_name_printed) { if (old_method == nullptr || !old_method->is_old()) { continue; } + assert(!old_method->is_deleted(), "cannot delete these methods"); Method* new_method = old_method->get_new_method(); resolved_indy_entry_at(j)->adjust_method_entry(new_method); log_adjust("indy", old_method, new_method, trace_name_printed); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index fd198f54fc957..6b6d35ee026de 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2721,6 +2721,13 @@ static void clear_all_breakpoints(Method* m) { #endif void InstanceKlass::unload_class(InstanceKlass* ik) { + + if (ik->is_scratch_class()) { + assert(ik->dependencies().is_empty(), "dependencies should be empty for scratch classes"); + return; + } + assert(ik->is_loaded(), "class should be loaded " PTR_FORMAT, p2i(ik)); + // Release dependencies. ik->dependencies().remove_all_dependents(); @@ -4096,7 +4103,7 @@ void InstanceKlass::set_init_state(ClassState state) { assert(good_state || state == allocated, "illegal state transition"); #endif assert(_init_thread == nullptr, "should be cleared before state change"); - _init_state = state; + Atomic::release_store(&_init_state, state); } #if INCLUDE_JVMTI diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index eaffa0250d133..45d65f273c866 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -507,14 +507,14 @@ class InstanceKlass: public Klass { public: // initialization state - bool is_loaded() const { return _init_state >= loaded; } - bool is_linked() const { return _init_state >= linked; } - bool is_initialized() const { return _init_state == fully_initialized; } - bool is_not_initialized() const { return _init_state < being_initialized; } - bool is_being_initialized() const { return _init_state == being_initialized; } - bool is_in_error_state() const { return _init_state == initialization_error; } + bool is_loaded() const { return init_state() >= loaded; } + bool is_linked() const { return init_state() >= linked; } + bool is_initialized() const { return init_state() == fully_initialized; } + bool is_not_initialized() const { return init_state() < being_initialized; } + bool is_being_initialized() const { return init_state() == being_initialized; } + bool is_in_error_state() const { return init_state() == initialization_error; } bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; } - ClassState init_state() const { return _init_state; } + ClassState init_state() const { return Atomic::load_acquire(&_init_state); } const char* init_state_name() const; bool is_rewritten() const { return _misc_flags.rewritten(); } diff --git a/src/hotspot/share/oops/klassVtable.cpp b/src/hotspot/share/oops/klassVtable.cpp index cbc379709f1f4..e1bffc90d5129 100644 --- a/src/hotspot/share/oops/klassVtable.cpp +++ b/src/hotspot/share/oops/klassVtable.cpp @@ -1230,9 +1230,10 @@ void klassItable::initialize_itable_and_check_constraints(TRAPS) { } inline bool interface_method_needs_itable_index(Method* m) { - if (m->is_static()) return false; // e.g., Stream.empty - if (m->is_initializer()) return false; // or - if (m->is_private()) return false; // uses direct call + if (m->is_static()) return false; // e.g., Stream.empty + if (m->is_object_initializer()) return false; // + if (m->is_static_initializer()) return false; // + if (m->is_private()) return false; // uses direct call // If an interface redeclares a method from java.lang.Object, // it should already have a vtable index, don't touch it. // e.g., CharSequence.toString (from initialize_vtable) diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index f4dcd4f149302..a1b380d364655 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -846,10 +846,6 @@ bool Method::is_constant_getter() const { Bytecodes::is_return(java_code_at(last_index))); } -bool Method::is_initializer() const { - return is_object_initializer() || is_static_initializer(); -} - bool Method::has_valid_initializer_flags() const { return (is_static() || method_holder()->major_version() < 51); diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index 905c53a4ea38b..6ffaebcdfdab2 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -576,9 +576,6 @@ class Method : public Metadata { // returns true if the method does nothing but return a constant of primitive type bool is_constant_getter() const; - // returns true if the method is an initializer ( or ). - bool is_initializer() const; - // returns true if the method is static OR if the classfile version < 51 bool has_valid_initializer_flags() const; diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 9a7d93dc469ba..802af20adae12 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -395,9 +395,159 @@ Node* AddNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { } } + // Convert a + a + ... + a into a*n + Node* serial_additions = convert_serial_additions(phase, bt); + if (serial_additions != nullptr) { + return serial_additions; + } + return AddNode::Ideal(phase, can_reshape); } +// Try to convert a serial of additions into a single multiplication. Also convert `(a * CON) + a` to `(CON + 1) * a` as +// a side effect. On success, a new MulNode is returned. +Node* AddNode::convert_serial_additions(PhaseGVN* phase, BasicType bt) { + // We need to make sure that the current AddNode is not part of a MulNode that has already been optimized to a + // power-of-2 addition (e.g., 3 * a => (a << 2) + a). Without this check, GVN would keep trying to optimize the same + // node and can't progress. For example, 3 * a => (a << 2) + a => 3 * a => (a << 2) + a => ... + if (find_power_of_two_addition_pattern(this, bt, nullptr) != nullptr) { + return nullptr; + } + + Node* in1 = in(1); + Node* in2 = in(2); + jlong multiplier; + + // While multiplications can be potentially optimized to power-of-2 subtractions (e.g., a * 7 => (a << 3) - a), + // (x - y) + y => x is already handled by the Identity() methods. So, we don't need to check for that pattern here. + if (find_simple_addition_pattern(in1, bt, &multiplier) == in2 + || find_simple_lshift_pattern(in1, bt, &multiplier) == in2 + || find_simple_multiplication_pattern(in1, bt, &multiplier) == in2 + || find_power_of_two_addition_pattern(in1, bt, &multiplier) == in2) { + multiplier++; // +1 for the in2 term + + Node* con = (bt == T_INT) + ? (Node*) phase->intcon((jint) multiplier) // intentional type narrowing to allow overflow at max_jint + : (Node*) phase->longcon(multiplier); + return MulNode::make(con, in2, bt); + } + + return nullptr; +} + +// Try to match `a + a`. On success, return `a` and set `2` as `multiplier`. +// The method matches `n` for pattern: AddNode(a, a). +Node* AddNode::find_simple_addition_pattern(Node* n, BasicType bt, jlong* multiplier) { + if (n->Opcode() == Op_Add(bt) && n->in(1) == n->in(2)) { + *multiplier = 2; + return n->in(1); + } + + return nullptr; +} + +// Try to match `a << CON`. On success, return `a` and set `1 << CON` as `multiplier`. +// Match `n` for pattern: LShiftNode(a, CON). +// Note that the power-of-2 multiplication optimization could potentially convert a MulNode to this pattern. +Node* AddNode::find_simple_lshift_pattern(Node* n, BasicType bt, jlong* multiplier) { + // Note that power-of-2 multiplication optimization could potentially convert a MulNode to this pattern + if (n->Opcode() == Op_LShift(bt) && n->in(2)->is_Con()) { + Node* con = n->in(2); + if (con->is_top()) { + return nullptr; + } + + *multiplier = ((jlong) 1 << con->get_int()); + return n->in(1); + } + + return nullptr; +} + +// Try to match `CON * a`. On success, return `a` and set `CON` as `multiplier`. +// Match `n` for patterns: +// - MulNode(CON, a) +// - MulNode(a, CON) +Node* AddNode::find_simple_multiplication_pattern(Node* n, BasicType bt, jlong* multiplier) { + // This optimization technically only produces MulNode(CON, a), but we might as match MulNode(a, CON), too. + if (n->Opcode() == Op_Mul(bt) && (n->in(1)->is_Con() || n->in(2)->is_Con())) { + Node* con = n->in(1); + Node* base = n->in(2); + + // swap ConNode to lhs for easier matching + if (!con->is_Con()) { + swap(con, base); + } + + if (con->is_top()) { + return nullptr; + } + + *multiplier = con->get_integer_as_long(bt); + return base; + } + + return nullptr; +} + +// Try to match `(a << CON1) + (a << CON2)`. On success, return `a` and set `(1 << CON1) + (1 << CON2)` as `multiplier`. +// Match `n` for patterns: +// - AddNode(LShiftNode(a, CON), LShiftNode(a, CON)/a) +// - AddNode(LShiftNode(a, CON)/a, LShiftNode(a, CON)) +// given that lhs is different from rhs. +// Note that one of the term of the addition could simply be `a` (i.e., a << 0). Calling this function with `multiplier` +// being null is safe. +Node* AddNode::find_power_of_two_addition_pattern(Node* n, BasicType bt, jlong* multiplier) { + if (n->Opcode() == Op_Add(bt) && n->in(1) != n->in(2)) { + Node* lhs = n->in(1); + Node* rhs = n->in(2); + + // swap LShiftNode to lhs for easier matching + if (lhs->Opcode() != Op_LShift(bt)) { + swap(lhs, rhs); + } + + // AddNode(LShiftNode(a, CON), *)? + if (lhs->Opcode() != Op_LShift(bt) || !lhs->in(2)->is_Con()) { + return nullptr; + } + + jlong lhs_multiplier = 0; + if (multiplier != nullptr) { + Node* con = lhs->in(2); + if (con->is_top()) { + return nullptr; + } + + lhs_multiplier = (jlong) 1 << con->get_int(); + } + + // AddNode(LShiftNode(a, CON), a)? + if (lhs->in(1) == rhs) { + if (multiplier != nullptr) { + *multiplier = lhs_multiplier + 1; + } + + return rhs; + } + + // AddNode(LShiftNode(a, CON), LShiftNode(a, CON2))? + if (rhs->Opcode() == Op_LShift(bt) && lhs->in(1) == rhs->in(1) && rhs->in(2)->is_Con()) { + if (multiplier != nullptr) { + Node* con = rhs->in(2); + if (con->is_top()) { + return nullptr; + } + + *multiplier = lhs_multiplier + ((jlong) 1 << con->get_int()); + } + + return lhs->in(1); + } + return nullptr; + } + return nullptr; +} Node* AddINode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* in1 = in(1); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 8879606954a52..8afbb440572bf 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -42,6 +42,13 @@ typedef const Pair ConstAddOperands; // by virtual functions. class AddNode : public Node { virtual uint hash() const; + + Node* convert_serial_additions(PhaseGVN* phase, BasicType bt); + static Node* find_simple_addition_pattern(Node* n, BasicType bt, jlong* multiplier); + static Node* find_simple_lshift_pattern(Node* n, BasicType bt, jlong* multiplier); + static Node* find_simple_multiplication_pattern(Node* n, BasicType bt, jlong* multiplier); + static Node* find_power_of_two_addition_pattern(Node* n, BasicType bt, jlong* multiplier); + public: AddNode( Node *in1, Node *in2 ) : Node(nullptr,in1,in2) { init_class_id(Class_Add); diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 1af085cd1282d..b39db528691de 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -398,7 +398,10 @@ PhaseCFG::PhaseCFG(Arena* arena, RootNode* root, Matcher& matcher) Node *x = new GotoNode(nullptr); x->init_req(0, x); _goto = matcher.match_tree(x); - assert(_goto != nullptr, ""); + assert(_goto != nullptr || C->failure_is_artificial(), ""); + if (C->failing()) { + return; + } _goto->set_req(0,_goto); // Build the CFG in Reverse Post Order diff --git a/src/hotspot/share/opto/buildOopMap.cpp b/src/hotspot/share/opto/buildOopMap.cpp index 4591e87da2d14..b553cc6ea6949 100644 --- a/src/hotspot/share/opto/buildOopMap.cpp +++ b/src/hotspot/share/opto/buildOopMap.cpp @@ -235,6 +235,13 @@ OopMap *OopFlow::build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, i Node *def = _defs[reg]; // Get reaching def assert( def, "since live better have reaching def" ); + if (def->is_MachTemp()) { + assert(!def->bottom_type()->isa_oop_ptr(), + "ADLC only assigns OOP types to MachTemp defs corresponding to xRegN operands"); + // Exclude MachTemp definitions even if they are typed as oops. + continue; + } + // Classify the reaching def as oop, derived, callee-save, dead, or other const Type *t = def->bottom_type(); if( t->isa_oop_ptr() ) { // Oop or derived? diff --git a/src/hotspot/share/opto/c2_CodeStubs.hpp b/src/hotspot/share/opto/c2_CodeStubs.hpp index 5db7596e072dc..e778cfcde47e6 100644 --- a/src/hotspot/share/opto/c2_CodeStubs.hpp +++ b/src/hotspot/share/opto/c2_CodeStubs.hpp @@ -105,7 +105,6 @@ class C2FastUnlockLightweightStub : public C2CodeStub { Register _thread; Label _slow_path; Label _push_and_slow_path; - Label _check_successor; Label _unlocked_continuation; public: C2FastUnlockLightweightStub(Register obj, Register mark, Register t, Register thread) : C2CodeStub(), @@ -114,26 +113,10 @@ class C2FastUnlockLightweightStub : public C2CodeStub { void emit(C2_MacroAssembler& masm); Label& slow_path() { return _slow_path; } Label& push_and_slow_path() { return _push_and_slow_path; } - Label& check_successor() { return _check_successor; } Label& unlocked_continuation() { return _unlocked_continuation; } Label& slow_path_continuation() { return continuation(); } }; -#ifdef _LP64 -class C2HandleAnonOMOwnerStub : public C2CodeStub { -private: - Register _monitor; - Register _tmp; -public: - C2HandleAnonOMOwnerStub(Register monitor, Register tmp = noreg) : C2CodeStub(), - _monitor(monitor), _tmp(tmp) {} - Register monitor() { return _monitor; } - Register tmp() { return _tmp; } - int max_size() const; - void emit(C2_MacroAssembler& masm); -}; -#endif - //-----------------------------C2GeneralStub----------------------------------- // A generalized stub that can be used to implement an arbitrary stub in a // type-safe manner. An example: diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 7288533cd33e7..c14162ddf6eed 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -58,6 +58,9 @@ product(bool, StressMacroExpansion, false, DIAGNOSTIC, \ "Randomize macro node expansion order") \ \ + product(bool, StressUnstableIfTraps, false, DIAGNOSTIC, \ + "Randomly take unstable if traps") \ + \ product(uint, StressSeed, 0, DIAGNOSTIC, \ "Seed for randomized stress testing (if unset, a random one is " \ "generated). The seed is recorded in the compilation log, if " \ @@ -67,6 +70,14 @@ develop(bool, StressMethodHandleLinkerInlining, false, \ "Stress inlining through method handle linkers") \ \ + develop(bool, StressBailout, false, \ + "Perform bailouts randomly at C2 failing() checks") \ + \ + develop(uint, StressBailoutMean, 100000, \ + "The expected number of failing() checks made until " \ + "a random bailout.") \ + range(1, max_juint) \ + \ develop(intx, OptoPrologueNops, 0, \ "Insert this many extra nop instructions " \ "in the prologue of every nmethod") \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 2f087858efd48..1a00f17f50530 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -610,6 +610,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dtanh: case vmIntrinsics::_dabs: case vmIntrinsics::_fabs: case vmIntrinsics::_iabs: @@ -765,6 +766,8 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_Reference_get: case vmIntrinsics::_Reference_refersTo0: case vmIntrinsics::_PhantomReference_refersTo0: + case vmIntrinsics::_Reference_clear0: + case vmIntrinsics::_PhantomReference_clear0: case vmIntrinsics::_Class_cast: case vmIntrinsics::_aescrypt_encryptBlock: case vmIntrinsics::_aescrypt_decryptBlock: @@ -810,6 +813,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_VectorFromBitsCoerced: case vmIntrinsics::_VectorShuffleIota: case vmIntrinsics::_VectorShuffleToVector: + case vmIntrinsics::_VectorWrapShuffleIndexes: case vmIntrinsics::_VectorLoadOp: case vmIntrinsics::_VectorLoadMaskedOp: case vmIntrinsics::_VectorStoreOp: @@ -820,6 +824,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_VectorTest: case vmIntrinsics::_VectorBlend: case vmIntrinsics::_VectorRearrange: + case vmIntrinsics::_VectorSelectFrom: case vmIntrinsics::_VectorCompare: case vmIntrinsics::_VectorBroadcastInt: case vmIntrinsics::_VectorConvert: diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index afd9ca25a56fe..36fca9f61b691 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -623,7 +623,10 @@ void CallGenerator::do_late_inline_helper() { // check for unreachable loop CallProjections callprojs; - call->extract_projections(&callprojs, true); + // Similar to incremental inlining, don't assert that all call + // projections are still there for post-parse call devirtualization. + bool do_asserts = !is_mh_late_inline() && !is_virtual_late_inline(); + call->extract_projections(&callprojs, true, do_asserts); if ((callprojs.fallthrough_catchproj == call->in(0)) || (callprojs.catchall_catchproj == call->in(0)) || (callprojs.fallthrough_memproj == call->in(TypeFunc::Memory)) || @@ -647,7 +650,7 @@ void CallGenerator::do_late_inline_helper() { if (is_pure_call() && result_not_used) { GraphKit kit(call->jvms()); - kit.replace_call(call, C->top(), true); + kit.replace_call(call, C->top(), true, do_asserts); } else { // Make a clone of the JVMState that appropriate to use for driving a parse JVMState* old_jvms = call->jvms(); @@ -729,7 +732,7 @@ void CallGenerator::do_late_inline_helper() { } C->set_inlining_progress(true); C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup - kit.replace_call(call, result, true); + kit.replace_call(call, result, true, do_asserts); } } diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index d715e6533432e..e800b3c736bf2 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -755,7 +755,7 @@ Node *CallNode::match( const ProjNode *proj, const Matcher *match ) { if (Opcode() == Op_CallLeafVector) { // If the return is in vector, compute appropriate regmask taking into account the whole range - if(ideal_reg >= Op_VecS && ideal_reg <= Op_VecZ) { + if(ideal_reg >= Op_VecA && ideal_reg <= Op_VecZ) { if(OptoReg::is_valid(regs.second())) { for (OptoReg::Name r = regs.first(); r <= regs.second(); r = OptoReg::add(r, 1)) { rm.Insert(r); diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 5edd31fa71695..ac8896705dea0 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -347,7 +347,6 @@ class IfNode : public MultiBranchNode { bool is_null_check(ProjNode* proj, PhaseIterGVN* igvn); bool is_side_effect_free_test(ProjNode* proj, PhaseIterGVN* igvn); void reroute_side_effect_free_unc(ProjNode* proj, ProjNode* dom_proj, PhaseIterGVN* igvn); - ProjNode* uncommon_trap_proj(CallStaticJavaNode*& call) const; bool fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn); static bool is_dominator_unc(CallStaticJavaNode* dom_unc, CallStaticJavaNode* unc); @@ -442,6 +441,7 @@ class IfNode : public MultiBranchNode { static Node* up_one_dom(Node* curr, bool linear_only = false); bool is_zero_trip_guard() const; Node* dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool pin_array_access_nodes); + ProjNode* uncommon_trap_proj(CallStaticJavaNode*& call, Deoptimization::DeoptReason reason = Deoptimization::Reason_none) const; // Takes the type of val and filters it through the test represented // by if_proj and returns a more refined type if one is produced. diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 220b916436ea8..be0aadacbc2b9 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -479,6 +479,9 @@ void PhaseChaitin::Register_Allocate() { } uint new_max_lrg_id = Split(_lrg_map.max_lrg_id(), &split_arena); // Split spilling LRG everywhere + if (C->failing()) { + return; + } _lrg_map.set_max_lrg_id(new_max_lrg_id); // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) // or we failed to split @@ -551,6 +554,9 @@ void PhaseChaitin::Register_Allocate() { return; } uint new_max_lrg_id = Split(_lrg_map.max_lrg_id(), &split_arena); // Split spilling LRG everywhere + if (C->failing()) { + return; + } _lrg_map.set_max_lrg_id(new_max_lrg_id); // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) C->check_node_count(2 * NodeLimitFudgeFactor, "out of nodes after split"); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 8bee6279446ef..215e48ef9da9c 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -341,7 +341,6 @@ macro(Start) macro(StartOSR) macro(StoreB) macro(StoreC) -macro(StoreCM) macro(StoreD) macro(StoreF) macro(StoreI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index f4cf9e7e4029b..c6b316e527760 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -719,6 +719,11 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, method()->ensure_method_data(); } + if (StressLCM || StressGCM || StressIGVN || StressCCP || + StressIncrementalInlining || StressMacroExpansion || StressUnstableIfTraps || StressBailout) { + initialize_stress_seed(directive); + } + Init(/*do_aliasing=*/ true); print_compile_messages(); @@ -793,7 +798,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, assert(failure_reason() != nullptr, "expect reason for parse failure"); stringStream ss; ss.print("method parse failed: %s", failure_reason()); - record_method_not_compilable(ss.as_string()); + record_method_not_compilable(ss.as_string() DEBUG_ONLY(COMMA true)); return; } GraphKit kit(jvms); @@ -843,11 +848,6 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, if (failing()) return; NOT_PRODUCT( verify_graph_edges(); ) - if (StressLCM || StressGCM || StressIGVN || StressCCP || - StressIncrementalInlining || StressMacroExpansion) { - initialize_stress_seed(directive); - } - // Now optimize Optimize(); if (failing()) return; @@ -973,7 +973,7 @@ Compile::Compile( ciEnv* ci_env, _types = new (comp_arena()) Type_Array(comp_arena()); _node_hash = new (comp_arena()) NodeHash(comp_arena(), 255); - if (StressLCM || StressGCM) { + if (StressLCM || StressGCM || StressBailout) { initialize_stress_seed(directive); } @@ -1018,6 +1018,7 @@ void Compile::Init(bool aliasing) { #ifdef ASSERT _phase_optimize_finished = false; + _phase_verify_ideal_loop = false; _exception_backedge = false; _type_verify = nullptr; #endif @@ -1108,7 +1109,7 @@ void Compile::Init(bool aliasing) { #ifdef ASSERT // Verify that the current StartNode is valid. void Compile::verify_start(StartNode* s) const { - assert(failing() || s == start(), "should be StartNode"); + assert(failing_internal() || s == start(), "should be StartNode"); } #endif @@ -1118,7 +1119,7 @@ void Compile::verify_start(StartNode* s) const { * the ideal graph. */ StartNode* Compile::start() const { - assert (!failing(), "Must not have pending failure. Reason is: %s", failure_reason()); + assert (!failing_internal() || C->failure_is_artificial(), "Must not have pending failure. Reason is: %s", failure_reason()); for (DUIterator_Fast imax, i = root()->fast_outs(imax); i < imax; i++) { Node* start = root()->fast_out(i); if (start->is_Start()) { @@ -1465,12 +1466,18 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const { } else { ciInstanceKlass *canonical_holder = ik->get_canonical_holder(offset); assert(offset < canonical_holder->layout_helper_size_in_bytes(), ""); - if (!ik->equals(canonical_holder) || tj->offset() != offset) { - if( is_known_inst ) { - tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, true, nullptr, offset, to->instance_id()); - } else { - tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, false, nullptr, offset); - } + assert(tj->offset() == offset, "no change to offset expected"); + bool xk = to->klass_is_exact(); + int instance_id = to->instance_id(); + + // If the input type's class is the holder: if exact, the type only includes interfaces implemented by the holder + // but if not exact, it may include extra interfaces: build new type from the holder class to make sure only + // its interfaces are included. + if (xk && ik->equals(canonical_holder)) { + assert(tj == TypeInstPtr::make(to->ptr(), canonical_holder, is_known_inst, nullptr, offset, instance_id), "exact type should be canonical type"); + } else { + assert(xk || !is_known_inst, "Known instance should be exact type"); + tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, is_known_inst, nullptr, offset, instance_id); } } } @@ -2114,7 +2121,7 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { igvn_worklist()->ensure_empty(); // should be done with igvn while (inline_incrementally_one()) { - assert(!failing(), "inconsistent"); + assert(!failing_internal() || failure_is_artificial(), "inconsistent"); } if (failing()) return; @@ -2157,7 +2164,7 @@ void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) { igvn_worklist()->ensure_empty(); // should be done with igvn while (inline_incrementally_one()) { - assert(!failing(), "inconsistent"); + assert(!failing_internal() || failure_is_artificial(), "inconsistent"); } if (failing()) return; @@ -2944,6 +2951,9 @@ void Compile::Code_Gen() { // Build a proper-looking CFG PhaseCFG cfg(node_arena(), root(), matcher); + if (failing()) { + return; + } _cfg = &cfg; { TracePhase tp("scheduler", &timers[_t_scheduler]); @@ -3051,52 +3061,6 @@ struct Final_Reshape_Counts : public StackObj { int get_inner_loop_count() const { return _inner_loop_count; } }; -// Eliminate trivially redundant StoreCMs and accumulate their -// precedence edges. -void Compile::eliminate_redundant_card_marks(Node* n) { - assert(n->Opcode() == Op_StoreCM, "expected StoreCM"); - if (n->in(MemNode::Address)->outcnt() > 1) { - // There are multiple users of the same address so it might be - // possible to eliminate some of the StoreCMs - Node* mem = n->in(MemNode::Memory); - Node* adr = n->in(MemNode::Address); - Node* val = n->in(MemNode::ValueIn); - Node* prev = n; - bool done = false; - // Walk the chain of StoreCMs eliminating ones that match. As - // long as it's a chain of single users then the optimization is - // safe. Eliminating partially redundant StoreCMs would require - // cloning copies down the other paths. - while (mem->Opcode() == Op_StoreCM && mem->outcnt() == 1 && !done) { - if (adr == mem->in(MemNode::Address) && - val == mem->in(MemNode::ValueIn)) { - // redundant StoreCM - if (mem->req() > MemNode::OopStore) { - // Hasn't been processed by this code yet. - n->add_prec(mem->in(MemNode::OopStore)); - } else { - // Already converted to precedence edge - for (uint i = mem->req(); i < mem->len(); i++) { - // Accumulate any precedence edges - if (mem->in(i) != nullptr) { - n->add_prec(mem->in(i)); - } - } - // Everything above this point has been processed. - done = true; - } - // Eliminate the previous StoreCM - prev->set_req(MemNode::Memory, mem->in(MemNode::Memory)); - assert(mem->outcnt() == 0, "should be dead"); - mem->disconnect_inputs(this); - } else { - prev = mem; - } - mem = prev->in(MemNode::Memory); - } - } -} - //------------------------------final_graph_reshaping_impl---------------------- // Implement items 1-5 from final_graph_reshaping below. void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { @@ -3266,18 +3230,6 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } break; } - - case Op_StoreCM: - { - // Convert OopStore dependence into precedence edge - Node* prec = n->in(MemNode::OopStore); - n->del_req(MemNode::OopStore); - n->add_prec(prec); - eliminate_redundant_card_marks(n); - } - - // fall through - case Op_StoreB: case Op_StoreC: case Op_StoreI: @@ -3347,8 +3299,9 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f bool is_oop = t->isa_oopptr() != nullptr; bool is_klass = t->isa_klassptr() != nullptr; - if ((is_oop && Matcher::const_oop_prefer_decode() ) || - (is_klass && Matcher::const_klass_prefer_decode() && t->isa_klassptr()->exact_klass()->is_in_encoding_range())) { + if ((is_oop && UseCompressedOops && Matcher::const_oop_prefer_decode() ) || + (is_klass && UseCompressedClassPointers && Matcher::const_klass_prefer_decode() && + t->isa_klassptr()->exact_klass()->is_in_encoding_range())) { Node* nn = nullptr; int op = is_oop ? Op_ConN : Op_ConNKlass; @@ -4328,7 +4281,7 @@ void Compile::verify_graph_edges(bool no_dead_code) { // to backtrack and retry without subsuming loads. Other than this backtracking // behavior, the Compile's failure reason is quietly copied up to the ciEnv // by the logic in C2Compiler. -void Compile::record_failure(const char* reason) { +void Compile::record_failure(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures)) { if (log() != nullptr) { log()->elem("failure reason='%s' phase='compile'", reason); } @@ -4338,6 +4291,8 @@ void Compile::record_failure(const char* reason) { if (CaptureBailoutInformation) { _first_failure_details = new CompilationFailureInfo(reason); } + } else { + assert(!StressBailout || allow_multiple_failures, "should have handled previous failure."); } if (!C->failure_reason_is(C2Compiler::retry_no_subsuming_loads())) { @@ -4365,7 +4320,9 @@ Compile::TracePhase::TracePhase(const char* name, elapsedTimer* accumulator) } Compile::TracePhase::~TracePhase() { - if (_compile->failing()) return; + if (_compile->failing_internal()) { + return; // timing code, not stressing bailouts. + } #ifdef ASSERT if (PrintIdealNodeCount) { tty->print_cr("phase name='%s' nodes='%d' live='%d' live_graph_walk='%d'", @@ -5056,6 +5013,22 @@ bool Compile::randomized_select(int count) { return (random() & RANDOMIZED_DOMAIN_MASK) < (RANDOMIZED_DOMAIN / count); } +#ifdef ASSERT +// Failures are geometrically distributed with probability 1/StressBailoutMean. +bool Compile::fail_randomly() { + if ((random() % StressBailoutMean) != 0) { + return false; + } + record_failure("StressBailout"); + return true; +} + +bool Compile::failure_is_artificial() { + assert(failing_internal(), "should be failing"); + return C->failure_reason_is("StressBailout"); +} +#endif + CloneMap& Compile::clone_map() { return _clone_map; } void Compile::set_clone_map(Dict* d) { _clone_map._dict = d; } @@ -5143,7 +5116,7 @@ void Compile::sort_macro_nodes() { } void Compile::print_method(CompilerPhaseType cpt, int level, Node* n) { - if (failing()) { return; } + if (failing_internal()) { return; } // failing_internal to not stress bailouts from printing code. EventCompilerPhase event(UNTIMED); if (event.should_commit()) { CompilerEvent::PhaseEvent::post(event, C->_latest_stage_start_counter, cpt, C->_compile_id, level); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 13ad980434b79..6568828125fe2 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -391,6 +391,8 @@ class Compile : public Phase { DEBUG_ONLY(Unique_Node_List* _modified_nodes;) // List of nodes which inputs were modified DEBUG_ONLY(bool _phase_optimize_finished;) // Used for live node verification while creating new nodes + DEBUG_ONLY(bool _phase_verify_ideal_loop;) // Are we in PhaseIdealLoop verification? + // Arenas for new-space and old-space nodes. // Swapped between using _node_arena. // The lifetime of the old-space nodes is during xform. @@ -786,6 +788,12 @@ class Compile : public Phase { void set_post_loop_opts_phase() { _post_loop_opts_phase = true; } void reset_post_loop_opts_phase() { _post_loop_opts_phase = false; } +#ifdef ASSERT + bool phase_verify_ideal_loop() const { return _phase_verify_ideal_loop; } + void set_phase_verify_ideal_loop() { _phase_verify_ideal_loop = true; } + void reset_phase_verify_ideal_loop() { _phase_verify_ideal_loop = false; } +#endif + bool allow_macro_nodes() { return _allow_macro_nodes; } void reset_allow_macro_nodes() { _allow_macro_nodes = false; } @@ -815,7 +823,7 @@ class Compile : public Phase { ciEnv* env() const { return _env; } CompileLog* log() const { return _log; } - bool failing() const { + bool failing_internal() const { return _env->failing() || _failure_reason.get() != nullptr; } @@ -827,6 +835,27 @@ class Compile : public Phase { const CompilationFailureInfo* first_failure_details() const { return _first_failure_details; } + bool failing() { + if (failing_internal()) { + return true; + } +#ifdef ASSERT + // Disable stress code for PhaseIdealLoop verification (would have cascading effects). + if (phase_verify_ideal_loop()) { + return false; + } + if (StressBailout) { + return fail_randomly(); + } +#endif + return false; + } + +#ifdef ASSERT + bool fail_randomly(); + bool failure_is_artificial(); +#endif + bool failure_reason_is(const char* r) const { return (r == _failure_reason.get()) || (r != nullptr && @@ -834,11 +863,11 @@ class Compile : public Phase { strcmp(r, _failure_reason.get()) == 0); } - void record_failure(const char* reason); - void record_method_not_compilable(const char* reason) { + void record_failure(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures = false)); + void record_method_not_compilable(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures = false)) { env()->record_method_not_compilable(reason); // Record failure reason. - record_failure(reason); + record_failure(reason DEBUG_ONLY(COMMA allow_multiple_failures)); } bool check_node_count(uint margin, const char* reason) { if (oom()) { @@ -1215,7 +1244,6 @@ class Compile : public Phase { void final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); - void eliminate_redundant_card_marks(Node* n); void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned); // Logic cone optimization. diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 6ab28eaa6eeba..b26480ba9b39a 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -4009,10 +4009,6 @@ void ConnectionGraph::move_inst_mem(Node* n, GrowableArray &orig_phi --i; #ifdef ASSERT } else if (use->is_Mem()) { - if (use->Opcode() == Op_StoreCM && use->in(MemNode::OopStore) == n) { - // Don't move related cardmark. - continue; - } // Memory nodes should have new memory input. tp = igvn->type(use->in(MemNode::Address))->isa_ptr(); assert(tp != nullptr, "ptr type"); @@ -4564,7 +4560,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // They overwrite memory edge corresponding to destination array, memnode_worklist.append_if_missing(use); } else if (!(op == Op_CmpP || op == Op_Conv2B || - op == Op_CastP2X || op == Op_StoreCM || + op == Op_CastP2X || op == Op_FastLock || op == Op_AryEq || op == Op_StrComp || op == Op_CountPositives || op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || @@ -4703,9 +4699,6 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, if (use->is_Phi() || use->is_ClearArray()) { memnode_worklist.append_if_missing(use); } else if (use->is_Mem() && use->in(MemNode::Memory) == n) { - if (use->Opcode() == Op_StoreCM) { // Ignore cardmark stores - continue; - } memnode_worklist.append_if_missing(use); } else if (use->is_MemBar() || use->is_CallLeaf()) { if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 3880bea07d711..c46d69058e99d 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -216,19 +216,13 @@ void PhaseCFG::schedule_pinned_nodes(VectorSet &visited) { for (uint i = node->len()-1; i >= node->req(); i--) { Node* m = node->in(i); if (m == nullptr) continue; - - // Only process precedence edges that are CFG nodes. Safepoints and control projections can be in the middle of a block - if (is_CFG(m)) { - node->rm_prec(i); - if (n == nullptr) { - n = m; - } else { - assert(is_dominator(n, m) || is_dominator(m, n), "one must dominate the other"); - n = is_dominator(n, m) ? m : n; - } + assert(is_CFG(m), "must be a CFG node"); + node->rm_prec(i); + if (n == nullptr) { + n = m; } else { - assert(node->is_Mach(), "sanity"); - assert(node->as_Mach()->ideal_Opcode() == Op_StoreCM, "must be StoreCM node"); + assert(is_dominator(n, m) || is_dominator(m, n), "one must dominate the other"); + n = is_dominator(n, m) ? m : n; } } if (n != nullptr) { @@ -562,6 +556,103 @@ bool PhaseCFG::unrelated_load_in_store_null_block(Node* store, Node* load) { return false; } +class DefUseMemStatesQueue : public StackObj { +private: + class DefUsePair : public StackObj { + private: + Node* _def; // memory state + Node* _use; // use of the memory state that also modifies the memory state + + public: + DefUsePair(Node* def, Node* use) : + _def(def), _use(use) { + } + + DefUsePair() : + _def(nullptr), _use(nullptr) { + } + + Node* def() const { + return _def; + } + + Node* use() const { + return _use; + } + }; + + GrowableArray _queue; + GrowableArray _worklist_visited; // visited mergemem nodes + + bool already_enqueued(Node* def_mem, PhiNode* use_phi) const { + // def_mem is one of the inputs of use_phi and at least one input of use_phi is + // not def_mem. It's however possible that use_phi has def_mem as input multiple + // times. If that happens, use_phi is recorded as a use of def_mem multiple + // times as well. When PhaseCFG::insert_anti_dependences() goes over + // uses of def_mem and enqueues them for processing, use_phi would then be + // enqueued for processing multiple times when it only needs to be + // processed once. The code below checks if use_phi as a use of def_mem was + // already enqueued to avoid redundant processing of use_phi. + int j = _queue.length()-1; + // If there are any use of def_mem already enqueued, they were enqueued + // last (all use of def_mem are processed in one go). + for (; j >= 0; j--) { + const DefUsePair& def_use_pair = _queue.at(j); + if (def_use_pair.def() != def_mem) { + // We're done with the uses of def_mem + break; + } + if (def_use_pair.use() == use_phi) { + return true; + } + } +#ifdef ASSERT + for (; j >= 0; j--) { + const DefUsePair& def_use_pair = _queue.at(j); + assert(def_use_pair.def() != def_mem, "Should be done with the uses of def_mem"); + } +#endif + return false; + } + +public: + DefUseMemStatesQueue(ResourceArea* area) { + } + + void push(Node* def_mem_state, Node* use_mem_state) { + if (use_mem_state->is_MergeMem()) { + // Be sure we don't get into combinatorial problems. + if (!_worklist_visited.append_if_missing(use_mem_state->as_MergeMem())) { + return; // already on work list; do not repeat + } + } else if (use_mem_state->is_Phi()) { + // A Phi could have the same mem as input multiple times. If that's the case, we don't need to enqueue it + // more than once. We otherwise allow phis to be repeated; they can merge two relevant states. + if (already_enqueued(def_mem_state, use_mem_state->as_Phi())) { + return; + } + } + + _queue.push(DefUsePair(def_mem_state, use_mem_state)); + } + + bool is_nonempty() const { + return _queue.is_nonempty(); + } + + Node* top_def() const { + return _queue.top().def(); + } + + Node* top_use() const { + return _queue.top().use(); + } + + void pop() { + _queue.pop(); + } +}; + //--------------------------insert_anti_dependences--------------------------- // A load may need to witness memory that nearby stores can overwrite. // For each nearby store, either insert an "anti-dependence" edge @@ -579,6 +670,7 @@ bool PhaseCFG::unrelated_load_in_store_null_block(Node* store, Node* load) { // preserve anti-dependences. The caller may also hoist the load // above the LCA, if it is not the early block. Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { + ResourceMark rm; assert(load->needs_anti_dependence_check(), "must be a load of some sort"); assert(LCA != nullptr, ""); DEBUG_ONLY(Block* LCA_orig = LCA); @@ -625,10 +717,8 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { early = memory_early_block(load, early, this); } - ResourceArea *area = Thread::current()->resource_area(); - Node_List worklist_mem(area); // prior memory state to store - Node_List worklist_store(area); // possible-def to explore - Node_List worklist_visited(area); // visited mergemem nodes + ResourceArea* area = Thread::current()->resource_area(); + DefUseMemStatesQueue worklist_def_use_mem_states(area); // prior memory state to store and possible-def to explore Node_List non_early_stores(area); // all relevant stores outside of early bool must_raise_LCA = false; @@ -644,60 +734,81 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { // The relevant stores "nearby" the load consist of a tree rooted // at initial_mem, with internal nodes of type MergeMem. // Therefore, the branches visited by the worklist are of this form: - // initial_mem -> (MergeMem ->)* store + // initial_mem -> (MergeMem ->)* Memory state modifying node + // Memory state modifying nodes include Store and Phi nodes and any node for which needs_anti_dependence_check() + // returns false. // The anti-dependence constraints apply only to the fringe of this tree. Node* initial_mem = load->in(MemNode::Memory); - worklist_store.push(initial_mem); - worklist_visited.push(initial_mem); - worklist_mem.push(nullptr); - while (worklist_store.size() > 0) { + + // We don't optimize the memory graph for pinned loads, so we may need to raise the + // root of our search tree through the corresponding slices of MergeMem nodes to + // get to the node that really creates the memory state for this slice. + if (load_alias_idx >= Compile::AliasIdxRaw) { + while (initial_mem->is_MergeMem()) { + MergeMemNode* mm = initial_mem->as_MergeMem(); + Node* p = mm->memory_at(load_alias_idx); + if (p != mm->base_memory()) { + initial_mem = p; + } else { + break; + } + } + } + worklist_def_use_mem_states.push(nullptr, initial_mem); + while (worklist_def_use_mem_states.is_nonempty()) { // Examine a nearby store to see if it might interfere with our load. - Node* mem = worklist_mem.pop(); - Node* store = worklist_store.pop(); - uint op = store->Opcode(); + Node* def_mem_state = worklist_def_use_mem_states.top_def(); + Node* use_mem_state = worklist_def_use_mem_states.top_use(); + worklist_def_use_mem_states.pop(); + + uint op = use_mem_state->Opcode(); + +#ifdef ASSERT + // CacheWB nodes are peculiar in a sense that they both are anti-dependent and produce memory. + // Allow them to be treated as a store. + bool is_cache_wb = false; + if (use_mem_state->is_Mach()) { + int ideal_op = use_mem_state->as_Mach()->ideal_Opcode(); + is_cache_wb = (ideal_op == Op_CacheWB); + } + assert(!use_mem_state->needs_anti_dependence_check() || is_cache_wb, "no loads"); +#endif // MergeMems do not directly have anti-deps. // Treat them as internal nodes in a forward tree of memory states, // the leaves of which are each a 'possible-def'. - if (store == initial_mem // root (exclusive) of tree we are searching + if (use_mem_state == initial_mem // root (exclusive) of tree we are searching || op == Op_MergeMem // internal node of tree we are searching ) { - mem = store; // It's not a possibly interfering store. - if (store == initial_mem) + def_mem_state = use_mem_state; // It's not a possibly interfering store. + if (use_mem_state == initial_mem) initial_mem = nullptr; // only process initial memory once - for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) { - store = mem->fast_out(i); - if (store->is_MergeMem()) { - // Be sure we don't get into combinatorial problems. - // (Allow phis to be repeated; they can merge two relevant states.) - uint j = worklist_visited.size(); - for (; j > 0; j--) { - if (worklist_visited.at(j-1) == store) break; - } - if (j > 0) continue; // already on work list; do not repeat - worklist_visited.push(store); + for (DUIterator_Fast imax, i = def_mem_state->fast_outs(imax); i < imax; i++) { + use_mem_state = def_mem_state->fast_out(i); + if (use_mem_state->needs_anti_dependence_check()) { + // use_mem_state is also a kind of load (i.e. needs_anti_dependence_check), and it is not a memory state + // modifying node (store, Phi or MergeMem). Hence, load can't be anti dependent on this node. + continue; } - worklist_mem.push(mem); - worklist_store.push(store); + worklist_def_use_mem_states.push(def_mem_state, use_mem_state); } continue; } if (op == Op_MachProj || op == Op_Catch) continue; - if (store->needs_anti_dependence_check()) continue; // not really a store // Compute the alias index. Loads and stores with different alias // indices do not need anti-dependence edges. Wide MemBar's are // anti-dependent on everything (except immutable memories). - const TypePtr* adr_type = store->adr_type(); + const TypePtr* adr_type = use_mem_state->adr_type(); if (!C->can_alias(adr_type, load_alias_idx)) continue; // Most slow-path runtime calls do NOT modify Java memory, but // they can block and so write Raw memory. - if (store->is_Mach()) { - MachNode* mstore = store->as_Mach(); + if (use_mem_state->is_Mach()) { + MachNode* mstore = use_mem_state->as_Mach(); if (load_alias_idx != Compile::AliasIdxRaw) { // Check for call into the runtime using the Java calling // convention (and from there into a wrapper); it has no @@ -736,10 +847,10 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { // or else observe that 'store' is all the way up in the // earliest legal block for 'load'. In the latter case, // immediately insert an anti-dependence edge. - Block* store_block = get_block_for_node(store); + Block* store_block = get_block_for_node(use_mem_state); assert(store_block != nullptr, "unused killing projections skipped above"); - if (store->is_Phi()) { + if (use_mem_state->is_Phi()) { // Loop-phis need to raise load before input. (Other phis are treated // as store below.) // @@ -754,8 +865,8 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { // Do not assert(store_block != early, "Phi merging memory after access") // PhiNode may be at start of block 'early' with backedge to 'early' DEBUG_ONLY(bool found_match = false); - for (uint j = PhiNode::Input, jmax = store->req(); j < jmax; j++) { - if (store->in(j) == mem) { // Found matching input? + for (uint j = PhiNode::Input, jmax = use_mem_state->req(); j < jmax; j++) { + if (use_mem_state->in(j) == def_mem_state) { // Found matching input? DEBUG_ONLY(found_match = true); Block* pred_block = get_block_for_node(store_block->pred(j)); if (pred_block != early) { @@ -781,21 +892,21 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { // will find him on the non_early_stores list and stick him // with a precedence edge. // (But, don't bother if LCA is already raised all the way.) - if (LCA != early && !unrelated_load_in_store_null_block(store, load)) { + if (LCA != early && !unrelated_load_in_store_null_block(use_mem_state, load)) { store_block->set_raise_LCA_mark(load_index); must_raise_LCA = true; - non_early_stores.push(store); + non_early_stores.push(use_mem_state); } } else { // Found a possibly-interfering store in the load's 'early' block. // This means 'load' cannot sink at all in the dominator tree. // Add an anti-dep edge, and squeeze 'load' into the highest block. - assert(store != load->find_exact_control(load->in(0)), "dependence cycle found"); + assert(use_mem_state != load->find_exact_control(load->in(0)), "dependence cycle found"); if (verify) { - assert(store->find_edge(load) != -1 || unrelated_load_in_store_null_block(store, load), + assert(use_mem_state->find_edge(load) != -1 || unrelated_load_in_store_null_block(use_mem_state, load), "missing precedence edge"); } else { - store->add_prec(load); + use_mem_state->add_prec(load); } LCA = early; // This turns off the process of gathering non_early_stores. @@ -1420,8 +1531,8 @@ void PhaseCFG::schedule_late(VectorSet &visited, Node_Stack &stack) { C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { // Bailout without retry when (early->_dom_depth > LCA->_dom_depth) - assert(false, "graph should be schedulable"); - C->record_method_not_compilable("late schedule failed: incorrect graph"); + assert(C->failure_is_artificial(), "graph should be schedulable"); + C->record_method_not_compilable("late schedule failed: incorrect graph" DEBUG_ONLY(COMMA true)); } return; } @@ -1601,8 +1712,8 @@ void PhaseCFG::global_code_motion() { Block* block = get_block(i); if (!schedule_local(block, ready_cnt, visited, recalc_pressure_nodes)) { if (!C->failure_reason_is(C2Compiler::retry_no_subsuming_loads())) { - assert(false, "local schedule failed"); - C->record_method_not_compilable("local schedule failed"); + assert(C->failure_is_artificial(), "local schedule failed"); + C->record_method_not_compilable("local schedule failed" DEBUG_ONLY(COMMA true)); } _regalloc = nullptr; return; diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 3bc5b9a8b2a7d..fe8ca76e318c1 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -340,7 +340,9 @@ static inline void add_one_req(Node* dstphi, Node* src) { // having a control input of its exception map, rather than null. Such // regions do not appear except in this function, and in use_exception_state. void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* phi_map) { - if (failing()) return; // dying anyway... + if (failing_internal()) { + return; // dying anyway... + } JVMState* ex_jvms = ex_map->_jvms; assert(ex_jvms->same_calls_as(phi_map->_jvms), "consistent call chains"); assert(ex_jvms->stkoff() == phi_map->_jvms->stkoff(), "matching locals"); @@ -446,7 +448,7 @@ void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* ph //--------------------------use_exception_state-------------------------------- Node* GraphKit::use_exception_state(SafePointNode* phi_map) { - if (failing()) { stop(); return top(); } + if (failing_internal()) { stop(); return top(); } Node* region = phi_map->control(); Node* hidden_merge_mark = root(); assert(phi_map->jvms()->map() == phi_map, "sanity: 1-1 relation"); @@ -1556,6 +1558,7 @@ Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, bool mismatched, bool unsafe, uint8_t barrier_data) { + assert(adr_idx == C->get_alias_index(_gvn.type(adr)->isa_ptr()), "slice of address and input slice don't match"); assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); const TypePtr* adr_type = nullptr; // debug-mode-only argument debug_only(adr_type = C->get_adr_type(adr_idx)); @@ -1585,6 +1588,7 @@ Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, bool unsafe, int barrier_data) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); + assert(adr_idx == C->get_alias_index(_gvn.type(adr)->isa_ptr()), "slice of address and input slice don't match"); const TypePtr* adr_type = nullptr; debug_only(adr_type = C->get_adr_type(adr_idx)); Node *mem = memory(adr_idx); @@ -1926,7 +1930,7 @@ static void add_mergemem_users_to_worklist(Unique_Node_List& wl, Node* mem) { } // Replace the call with the current state of the kit. -void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes) { +void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes, bool do_asserts) { JVMState* ejvms = nullptr; if (has_exceptions()) { ejvms = transfer_exceptions_into_jvms(); @@ -1940,7 +1944,7 @@ void GraphKit::replace_call(CallNode* call, Node* result, bool do_replaced_nodes // Find all the needed outputs of this call CallProjections callprojs; - call->extract_projections(&callprojs, true); + call->extract_projections(&callprojs, true, do_asserts); Unique_Node_List wl; Node* init_mem = call->in(TypeFunc::Memory); @@ -2056,7 +2060,9 @@ Node* GraphKit::uncommon_trap(int trap_request, ciKlass* klass, const char* comment, bool must_throw, bool keep_exact_action) { - if (failing()) stop(); + if (failing_internal()) { + stop(); + } if (stopped()) return nullptr; // trap reachable? // Note: If ProfileTraps is true, and if a deopt. actually @@ -3008,7 +3014,7 @@ void GraphKit::guard_klass_being_initialized(Node* klass) { Node* adr = basic_plus_adr(top(), klass, init_state_off); Node* init_state = LoadNode::make(_gvn, nullptr, immutable_memory(), adr, adr->bottom_type()->is_ptr(), TypeInt::BYTE, - T_BYTE, MemNode::unordered); + T_BYTE, MemNode::acquire); init_state = _gvn.transform(init_state); Node* being_initialized_state = makecon(TypeInt::make(InstanceKlass::being_initialized)); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index e7f17c72a1b99..3333b7d1bd95f 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -82,7 +82,7 @@ class GraphKit : public Phase { #ifdef ASSERT ~GraphKit() { - assert(failing() || !has_exceptions(), + assert(failing_internal() || !has_exceptions(), "unless compilation failed, user must call transfer_exceptions_into_jvms"); } #endif @@ -182,6 +182,7 @@ class GraphKit : public Phase { // Tell if the compilation is failing. bool failing() const { return C->failing(); } + bool failing_internal() const { return C->failing_internal(); } // Set _map to null, signalling a stop to further bytecode execution. // Preserve the map intact for future use, and return it back to the caller. @@ -730,7 +731,7 @@ class GraphKit : public Phase { // Replace the call with the current state of the kit. Requires // that the call was generated with separate io_projs so that // exceptional control flow can be handled properly. - void replace_call(CallNode* call, Node* result, bool do_replaced_nodes = false); + void replace_call(CallNode* call, Node* result, bool do_replaced_nodes = false, bool do_asserts = true); // helper functions for statistics void increment_counter(address counter_addr); // increment a debug counter diff --git a/src/hotspot/share/opto/idealKit.cpp b/src/hotspot/share/opto/idealKit.cpp index 80c4791cfd5cc..baa055bc60aeb 100644 --- a/src/hotspot/share/opto/idealKit.cpp +++ b/src/hotspot/share/opto/idealKit.cpp @@ -381,26 +381,6 @@ Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, return st; } -// Card mark store. Must be ordered so that it will come after the store of -// the oop. -Node* IdealKit::storeCM(Node* ctl, Node* adr, Node *val, Node* oop_store, int oop_adr_idx, - BasicType bt, - int adr_idx) { - assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); - const TypePtr* adr_type = nullptr; - debug_only(adr_type = C->get_adr_type(adr_idx)); - Node *mem = memory(adr_idx); - - // Add required edge to oop_store, optimizer does not support precedence edges. - // Convert required edge to precedence edge before allocation. - Node* st = new StoreCMNode(ctl, mem, adr, adr_type, val, oop_store, oop_adr_idx); - - st = transform(st); - set_memory(st, adr_idx); - - return st; -} - //---------------------------- do_memory_merge -------------------------------- // The memory from one merging cvstate needs to be merged with the memory for another // join cvstate. If the join cvstate doesn't have a merged memory yet then we diff --git a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp index 20acec4721118..727b70129ef90 100644 --- a/src/hotspot/share/opto/idealKit.hpp +++ b/src/hotspot/share/opto/idealKit.hpp @@ -234,15 +234,6 @@ class IdealKit: public StackObj { bool require_atomic_access = false, bool mismatched = false); - // Store a card mark ordered after store_oop - Node* storeCM(Node* ctl, - Node* adr, - Node* val, - Node* oop_store, - int oop_adr_idx, - BasicType bt, - int adr_idx); - // Trivial call Node* make_leaf_call(const TypeFunc *slow_call_type, address slow_call, diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 8e5cd2702137a..4313b2cf907a9 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -840,9 +840,9 @@ bool IfNode::is_dominator_unc(CallStaticJavaNode* dom_unc, CallStaticJavaNode* u } // Return projection that leads to an uncommon trap if any -ProjNode* IfNode::uncommon_trap_proj(CallStaticJavaNode*& call) const { +ProjNode* IfNode::uncommon_trap_proj(CallStaticJavaNode*& call, Deoptimization::DeoptReason reason) const { for (int i = 0; i < 2; i++) { - call = proj_out(i)->is_uncommon_trap_proj(); + call = proj_out(i)->is_uncommon_trap_proj(reason); if (call != nullptr) { return proj_out(i); } diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 9db94748ca27c..2a40cf000d849 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -161,6 +161,14 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo Node *m = val->out(i); if( !m->is_Mach() ) continue; MachNode *mach = m->as_Mach(); + if (mach->barrier_data() != 0) { + // Using memory accesses with barriers to perform implicit null checks is + // not supported. These operations might expand into multiple assembly + // instructions during code emission, including new memory accesses (e.g. + // in G1's pre-barrier), which would invalidate the implicit null + // exception table. + continue; + } was_store = false; int iop = mach->ideal_Opcode(); switch( iop ) { @@ -183,7 +191,6 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo break; case Op_StoreB: case Op_StoreC: - case Op_StoreCM: case Op_StoreD: case Op_StoreF: case Op_StoreI: @@ -715,7 +722,6 @@ void PhaseCFG::adjust_register_pressure(Node* n, Block* block, intptr_t* recalc_ switch (iop) { case Op_StoreB: case Op_StoreC: - case Op_StoreCM: case Op_StoreD: case Op_StoreF: case Op_StoreI: @@ -996,21 +1002,6 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto local++; // One more block-local input } ready_cnt.at_put(n->_idx, local); // Count em up - -#ifdef ASSERT - if (UseG1GC) { - if( n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_StoreCM ) { - // Check the precedence edges - for (uint prec = n->req(); prec < n->len(); prec++) { - Node* oop_store = n->in(prec); - if (oop_store != nullptr) { - assert(get_block_for_node(oop_store)->_dom_depth <= block->_dom_depth, "oop_store must dominate card-mark"); - } - } - } - } -#endif - // A few node types require changing a required edge to a precedence edge // before allocation. if( n->is_Mach() && n->req() > TypeFunc::Parms && @@ -1196,7 +1187,7 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto // to the Compile object, and the C2Compiler will see it and retry. C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { - assert(false, "graph should be schedulable"); + assert(C->failure_is_artificial(), "graph should be schedulable"); } // assert( phi_cnt == end_idx(), "did not schedule all" ); return false; diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index c95a450272989..49750cd2697e9 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -254,6 +254,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dtanh: case vmIntrinsics::_dabs: case vmIntrinsics::_fabs: case vmIntrinsics::_iabs: @@ -579,6 +580,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_Reference_get: return inline_reference_get(); case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false); case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true); + case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false); + case vmIntrinsics::_PhantomReference_clear0: return inline_reference_clear0(true); case vmIntrinsics::_Class_cast: return inline_Class_cast(); @@ -716,6 +719,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { return inline_vector_mask_operation(); case vmIntrinsics::_VectorShuffleToVector: return inline_vector_shuffle_to_vector(); + case vmIntrinsics::_VectorWrapShuffleIndexes: + return inline_vector_wrap_shuffle_indexes(); case vmIntrinsics::_VectorLoadOp: return inline_vector_mem_operation(/*is_store=*/false); case vmIntrinsics::_VectorLoadMaskedOp: @@ -736,6 +741,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { return inline_vector_blend(); case vmIntrinsics::_VectorRearrange: return inline_vector_rearrange(); + case vmIntrinsics::_VectorSelectFrom: + return inline_vector_select_from(); case vmIntrinsics::_VectorCompare: return inline_vector_compare(); case vmIntrinsics::_VectorBroadcastInt: @@ -1879,6 +1886,9 @@ bool LibraryCallKit::inline_math_native(vmIntrinsics::ID id) { return StubRoutines::dtan() != nullptr ? runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dtan(), "dtan") : runtime_math(OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dtan), "TAN"); + case vmIntrinsics::_dtanh: + return StubRoutines::dtanh() != nullptr ? + runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dtanh(), "dtanh") : false; case vmIntrinsics::_dexp: return StubRoutines::dexp() != nullptr ? runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dexp(), "dexp") : @@ -2044,7 +2054,7 @@ LibraryCallKit::classify_unsafe_addr(Node* &base, Node* &offset, BasicType type) if (base_type == nullptr) { // Unknown type. return Type::AnyPtr; - } else if (base_type == TypePtr::NULL_PTR) { + } else if (_gvn.type(base->uncast()) == TypePtr::NULL_PTR) { // Since this is a null+long form, we have to switch to a rawptr. base = _gvn.transform(new CastX2PNode(offset)); offset = MakeConX(0); @@ -2362,8 +2372,9 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c SafePointNode* old_map = clone_map(); Node* adr = make_unsafe_address(base, offset, type, kind == Relaxed); + assert(!stopped(), "Inlining of unsafe access failed: address construction stopped unexpectedly"); - if (_gvn.type(base)->isa_ptr() == TypePtr::NULL_PTR) { + if (_gvn.type(base->uncast())->isa_ptr() == TypePtr::NULL_PTR) { if (type != T_OBJECT) { decorators |= IN_NATIVE; // off-heap primitive access } else { @@ -2895,7 +2906,7 @@ bool LibraryCallKit::inline_unsafe_allocate() { Node* insp = basic_plus_adr(kls, in_bytes(InstanceKlass::init_state_offset())); // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler // can generate code to load it as unsigned byte. - Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); + Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::acquire); Node* bits = intcon(InstanceKlass::fully_initialized); test = _gvn.transform(new SubINode(inst, bits)); // The 'test' is non-zero if we need to take a slow path. @@ -2950,11 +2961,10 @@ bool LibraryCallKit::inline_native_notify_jvmti_funcs(address funcAddr, const ch Node* thread = ideal.thread(); Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_VTMS_transition_offset())); Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_VTMS_transition_offset()); - const TypePtr *addr_type = _gvn.type(addr)->isa_ptr(); sync_kit(ideal); - access_store_at(nullptr, jt_addr, addr_type, hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); - access_store_at(nullptr, vt_addr, addr_type, hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); ideal.sync_kit(this); } ideal.end_if(); @@ -3316,7 +3326,9 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Load the raw epoch value from the threadObj. Node* threadObj_epoch_offset = basic_plus_adr(threadObj, java_lang_Thread::jfr_epoch_offset()); - Node* threadObj_epoch_raw = access_load_at(threadObj, threadObj_epoch_offset, TypeRawPtr::BOTTOM, TypeInt::CHAR, T_CHAR, + Node* threadObj_epoch_raw = access_load_at(threadObj, threadObj_epoch_offset, + _gvn.type(threadObj_epoch_offset)->isa_ptr(), + TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. @@ -3335,7 +3347,8 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Load the raw epoch value from the vthread. Node* vthread_epoch_offset = basic_plus_adr(vthread, java_lang_Thread::jfr_epoch_offset()); - Node* vthread_epoch_raw = access_load_at(vthread, vthread_epoch_offset, TypeRawPtr::BOTTOM, TypeInt::CHAR, T_CHAR, + Node* vthread_epoch_raw = access_load_at(vthread, vthread_epoch_offset, _gvn.type(vthread_epoch_offset)->is_ptr(), + TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. @@ -3581,7 +3594,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { // Load the raw epoch value from the vthread. Node* epoch_offset = basic_plus_adr(thread, java_lang_Thread::jfr_epoch_offset()); - Node* epoch_raw = access_load_at(thread, epoch_offset, TypeRawPtr::BOTTOM, TypeInt::CHAR, T_CHAR, + Node* epoch_raw = access_load_at(thread, epoch_offset, _gvn.type(epoch_offset)->is_ptr(), TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. @@ -6951,6 +6964,48 @@ bool LibraryCallKit::inline_reference_refersTo0(bool is_phantom) { return true; } +//----------------------------inline_reference_clear0---------------------------- +// void java.lang.ref.Reference.clear0(); +// void java.lang.ref.PhantomReference.clear0(); +bool LibraryCallKit::inline_reference_clear0(bool is_phantom) { + // This matches the implementation in JVM_ReferenceClear, see the comments there. + + // Get arguments + Node* reference_obj = null_check_receiver(); + if (stopped()) return true; + + // Common access parameters + DecoratorSet decorators = IN_HEAP | AS_NO_KEEPALIVE; + decorators |= (is_phantom ? ON_PHANTOM_OOP_REF : ON_WEAK_OOP_REF); + Node* referent_field_addr = basic_plus_adr(reference_obj, java_lang_ref_Reference::referent_offset()); + const TypePtr* referent_field_addr_type = _gvn.type(referent_field_addr)->isa_ptr(); + const Type* val_type = TypeOopPtr::make_from_klass(env()->Object_klass()); + + Node* referent = access_load_at(reference_obj, + referent_field_addr, + referent_field_addr_type, + val_type, + T_OBJECT, + decorators); + + IdealKit ideal(this); +#define __ ideal. + __ if_then(referent, BoolTest::ne, null()); + sync_kit(ideal); + access_store_at(reference_obj, + referent_field_addr, + referent_field_addr_type, + null(), + val_type, + T_OBJECT, + decorators); + __ sync_kit(this); + __ end_if(); + final_sync(ideal); +#undef __ + + return true; +} Node* LibraryCallKit::load_field_from_object(Node* fromObj, const char* fieldName, const char* fieldTypeString, DecoratorSet decorators, bool is_static, diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index dd74734802f65..c9b2a02d3f1ab 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -297,6 +297,7 @@ class LibraryCallKit : public GraphKit { bool inline_divmod_methods(vmIntrinsics::ID id); bool inline_reference_get(); bool inline_reference_refersTo0(bool is_phantom); + bool inline_reference_clear0(bool is_phantom); bool inline_Class_cast(); bool inline_aescrypt_Block(vmIntrinsics::ID id); bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); @@ -353,6 +354,7 @@ class LibraryCallKit : public GraphKit { bool inline_vector_nary_operation(int n); bool inline_vector_frombits_coerced(); bool inline_vector_shuffle_to_vector(); + bool inline_vector_wrap_shuffle_indexes(); bool inline_vector_shuffle_iota(); Node* partially_wrap_indexes(Node* index_vec, int num_elem, BasicType type_bt); bool inline_vector_mask_operation(); @@ -363,6 +365,7 @@ class LibraryCallKit : public GraphKit { bool inline_vector_test(); bool inline_vector_blend(); bool inline_vector_rearrange(); + bool inline_vector_select_from(); bool inline_vector_compare(); bool inline_vector_broadcast_int(); bool inline_vector_convert(); @@ -372,7 +375,7 @@ class LibraryCallKit : public GraphKit { bool inline_index_vector(); bool inline_index_partially_in_upper_range(); - Node* gen_call_to_svml(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2); + Node* gen_call_to_vector_math(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2); enum VectorMaskUseType { VecMaskUseLoad = 1 << 0, diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 59662ad53fe07..c3cc8532e7d30 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1464,7 +1464,8 @@ IfTrueNode* PhaseIdealLoop::create_initialized_assertion_predicate(IfNode* templ Node* new_stride, Node* control) { assert(assertion_predicate_has_loop_opaque_node(template_assertion_predicate), "must find OpaqueLoop* nodes for Template Assertion Predicate"); - InitializedAssertionPredicate initialized_assertion_predicate(template_assertion_predicate, new_init, new_stride, this); + InitializedAssertionPredicateCreator initialized_assertion_predicate(template_assertion_predicate, new_init, + new_stride, this); IfTrueNode* success_proj = initialized_assertion_predicate.create(control); assert(!assertion_predicate_has_loop_opaque_node(success_proj->in(0)->as_If()), "Initialized Assertion Predicates do not have OpaqueLoop* nodes in the bool expression anymore"); @@ -3816,7 +3817,7 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st break; } int opc = n->Opcode(); - if (opc == Op_StoreP || opc == Op_StoreN || opc == Op_StoreNKlass || opc == Op_StoreCM) { + if (opc == Op_StoreP || opc == Op_StoreN || opc == Op_StoreNKlass) { msg = "oop fills not handled"; break; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 3128e23d79c49..c0e929ca1f4f4 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -692,14 +692,24 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal // We can only use that safepoint if there's no side effect between the backedge and the safepoint. - // mm is used for book keeping + // mm is the memory state at the safepoint (when it's a MergeMem) + // no_side_effect_since_safepoint() goes over the memory state at the backedge. It resets the mm input for each + // component of the memory state it encounters so it points to the base memory. Once no_side_effect_since_safepoint() + // is done, if no side effect after the safepoint was found, mm should transform to the base memory: the states at + // the backedge and safepoint are the same so all components of the memory state at the safepoint should have been + // reset. MergeMemNode* mm = nullptr; #ifdef ASSERT if (mem->is_MergeMem()) { mm = mem->clone()->as_MergeMem(); _igvn._worklist.push(mm); for (MergeMemStream mms(mem->as_MergeMem()); mms.next_non_empty(); ) { - if (mms.alias_idx() != Compile::AliasIdxBot && loop != get_loop(ctrl_or_self(mms.memory()))) { + // Loop invariant memory state won't be reset by no_side_effect_since_safepoint(). Do it here. + // Escape Analysis can add state to mm that it doesn't add to the backedge memory Phis, breaking verification + // code that relies on mm. Clear that extra state here. + if (mms.alias_idx() != Compile::AliasIdxBot && + (loop != get_loop(ctrl_or_self(mms.memory())) || + (mms.adr_type()->isa_oop_ptr() && mms.adr_type()->is_known_instance()))) { mm->set_memory_at(mms.alias_idx(), mem->as_MergeMem()->base_memory()); } } @@ -1918,12 +1928,28 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ // Since stride > 0 and limit_correction <= stride + 1, we can restate this with no over- or underflow into: // max_int - canonicalized_correction - limit_correction >= limit // Since canonicalized_correction and limit_correction are both constants, we can replace them with a new constant: - // final_correction = canonicalized_correction + limit_correction + // (v) final_correction = canonicalized_correction + limit_correction + // // which gives us: // // Final predicate condition: // max_int - final_correction >= limit // + // However, we need to be careful that (v) does not over- or underflow. + // We know that: + // canonicalized_correction = stride - 1 + // and + // limit_correction <= stride + 1 + // and thus + // canonicalized_correction + limit_correction <= 2 * stride + // To prevent an over- or underflow of (v), we must ensure that + // 2 * stride <= max_int + // which can safely be checked without over- or underflow with + // (vi) stride != min_int AND abs(stride) <= max_int / 2 + // + // We could try to further optimize the cases where (vi) does not hold but given that such large strides are + // very uncommon and the loop would only run for a very few iterations anyway, we simply bail out if (vi) fails. + // // (2) Loop Limit Check Predicate for (ii): // Using (ii): init < limit // @@ -1954,6 +1980,10 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_ // there is no overflow of the iv phi after the first iteration. In this case, we don't need to check (ii) // again and can skip the predicate. + // Check (vi) and bail out if the stride is too big. + if (stride_con == min_signed_integer(iv_bt) || (ABS(stride_con) > max_signed_integer(iv_bt) / 2)) { + return false; + } // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. const jlong limit_correction_for_pre_iv_exit_check = (phi_incr != nullptr) ? stride_con : 0; @@ -2571,7 +2601,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) { @@ -2580,10 +2610,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) { @@ -4319,13 +4349,21 @@ void PhaseIdealLoop::mark_loop_associated_parse_predicates_useful() { } } +// This visitor marks all visited Parse Predicates useful. +class ParsePredicateUsefulMarker : public PredicateVisitor { + public: + using PredicateVisitor::visit; + + void visit(const ParsePredicate& parse_predicate) override { + parse_predicate.head()->mark_useful(); + } +}; + void PhaseIdealLoop::mark_useful_parse_predicates_for_loop(IdealLoopTree* loop) { Node* entry = loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl); - const Predicates predicates(entry); - ParsePredicateIterator iterator(predicates); - while (iterator.has_next()) { - iterator.next()->mark_useful(); - } + const PredicateIterator predicate_iterator(entry); + ParsePredicateUsefulMarker useful_marker; + predicate_iterator.for_each(useful_marker); } void PhaseIdealLoop::add_useless_parse_predicates_to_igvn_worklist() { @@ -4915,7 +4953,9 @@ void PhaseIdealLoop::verify() const { bool success = true; PhaseIdealLoop phase_verify(_igvn, this); - if (C->failing()) return; + if (C->failing_internal()) { + return; + } // Verify ctrl and idom of every node. success &= verify_idom_and_nodes(C->root(), &phase_verify); @@ -6267,6 +6307,43 @@ void PhaseIdealLoop::build_loop_late_post(Node *n) { build_loop_late_post_work(n, true); } +// Class to visit all predicates in a predicate chain to find out which are dominated by a given node. Keeps track of +// the entry to the earliest predicate that is still dominated by the given dominator. This class is used when trying to +// legally skip all predicates when figuring out the latest placement such that a node does not interfere with Loop +// Predication or creating a Loop Limit Check Predicate later. +class DominatedPredicates : public UnifiedPredicateVisitor { + Node* const _dominator; + Node* _earliest_dominated_predicate_entry; + bool _should_continue; + PhaseIdealLoop* const _phase; + + public: + DominatedPredicates(Node* dominator, Node* start_node, PhaseIdealLoop* phase) + : _dominator(dominator), + _earliest_dominated_predicate_entry(start_node), + _should_continue(true), + _phase(phase) {} + NONCOPYABLE(DominatedPredicates); + + bool should_continue() const override { + return _should_continue; + } + + // Returns the entry to the earliest predicate that is still dominated by the given dominator (all could be dominated). + Node* earliest_dominated_predicate_entry() const { + return _earliest_dominated_predicate_entry; + } + + void visit_predicate(const Predicate& predicate) override { + Node* entry = predicate.entry(); + if (_phase->is_strict_dominator(entry, _dominator)) { + _should_continue = false; + } else { + _earliest_dominated_predicate_entry = entry; + } + } +}; + void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) { if (n->req() == 2 && (n->Opcode() == Op_ConvI2L || n->Opcode() == Op_CastII) && !C->major_progress() && !_verify_only) { @@ -6378,14 +6455,10 @@ void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) { // Move the node above predicates as far up as possible so a // following pass of Loop Predication doesn't hoist a predicate // that depends on it above that node. - PredicateEntryIterator predicate_iterator(least); - while (predicate_iterator.has_next()) { - Node* next_predicate_entry = predicate_iterator.next_entry(); - if (is_strict_dominator(next_predicate_entry, early)) { - break; - } - least = next_predicate_entry; - } + const PredicateIterator predicate_iterator(least); + DominatedPredicates dominated_predicates(early, least, this); + predicate_iterator.for_each(dominated_predicates); + least = dominated_predicates.earliest_dominated_predicate_entry(); } // Try not to place code on a loop entry projection // which can inhibit range check elimination. diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 2d169a6459b38..3aa67bcb5cb8b 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1128,7 +1128,9 @@ class PhaseIdealLoop : public PhaseTransform { _verify_only(verify_me == nullptr), _mode(LoopOptsVerify), _nodes_required(UINT_MAX) { + DEBUG_ONLY(C->set_phase_verify_ideal_loop();) build_and_optimize(); + DEBUG_ONLY(C->reset_phase_verify_ideal_loop();) } #endif diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 7f42d2d4beb42..654262d21cb71 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4548,7 +4548,6 @@ void PhaseIdealLoop::move_unordered_reduction_out_of_loop(IdealLoopTree* loop) { const TypeVect* vec_t = last_ur->vect_type(); uint vector_length = vec_t->length(); BasicType bt = vec_t->element_basic_type(); - const Type* bt_t = Type::get_const_basic_type(bt); // Convert opcode from vector-reduction -> scalar -> normal-vector-op const int sopc = VectorNode::scalar_opcode(last_ur->Opcode(), bt); @@ -4628,7 +4627,7 @@ void PhaseIdealLoop::move_unordered_reduction_out_of_loop(IdealLoopTree* loop) { Node* identity_scalar = ReductionNode::make_identity_con_scalar(_igvn, sopc, bt); set_ctrl(identity_scalar, C->root()); - VectorNode* identity_vector = VectorNode::scalar2vector(identity_scalar, vector_length, bt_t); + VectorNode* identity_vector = VectorNode::scalar2vector(identity_scalar, vector_length, bt); register_new_node(identity_vector, C->root()); assert(vec_t == identity_vector->vect_type(), "matching vector type"); VectorNode::trace_new_vector(identity_vector, "Unordered Reduction"); diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index bf773d43d3d39..2031b09ca9d18 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -194,6 +194,9 @@ void Matcher::match( ) { } // One-time initialization of some register masks. init_spill_mask( C->root()->in(1) ); + if (C->failing()) { + return; + } _return_addr_mask = return_addr(); #ifdef _LP64 // Pointers take 2 slots in 64-bit land @@ -287,10 +290,16 @@ void Matcher::match( ) { // preserve area, locks & pad2. OptoReg::Name reg1 = warp_incoming_stk_arg(vm_parm_regs[i].first()); + if (C->failing()) { + return; + } if( OptoReg::is_valid(reg1)) _calling_convention_mask[i].Insert(reg1); OptoReg::Name reg2 = warp_incoming_stk_arg(vm_parm_regs[i].second()); + if (C->failing()) { + return; + } if( OptoReg::is_valid(reg2)) _calling_convention_mask[i].Insert(reg2); @@ -386,7 +395,7 @@ void Matcher::match( ) { // Don't set control, it will confuse GCM since there are no uses. // The control will be set when this node is used first time // in find_base_for_derived(). - assert(_mach_null != nullptr, ""); + assert(_mach_null != nullptr || C->failure_is_artificial(), ""); // bailouts are handled below. C->set_root(xroot->is_Root() ? xroot->as_Root() : nullptr); @@ -404,7 +413,7 @@ void Matcher::match( ) { assert(C->failure_reason() != nullptr, "graph lost: reason unknown"); ss.print("graph lost: reason unknown"); } - C->record_method_not_compilable(ss.as_string()); + C->record_method_not_compilable(ss.as_string() DEBUG_ONLY(COMMA true)); } if (C->failing()) { // delete old; @@ -1439,10 +1448,16 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { } // Grab first register, adjust stack slots and insert in mask. OptoReg::Name reg1 = warp_outgoing_stk_arg(first, begin_out_arg_area, out_arg_limit_per_call ); + if (C->failing()) { + return nullptr; + } if (OptoReg::is_valid(reg1)) rm->Insert( reg1 ); // Grab second register (if any), adjust stack slots and insert in mask. OptoReg::Name reg2 = warp_outgoing_stk_arg(second, begin_out_arg_area, out_arg_limit_per_call ); + if (C->failing()) { + return nullptr; + } if (OptoReg::is_valid(reg2)) rm->Insert( reg2 ); } // End of for all arguments @@ -1594,6 +1609,14 @@ static bool match_into_reg( const Node *n, Node *m, Node *control, int i, bool s // the same register. See find_shared_node. return false; } else { // Not a constant + if (!shared && Matcher::is_encode_and_store_pattern(n, m)) { + // Make it possible to match "encode and store" patterns with non-shared + // encode operations that are pinned to a control node (e.g. by CastPP + // node removal in final graph reshaping). The matched instruction cannot + // float above the encode's control node because it is pinned to the + // store's control node. + return false; + } // Stop recursion if they have different Controls. Node* m_control = m->in(0); // Control of load's memory can post-dominates load's control. @@ -2671,6 +2694,10 @@ bool Matcher::gen_narrow_oop_implicit_null_checks() { // Compute RegMask for an ideal register. const RegMask* Matcher::regmask_for_ideal_register(uint ideal_reg, Node* ret) { + assert(!C->failing_internal() || C->failure_is_artificial(), "already failing."); + if (C->failing()) { + return nullptr; + } const Type* t = Type::mreg2type[ideal_reg]; if (t == nullptr) { assert(ideal_reg >= Op_VecA && ideal_reg <= Op_VecZ, "not a vector: %d", ideal_reg); @@ -2701,7 +2728,10 @@ const RegMask* Matcher::regmask_for_ideal_register(uint ideal_reg, Node* ret) { default: ShouldNotReachHere(); } MachNode* mspill = match_tree(spill); - assert(mspill != nullptr, "matching failed: %d", ideal_reg); + assert(mspill != nullptr || C->failure_is_artificial(), "matching failed: %d", ideal_reg); + if (C->failing()) { + return nullptr; + } // Handle generic vector operand case if (Matcher::supports_generic_vector_operands && t->isa_vect()) { specialize_mach_node(mspill); @@ -2833,9 +2863,21 @@ bool Matcher::is_non_long_integral_vector(const Node* n) { return is_subword_type(bt) || bt == T_INT; } +bool Matcher::is_encode_and_store_pattern(const Node* n, const Node* m) { + if (n == nullptr || + m == nullptr || + n->Opcode() != Op_StoreN || + !m->is_EncodeP() || + n->as_Store()->barrier_data() == 0) { + return false; + } + assert(m == n->in(MemNode::ValueIn), "m should be input to n"); + return true; +} + #ifdef ASSERT bool Matcher::verify_after_postselect_cleanup() { - assert(!C->failing(), "sanity"); + assert(!C->failing_internal() || C->failure_is_artificial(), "sanity"); if (supports_generic_vector_operands) { Unique_Node_List useful; C->identify_useful_nodes(useful); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 84e48086f92d3..de3b23aa6d2a1 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -342,7 +342,6 @@ class Matcher : public PhaseTransform { static bool vector_needs_partial_operations(Node* node, const TypeVect* vt); static const RegMask* predicate_reg_mask(void); - static const TypeVectMask* predicate_reg_type(const Type* elemTy, int length); // Vector width in bytes static int vector_width_in_bytes(BasicType bt); @@ -385,6 +384,8 @@ class Matcher : public PhaseTransform { return ((bt & BoolTest::unsigned_compare) == BoolTest::unsigned_compare); } + static bool is_encode_and_store_pattern(const Node* n, const Node* m); + // These calls are all generated by the ADLC // Java-Java calling convention diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index eee14e5ba03f1..27c0d16fac1b6 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -3462,9 +3462,7 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* address = in(MemNode::Address); Node* value = in(MemNode::ValueIn); // Back-to-back stores to same address? Fold em up. Generally - // unsafe if I have intervening uses... Also disallowed for StoreCM - // since they must follow each StoreP operation. Redundant StoreCMs - // are eliminated just before matching in final_graph_reshape. + // unsafe if I have intervening uses. { Node* st = mem; // If Store 'st' has more than one use, we cannot fold 'st' away. @@ -3474,7 +3472,7 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { // require exactly ONE user until such time as we clone 'mem' for // each of 'mem's uses (thus making the exactly-1-user-rule hold // true). - while (st->is_Store() && st->outcnt() == 1 && st->Opcode() != Op_StoreCM) { + while (st->is_Store() && st->outcnt() == 1) { // Looking at a dead closed cycle of memory? assert(st != st->in(MemNode::Memory), "dead loop in StoreNode::Ideal"); assert(Opcode() == st->Opcode() || @@ -3781,48 +3779,6 @@ Node *StoreCNode::Ideal(PhaseGVN *phase, bool can_reshape){ return StoreNode::Ideal(phase, can_reshape); } -//============================================================================= -//------------------------------Identity--------------------------------------- -Node* StoreCMNode::Identity(PhaseGVN* phase) { - // No need to card mark when storing a null ptr - Node* my_store = in(MemNode::OopStore); - if (my_store->is_Store()) { - const Type *t1 = phase->type( my_store->in(MemNode::ValueIn) ); - if( t1 == TypePtr::NULL_PTR ) { - return in(MemNode::Memory); - } - } - return this; -} - -//============================================================================= -//------------------------------Ideal--------------------------------------- -Node *StoreCMNode::Ideal(PhaseGVN *phase, bool can_reshape){ - Node* progress = StoreNode::Ideal(phase, can_reshape); - if (progress != nullptr) return progress; - - Node* my_store = in(MemNode::OopStore); - if (my_store->is_MergeMem()) { - Node* mem = my_store->as_MergeMem()->memory_at(oop_alias_idx()); - set_req_X(MemNode::OopStore, mem, phase); - return this; - } - - return nullptr; -} - -//------------------------------Value----------------------------------------- -const Type* StoreCMNode::Value(PhaseGVN* phase) const { - // Either input is TOP ==> the result is TOP (checked in StoreNode::Value). - // If extra input is TOP ==> the result is TOP - const Type* t = phase->type(in(MemNode::OopStore)); - if (t == Type::TOP) { - return Type::TOP; - } - return StoreNode::Value(phase); -} - - //============================================================================= //----------------------------------SCMemProjNode------------------------------ const Type* SCMemProjNode::Value(PhaseGVN* phase) const @@ -4644,6 +4600,11 @@ intptr_t InitializeNode::can_capture_store(StoreNode* st, PhaseGVN* phase, bool Node* mem = st->in(MemNode::Memory); if (!(mem->is_Proj() && mem->in(0) == this)) return FAIL; // must not be preceded by other stores + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); + if ((st->Opcode() == Op_StoreP || st->Opcode() == Op_StoreN) && + !bs->can_initialize_object(st)) { + return FAIL; + } Node* adr = st->in(MemNode::Address); intptr_t offset; AllocateNode* alloc = AllocateNode::Ideal_allocation(adr, phase, offset); diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 85d206749f6be..1ca3a4b16ce1e 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -55,8 +55,7 @@ class MemNode : public Node { enum { Control, // When is it safe to do this load? Memory, // Chunk of memory is being loaded from Address, // Actually address, derived from base - ValueIn, // Value to store - OopStore // Preceding oop store, only in StoreCM + ValueIn // Value to store }; typedef enum { unordered = 0, acquire, // Load has to acquire or be succeeded by MemBarAcquire. @@ -124,11 +123,7 @@ class MemNode : public Node { // Raw access function, to allow copying of adr_type efficiently in // product builds and retain the debug info for debug builds. const TypePtr *raw_adr_type() const { -#ifdef ASSERT - return _adr_type; -#else - return 0; -#endif + return DEBUG_ONLY(_adr_type) NOT_DEBUG(nullptr); } // Return the barrier data of n, if available, or 0 otherwise. @@ -781,36 +776,6 @@ class StoreNKlassNode : public StoreNNode { virtual BasicType memory_type() const { return T_NARROWKLASS; } }; -//------------------------------StoreCMNode----------------------------------- -// Store card-mark byte to memory for CM -// The last StoreCM before a SafePoint must be preserved and occur after its "oop" store -// Preceding equivalent StoreCMs may be eliminated. -class StoreCMNode : public StoreNode { - private: - virtual uint hash() const { return StoreNode::hash() + _oop_alias_idx; } - virtual bool cmp( const Node &n ) const { - return _oop_alias_idx == ((StoreCMNode&)n)._oop_alias_idx - && StoreNode::cmp(n); - } - virtual uint size_of() const { return sizeof(*this); } - int _oop_alias_idx; // The alias_idx of OopStore - -public: - StoreCMNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store, int oop_alias_idx ) : - StoreNode(c, mem, adr, at, val, oop_store, MemNode::release), - _oop_alias_idx(oop_alias_idx) { - assert(_oop_alias_idx >= Compile::AliasIdxRaw || - (_oop_alias_idx == Compile::AliasIdxBot && !Compile::current()->do_aliasing()), - "bad oop alias idx"); - } - virtual int Opcode() const; - virtual Node* Identity(PhaseGVN* phase); - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual const Type* Value(PhaseGVN* phase) const; - virtual BasicType memory_type() const { return T_VOID; } // unspecific - int oop_alias_idx() const { return _oop_alias_idx; } -}; - //------------------------------SCMemProjNode--------------------------------------- // This class defines a projection of the memory state of a store conditional node. // These nodes return a value, but also update memory. diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index b3f251bb361ba..2865cf674295b 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -1665,30 +1665,7 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { } } } - } -#ifdef ASSERT - // Check that oop-store precedes the card-mark - else if (mach->ideal_Opcode() == Op_StoreCM) { - uint storeCM_idx = j; - int count = 0; - for (uint prec = mach->req(); prec < mach->len(); prec++) { - Node *oop_store = mach->in(prec); // Precedence edge - if (oop_store == nullptr) continue; - count++; - uint i4; - for (i4 = 0; i4 < last_inst; ++i4) { - if (block->get_node(i4) == oop_store) { - break; - } - } - // Note: This test can provide a false failure if other precedence - // edges have been added to the storeCMNode. - assert(i4 == last_inst || i4 < storeCM_idx, "CM card-mark executes before oop-store"); - } - assert(count > 0, "storeCM expects at least one precedence edge"); - } -#endif - else if (!n->is_Proj()) { + } else if (!n->is_Proj()) { // Remember the beginning of the previous instruction, in case // it's followed by a flag-kill and a null-check. Happens on // Intel all the time, with add-to-memory kind of opcodes. @@ -1715,7 +1692,7 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { node_offsets[n->_idx] = masm->offset(); } #endif - assert(!C->failing(), "Should not reach here if failing."); + assert(!C->failing_internal() || C->failure_is_artificial(), "Should not reach here if failing."); // "Normal" instruction case DEBUG_ONLY(uint instr_offset = masm->offset()); @@ -2022,6 +1999,8 @@ void PhaseOutput::FillExceptionTables(uint cnt, uint *call_returns, uint *inct_s // Handle implicit null exception table updates if (n->is_MachNullCheck()) { + assert(n->in(1)->as_Mach()->barrier_data() == 0, + "Implicit null checks on memory accesses with barriers are not yet supported"); uint block_num = block->non_connector_successor(0)->_pre_order; _inc_table.append(inct_starts[inct_cnt++], blk_labels[block_num].loc_pos()); continue; @@ -3391,7 +3370,7 @@ uint PhaseOutput::scratch_emit_size(const Node* n) { n->emit(&masm, C->regalloc()); // Emitting into the scratch buffer should not fail - assert (!C->failing(), "Must not have pending failure. Reason is: %s", C->failure_reason()); + assert(!C->failing_internal() || C->failure_is_artificial(), "Must not have pending failure. Reason is: %s", C->failure_reason()); if (is_branch) // Restore label. n->as_MachBranch()->label_set(saveL, save_bnum); diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index a2690aa6704f0..039283bc863d1 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -426,7 +426,7 @@ class Parse : public GraphKit { void set_parse_bci(int bci); // Must this parse be aborted? - bool failing() { return C->failing(); } + bool failing() const { return C->failing_internal(); } // might have cascading effects, not stressing bailouts for now. Block* rpo_at(int rpo) { assert(0 <= rpo && rpo < _block_count, "oob"); @@ -612,6 +612,11 @@ class Parse : public GraphKit { // Use speculative type to optimize CmpP node Node* optimize_cmp_with_klass(Node* c); + // Stress unstable if traps + void stress_trap(IfNode* orig_iff, Node* counter, Node* incr_store); + // Increment counter used by StressUnstableIfTraps + void increment_trap_stress_counter(Node*& counter, Node*& incr_store); + public: #ifndef PRODUCT // Handle PrintOpto, etc. diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index cbd9323c3a93a..6b7cb4eaa99e3 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1371,10 +1371,27 @@ inline int Parse::repush_if_args() { return bc_depth; } +// Used by StressUnstableIfTraps +static volatile int _trap_stress_counter = 0; + +void Parse::increment_trap_stress_counter(Node*& counter, Node*& incr_store) { + Node* counter_addr = makecon(TypeRawPtr::make((address)&_trap_stress_counter)); + counter = make_load(control(), counter_addr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, MemNode::unordered); + counter = _gvn.transform(new AddINode(counter, intcon(1))); + incr_store = store_to_memory(control(), counter_addr, counter, T_INT, Compile::AliasIdxRaw, MemNode::unordered); +} + //----------------------------------do_ifnull---------------------------------- void Parse::do_ifnull(BoolTest::mask btest, Node *c) { int target_bci = iter().get_dest(); + Node* counter = nullptr; + Node* incr_store = nullptr; + bool do_stress_trap = StressUnstableIfTraps && ((C->random() % 2) == 0); + if (do_stress_trap) { + increment_trap_stress_counter(counter, incr_store); + } + Block* branch_block = successor_for_bci(target_bci); Block* next_block = successor_for_bci(iter().next_bci()); @@ -1439,6 +1456,10 @@ void Parse::do_ifnull(BoolTest::mask btest, Node *c) { } else { // Path is live. adjust_map_after_if(BoolTest(btest).negate(), c, 1.0-prob, next_block); } + + if (do_stress_trap) { + stress_trap(iff, counter, incr_store); + } } //------------------------------------do_if------------------------------------ @@ -1468,6 +1489,13 @@ void Parse::do_if(BoolTest::mask btest, Node* c) { return; } + Node* counter = nullptr; + Node* incr_store = nullptr; + bool do_stress_trap = StressUnstableIfTraps && ((C->random() % 2) == 0); + if (do_stress_trap) { + increment_trap_stress_counter(counter, incr_store); + } + // Sanity check the probability value assert(0.0f < prob && prob < 1.0f,"Bad probability in Parser"); @@ -1550,9 +1578,58 @@ void Parse::do_if(BoolTest::mask btest, Node* c) { } else { adjust_map_after_if(untaken_btest, c, untaken_prob, next_block); } + + if (do_stress_trap) { + stress_trap(iff, counter, incr_store); + } +} + +// Force unstable if traps to be taken randomly to trigger intermittent bugs such as incorrect debug information. +// Add another if before the unstable if that checks a "random" condition at runtime (a simple shared counter) and +// then either takes the trap or executes the original, unstable if. +void Parse::stress_trap(IfNode* orig_iff, Node* counter, Node* incr_store) { + // Search for an unstable if trap + CallStaticJavaNode* trap = nullptr; + assert(orig_iff->Opcode() == Op_If && orig_iff->outcnt() == 2, "malformed if"); + ProjNode* trap_proj = orig_iff->uncommon_trap_proj(trap, Deoptimization::Reason_unstable_if); + if (trap == nullptr || !trap->jvms()->should_reexecute()) { + // No suitable trap found. Remove unused counter load and increment. + C->gvn_replace_by(incr_store, incr_store->in(MemNode::Memory)); + return; + } + + // Remove trap from optimization list since we add another path to the trap. + bool success = C->remove_unstable_if_trap(trap, true); + assert(success, "Trap already modified"); + + // Add a check before the original if that will trap with a certain frequency and execute the original if otherwise + int freq_log = (C->random() % 31) + 1; // Random logarithmic frequency in [1, 31] + Node* mask = intcon(right_n_bits(freq_log)); + counter = _gvn.transform(new AndINode(counter, mask)); + Node* cmp = _gvn.transform(new CmpINode(counter, intcon(0))); + Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::mask::eq)); + IfNode* iff = _gvn.transform(new IfNode(orig_iff->in(0), bol, orig_iff->_prob, orig_iff->_fcnt))->as_If(); + Node* if_true = _gvn.transform(new IfTrueNode(iff)); + Node* if_false = _gvn.transform(new IfFalseNode(iff)); + assert(!if_true->is_top() && !if_false->is_top(), "trap always / never taken"); + + // Trap + assert(trap_proj->outcnt() == 1, "some other nodes are dependent on the trap projection"); + + Node* trap_region = new RegionNode(3); + trap_region->set_req(1, trap_proj); + trap_region->set_req(2, if_true); + trap->set_req(0, _gvn.transform(trap_region)); + + // Don't trap, execute original if + orig_iff->set_req(0, if_false); } bool Parse::path_is_suitable_for_uncommon_trap(float prob) const { + // Randomly skip emitting an uncommon trap + if (StressUnstableIfTraps && ((C->random() % 2) == 0)) { + return false; + } // Don't want to speculate on uncommon traps when running with -Xcomp if (!UseInterpreter) { return false; diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 3887e8a5f6cdf..18eea3a10bcc6 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -73,14 +73,6 @@ ParsePredicateNode* ParsePredicate::init_parse_predicate(Node* parse_predicate_p return nullptr; } -bool ParsePredicate::is_predicate(Node* maybe_success_proj) { - if (!maybe_success_proj->is_IfProj()) { - return false; - } - IfNode* if_node = maybe_success_proj->in(0)->as_If(); - return if_node->is_ParsePredicate(); -} - Deoptimization::DeoptReason RegularPredicateWithUCT::uncommon_trap_reason(IfProjNode* if_proj) { CallStaticJavaNode* uct_call = if_proj->is_uncommon_trap_if_pattern(); if (uct_call == nullptr) { @@ -90,27 +82,31 @@ Deoptimization::DeoptReason RegularPredicateWithUCT::uncommon_trap_reason(IfProj } bool RegularPredicateWithUCT::is_predicate(Node* maybe_success_proj) { - if (may_be_predicate_if(maybe_success_proj)) { - IfProjNode* success_proj = maybe_success_proj->as_IfProj(); - const Deoptimization::DeoptReason deopt_reason = uncommon_trap_reason(success_proj); - return (deopt_reason == Deoptimization::Reason_loop_limit_check || - deopt_reason == Deoptimization::Reason_predicate || - deopt_reason == Deoptimization::Reason_profile_predicate); + if (RegularPredicate::may_be_predicate_if(maybe_success_proj)) { + return has_valid_uncommon_trap(maybe_success_proj); } else { return false; } } -bool RegularPredicateWithUCT::is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason) { - if (may_be_predicate_if(node)) { +bool RegularPredicateWithUCT::has_valid_uncommon_trap(const Node* success_proj) { + assert(RegularPredicate::may_be_predicate_if(success_proj), "must have been checked before"); + const Deoptimization::DeoptReason deopt_reason = uncommon_trap_reason(success_proj->as_IfProj()); + return (deopt_reason == Deoptimization::Reason_loop_limit_check || + deopt_reason == Deoptimization::Reason_predicate || + deopt_reason == Deoptimization::Reason_profile_predicate); +} + +bool RegularPredicateWithUCT::is_predicate(const Node* node, Deoptimization::DeoptReason deopt_reason) { + if (RegularPredicate::may_be_predicate_if(node)) { return deopt_reason == uncommon_trap_reason(node->as_IfProj()); } else { return false; } } -// A Runtime Predicate must have an If or a RangeCheck node, while the If should not be a zero trip guard check. -bool RegularPredicateWithUCT::may_be_predicate_if(Node* node) { +// A Regular Predicate must have an If or a RangeCheck node, while the If should not be a zero trip guard check. +bool RegularPredicate::may_be_predicate_if(const Node* node) { if (node->is_IfProj()) { const IfNode* if_node = node->in(0)->as_If(); const int opcode_if = if_node->Opcode(); @@ -122,39 +118,43 @@ bool RegularPredicateWithUCT::may_be_predicate_if(Node* node) { return false; } -bool RuntimePredicate::is_success_proj(Node* node, Deoptimization::DeoptReason deopt_reason) { +// Runtime Predicates always have an UCT since they could normally fail at runtime. In this case we execute the trap +// on the failing path. +bool RuntimePredicate::is_predicate(Node* node) { + return RegularPredicateWithUCT::is_predicate(node); +} + +bool RuntimePredicate::is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason) { return RegularPredicateWithUCT::is_predicate(node, deopt_reason); } -ParsePredicateIterator::ParsePredicateIterator(const Predicates& predicates) : _current_index(0) { - const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); - if (loop_limit_check_predicate_block->has_parse_predicate()) { - _parse_predicates.push(loop_limit_check_predicate_block->parse_predicate()); - } - if (UseProfiledLoopPredicate) { - const PredicateBlock* profiled_loop_predicate_block = predicates.profiled_loop_predicate_block(); - if (profiled_loop_predicate_block->has_parse_predicate()) { - _parse_predicates.push(profiled_loop_predicate_block->parse_predicate()); - } +// A Template Assertion Predicate has an If/RangeCheckNode and either an UCT or a halt node depending on where it +// was created. +bool TemplateAssertionPredicate::is_predicate(Node* node) { + if (!RegularPredicate::may_be_predicate_if(node)) { + return false; } - if (UseLoopPredicate) { - const PredicateBlock* loop_predicate_block = predicates.loop_predicate_block(); - if (loop_predicate_block->has_parse_predicate()) { - _parse_predicates.push(loop_predicate_block->parse_predicate()); - } + IfNode* if_node = node->in(0)->as_If(); + if (if_node->in(1)->is_Opaque4()) { + return RegularPredicateWithUCT::has_valid_uncommon_trap(node) || AssertionPredicateWithHalt::has_halt(node); } + return false; } -ParsePredicateNode* ParsePredicateIterator::next() { - assert(has_next(), "always check has_next() first"); - return _parse_predicates.at(_current_index++); +// Initialized Assertion Predicates always have the dedicated opaque node and a halt node. +bool InitializedAssertionPredicate::is_predicate(Node* node) { + if (!AssertionPredicateWithHalt::is_predicate(node)) { + return false; + } + IfNode* if_node = node->in(0)->as_If(); + return if_node->in(1)->is_OpaqueInitializedAssertionPredicate(); } #ifdef ASSERT // Check that the block has at most one Parse Predicate and that we only find Regular Predicate nodes (i.e. IfProj, // If, or RangeCheck nodes). -void PredicateBlock::verify_block() { - Node* next = _parse_predicate.entry(); // Skip unique Parse Predicate of this block if present +void RegularPredicateBlock::verify_block(Node* tail) { + Node* next = tail; while (next != _entry) { assert(!next->is_ParsePredicate(), "can only have one Parse Predicate in a block"); const int opcode = next->Opcode(); @@ -166,17 +166,6 @@ void PredicateBlock::verify_block() { } #endif // ASSERT -// Walk over all Regular Predicates of this block (if any) and return the first node not belonging to the block -// anymore (i.e. entry to the first Regular Predicate in this block if any or `regular_predicate_proj` otherwise). -Node* PredicateBlock::skip_regular_predicates(Node* regular_predicate_proj, Deoptimization::DeoptReason deopt_reason) { - Node* entry = regular_predicate_proj; - while (RuntimePredicate::is_success_proj(entry, deopt_reason)) { - assert(entry->in(0)->as_If(), "must be If node"); - entry = entry->in(0)->in(0); - } - return entry; -} - // This strategy clones the OpaqueLoopInit and OpaqueLoopStride nodes. class CloneStrategy : public TransformStrategyForOpaqueLoopNodes { PhaseIdealLoop* const _phase; @@ -381,8 +370,8 @@ bool TemplateAssertionExpressionNode::is_template_assertion_predicate(Node* node return node->is_If() && node->in(1)->is_Opaque4(); } -InitializedAssertionPredicate::InitializedAssertionPredicate(IfNode* template_assertion_predicate, Node* new_init, - Node* new_stride, PhaseIdealLoop* phase) +InitializedAssertionPredicateCreator::InitializedAssertionPredicateCreator(IfNode* template_assertion_predicate, Node* new_init, + Node* new_stride, PhaseIdealLoop* phase) : _template_assertion_predicate(template_assertion_predicate), _new_init(new_init), _new_stride(new_stride), @@ -408,7 +397,7 @@ InitializedAssertionPredicate::InitializedAssertionPredicate(IfNode* template_as // success fail path new success new Halt // proj (Halt or UCT) proj // -IfTrueNode* InitializedAssertionPredicate::create(Node* control) { +IfTrueNode* InitializedAssertionPredicateCreator::create(Node* control) { IdealLoopTree* loop = _phase->get_loop(control); OpaqueInitializedAssertionPredicateNode* assertion_expression = create_assertion_expression(control); IfNode* if_node = create_if_node(control, assertion_expression, loop); @@ -417,7 +406,7 @@ IfTrueNode* InitializedAssertionPredicate::create(Node* control) { } // Create a new Assertion Expression to be used as bool input for the Initialized Assertion Predicate IfNode. -OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicate::create_assertion_expression(Node* control) { +OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicateCreator::create_assertion_expression(Node* control) { Opaque4Node* template_opaque = _template_assertion_predicate->in(1)->as_Opaque4(); TemplateAssertionExpression template_assertion_expression(template_opaque); Opaque4Node* tmp_opaque = template_assertion_expression.clone_and_replace_init_and_stride(_new_init, _new_stride, @@ -428,9 +417,9 @@ OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicate::create_a return assertion_expression; } -IfNode* InitializedAssertionPredicate::create_if_node(Node* control, - OpaqueInitializedAssertionPredicateNode* assertion_expression, - IdealLoopTree* loop) { +IfNode* InitializedAssertionPredicateCreator::create_if_node(Node* control, + OpaqueInitializedAssertionPredicateNode* assertion_expression, + IdealLoopTree* loop) { const int if_opcode = _template_assertion_predicate->Opcode(); NOT_PRODUCT(const AssertionPredicateType assertion_predicate_type = _template_assertion_predicate->assertion_predicate_type();) IfNode* if_node = if_opcode == Op_If ? @@ -440,19 +429,19 @@ IfNode* InitializedAssertionPredicate::create_if_node(Node* control, return if_node; } -IfTrueNode* InitializedAssertionPredicate::create_success_path(IfNode* if_node, IdealLoopTree* loop) { +IfTrueNode* InitializedAssertionPredicateCreator::create_success_path(IfNode* if_node, IdealLoopTree* loop) { IfTrueNode* success_proj = new IfTrueNode(if_node); _phase->register_control(success_proj, loop, if_node); return success_proj; } -void InitializedAssertionPredicate::create_fail_path(IfNode* if_node, IdealLoopTree* loop) { +void InitializedAssertionPredicateCreator::create_fail_path(IfNode* if_node, IdealLoopTree* loop) { IfFalseNode* fail_proj = new IfFalseNode(if_node); _phase->register_control(fail_proj, loop, if_node); create_halt_node(fail_proj, loop); } -void InitializedAssertionPredicate::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop) { +void InitializedAssertionPredicateCreator::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop) { StartNode* start_node = _phase->C->start(); Node* frame = new ParmNode(start_node, TypeFunc::FramePtr); _phase->register_new_node(frame, start_node); @@ -461,17 +450,45 @@ void InitializedAssertionPredicate::create_halt_node(IfFalseNode* fail_proj, Ide _phase->register_control(halt, loop, fail_proj); } -// Is current node pointed to by iterator a predicate? -bool PredicateEntryIterator::has_next() const { - return ParsePredicate::is_predicate(_current) || - RegularPredicateWithUCT::is_predicate(_current) || - AssertionPredicateWithHalt::is_predicate(_current); +#ifndef PRODUCT +void PredicateBlock::dump() const { + dump(""); +} + +void PredicateBlock::dump(const char* prefix) const { + if (is_non_empty()) { + PredicatePrinter printer(prefix); + PredicateBlockIterator iterator(_tail, _deopt_reason); + iterator.for_each(printer); + } else { + tty->print_cr("%s- ", prefix); + } +} + +// Dumps all predicates from the loop to the earliest predicate in a pretty format. +void Predicates::dump() const { + if (has_any()) { + Node* loop_head = _tail->unique_ctrl_out(); + tty->print_cr("%d %s:", loop_head->_idx, loop_head->Name()); + tty->print_cr("- Loop Limit Check Predicate Block:"); + _loop_limit_check_predicate_block.dump(" "); + tty->print_cr("- Profiled Loop Predicate Block:"); + _profiled_loop_predicate_block.dump(" "); + tty->print_cr("- Loop Predicate Block:"); + _loop_predicate_block.dump(" "); + tty->cr(); + } else { + tty->print_cr(""); + } +} + +void Predicates::dump_at(Node* node) { + Predicates predicates(node); + predicates.dump(); } -// Skip the current predicate pointed to by iterator by returning the input into the predicate. This could possibly be -// a non-predicate node. -Node* PredicateEntryIterator::next_entry() { - assert(has_next(), "current must be predicate"); - _current = _current->in(0)->in(0); - return _current; +// Debug method to dump all predicates that are found above 'loop_node'. +void Predicates::dump_for_loop(LoopNode* loop_node) { + dump_at(loop_node->skip_strip_mined()->in(LoopNode::EntryControl)); } +#endif // NOT PRODUCT diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index 96f5c438b802f..b38b888cc3dba 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -30,6 +30,11 @@ #include "opto/opaquenode.hpp" class IdealLoopTree; +class InitializedAssertionPredicate; +class ParsePredicate; +class PredicateVisitor; +class RuntimePredicate; +class TemplateAssertionPredicate; /* * There are different kinds of predicates throughout the code. We differentiate between the following predicates: @@ -152,7 +157,8 @@ class IdealLoopTree; * together. * - Loop Limit Check Groups the Loop Limit Check Predicate (if created) and the Loop Limit * Predicate Block: Check Parse Predicate (if not removed, yet) together. - * + * - Regular Predicate Block: A block that only contains the Regular Predicates of a Predicate Block without the + * Parse Predicate. * * Initially, before applying any loop-splitting optimizations, we find the following structure after Loop Predication * (predicates inside square brackets [] do not need to exist if there are no checks to hoist): @@ -205,6 +211,41 @@ enum class AssertionPredicateType { }; #endif // NOT PRODUCT +// Interface to represent a C2 predicate. A predicate is always represented by two CFG nodes: +// - An If node (head) +// - An IfProj node representing the success projection of the If node (tail). +class Predicate : public StackObj { + public: + // Return the unique entry CFG node into the predicate. + virtual Node* entry() const = 0; + + // Return the head node of the predicate which is either: + // - A ParsePredicateNode if the predicate is a Parse Predicate + // - An IfNode or RangeCheckNode, otherwise. + virtual IfNode* head() const = 0; + + // Return the tail node of the predicate. Runtime Predicates can either have a true of false projection as success + // projection while Parse Predicates and Assertion Predicates always have a true projection as success projection. + virtual IfProjNode* tail() const = 0; +}; + +// Generic predicate visitor that does nothing. Subclass this visitor to add customized actions for each predicate. +// The visit methods of this visitor are called from the predicate iterator classes which walk the predicate chain. +// Use the UnifiedPredicateVisitor if the type of the predicate does not matter. +class PredicateVisitor : StackObj { + public: + virtual void visit(const ParsePredicate& parse_predicate) {} + virtual void visit(const RuntimePredicate& runtime_predicate) {} + virtual void visit(const TemplateAssertionPredicate& template_assertion_predicate) {} + virtual void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) {} + + // This method can be overridden to stop the predicate iterators from visiting more predicates further up in the + // predicate chain. + virtual bool should_continue() const { + return true; + } +}; + // Class to represent Assertion Predicates with a HaltNode instead of an UCT (i.e. either an Initialized Assertion // Predicate or a Template Assertion Predicate created after the initial one at Loop Predication). class AssertionPredicatesWithHalt : public StackObj { @@ -228,9 +269,15 @@ class AssertionPredicatesWithHalt : public StackObj { // Note that all other Regular Predicates have an UCT node. class AssertionPredicateWithHalt : public StackObj { static bool has_assertion_predicate_opaque(const Node* predicate_proj); - static bool has_halt(const Node* success_proj); public: static bool is_predicate(const Node* maybe_success_proj); + static bool has_halt(const Node* success_proj); +}; + +// Utility class representing a Regular Predicate which is either a Runtime Predicate or an Assertion Predicate. +class RegularPredicate : public StackObj { + public: + static bool may_be_predicate_if(const Node* node); }; // Class to represent a single Regular Predicate with an UCT. This could either be: @@ -239,15 +286,15 @@ class AssertionPredicateWithHalt : public StackObj { // Note that all other Regular Predicates have a Halt node. class RegularPredicateWithUCT : public StackObj { static Deoptimization::DeoptReason uncommon_trap_reason(IfProjNode* if_proj); - static bool may_be_predicate_if(Node* node); public: static bool is_predicate(Node* maybe_success_proj); - static bool is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason); + static bool is_predicate(const Node* node, Deoptimization::DeoptReason deopt_reason); + static bool has_valid_uncommon_trap(const Node* success_proj); }; // Class to represent a Parse Predicate. -class ParsePredicate : public StackObj { +class ParsePredicate : public Predicate { ParsePredicateSuccessProj* _success_proj; ParsePredicateNode* _parse_predicate_node; Node* _entry; @@ -267,7 +314,7 @@ class ParsePredicate : public StackObj { // Returns the control input node into this Parse Predicate if it is valid. Otherwise, it returns the passed node // into the constructor of this class. - Node* entry() const { + Node* entry() const override { return _entry; } @@ -277,23 +324,102 @@ class ParsePredicate : public StackObj { return _parse_predicate_node != nullptr; } - ParsePredicateNode* node() const { + ParsePredicateNode* head() const override { assert(is_valid(), "must be valid"); return _parse_predicate_node; } - ParsePredicateSuccessProj* success_proj() const { + ParsePredicateSuccessProj* tail() const override { assert(is_valid(), "must be valid"); return _success_proj; } +}; + +// Class to represent a Runtime Predicate which always has an associated UCT on the failing path. +class RuntimePredicate : public Predicate { + IfProjNode* _success_proj; + IfNode* _if_node; + + public: + explicit RuntimePredicate(IfProjNode* success_proj) + : _success_proj(success_proj), + _if_node(success_proj->in(0)->as_If()) { + assert(is_predicate(success_proj), "must be valid"); + } + NONCOPYABLE(RuntimePredicate); + private: static bool is_predicate(Node* maybe_success_proj); + + public: + Node* entry() const override { + return _if_node->in(0); + } + + IfNode* head() const override { + return _if_node; + } + + IfProjNode* tail() const override { + return _success_proj; + } + + static bool is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason); }; -// Utility class for queries on Runtime Predicates. -class RuntimePredicate : public StackObj { +// Class to represent a Template Assertion Predicate. +class TemplateAssertionPredicate : public Predicate { + IfTrueNode* _success_proj; + IfNode* _if_node; + public: - static bool is_success_proj(Node* node, Deoptimization::DeoptReason deopt_reason); + explicit TemplateAssertionPredicate(IfTrueNode* success_proj) + : _success_proj(success_proj), + _if_node(success_proj->in(0)->as_If()) { + assert(is_predicate(success_proj), "must be valid"); + } + + Node* entry() const override { + return _if_node->in(0); + } + + IfNode* head() const override { + return _if_node; + } + + IfTrueNode* tail() const override { + return _success_proj; + } + + static bool is_predicate(Node* node); +}; + +// Class to represent an Initialized Assertion Predicate which always has a halt node on the failing path. +// This predicate should never fail at runtime by design. +class InitializedAssertionPredicate : public Predicate { + IfTrueNode* _success_proj; + IfNode* _if_node; + + public: + explicit InitializedAssertionPredicate(IfTrueNode* success_proj) + : _success_proj(success_proj), + _if_node(success_proj->in(0)->as_If()) { + assert(is_predicate(success_proj), "must be valid"); + } + + Node* entry() const override { + return _if_node->in(0); + } + + IfNode* head() const override { + return _if_node; + } + + IfTrueNode* tail() const override { + return _success_proj; + } + + static bool is_predicate(Node* node); }; // Interface to transform OpaqueLoopInit and OpaqueLoopStride nodes of a Template Assertion Expression. @@ -395,16 +521,16 @@ class TemplateAssertionExpressionNode : public StackObj { }; // This class creates a new Initialized Assertion Predicate. -class InitializedAssertionPredicate : public StackObj { +class InitializedAssertionPredicateCreator : public StackObj { IfNode* const _template_assertion_predicate; Node* const _new_init; Node* const _new_stride; PhaseIdealLoop* const _phase; public: - InitializedAssertionPredicate(IfNode* template_assertion_predicate, Node* new_init, Node* new_stride, - PhaseIdealLoop* phase); - NONCOPYABLE(InitializedAssertionPredicate); + InitializedAssertionPredicateCreator(IfNode* template_assertion_predicate, Node* new_init, Node* new_stride, + PhaseIdealLoop* phase); + NONCOPYABLE(InitializedAssertionPredicateCreator); IfTrueNode* create(Node* control); @@ -416,23 +542,208 @@ class InitializedAssertionPredicate : public StackObj { IfTrueNode* create_success_path(IfNode* if_node, IdealLoopTree* loop); }; +// This class iterates through all predicates of a Regular Predicate Block and applies the given visitor to each. +class RegularPredicateBlockIterator : public StackObj { + Node* const _start_node; + const Deoptimization::DeoptReason _deopt_reason; + + public: + RegularPredicateBlockIterator(Node* start_node, Deoptimization::DeoptReason deopt_reason) + : _start_node(start_node), + _deopt_reason(deopt_reason) {} + NONCOPYABLE(RegularPredicateBlockIterator); + + // Skip all predicates by just following the inputs. We do not call any user provided visitor. + Node* skip_all() const { + PredicateVisitor do_nothing; // No real visits, just do nothing. + return for_each(do_nothing); + } + + // Walk over all predicates of this block (if any) and apply the given 'predicate_visitor' to each predicate. + // Returns the entry to the earliest predicate. + Node* for_each(PredicateVisitor& predicate_visitor) const { + Node* current = _start_node; + while (predicate_visitor.should_continue()) { + if (TemplateAssertionPredicate::is_predicate(current)) { + TemplateAssertionPredicate template_assertion_predicate(current->as_IfTrue()); + predicate_visitor.visit(template_assertion_predicate); + current = template_assertion_predicate.entry(); + } else if (RuntimePredicate::is_predicate(current, _deopt_reason)) { + RuntimePredicate runtime_predicate(current->as_IfProj()); + predicate_visitor.visit(runtime_predicate); + current = runtime_predicate.entry(); + } else if (InitializedAssertionPredicate::is_predicate(current)) { + InitializedAssertionPredicate initialized_assertion_predicate(current->as_IfTrue()); + predicate_visitor.visit(initialized_assertion_predicate); + current = initialized_assertion_predicate.entry(); + } else { + // Either a Parse Predicate or not a Regular Predicate. In both cases, the node does not belong to this block. + break; + } + } + return current; + } +}; + +// This class iterates through all predicates of a Predicate Block and applies the given visitor to each. +class PredicateBlockIterator : public StackObj { + Node* const _start_node; + const ParsePredicate _parse_predicate; // Could be missing. + const RegularPredicateBlockIterator _regular_predicate_block_iterator; + + public: + PredicateBlockIterator(Node* start_node, Deoptimization::DeoptReason deopt_reason) + : _start_node(start_node), + _parse_predicate(start_node, deopt_reason), + _regular_predicate_block_iterator(_parse_predicate.entry(), deopt_reason) {} + + // Walk over all predicates of this block (if any) and apply the given 'predicate_visitor' to each predicate. + // Returns the entry to the earliest predicate. + Node* for_each(PredicateVisitor& predicate_visitor) const { + if (!predicate_visitor.should_continue()) { + return _start_node; + } + if (_parse_predicate.is_valid()) { + predicate_visitor.visit(_parse_predicate); + } + return _regular_predicate_block_iterator.for_each(predicate_visitor); + } +}; + +// Class to walk over all predicates starting at a node, which usually is the loop entry node, and following the inputs. +// At each predicate, a PredicateVisitor is applied which the user can implement freely. +class PredicateIterator : public StackObj { + Node* _start_node; + + public: + explicit PredicateIterator(Node* start_node) + : _start_node(start_node) {} + NONCOPYABLE(PredicateIterator); + + // Apply the 'predicate_visitor' for each predicate found in the predicate chain started at the provided node. + // Returns the entry to the earliest predicate. + Node* for_each(PredicateVisitor& predicate_visitor) const { + Node* current = _start_node; + PredicateBlockIterator loop_limit_check_predicate_iterator(current, Deoptimization::Reason_loop_limit_check); + current = loop_limit_check_predicate_iterator.for_each(predicate_visitor); + PredicateBlockIterator profiled_loop_predicate_iterator(current, Deoptimization::Reason_profile_predicate); + current = profiled_loop_predicate_iterator.for_each(predicate_visitor); + PredicateBlockIterator loop_predicate_iterator(current, Deoptimization::Reason_predicate); + return loop_predicate_iterator.for_each(predicate_visitor); + } +}; + +// Unified PredicateVisitor which only provides a single visit method for a generic Predicate. This visitor can be used +// when it does not matter what kind of predicate is visited. Note that we override all normal visit methods from +// PredicateVisitor by calling the unified method. These visit methods are marked final such that they cannot be +// overridden by implementors of this class. +class UnifiedPredicateVisitor : public PredicateVisitor { + public: + virtual void visit(const TemplateAssertionPredicate& template_assertion_predicate) override final { + visit_predicate(template_assertion_predicate); + } + + virtual void visit(const ParsePredicate& parse_predicate) override final { + visit_predicate(parse_predicate); + } + + virtual void visit(const RuntimePredicate& runtime_predicate) override final { + visit_predicate(runtime_predicate); + } + + virtual void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) override final { + visit_predicate(initialized_assertion_predicate); + } + + virtual void visit_predicate(const Predicate& predicate) = 0; +}; + +// A block of Regular Predicates inside a Predicate Block without its Parse Predicate. +class RegularPredicateBlock : public StackObj { + const Deoptimization::DeoptReason _deopt_reason; + Node* const _entry; + + public: + RegularPredicateBlock(Node* tail, Deoptimization::DeoptReason deopt_reason) + : _deopt_reason(deopt_reason), + _entry(skip_all(tail)) { + DEBUG_ONLY(verify_block(tail);) + } + NONCOPYABLE(RegularPredicateBlock); + + private: + // Walk over all Regular Predicates of this block (if any) and return the first node not belonging to the block + // anymore (i.e. entry to the first Regular Predicate in this block if any or `tail` otherwise). + Node* skip_all(Node* tail) const { + RegularPredicateBlockIterator iterator(tail, _deopt_reason); + return iterator.skip_all(); + } + + DEBUG_ONLY(void verify_block(Node* tail);) + + public: + Node* entry() const { + return _entry; + } +}; + +#ifndef PRODUCT +// Visitor class to print all the visited predicates. Used by the Predicates class which does the printing starting +// at the loop node and then following the inputs to the earliest predicate. +class PredicatePrinter : public PredicateVisitor { + const char* _prefix; // Prefix added to each dumped string. + + public: + explicit PredicatePrinter(const char* prefix) : _prefix(prefix) {} + NONCOPYABLE(PredicatePrinter); + + void visit(const ParsePredicate& parse_predicate) override { + print_predicate_node("Parse Predicate", parse_predicate); + } + + void visit(const RuntimePredicate& runtime_predicate) override { + print_predicate_node("Runtime Predicate", runtime_predicate); + } + + void visit(const TemplateAssertionPredicate& template_assertion_predicate) override { + print_predicate_node("Template Assertion Predicate", template_assertion_predicate); + } + + void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) override { + print_predicate_node("Initialized Assertion Predicate", initialized_assertion_predicate); + } + + private: + void print_predicate_node(const char* predicate_name, const Predicate& predicate) const { + tty->print_cr("%s- %s: %d %s", _prefix, predicate_name, predicate.head()->_idx, predicate.head()->Name()); + } +}; +#endif // NOT PRODUCT // This class represents a Predicate Block (i.e. either a Loop Predicate Block, a Profiled Loop Predicate Block, // or a Loop Limit Check Predicate Block). It contains zero or more Regular Predicates followed by a Parse Predicate // which, however, does not need to exist (we could already have decided to remove Parse Predicates for this loop). class PredicateBlock : public StackObj { - ParsePredicate _parse_predicate; // Could be missing. - Node* _entry; - - static Node* skip_regular_predicates(Node* regular_predicate_proj, Deoptimization::DeoptReason deopt_reason); - DEBUG_ONLY(void verify_block();) + const ParsePredicate _parse_predicate; // Could be missing. + const RegularPredicateBlock _regular_predicate_block; + Node* const _entry; +#ifndef PRODUCT + // Used for dumping. + Node* const _tail; + const Deoptimization::DeoptReason _deopt_reason; +#endif // NOT PRODUCT public: - PredicateBlock(Node* predicate_proj, Deoptimization::DeoptReason deopt_reason) - : _parse_predicate(predicate_proj, deopt_reason), - _entry(skip_regular_predicates(_parse_predicate.entry(), deopt_reason)) { - DEBUG_ONLY(verify_block();) - } + PredicateBlock(Node* tail, Deoptimization::DeoptReason deopt_reason) + : _parse_predicate(tail, deopt_reason), + _regular_predicate_block(_parse_predicate.entry(), deopt_reason), + _entry(_regular_predicate_block.entry()) +#ifndef PRODUCT + , _tail(tail) + , _deopt_reason(deopt_reason) +#endif // NOT PRODUCT + {} + NONCOPYABLE(PredicateBlock); // Returns the control input node into this Regular Predicate block. This is either: // - The control input to the first If node in the block representing a Runtime Predicate if there is at least one @@ -453,11 +764,11 @@ class PredicateBlock : public StackObj { } ParsePredicateNode* parse_predicate() const { - return _parse_predicate.node(); + return _parse_predicate.head(); } ParsePredicateSuccessProj* parse_predicate_success_proj() const { - return _parse_predicate.success_proj(); + return _parse_predicate.tail(); } bool has_runtime_predicates() const { @@ -471,25 +782,31 @@ class PredicateBlock : public StackObj { Node* skip_parse_predicate() const { return _parse_predicate.entry(); } + +#ifndef PRODUCT + void dump() const; + void dump(const char* prefix) const; +#endif // NOT PRODUCT }; // This class takes a loop entry node and finds all the available predicates for the loop. class Predicates : public StackObj { - Node* _loop_entry; - PredicateBlock _loop_limit_check_predicate_block; - PredicateBlock _profiled_loop_predicate_block; - PredicateBlock _loop_predicate_block; - Node* _entry; + Node* const _tail; + const PredicateBlock _loop_limit_check_predicate_block; + const PredicateBlock _profiled_loop_predicate_block; + const PredicateBlock _loop_predicate_block; + Node* const _entry; public: - Predicates(Node* loop_entry) - : _loop_entry(loop_entry), + explicit Predicates(Node* loop_entry) + : _tail(loop_entry), _loop_limit_check_predicate_block(loop_entry, Deoptimization::Reason_loop_limit_check), _profiled_loop_predicate_block(_loop_limit_check_predicate_block.entry(), Deoptimization::Reason_profile_predicate), _loop_predicate_block(_profiled_loop_predicate_block.entry(), Deoptimization::Reason_predicate), _entry(_loop_predicate_block.entry()) {} + NONCOPYABLE(Predicates); // Returns the control input the first predicate if there are any predicates. If there are no predicates, the same // node initially passed to the constructor is returned. @@ -510,35 +827,17 @@ class Predicates : public StackObj { } bool has_any() const { - return _entry != _loop_entry; + return _entry != _tail; } -}; - -// This class iterates over the Parse Predicates of a loop. -class ParsePredicateIterator : public StackObj { - GrowableArray _parse_predicates; - int _current_index; - - public: - ParsePredicateIterator(const Predicates& predicates); - bool has_next() const { - return _current_index < _parse_predicates.length(); - } - - ParsePredicateNode* next(); +#ifndef PRODUCT + /* + * Debug printing functions. + */ + void dump() const; + static void dump_at(Node* node); + static void dump_for_loop(LoopNode* loop_node); +#endif // NOT PRODUCT }; -// Special predicate iterator that can be used to walk through predicate entries, regardless of whether the predicate -// belongs to the same loop or not (i.e. leftovers from already folded nodes). The iterator returns the next entry -// to a predicate. -class PredicateEntryIterator : public StackObj { - Node* _current; - - public: - explicit PredicateEntryIterator(Node* start) : _current(start) {}; - - bool has_next() const; - Node* next_entry(); -}; #endif // SHARE_OPTO_PREDICATES_HPP diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp index 9f89c683b34b5..6d948aff011cf 100644 --- a/src/hotspot/share/opto/reg_split.cpp +++ b/src/hotspot/share/opto/reg_split.cpp @@ -306,8 +306,8 @@ static Node* clone_node(Node* def, Block *b, Compile* C) { C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { // Bailout without retry - assert(false, "RA Split failed: attempt to clone node with anti_dependence"); - C->record_method_not_compilable("RA Split failed: attempt to clone node with anti_dependence"); + assert(C->failure_is_artificial(), "RA Split failed: attempt to clone node with anti_dependence"); + C->record_method_not_compilable("RA Split failed: attempt to clone node with anti_dependence" DEBUG_ONLY(COMMA true)); } return nullptr; } diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index bb5fed78b0274..20c8dfbff1776 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -657,9 +657,6 @@ void VLoopMemorySlices::get_slice_in_reverse_order(PhiNode* head, MemNode* tail, // or need to run igvn.optimize() again before SLP } else if (out->is_memory_phi() && !_vloop.in_bb(out)) { // Ditto. Not sure what else to check further. - } else if (out->Opcode() == Op_StoreCM && out->in(MemNode::OopStore) == n) { - // StoreCM has an input edge used as a precedence edge. - // Maybe an issue when oop stores are vectorized. } else { assert(out == prev || prev == nullptr, "no branches off of store slice"); } diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.cpp b/src/hotspot/share/opto/superwordVTransformBuilder.cpp index b0a0c97cb1676..6c2d3a3be35c3 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.cpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.cpp @@ -228,8 +228,8 @@ VTransformNode* SuperWordVTransformBuilder::get_or_make_vtnode_vector_input_at_i return shift_count; } else { // Replicate the scalar same_input to every vector element. - const Type* element_type = _vloop_analyzer.types().velt_type(p0); - if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_type->isa_long()) { + BasicType element_type = _vloop_analyzer.types().velt_basic_type(p0); + if (index == 2 && VectorNode::is_scalar_rotate(p0) && element_type == T_LONG) { // Scalar rotate has int rotation value, but the scalar rotate expects longs. assert(same_input->bottom_type()->isa_int(), "scalar rotate expects int rotation"); VTransformNode* conv = new (_vtransform.arena()) VTransformConvI2LNode(_vtransform); diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 967b4a815d09e..70cd46c900dc2 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -679,7 +679,7 @@ void Type::Initialize_shared(Compile* current) { // get_zero_type() should not happen for T_CONFLICT _zero_type[T_CONFLICT]= nullptr; - TypeVect::VECTMASK = (TypeVect*)(new TypeVectMask(TypeInt::BOOL, MaxVectorSize))->hashcons(); + TypeVect::VECTMASK = (TypeVect*)(new TypeVectMask(T_BOOLEAN, MaxVectorSize))->hashcons(); mreg2type[Op_RegVectMask] = TypeVect::VECTMASK; if (Matcher::supports_scalable_vector()) { @@ -687,20 +687,20 @@ void Type::Initialize_shared(Compile* current) { } // Vector predefined types, it needs initialized _const_basic_type[]. - if (Matcher::vector_size_supported(T_BYTE,4)) { - TypeVect::VECTS = TypeVect::make(T_BYTE,4); + if (Matcher::vector_size_supported(T_BYTE, 4)) { + TypeVect::VECTS = TypeVect::make(T_BYTE, 4); } - if (Matcher::vector_size_supported(T_FLOAT,2)) { - TypeVect::VECTD = TypeVect::make(T_FLOAT,2); + if (Matcher::vector_size_supported(T_FLOAT, 2)) { + TypeVect::VECTD = TypeVect::make(T_FLOAT, 2); } - if (Matcher::vector_size_supported(T_FLOAT,4)) { - TypeVect::VECTX = TypeVect::make(T_FLOAT,4); + if (Matcher::vector_size_supported(T_FLOAT, 4)) { + TypeVect::VECTX = TypeVect::make(T_FLOAT, 4); } - if (Matcher::vector_size_supported(T_FLOAT,8)) { - TypeVect::VECTY = TypeVect::make(T_FLOAT,8); + if (Matcher::vector_size_supported(T_FLOAT, 8)) { + TypeVect::VECTY = TypeVect::make(T_FLOAT, 8); } - if (Matcher::vector_size_supported(T_FLOAT,16)) { - TypeVect::VECTZ = TypeVect::make(T_FLOAT,16); + if (Matcher::vector_size_supported(T_FLOAT, 16)) { + TypeVect::VECTZ = TypeVect::make(T_FLOAT, 16); } mreg2type[Op_VecA] = TypeVect::VECTA; @@ -2482,58 +2482,59 @@ bool TypeAry::ary_must_be_exact() const { //==============================TypeVect======================================= // Convenience common pre-built types. -const TypeVect *TypeVect::VECTA = nullptr; // vector length agnostic -const TypeVect *TypeVect::VECTS = nullptr; // 32-bit vectors -const TypeVect *TypeVect::VECTD = nullptr; // 64-bit vectors -const TypeVect *TypeVect::VECTX = nullptr; // 128-bit vectors -const TypeVect *TypeVect::VECTY = nullptr; // 256-bit vectors -const TypeVect *TypeVect::VECTZ = nullptr; // 512-bit vectors -const TypeVect *TypeVect::VECTMASK = nullptr; // predicate/mask vector +const TypeVect* TypeVect::VECTA = nullptr; // vector length agnostic +const TypeVect* TypeVect::VECTS = nullptr; // 32-bit vectors +const TypeVect* TypeVect::VECTD = nullptr; // 64-bit vectors +const TypeVect* TypeVect::VECTX = nullptr; // 128-bit vectors +const TypeVect* TypeVect::VECTY = nullptr; // 256-bit vectors +const TypeVect* TypeVect::VECTZ = nullptr; // 512-bit vectors +const TypeVect* TypeVect::VECTMASK = nullptr; // predicate/mask vector //------------------------------make------------------------------------------- -const TypeVect* TypeVect::make(const Type *elem, uint length, bool is_mask) { +const TypeVect* TypeVect::make(BasicType elem_bt, uint length, bool is_mask) { if (is_mask) { - return makemask(elem, length); + return makemask(elem_bt, length); } - BasicType elem_bt = elem->array_element_basic_type(); assert(is_java_primitive(elem_bt), "only primitive types in vector"); assert(Matcher::vector_size_supported(elem_bt, length), "length in range"); int size = length * type2aelembytes(elem_bt); switch (Matcher::vector_ideal_reg(size)) { case Op_VecA: - return (TypeVect*)(new TypeVectA(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectA(elem_bt, length))->hashcons(); case Op_VecS: - return (TypeVect*)(new TypeVectS(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectS(elem_bt, length))->hashcons(); case Op_RegL: case Op_VecD: case Op_RegD: - return (TypeVect*)(new TypeVectD(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectD(elem_bt, length))->hashcons(); case Op_VecX: - return (TypeVect*)(new TypeVectX(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectX(elem_bt, length))->hashcons(); case Op_VecY: - return (TypeVect*)(new TypeVectY(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectY(elem_bt, length))->hashcons(); case Op_VecZ: - return (TypeVect*)(new TypeVectZ(elem, length))->hashcons(); + return (TypeVect*)(new TypeVectZ(elem_bt, length))->hashcons(); } ShouldNotReachHere(); return nullptr; } -const TypeVect *TypeVect::makemask(const Type* elem, uint length) { - BasicType elem_bt = elem->array_element_basic_type(); +const TypeVect* TypeVect::makemask(BasicType elem_bt, uint length) { if (Matcher::has_predicated_vectors() && Matcher::match_rule_supported_vector_masked(Op_VectorLoadMask, length, elem_bt)) { - return TypeVectMask::make(elem, length); + return TypeVectMask::make(elem_bt, length); } else { - return make(elem, length); + return make(elem_bt, length); } } //------------------------------meet------------------------------------------- -// Compute the MEET of two types. It returns a new Type object. -const Type *TypeVect::xmeet( const Type *t ) const { +// Compute the MEET of two types. Since each TypeVect is the only instance of +// its species, meeting often returns itself +const Type* TypeVect::xmeet(const Type* t) const { // Perform a fast test for common case; meeting the same types together. - if( this == t ) return this; // Meeting same type-rep? + if (this == t) { + return this; + } // Current "this->_base" is Vector switch (t->base()) { // switch on original type @@ -2543,13 +2544,7 @@ const Type *TypeVect::xmeet( const Type *t ) const { default: // All else is a mistake typerr(t); - case VectorMask: { - const TypeVectMask* v = t->is_vectmask(); - assert( base() == v->base(), ""); - assert(length() == v->length(), ""); - assert(element_basic_type() == v->element_basic_type(), ""); - return TypeVect::makemask(_elem->xmeet(v->_elem), _length); - } + case VectorMask: case VectorA: case VectorS: case VectorD: @@ -2557,10 +2552,10 @@ const Type *TypeVect::xmeet( const Type *t ) const { case VectorY: case VectorZ: { // Meeting 2 vectors? const TypeVect* v = t->is_vect(); - assert( base() == v->base(), ""); + assert(base() == v->base(), ""); assert(length() == v->length(), ""); assert(element_basic_type() == v->element_basic_type(), ""); - return TypeVect::make(_elem->xmeet(v->_elem), _length); + return this; } case Top: break; @@ -2569,26 +2564,26 @@ const Type *TypeVect::xmeet( const Type *t ) const { } //------------------------------xdual------------------------------------------ -// Dual: compute field-by-field dual -const Type *TypeVect::xdual() const { - return new TypeVect(base(), _elem->dual(), _length); +// Since each TypeVect is the only instance of its species, it is self-dual +const Type* TypeVect::xdual() const { + return this; } //------------------------------eq--------------------------------------------- // Structural equality check for Type representations -bool TypeVect::eq(const Type *t) const { - const TypeVect *v = t->is_vect(); - return (_elem == v->_elem) && (_length == v->_length); +bool TypeVect::eq(const Type* t) const { + const TypeVect* v = t->is_vect(); + return (element_basic_type() == v->element_basic_type()) && (length() == v->length()); } //------------------------------hash------------------------------------------- // Type-specific hashing function. uint TypeVect::hash(void) const { - return (uint)(uintptr_t)_elem + (uint)(uintptr_t)_length; + return (uint)base() + (uint)(uintptr_t)_elem_bt + (uint)(uintptr_t)_length; } //------------------------------singleton-------------------------------------- -// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple +// TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple // constants (Ldi nodes). Vector is singleton if all elements are the same // constant value (when vector is created with Replicate code). bool TypeVect::singleton(void) const { @@ -2598,52 +2593,36 @@ bool TypeVect::singleton(void) const { } bool TypeVect::empty(void) const { - return _elem->empty(); + return false; } //------------------------------dump2------------------------------------------ #ifndef PRODUCT -void TypeVect::dump2(Dict &d, uint depth, outputStream *st) const { +void TypeVect::dump2(Dict& d, uint depth, outputStream* st) const { switch (base()) { case VectorA: - st->print("vectora["); break; + st->print("vectora"); break; case VectorS: - st->print("vectors["); break; + st->print("vectors"); break; case VectorD: - st->print("vectord["); break; + st->print("vectord"); break; case VectorX: - st->print("vectorx["); break; + st->print("vectorx"); break; case VectorY: - st->print("vectory["); break; + st->print("vectory"); break; case VectorZ: - st->print("vectorz["); break; + st->print("vectorz"); break; case VectorMask: - st->print("vectormask["); break; + st->print("vectormask"); break; default: ShouldNotReachHere(); } - st->print("%d]:{", _length); - _elem->dump2(d, depth, st); - st->print("}"); + st->print("<%c,%u>", type2char(element_basic_type()), length()); } #endif -bool TypeVectMask::eq(const Type *t) const { - const TypeVectMask *v = t->is_vectmask(); - return (element_type() == v->element_type()) && (length() == v->length()); -} - -const Type *TypeVectMask::xdual() const { - return new TypeVectMask(element_type()->dual(), length()); -} - -const TypeVectMask *TypeVectMask::make(const BasicType elem_bt, uint length) { - return make(get_const_basic_type(elem_bt), length); -} - -const TypeVectMask *TypeVectMask::make(const Type* elem, uint length) { - const TypeVectMask* mtype = Matcher::predicate_reg_type(elem, length); - return (TypeVectMask*) const_cast(mtype)->hashcons(); +const TypeVectMask* TypeVectMask::make(const BasicType elem_bt, uint length) { + return (TypeVectMask*) (new TypeVectMask(elem_bt, length))->hashcons(); } //============================================================================= @@ -3132,8 +3111,8 @@ const TypeRawPtr *TypeRawPtr::make( enum PTR ptr ) { return (TypeRawPtr*)(new TypeRawPtr(ptr,nullptr))->hashcons(); } -const TypeRawPtr *TypeRawPtr::make( address bits ) { - assert( bits, "Use TypePtr for null" ); +const TypeRawPtr *TypeRawPtr::make(address bits) { + assert(bits != nullptr, "Use TypePtr for null"); return (TypeRawPtr*)(new TypeRawPtr(Constant,bits))->hashcons(); } @@ -3222,15 +3201,21 @@ const TypePtr* TypeRawPtr::add_offset(intptr_t offset) const { case TypePtr::BotPTR: case TypePtr::NotNull: return this; - case TypePtr::Null: case TypePtr::Constant: { - address bits = _bits+offset; - if ( bits == 0 ) return TypePtr::NULL_PTR; - return make( bits ); + uintptr_t bits = (uintptr_t)_bits; + uintptr_t sum = bits + offset; + if (( offset < 0 ) + ? ( sum > bits ) // Underflow? + : ( sum < bits )) { // Overflow? + return BOTTOM; + } else if ( sum == 0 ) { + return TypePtr::NULL_PTR; + } else { + return make( (address)sum ); + } } default: ShouldNotReachHere(); } - return nullptr; // Lint noise } //------------------------------eq--------------------------------------------- @@ -3260,23 +3245,28 @@ void TypeRawPtr::dump2( Dict &d, uint depth, outputStream *st ) const { // Convenience common pre-built type. const TypeOopPtr *TypeOopPtr::BOTTOM; -TypeInterfaces::TypeInterfaces() - : Type(Interfaces), _list(Compile::current()->type_arena(), 0, 0, nullptr), +TypeInterfaces::TypeInterfaces(ciInstanceKlass** interfaces_base, int nb_interfaces) + : Type(Interfaces), _interfaces(interfaces_base, nb_interfaces), _hash(0), _exact_klass(nullptr) { - DEBUG_ONLY(_initialized = true); -} - -TypeInterfaces::TypeInterfaces(GrowableArray* interfaces) - : Type(Interfaces), _list(Compile::current()->type_arena(), interfaces->length(), 0, nullptr), - _hash(0), _exact_klass(nullptr) { - for (int i = 0; i < interfaces->length(); i++) { - add(interfaces->at(i)); - } + _interfaces.sort(compare); initialize(); } const TypeInterfaces* TypeInterfaces::make(GrowableArray* interfaces) { - TypeInterfaces* result = (interfaces == nullptr) ? new TypeInterfaces() : new TypeInterfaces(interfaces); + // hashcons() can only delete the last thing that was allocated: to + // make sure all memory for the newly created TypeInterfaces can be + // freed if an identical one exists, allocate space for the array of + // interfaces right after the TypeInterfaces object so that they + // form a contiguous piece of memory. + int nb_interfaces = interfaces == nullptr ? 0 : interfaces->length(); + size_t total_size = sizeof(TypeInterfaces) + nb_interfaces * sizeof(ciInstanceKlass*); + + void* allocated_mem = operator new(total_size); + ciInstanceKlass** interfaces_base = (ciInstanceKlass**)((char*)allocated_mem + sizeof(TypeInterfaces)); + for (int i = 0; i < nb_interfaces; ++i) { + interfaces_base[i] = interfaces->at(i); + } + TypeInterfaces* result = ::new (allocated_mem) TypeInterfaces(interfaces_base, nb_interfaces); return (const TypeInterfaces*)result->hashcons(); } @@ -3295,20 +3285,18 @@ int TypeInterfaces::compare(ciInstanceKlass* const& k1, ciInstanceKlass* const& return 0; } -void TypeInterfaces::add(ciInstanceKlass* interface) { - assert(interface->is_interface(), "for interfaces only"); - _list.insert_sorted(interface); - verify(); +int TypeInterfaces::compare(ciInstanceKlass** k1, ciInstanceKlass** k2) { + return compare(*k1, *k2); } bool TypeInterfaces::eq(const Type* t) const { const TypeInterfaces* other = (const TypeInterfaces*)t; - if (_list.length() != other->_list.length()) { + if (_interfaces.length() != other->_interfaces.length()) { return false; } - for (int i = 0; i < _list.length(); i++) { - ciKlass* k1 = _list.at(i); - ciKlass* k2 = other->_list.at(i); + for (int i = 0; i < _interfaces.length(); i++) { + ciKlass* k1 = _interfaces.at(i); + ciKlass* k2 = other->_interfaces.at(i); if (!k1->equals(k2)) { return false; } @@ -3319,12 +3307,12 @@ bool TypeInterfaces::eq(const Type* t) const { bool TypeInterfaces::eq(ciInstanceKlass* k) const { assert(k->is_loaded(), "should be loaded"); GrowableArray* interfaces = k->transitive_interfaces(); - if (_list.length() != interfaces->length()) { + if (_interfaces.length() != interfaces->length()) { return false; } for (int i = 0; i < interfaces->length(); i++) { bool found = false; - _list.find_sorted(interfaces->at(i), found); + _interfaces.find_sorted(interfaces->at(i), found); if (!found) { return false; } @@ -3344,8 +3332,8 @@ const Type* TypeInterfaces::xdual() const { void TypeInterfaces::compute_hash() { uint hash = 0; - for (int i = 0; i < _list.length(); i++) { - ciKlass* k = _list.at(i); + for (int i = 0; i < _interfaces.length(); i++) { + ciKlass* k = _interfaces.at(i); hash += k->hash(); } _hash = hash; @@ -3356,13 +3344,13 @@ static int compare_interfaces(ciInstanceKlass** k1, ciInstanceKlass** k2) { } void TypeInterfaces::dump(outputStream* st) const { - if (_list.length() == 0) { + if (_interfaces.length() == 0) { return; } ResourceMark rm; st->print(" ("); GrowableArray interfaces; - interfaces.appendAll(&_list); + interfaces.appendAll(&_interfaces); // Sort the interfaces so they are listed in the same order from one run to the other of the same compilation interfaces.sort(compare_interfaces); for (int i = 0; i < interfaces.length(); i++) { @@ -3377,9 +3365,9 @@ void TypeInterfaces::dump(outputStream* st) const { #ifdef ASSERT void TypeInterfaces::verify() const { - for (int i = 1; i < _list.length(); i++) { - ciInstanceKlass* k1 = _list.at(i-1); - ciInstanceKlass* k2 = _list.at(i); + for (int i = 1; i < _interfaces.length(); i++) { + ciInstanceKlass* k1 = _interfaces.at(i-1); + ciInstanceKlass* k2 = _interfaces.at(i); assert(compare(k2, k1) > 0, "should be ordered"); assert(k1 != k2, "no duplicate"); } @@ -3390,23 +3378,23 @@ const TypeInterfaces* TypeInterfaces::union_with(const TypeInterfaces* other) co GrowableArray result_list; int i = 0; int j = 0; - while (i < _list.length() || j < other->_list.length()) { - while (i < _list.length() && - (j >= other->_list.length() || - compare(_list.at(i), other->_list.at(j)) < 0)) { - result_list.push(_list.at(i)); + while (i < _interfaces.length() || j < other->_interfaces.length()) { + while (i < _interfaces.length() && + (j >= other->_interfaces.length() || + compare(_interfaces.at(i), other->_interfaces.at(j)) < 0)) { + result_list.push(_interfaces.at(i)); i++; } - while (j < other->_list.length() && - (i >= _list.length() || - compare(other->_list.at(j), _list.at(i)) < 0)) { - result_list.push(other->_list.at(j)); + while (j < other->_interfaces.length() && + (i >= _interfaces.length() || + compare(other->_interfaces.at(j), _interfaces.at(i)) < 0)) { + result_list.push(other->_interfaces.at(j)); j++; } - if (i < _list.length() && - j < other->_list.length() && - _list.at(i) == other->_list.at(j)) { - result_list.push(_list.at(i)); + if (i < _interfaces.length() && + j < other->_interfaces.length() && + _interfaces.at(i) == other->_interfaces.at(j)) { + result_list.push(_interfaces.at(i)); i++; j++; } @@ -3414,14 +3402,14 @@ const TypeInterfaces* TypeInterfaces::union_with(const TypeInterfaces* other) co const TypeInterfaces* result = TypeInterfaces::make(&result_list); #ifdef ASSERT result->verify(); - for (int i = 0; i < _list.length(); i++) { - assert(result->_list.contains(_list.at(i)), "missing"); + for (int i = 0; i < _interfaces.length(); i++) { + assert(result->_interfaces.contains(_interfaces.at(i)), "missing"); } - for (int i = 0; i < other->_list.length(); i++) { - assert(result->_list.contains(other->_list.at(i)), "missing"); + for (int i = 0; i < other->_interfaces.length(); i++) { + assert(result->_interfaces.contains(other->_interfaces.at(i)), "missing"); } - for (int i = 0; i < result->_list.length(); i++) { - assert(_list.contains(result->_list.at(i)) || other->_list.contains(result->_list.at(i)), "missing"); + for (int i = 0; i < result->_interfaces.length(); i++) { + assert(_interfaces.contains(result->_interfaces.at(i)) || other->_interfaces.contains(result->_interfaces.at(i)), "missing"); } #endif return result; @@ -3431,21 +3419,21 @@ const TypeInterfaces* TypeInterfaces::intersection_with(const TypeInterfaces* ot GrowableArray result_list; int i = 0; int j = 0; - while (i < _list.length() || j < other->_list.length()) { - while (i < _list.length() && - (j >= other->_list.length() || - compare(_list.at(i), other->_list.at(j)) < 0)) { + while (i < _interfaces.length() || j < other->_interfaces.length()) { + while (i < _interfaces.length() && + (j >= other->_interfaces.length() || + compare(_interfaces.at(i), other->_interfaces.at(j)) < 0)) { i++; } - while (j < other->_list.length() && - (i >= _list.length() || - compare(other->_list.at(j), _list.at(i)) < 0)) { + while (j < other->_interfaces.length() && + (i >= _interfaces.length() || + compare(other->_interfaces.at(j), _interfaces.at(i)) < 0)) { j++; } - if (i < _list.length() && - j < other->_list.length() && - _list.at(i) == other->_list.at(j)) { - result_list.push(_list.at(i)); + if (i < _interfaces.length() && + j < other->_interfaces.length() && + _interfaces.at(i) == other->_interfaces.at(j)) { + result_list.push(_interfaces.at(i)); i++; j++; } @@ -3453,14 +3441,14 @@ const TypeInterfaces* TypeInterfaces::intersection_with(const TypeInterfaces* ot const TypeInterfaces* result = TypeInterfaces::make(&result_list); #ifdef ASSERT result->verify(); - for (int i = 0; i < _list.length(); i++) { - assert(!other->_list.contains(_list.at(i)) || result->_list.contains(_list.at(i)), "missing"); + for (int i = 0; i < _interfaces.length(); i++) { + assert(!other->_interfaces.contains(_interfaces.at(i)) || result->_interfaces.contains(_interfaces.at(i)), "missing"); } - for (int i = 0; i < other->_list.length(); i++) { - assert(!_list.contains(other->_list.at(i)) || result->_list.contains(other->_list.at(i)), "missing"); + for (int i = 0; i < other->_interfaces.length(); i++) { + assert(!_interfaces.contains(other->_interfaces.at(i)) || result->_interfaces.contains(other->_interfaces.at(i)), "missing"); } - for (int i = 0; i < result->_list.length(); i++) { - assert(_list.contains(result->_list.at(i)) && other->_list.contains(result->_list.at(i)), "missing"); + for (int i = 0; i < result->_interfaces.length(); i++) { + assert(_interfaces.contains(result->_interfaces.at(i)) && other->_interfaces.contains(result->_interfaces.at(i)), "missing"); } #endif return result; @@ -3473,13 +3461,13 @@ ciInstanceKlass* TypeInterfaces::exact_klass() const { } void TypeInterfaces::compute_exact_klass() { - if (_list.length() == 0) { + if (_interfaces.length() == 0) { _exact_klass = nullptr; return; } ciInstanceKlass* res = nullptr; - for (int i = 0; i < _list.length(); i++) { - ciInstanceKlass* interface = _list.at(i); + for (int i = 0; i < _interfaces.length(); i++) { + ciInstanceKlass* interface = _interfaces.at(i); if (eq(interface)) { assert(res == nullptr, ""); res = interface; @@ -3490,8 +3478,8 @@ void TypeInterfaces::compute_exact_klass() { #ifdef ASSERT void TypeInterfaces::verify_is_loaded() const { - for (int i = 0; i < _list.length(); i++) { - ciKlass* interface = _list.at(i); + for (int i = 0; i < _interfaces.length(); i++) { + ciKlass* interface = _interfaces.at(i); assert(interface->is_loaded(), "Interface not loaded"); } } diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index b9883d5139136..f6b7efcae3bcc 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -784,112 +784,96 @@ class TypeAry : public Type { //------------------------------TypeVect--------------------------------------- // Class of Vector Types class TypeVect : public Type { - const Type* _elem; // Vector's element type - const uint _length; // Elements in vector (power of 2) + const BasicType _elem_bt; // Vector's element type + const uint _length; // Elements in vector (power of 2) protected: - TypeVect(TYPES t, const Type* elem, uint length) : Type(t), - _elem(elem), _length(length) {} + TypeVect(TYPES t, BasicType elem_bt, uint length) : Type(t), + _elem_bt(elem_bt), _length(length) {} public: - const Type* element_type() const { return _elem; } - BasicType element_basic_type() const { return _elem->array_element_basic_type(); } + BasicType element_basic_type() const { return _elem_bt; } uint length() const { return _length; } uint length_in_bytes() const { - return _length * type2aelembytes(element_basic_type()); + return _length * type2aelembytes(element_basic_type()); } - virtual bool eq(const Type *t) const; + virtual bool eq(const Type* t) const; virtual uint hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous - static const TypeVect *make(const BasicType elem_bt, uint length, bool is_mask = false) { - // Use bottom primitive type. - return make(get_const_basic_type(elem_bt), length, is_mask); - } - // Used directly by Replicate nodes to construct singleton vector. - static const TypeVect *make(const Type* elem, uint length, bool is_mask = false); - - static const TypeVect *makemask(const BasicType elem_bt, uint length) { - // Use bottom primitive type. - return makemask(get_const_basic_type(elem_bt), length); - } - static const TypeVect *makemask(const Type* elem, uint length); + static const TypeVect* make(const BasicType elem_bt, uint length, bool is_mask = false); + static const TypeVect* makemask(const BasicType elem_bt, uint length); + virtual const Type* xmeet( const Type *t) const; + virtual const Type* xdual() const; // Compute dual right now. - virtual const Type *xmeet( const Type *t) const; - virtual const Type *xdual() const; // Compute dual right now. - - static const TypeVect *VECTA; - static const TypeVect *VECTS; - static const TypeVect *VECTD; - static const TypeVect *VECTX; - static const TypeVect *VECTY; - static const TypeVect *VECTZ; - static const TypeVect *VECTMASK; + static const TypeVect* VECTA; + static const TypeVect* VECTS; + static const TypeVect* VECTD; + static const TypeVect* VECTX; + static const TypeVect* VECTY; + static const TypeVect* VECTZ; + static const TypeVect* VECTMASK; #ifndef PRODUCT - virtual void dump2(Dict &d, uint, outputStream *st) const; // Specialized per-Type dumping + virtual void dump2(Dict& d, uint, outputStream* st) const; // Specialized per-Type dumping #endif }; class TypeVectA : public TypeVect { friend class TypeVect; - TypeVectA(const Type* elem, uint length) : TypeVect(VectorA, elem, length) {} + TypeVectA(BasicType elem_bt, uint length) : TypeVect(VectorA, elem_bt, length) {} }; class TypeVectS : public TypeVect { friend class TypeVect; - TypeVectS(const Type* elem, uint length) : TypeVect(VectorS, elem, length) {} + TypeVectS(BasicType elem_bt, uint length) : TypeVect(VectorS, elem_bt, length) {} }; class TypeVectD : public TypeVect { friend class TypeVect; - TypeVectD(const Type* elem, uint length) : TypeVect(VectorD, elem, length) {} + TypeVectD(BasicType elem_bt, uint length) : TypeVect(VectorD, elem_bt, length) {} }; class TypeVectX : public TypeVect { friend class TypeVect; - TypeVectX(const Type* elem, uint length) : TypeVect(VectorX, elem, length) {} + TypeVectX(BasicType elem_bt, uint length) : TypeVect(VectorX, elem_bt, length) {} }; class TypeVectY : public TypeVect { friend class TypeVect; - TypeVectY(const Type* elem, uint length) : TypeVect(VectorY, elem, length) {} + TypeVectY(BasicType elem_bt, uint length) : TypeVect(VectorY, elem_bt, length) {} }; class TypeVectZ : public TypeVect { friend class TypeVect; - TypeVectZ(const Type* elem, uint length) : TypeVect(VectorZ, elem, length) {} + TypeVectZ(BasicType elem_bt, uint length) : TypeVect(VectorZ, elem_bt, length) {} }; class TypeVectMask : public TypeVect { public: friend class TypeVect; - TypeVectMask(const Type* elem, uint length) : TypeVect(VectorMask, elem, length) {} - virtual bool eq(const Type *t) const; - virtual const Type *xdual() const; + TypeVectMask(BasicType elem_bt, uint length) : TypeVect(VectorMask, elem_bt, length) {} static const TypeVectMask* make(const BasicType elem_bt, uint length); - static const TypeVectMask* make(const Type* elem, uint length); }; // Set of implemented interfaces. Referenced from TypeOopPtr and TypeKlassPtr. class TypeInterfaces : public Type { private: - GrowableArray _list; + GrowableArrayFromArray _interfaces; uint _hash; ciInstanceKlass* _exact_klass; DEBUG_ONLY(bool _initialized;) void initialize(); - void add(ciInstanceKlass* interface); void verify() const NOT_DEBUG_RETURN; void compute_hash(); void compute_exact_klass(); - TypeInterfaces(); - TypeInterfaces(GrowableArray* interfaces); + + TypeInterfaces(ciInstanceKlass** interfaces_base, int nb_interfaces); NONCOPYABLE(TypeInterfaces); public: @@ -904,12 +888,13 @@ class TypeInterfaces : public Type { bool contains(const TypeInterfaces* other) const { return intersection_with(other)->eq(other); } - bool empty() const { return _list.length() == 0; } + bool empty() const { return _interfaces.length() == 0; } ciInstanceKlass* exact_klass() const; void verify_is_loaded() const NOT_DEBUG_RETURN; static int compare(ciInstanceKlass* const& k1, ciInstanceKlass* const& k2); + static int compare(ciInstanceKlass** k1, ciInstanceKlass** k2); const Type* xmeet(const Type* t) const; diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index cfcd903e79d95..838f87eac011f 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -468,11 +468,11 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { Node* operation = nullptr; if (opc == Op_CallLeafVector) { assert(UseVectorStubs, "sanity"); - operation = gen_call_to_svml(opr->get_con(), elem_bt, num_elem, opd1, opd2); + operation = gen_call_to_vector_math(opr->get_con(), elem_bt, num_elem, opd1, opd2); if (operation == nullptr) { - log_if_needed(" ** svml call failed for %s_%s_%d", - (elem_bt == T_FLOAT)?"float":"double", - VectorSupport::svmlname[opr->get_con() - VectorSupport::VECTOR_OP_SVML_START], + log_if_needed(" ** Vector math call failed for %s_%s_%d", + (elem_bt == T_FLOAT) ? "float" : "double", + VectorSupport::mathname[opr->get_con() - VectorSupport::VECTOR_OP_MATH_START], num_elem * type2aelembytes(elem_bt)); return false; } @@ -524,17 +524,16 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { Node* LibraryCallKit::partially_wrap_indexes(Node* index_vec, int num_elem, BasicType elem_bt) { assert(elem_bt == T_BYTE, "Shuffles use byte array based backing storage."); const TypeVect* vt = TypeVect::make(elem_bt, num_elem); - const Type* type_bt = Type::get_const_basic_type(elem_bt); Node* mod_mask = gvn().makecon(TypeInt::make(num_elem-1)); - Node* bcast_mod_mask = gvn().transform(VectorNode::scalar2vector(mod_mask, num_elem, type_bt)); + Node* bcast_mod_mask = gvn().transform(VectorNode::scalar2vector(mod_mask, num_elem, elem_bt)); BoolTest::mask pred = BoolTest::ugt; ConINode* pred_node = (ConINode*)gvn().makecon(TypeInt::make(pred)); Node* lane_cnt = gvn().makecon(TypeInt::make(num_elem)); - Node* bcast_lane_cnt = gvn().transform(VectorNode::scalar2vector(lane_cnt, num_elem, type_bt)); - const TypeVect* vmask_type = TypeVect::makemask(type_bt, num_elem); - Node* mask = gvn().transform(new VectorMaskCmpNode(pred, bcast_lane_cnt, index_vec, pred_node, vmask_type)); + Node* bcast_lane_cnt = gvn().transform(VectorNode::scalar2vector(lane_cnt, num_elem, elem_bt)); + const TypeVect* vmask_type = TypeVect::makemask(elem_bt, num_elem); + Node* mask = gvn().transform(new VectorMaskCmpNode(pred, bcast_lane_cnt, index_vec, pred_node, vmask_type)); // Make the indices greater than lane count as -ve values to match the java side implementation. index_vec = gvn().transform(VectorNode::make(Op_AndV, index_vec, bcast_mod_mask, vt)); @@ -600,8 +599,7 @@ bool LibraryCallKit::inline_vector_shuffle_iota() { return false; } - const Type * type_bt = Type::get_const_basic_type(elem_bt); - const TypeVect * vt = TypeVect::make(type_bt, num_elem); + const TypeVect* vt = TypeVect::make(elem_bt, num_elem); Node* res = gvn().transform(new VectorLoadConstNode(gvn().makecon(TypeInt::ZERO), vt)); @@ -609,7 +607,7 @@ bool LibraryCallKit::inline_vector_shuffle_iota() { Node* step = argument(5); if (step_multiply) { - Node* bcast_step = gvn().transform(VectorNode::scalar2vector(step, num_elem, type_bt)); + Node* bcast_step = gvn().transform(VectorNode::scalar2vector(step, num_elem, elem_bt)); res = gvn().transform(VectorNode::make(Op_MulVB, res, bcast_step, vt)); } else if (step_val->get_con() > 1) { Node* cnt = gvn().makecon(TypeInt::make(log2i_exact(step_val->get_con()))); @@ -618,12 +616,12 @@ bool LibraryCallKit::inline_vector_shuffle_iota() { } if (!start_val->is_con() || start_val->get_con() != 0) { - Node* bcast_start = gvn().transform(VectorNode::scalar2vector(start, num_elem, type_bt)); + Node* bcast_start = gvn().transform(VectorNode::scalar2vector(start, num_elem, elem_bt)); res = gvn().transform(VectorNode::make(Op_AddVB, res, bcast_start, vt)); } - Node * mod_val = gvn().makecon(TypeInt::make(num_elem-1)); - Node * bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, type_bt)); + Node* mod_val = gvn().makecon(TypeInt::make(num_elem-1)); + Node* bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, elem_bt)); if (do_wrap) { // Wrap the indices greater than lane count. @@ -757,6 +755,63 @@ bool LibraryCallKit::inline_vector_shuffle_to_vector() { return true; } +// public static +// > +// SH wrapShuffleIndexes(Class eClass, Class shClass, SH sh, int length, +// ShuffleWrapIndexesOperation defaultImpl) +bool LibraryCallKit::inline_vector_wrap_shuffle_indexes() { + const TypeInstPtr* elem_klass = gvn().type(argument(0))->isa_instptr(); + const TypeInstPtr* shuffle_klass = gvn().type(argument(1))->isa_instptr(); + Node* shuffle = argument(2); + const TypeInt* vlen = gvn().type(argument(3))->isa_int(); + + if (elem_klass == nullptr || shuffle_klass == nullptr || shuffle->is_top() || vlen == nullptr || + !vlen->is_con() || shuffle_klass->const_oop() == nullptr) { + // not enough info for intrinsification + return false; + } + + if (!is_klass_initialized(shuffle_klass)) { + log_if_needed(" ** klass argument not initialized"); + return false; + } + + int num_elem = vlen->get_con(); + if ((num_elem < 4) || !is_power_of_2(num_elem)) { + log_if_needed(" ** vlen < 4 or not power of two=%d", num_elem); + return false; + } + + // Shuffles use byte array based backing storage + BasicType shuffle_bt = T_BYTE; + if (!arch_supports_vector(Op_AndV, num_elem, shuffle_bt, VecMaskNotUsed) || + !arch_supports_vector(Op_Replicate, num_elem, shuffle_bt, VecMaskNotUsed)) { + log_if_needed(" ** not supported: op=wrapShuffleIndexes vlen=%d etype=%s", + num_elem, type2name(shuffle_bt)); + return false; + } + + ciKlass* sbox_klass = shuffle_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* shuffle_box_type = TypeInstPtr::make_exact(TypePtr::NotNull, sbox_klass); + + // Unbox shuffle with true flag to indicate its load shuffle to vector + // shuffle is a byte array + Node* shuffle_vec = unbox_vector(shuffle, shuffle_box_type, shuffle_bt, num_elem, true); + + const TypeVect* vt = TypeVect::make(shuffle_bt, num_elem); + Node* mod_mask = gvn().makecon(TypeInt::make(num_elem - 1)); + Node* bcast_mod_mask = gvn().transform(VectorNode::scalar2vector(mod_mask, num_elem, shuffle_bt)); + // Wrap the indices greater than lane count. + Node* res = gvn().transform(VectorNode::make(Op_AndV, shuffle_vec, bcast_mod_mask, vt)); + + // Wrap it up in VectorBox to keep object type information. + res = box_vector(res, shuffle_box_type, shuffle_bt, num_elem); + set_result(res); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(shuffle_bt)))); + return true; +} + // public static // , @@ -850,7 +905,7 @@ bool LibraryCallKit::inline_vector_frombits_coerced() { } default: fatal("%s", type2name(elem_bt)); } - broadcast = VectorNode::scalar2vector(elem, num_elem, Type::get_const_basic_type(elem_bt), is_mask); + broadcast = VectorNode::scalar2vector(elem, num_elem, elem_bt, is_mask); broadcast = gvn().transform(broadcast); } @@ -1294,7 +1349,7 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { } else { // Use the vector blend to implement the masked load vector. The biased elements are zeros. Node* zero = gvn().transform(gvn().zerocon(mem_elem_bt)); - zero = gvn().transform(VectorNode::scalar2vector(zero, mem_num_elem, Type::get_const_basic_type(mem_elem_bt))); + zero = gvn().transform(VectorNode::scalar2vector(zero, mem_num_elem, mem_elem_bt)); vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt)); vload = gvn().transform(new VectorBlendNode(zero, vload, mask)); } @@ -1620,7 +1675,7 @@ bool LibraryCallKit::inline_vector_reduction() { assert(mask != nullptr || !is_masked_op, "Masked op needs the mask value never null"); if (mask != nullptr && !use_predicate) { - Node* reduce_identity = gvn().transform(VectorNode::scalar2vector(init, num_elem, Type::get_const_basic_type(elem_bt))); + Node* reduce_identity = gvn().transform(VectorNode::scalar2vector(init, num_elem, elem_bt)); value = gvn().transform(new VectorBlendNode(reduce_identity, value, mask)); } @@ -2001,7 +2056,7 @@ bool LibraryCallKit::inline_vector_rearrange() { const TypeVect* vt = v1->bottom_type()->is_vect(); rearrange = gvn().transform(rearrange); Node* zero = gvn().makecon(Type::get_zero_type(elem_bt)); - Node* zerovec = gvn().transform(VectorNode::scalar2vector(zero, num_elem, Type::get_const_basic_type(elem_bt))); + Node* zerovec = gvn().transform(VectorNode::scalar2vector(zero, num_elem, elem_bt)); rearrange = new VectorBlendNode(zerovec, rearrange, mask); } } @@ -2013,12 +2068,12 @@ bool LibraryCallKit::inline_vector_rearrange() { return true; } -static address get_svml_address(int vop, int bits, BasicType bt, char* name_ptr, int name_len) { +static address get_vector_math_address(int vop, int bits, BasicType bt, char* name_ptr, int name_len) { address addr = nullptr; assert(UseVectorStubs, "sanity"); assert(name_ptr != nullptr, "unexpected"); - assert((vop >= VectorSupport::VECTOR_OP_SVML_START) && (vop <= VectorSupport::VECTOR_OP_SVML_END), "unexpected"); - int op = vop - VectorSupport::VECTOR_OP_SVML_START; + assert((vop >= VectorSupport::VECTOR_OP_MATH_START) && (vop <= VectorSupport::VECTOR_OP_MATH_END), "unexpected"); + int op = vop - VectorSupport::VECTOR_OP_MATH_START; switch(bits) { case 64: //fallthough @@ -2026,34 +2081,190 @@ static address get_svml_address(int vop, int bits, BasicType bt, char* name_ptr, case 256: //fallthough case 512: if (bt == T_FLOAT) { - snprintf(name_ptr, name_len, "vector_%s_float%d", VectorSupport::svmlname[op], bits); + snprintf(name_ptr, name_len, "vector_%s_float_%dbits_fixed", VectorSupport::mathname[op], bits); addr = StubRoutines::_vector_f_math[exact_log2(bits/64)][op]; } else { assert(bt == T_DOUBLE, "must be FP type only"); - snprintf(name_ptr, name_len, "vector_%s_double%d", VectorSupport::svmlname[op], bits); + snprintf(name_ptr, name_len, "vector_%s_double_%dbits_fixed", VectorSupport::mathname[op], bits); addr = StubRoutines::_vector_d_math[exact_log2(bits/64)][op]; } break; default: - snprintf(name_ptr, name_len, "invalid"); - addr = nullptr; - Unimplemented(); + if (!Matcher::supports_scalable_vector() || !Matcher::vector_size_supported(bt, bits/type2aelembytes(bt)) ) { + snprintf(name_ptr, name_len, "invalid"); + addr = nullptr; + Unimplemented(); + } break; } + if (addr == nullptr && Matcher::supports_scalable_vector()) { + if (bt == T_FLOAT) { + snprintf(name_ptr, name_len, "vector_%s_float_%dbits_scalable", VectorSupport::mathname[op], bits); + addr = StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_SCALABLE][op]; + } else { + assert(bt == T_DOUBLE, "must be FP type only"); + snprintf(name_ptr, name_len, "vector_%s_double_%dbits_scalable", VectorSupport::mathname[op], bits); + addr = StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_SCALABLE][op]; + } + } + return addr; } -Node* LibraryCallKit::gen_call_to_svml(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2) { +// public static +// , +// M extends VectorMask, +// E> +// V selectFromOp(Class vClass, Class mClass, Class eClass, +// int length, V v1, V v2, M m, +// VectorSelectFromOp defaultImpl) +bool LibraryCallKit::inline_vector_select_from() { + const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); + const TypeInstPtr* mask_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInt* vlen = gvn().type(argument(3))->isa_int(); + + if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || + vector_klass->const_oop() == nullptr || + elem_klass->const_oop() == nullptr || + !vlen->is_con()) { + log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); + return false; // not enough info for intrinsification + } + if (!is_klass_initialized(vector_klass)) { + log_if_needed(" ** klass argument not initialized"); + return false; + } + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); + return false; // should be primitive type + } + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + if (!is_power_of_2(num_elem)) { + log_if_needed(" ** vlen not power of two=%d", num_elem); + return false; + } + + int cast_vopc = VectorCastNode::opcode(-1, elem_bt); // from vector of type elem_bt + if (!arch_supports_vector(Op_VectorLoadShuffle, num_elem, elem_bt, VecMaskNotUsed)|| + !arch_supports_vector(Op_AndV, num_elem, T_BYTE, VecMaskNotUsed) || + !arch_supports_vector(Op_Replicate, num_elem, T_BYTE, VecMaskNotUsed) || + !arch_supports_vector(cast_vopc, num_elem, T_BYTE, VecMaskNotUsed)) { + log_if_needed(" ** not supported: arity=0 op=selectFrom vlen=%d etype=%s ismask=no", + num_elem, type2name(elem_bt)); + return false; // not supported + } + + bool is_masked_op = argument(6)->bottom_type() != TypePtr::NULL_PTR; + bool use_predicate = is_masked_op; + if (is_masked_op && + (mask_klass == nullptr || + mask_klass->const_oop() == nullptr || + !is_klass_initialized(mask_klass))) { + log_if_needed(" ** mask_klass argument not initialized"); + return false; // not supported + } + VectorMaskUseType checkFlags = (VectorMaskUseType)(is_masked_op ? (VecMaskUseLoad | VecMaskUsePred) : VecMaskNotUsed); + if (!arch_supports_vector(Op_VectorRearrange, num_elem, elem_bt, checkFlags)) { + use_predicate = false; + if(!is_masked_op || + (!arch_supports_vector(Op_VectorRearrange, num_elem, elem_bt, VecMaskNotUsed) || + !arch_supports_vector(Op_VectorBlend, num_elem, elem_bt, VecMaskUseLoad) || + !arch_supports_vector(Op_Replicate, num_elem, elem_bt, VecMaskNotUsed))) { + log_if_needed(" ** not supported: op=selectFrom vlen=%d etype=%s is_masked_op=%d", + num_elem, type2name(elem_bt), is_masked_op); + return false; // not supported + } + } + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + // v1 is the index vector + Node* v1 = unbox_vector(argument(4), vbox_type, elem_bt, num_elem); + // v2 is the vector being rearranged + Node* v2 = unbox_vector(argument(5), vbox_type, elem_bt, num_elem); + + if (v1 == nullptr) { + log_if_needed(" ** unbox failed v1=%s", NodeClassNames[argument(4)->Opcode()]); + return false; // operand unboxing failed + } + + if (v2 == nullptr) { + log_if_needed(" ** unbox failed v2=%s", NodeClassNames[argument(5)->Opcode()]); + return false; // operand unboxing failed + } + + Node* mask = nullptr; + if (is_masked_op) { + ciKlass* mbox_klass = mask_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* mbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, mbox_klass); + mask = unbox_vector(argument(6), mbox_type, elem_bt, num_elem); + if (mask == nullptr) { + log_if_needed(" ** unbox failed mask=%s", NodeClassNames[argument(6)->Opcode()]); + return false; + } + } + + // cast index vector from elem_bt vector to byte vector + const TypeVect* byte_vt = TypeVect::make(T_BYTE, num_elem); + Node* byte_shuffle = gvn().transform(VectorCastNode::make(cast_vopc, v1, T_BYTE, num_elem)); + + // wrap the byte vector lanes to (num_elem - 1) to form the shuffle vector where num_elem is vector length + // this is a simple AND operation as we come here only for power of two vector length + Node* mod_val = gvn().makecon(TypeInt::make(num_elem-1)); + Node* bcast_mod = gvn().transform(VectorNode::scalar2vector(mod_val, num_elem, T_BYTE)); + byte_shuffle = gvn().transform(VectorNode::make(Op_AndV, byte_shuffle, bcast_mod, byte_vt)); + + // load the shuffle to use in rearrange + const TypeVect* shuffle_vt = TypeVect::make(elem_bt, num_elem); + Node* load_shuffle = gvn().transform(new VectorLoadShuffleNode(byte_shuffle, shuffle_vt)); + + // and finally rearrange + Node* rearrange = new VectorRearrangeNode(v2, load_shuffle); + if (is_masked_op) { + if (use_predicate) { + // masked rearrange is supported so use that directly + rearrange->add_req(mask); + rearrange->add_flag(Node::Flag_is_predicated_vector); + } else { + // masked rearrange is not supported so emulate usig blend + const TypeVect* vt = v1->bottom_type()->is_vect(); + rearrange = gvn().transform(rearrange); + + // create a zero vector with each lane element set as zero + Node* zero = gvn().makecon(Type::get_zero_type(elem_bt)); + Node* zerovec = gvn().transform(VectorNode::scalar2vector(zero, num_elem, elem_bt)); + + // For each lane for which mask is set, blend in the rearranged lane into zero vector + rearrange = new VectorBlendNode(zerovec, rearrange, mask); + } + } + rearrange = gvn().transform(rearrange); + + // box the result + Node* box = box_vector(rearrange, vbox_type, elem_bt, num_elem); + set_result(box); + + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + +Node* LibraryCallKit::gen_call_to_vector_math(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2) { assert(UseVectorStubs, "sanity"); - assert(vector_api_op_id >= VectorSupport::VECTOR_OP_SVML_START && vector_api_op_id <= VectorSupport::VECTOR_OP_SVML_END, "need valid op id"); + assert(vector_api_op_id >= VectorSupport::VECTOR_OP_MATH_START && vector_api_op_id <= VectorSupport::VECTOR_OP_MATH_END, "need valid op id"); assert(opd1 != nullptr, "must not be null"); const TypeVect* vt = TypeVect::make(bt, num_elem); const TypeFunc* call_type = OptoRuntime::Math_Vector_Vector_Type(opd2 != nullptr ? 2 : 1, vt, vt); char name[100] = ""; - // Get address for svml method. - address addr = get_svml_address(vector_api_op_id, vt->length_in_bytes() * BitsPerByte, bt, name, 100); + // Get address for vector math method. + address addr = get_vector_math_address(vector_api_op_id, vt->length_in_bytes() * BitsPerByte, bt, name, 100); if (addr == nullptr) { return nullptr; @@ -2176,9 +2387,8 @@ bool LibraryCallKit::inline_vector_broadcast_int() { } else { assert(is_rotate, "unexpected operation"); if (!is_const_rotate) { - const Type * type_bt = Type::get_const_basic_type(elem_bt); cnt = elem_bt == T_LONG ? gvn().transform(new ConvI2LNode(cnt)) : cnt; - opd2 = gvn().transform(VectorNode::scalar2vector(cnt, num_elem, type_bt)); + opd2 = gvn().transform(VectorNode::scalar2vector(cnt, num_elem, elem_bt)); } else { // Constant shift value. opd2 = cnt; @@ -2836,7 +3046,7 @@ bool LibraryCallKit::inline_index_vector() { } default: fatal("%s", type2name(elem_bt)); } - scale = gvn().transform(VectorNode::scalar2vector(scale, num_elem, Type::get_const_basic_type(elem_bt))); + scale = gvn().transform(VectorNode::scalar2vector(scale, num_elem, elem_bt)); index = gvn().transform(VectorNode::make(vmul_op, index, scale, vt)); } @@ -2949,7 +3159,7 @@ bool LibraryCallKit::inline_index_partially_in_upper_range() { } default: fatal("%s", type2name(elem_bt)); } - indexLimit = gvn().transform(VectorNode::scalar2vector(indexLimit, num_elem, Type::get_const_basic_type(elem_bt))); + indexLimit = gvn().transform(VectorNode::scalar2vector(indexLimit, num_elem, elem_bt)); // Load the "iota" vector. const TypeVect* vt = TypeVect::make(elem_bt, num_elem); diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index 8d2d3868fe635..fc4eaccff5ce5 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -416,6 +416,10 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop, #ifdef ASSERT _debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr), #endif + _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 , _tracer(vloop.is_trace_pointer_analysis()) @@ -495,6 +499,11 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop, return; } + 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"); @@ -508,6 +517,10 @@ VPointer::VPointer(VPointer* p) : #ifdef ASSERT _debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr), #endif + _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 , _tracer(p->_tracer._is_trace_alignment) @@ -530,6 +543,354 @@ int VPointer::invar_factor() const { return 1; } +// 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 VPointer, +// and the associated memop cannot be vectorized. +bool VPointer::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 VPointer 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 VPointer. + 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 VPointers are "comparable" (i.e. VPointer::comparable is true, set with VPointer::cmp()), + // iff all of these conditions apply for the simple form: + // 1) Both VPointers 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 VPointers 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 VPointers 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 VPointers 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 VPointers 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 VPointer 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 VPointer 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 VPointer::is_loop_member(Node* n) const { Node* n_c = phase()->get_ctrl(n); return lpt()->is_member(phase()->get_loop(n_c)); @@ -582,11 +943,40 @@ bool VPointer::scaled_iv_plus_offset(Node* n) { } } else if (opc == Op_SubI || opc == Op_SubL) { if (offset_plus_k(n->in(2), true) && scaled_iv_plus_offset(n->in(1))) { + // (offset1 + invar1 + scale * iv) - (offset2 + invar2) + // 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; + VPointer tmp(this); + if (offset_plus_k(n->in(1)) && tmp.scaled_iv_plus_offset(n->in(2))) { + // (offset1 + invar1) - (offset2 + invar2 + 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) { + maybe_add_to_invar(tmp._invar, true); +#ifdef ASSERT + _debug_invar_scale = tmp._debug_invar_scale; + _debug_negate_invar = !tmp._debug_negate_invar; +#endif + } + + // 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_plus_offset_7(n);) return true; } @@ -628,10 +1018,52 @@ bool VPointer::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 VPointer, then the int_offset and + // int_invar simply get added to the long offset and invar. But for the checks in + // VPointer::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 + // VPointer, to pattern match the int_index sub-expression of the address. + + NOT_PRODUCT(Tracer::Depth dddd;) + VPointer 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. + } + // Accumulate invar. + if (tmp._invar != nullptr) { + maybe_add_to_invar(tmp._invar, false); + } + // 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);) @@ -647,9 +1079,20 @@ bool VPointer::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. if (tmp._invar != nullptr) { BasicType bt = tmp._invar->bottom_type()->basic_type(); assert(bt == T_INT || bt == T_LONG, ""); @@ -658,6 +1101,14 @@ bool VPointer::scaled_iv(Node* n) { _debug_invar_scale = n->in(2); #endif } + + // 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);) return true; } @@ -675,7 +1126,9 @@ bool VPointer::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) { @@ -684,7 +1137,9 @@ bool VPointer::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; } @@ -699,11 +1154,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) { if (opc == Op_AddI) { if (n->in(2)->is_Con() && invariant(n->in(1))) { maybe_add_to_invar(n->in(1), negate); - _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, _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. + } maybe_add_to_invar(n->in(2), negate); NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, negate, _offset);) return true; @@ -712,11 +1171,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) { if (opc == Op_SubI) { if (n->in(2)->is_Con() && invariant(n->in(1))) { maybe_add_to_invar(n->in(1), negate); - _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, _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. + } maybe_add_to_invar(n->in(2), !negate); NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, !negate, _offset);) return true; @@ -806,6 +1269,57 @@ void VPointer::maybe_add_to_invar(Node* new_invar, bool negate) { _invar = register_if_new(add); } +bool VPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_add( offset1, offset2); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool VPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_subtract( offset1, offset2); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool VPointer::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 VPointer::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), shift); + jint int_offset = java_shift_left( offset, shift); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool VPointer::try_MulI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_multiply((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_multiply( offset1, offset2); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + // We use two comparisons, because a subtraction could underflow. #define RETURN_CMP_VALUE_IF_NOT_EQUAL(a, b) \ if (a < b) { return -1; } \ diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index 3984407c5654b..b084edd44b339 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -670,13 +670,51 @@ class VLoopAnalyzer : StackObj { // A vectorization pointer (VPointer) has information about an address for // dependence checking and vector alignment. It's usually bound to a memory // operation in a counted loop for vectorizable analysis. +// +// 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. VPointer::cmp()) we always use the simple form to make +// decisions. Hence, we must make sure to only create a "valid" VPointer 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 VPointer::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 VPointer : public ArenaObj { protected: MemNode* const _mem; // My memory reference node const VLoop& _vloop; - 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) @@ -687,6 +725,13 @@ class VPointer : public ArenaObj { Node* _debug_invar_scale; // multiplier for invariant #endif + // 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 vpointer trace of variants bool _analyze_only; // Used in loop unrolling only for vpointer trace uint _stack_idx; // Used in loop unrolling only for vpointer trace @@ -726,6 +771,8 @@ class VPointer : public ArenaObj { VPointer(VPointer* p); NONCOPYABLE(VPointer); + bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const; + public: bool valid() const { return _adr != nullptr; } bool has_iv() const { return _scale != 0; } @@ -751,10 +798,43 @@ class VPointer : public ArenaObj { return _invar == q._invar; } + // We compute if and how two VPointers 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 VPointers 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 VPointers 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 VPointers point to entirely different objects. + // -> No memory edge required. Aliasing impossible. + // + // - Equal: Synonymous to "overlap, or point to different memory objects". + // The two VPointers 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 VPointer 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(const VPointer& q) const { 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); @@ -859,6 +939,12 @@ class VPointer : public ArenaObj { void maybe_add_to_invar(Node* new_invar, bool negate); + 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); + Node* register_if_new(Node* n) const; }; diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 72b49c043b6b6..094d4dca564a8 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -799,15 +799,13 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, Node* n3, uint vlen, B } // Scalar promotion -VectorNode* VectorNode::scalar2vector(Node* s, uint vlen, const Type* opd_t, bool is_mask) { - BasicType bt = opd_t->array_element_basic_type(); +VectorNode* VectorNode::scalar2vector(Node* s, uint vlen, BasicType bt, bool is_mask) { if (is_mask && Matcher::match_rule_supported_vector(Op_MaskAll, vlen, bt)) { - const TypeVect* vt = TypeVect::make(opd_t, vlen, true); + const TypeVect* vt = TypeVect::make(bt, vlen, true); return new MaskAllNode(s, vt); } - const TypeVect* vt = opd_t->singleton() ? TypeVect::make(opd_t, vlen) - : TypeVect::make(bt, vlen); + const TypeVect* vt = TypeVect::make(bt, vlen); return new ReplicateNode(s, vt); } @@ -1626,8 +1624,6 @@ Node* VectorNode::degenerate_vector_rotate(Node* src, Node* cnt, bool is_rotate_ Node* const_one_node = nullptr; assert(cnt->bottom_type()->isa_vect(), "Unexpected shift"); - const Type* elem_ty = Type::get_const_basic_type(bt); - if (bt == T_LONG) { shift_mask_node = phase->longcon(shift_mask); const_one_node = phase->longcon(1L); @@ -1639,8 +1635,8 @@ Node* VectorNode::degenerate_vector_rotate(Node* src, Node* cnt, bool is_rotate_ subVopc = VectorNode::opcode(Op_SubI, bt); addVopc = VectorNode::opcode(Op_AddI, bt); } - Node* vector_mask = phase->transform(VectorNode::scalar2vector(shift_mask_node, vlen, elem_ty)); - Node* vector_one = phase->transform(VectorNode::scalar2vector(const_one_node, vlen, elem_ty)); + Node* vector_mask = phase->transform(VectorNode::scalar2vector(shift_mask_node, vlen, bt)); + Node* vector_one = phase->transform(VectorNode::scalar2vector(const_one_node, vlen, bt)); shiftRCnt = cnt; shiftRCnt = phase->transform(VectorNode::make(Op_AndV, shiftRCnt, vector_mask, vt)); @@ -1882,12 +1878,12 @@ Node* NegVNode::degenerate_integral_negate(PhaseGVN* phase, bool is_predicated) const_one = phase->intcon(1); add_opc = Op_AddI; } - const_minus_one = phase->transform(VectorNode::scalar2vector(const_minus_one, vlen, Type::get_const_basic_type(bt))); + const_minus_one = phase->transform(VectorNode::scalar2vector(const_minus_one, vlen, bt)); Node* xorv = VectorNode::make(Op_XorV, in(1), const_minus_one, vt); xorv->add_req(in(2)); xorv->add_flag(Node::Flag_is_predicated_vector); phase->transform(xorv); - const_one = phase->transform(VectorNode::scalar2vector(const_one, vlen, Type::get_const_basic_type(bt))); + const_one = phase->transform(VectorNode::scalar2vector(const_one, vlen, bt)); Node* addv = VectorNode::make(VectorNode::opcode(add_opc, bt), xorv, const_one, vt); addv->add_req(in(2)); addv->add_flag(Node::Flag_is_predicated_vector); @@ -1904,7 +1900,7 @@ Node* NegVNode::degenerate_integral_negate(PhaseGVN* phase, bool is_predicated) const_zero = phase->intcon(0); sub_opc = Op_SubI; } - const_zero = phase->transform(VectorNode::scalar2vector(const_zero, vlen, Type::get_const_basic_type(bt))); + const_zero = phase->transform(VectorNode::scalar2vector(const_zero, vlen, bt)); return VectorNode::make(VectorNode::opcode(sub_opc, bt), const_zero, in(1), vt); } @@ -2069,8 +2065,7 @@ Node* XorVNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (!is_predicated_vector() && (in(1) == in(2))) { BasicType bt = vect_type()->element_basic_type(); Node* zero = phase->transform(phase->zerocon(bt)); - return VectorNode::scalar2vector(zero, length(), Type::get_const_basic_type(bt), - bottom_type()->isa_vectmask() != nullptr); + return VectorNode::scalar2vector(zero, length(), bt, bottom_type()->isa_vectmask() != nullptr); } return nullptr; } diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 23ddebaf33889..d23e6b8c9268d 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -75,7 +75,7 @@ class VectorNode : public TypeNode { virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - static VectorNode* scalar2vector(Node* s, uint vlen, const Type* opd_t, bool is_mask = false); + static VectorNode* scalar2vector(Node* s, uint vlen, BasicType bt, bool is_mask = false); static VectorNode* shift_count(int opc, Node* cnt, uint vlen, BasicType bt); static VectorNode* make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt, bool is_var_shift = false); static VectorNode* make(int vopc, Node* n1, Node* n2, const TypeVect* vt, bool is_mask = false, bool is_var_shift = false); diff --git a/src/hotspot/share/opto/vtransform.cpp b/src/hotspot/share/opto/vtransform.cpp index e40157caa362b..7c7aca3b90e7c 100644 --- a/src/hotspot/share/opto/vtransform.cpp +++ b/src/hotspot/share/opto/vtransform.cpp @@ -422,8 +422,7 @@ void VTransformScalarNode::print_spec() const { } void VTransformReplicateNode::print_spec() const { - tty->print("vlen=%d element_type=", _vlen); - _element_type->dump(); + tty->print("vlen=%d element_type=%s", _vlen, type2name(_element_type)); } void VTransformShiftCountNode::print_spec() const { diff --git a/src/hotspot/share/opto/vtransform.hpp b/src/hotspot/share/opto/vtransform.hpp index 071674533a798..ee298e7fe723f 100644 --- a/src/hotspot/share/opto/vtransform.hpp +++ b/src/hotspot/share/opto/vtransform.hpp @@ -354,9 +354,9 @@ class VTransformInputScalarNode : public VTransformScalarNode { class VTransformReplicateNode : public VTransformNode { private: int _vlen; - const Type* _element_type; + BasicType _element_type; public: - VTransformReplicateNode(VTransform& vtransform, int vlen, const Type* element_type) : + VTransformReplicateNode(VTransform& vtransform, int vlen, BasicType element_type) : VTransformNode(vtransform, 2), _vlen(vlen), _element_type(element_type) {} virtual VTransformApplyResult apply(const VLoopAnalyzer& vloop_analyzer, const GrowableArray& vnode_idx_to_transformed_node) const override; diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index fbb2c6e3e08eb..a869e9821a0b2 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -444,9 +444,11 @@ JNI_ENTRY(jobject, jni_ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID meth methodHandle m (THREAD, Method::resolve_jmethod_id(method_id)); assert(m->is_static() == (isStatic != 0), "jni_ToReflectedMethod access flags doesn't match"); oop reflection_method; - if (m->is_initializer()) { + if (m->is_object_initializer()) { reflection_method = Reflection::new_constructor(m, CHECK_NULL); } else { + // Note: Static initializers can theoretically be here, if JNI users manage + // to get their jmethodID. Record them as plain methods. reflection_method = Reflection::new_method(m, false, CHECK_NULL); } ret = JNIHandles::make_local(THREAD, reflection_method); @@ -2409,7 +2411,7 @@ static char* get_bad_address() { if (bad_address != nullptr) { os::protect_memory(bad_address, size, os::MEM_PROT_READ, /*is_committed*/false); - MemTracker::record_virtual_memory_type((void*)bad_address, mtInternal); + MemTracker::record_virtual_memory_tag((void*)bad_address, mtInternal); } } return bad_address; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index bf9874956bae2..5d5d8aa7df9fa 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -1826,14 +1826,6 @@ JVM_ENTRY(jobjectArray, JVM_GetRecordComponents(JNIEnv* env, jclass ofClass)) } JVM_END -static bool select_method(const methodHandle& method, bool want_constructor) { - if (want_constructor) { - return (method->is_initializer() && !method->is_static()); - } else { - return (!method->is_initializer() && !method->is_overpass()); - } -} - static jobjectArray get_class_declared_methods_helper( JNIEnv *env, jclass ofClass, jboolean publicOnly, @@ -1866,14 +1858,22 @@ static jobjectArray get_class_declared_methods_helper( GrowableArray* idnums = new GrowableArray(methods_length); int num_methods = 0; + // Select methods matching the criteria. for (int i = 0; i < methods_length; i++) { - methodHandle method(THREAD, methods->at(i)); - if (select_method(method, want_constructor)) { - if (!publicOnly || method->is_public()) { - idnums->push(method->method_idnum()); - ++num_methods; - } + Method* method = methods->at(i); + if (want_constructor && !method->is_object_initializer()) { + continue; + } + if (!want_constructor && + (method->is_object_initializer() || method->is_static_initializer() || + method->is_overpass())) { + continue; } + if (publicOnly && !method->is_public()) { + continue; + } + idnums->push(method->method_idnum()); + ++num_methods; } // Allocate result @@ -2175,10 +2175,11 @@ static jobject get_method_at_helper(const constantPoolHandle& cp, jint index, bo THROW_MSG_NULL(vmSymbols::java_lang_RuntimeException(), "Unable to look up method in target class"); } oop method; - if (!m->is_initializer() || m->is_static()) { - method = Reflection::new_method(m, true, CHECK_NULL); - } else { + if (m->is_object_initializer()) { method = Reflection::new_constructor(m, CHECK_NULL); + } else { + // new_method accepts as Method here + method = Reflection::new_method(m, true, CHECK_NULL); } return JNIHandles::make_local(THREAD, method); } @@ -2947,9 +2948,10 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) // We must release the Threads_lock before we can post a jvmti event // in Thread::start. { + ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); // Ensure that the C++ Thread and OSThread structures aren't freed before // we operate. - MutexLocker mu(Threads_lock); + MutexLocker ml(Threads_lock); // Since JDK 5 the java.lang.Thread threadStatus is used to prevent // re-starting an already started thread, so we should usually find diff --git a/src/hotspot/share/prims/jvmtiAgentList.hpp b/src/hotspot/share/prims/jvmtiAgentList.hpp index 671def0268197..d53f5e63d9b86 100644 --- a/src/hotspot/share/prims/jvmtiAgentList.hpp +++ b/src/hotspot/share/prims/jvmtiAgentList.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_PRIMS_JVMTIAGENTLIST_HPP #define SHARE_PRIMS_JVMTIAGENTLIST_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "prims/jvmtiAgent.hpp" #include "utilities/growableArray.hpp" diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index 4f33055d6a3fe..498da559cf526 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -313,8 +313,9 @@ oop MethodHandles::init_method_MemberName(Handle mname, CallInfo& info) { case CallInfo::direct_call: vmindex = Method::nonvirtual_vtable_index; if (m->is_static()) { + assert(!m->is_static_initializer(), "Cannot be static initializer"); flags |= IS_METHOD | (JVM_REF_invokeStatic << REFERENCE_KIND_SHIFT); - } else if (m->is_initializer()) { + } else if (m->is_object_initializer()) { flags |= IS_CONSTRUCTOR | (JVM_REF_invokeSpecial << REFERENCE_KIND_SHIFT); } else { // "special" reflects that this is a direct call, not that it diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index b02746911a808..1abce57652a9f 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -22,7 +22,7 @@ */ #include "precompiled.hpp" -#include "classfile/javaClasses.hpp" +#include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "compiler/compilationPolicy.hpp" @@ -73,7 +73,7 @@ JavaThread* UpcallLinker::maybe_attach_and_get_thread() { } // modelled after JavaCallWrapper::JavaCallWrapper -JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context, jobject receiver) { +JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { JavaThread* thread = maybe_attach_and_get_thread(); guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall"); context->thread = thread; @@ -108,8 +108,6 @@ JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context, jobject recei debug_only(thread->inc_java_call_counter()); thread->set_active_handles(context->new_handles); // install new handle block and reset Java frame linkage - thread->set_vm_result(JNIHandles::resolve(receiver)); - return thread; } @@ -138,11 +136,10 @@ void UpcallLinker::on_exit(UpcallStub::FrameData* context) { } void UpcallLinker::handle_uncaught_exception(oop exception) { - ResourceMark rm; - // Based on CATCH macro tty->print_cr("Uncaught exception:"); - exception->print(); - ShouldNotReachHere(); + Handle exception_h(Thread::current(), exception); + java_lang_Throwable::print_stack_trace(exception_h, tty); + fatal("Unrecoverable uncaught exception encountered"); } JVM_ENTRY(jlong, UL_MakeUpcallStub(JNIEnv *env, jclass unused, jobject mh, jobject abi, jobject conv, @@ -150,36 +147,30 @@ JVM_ENTRY(jlong, UL_MakeUpcallStub(JNIEnv *env, jclass unused, jobject mh, jobje ResourceMark rm(THREAD); Handle mh_h(THREAD, JNIHandles::resolve(mh)); jobject mh_j = JNIHandles::make_global(mh_h); + oop type = java_lang_invoke_MethodHandle::type(mh_h()); - oop lform = java_lang_invoke_MethodHandle::form(mh_h()); - oop vmentry = java_lang_invoke_LambdaForm::vmentry(lform); - Method* entry = java_lang_invoke_MemberName::vmtarget(vmentry); - const methodHandle mh_entry(THREAD, entry); - - assert(entry->method_holder()->is_initialized(), "no clinit barrier"); - CompilationPolicy::compile_if_required(mh_entry, CHECK_0); - - assert(entry->is_static(), "static only"); // Fill in the signature array, for the calling-convention call. - const int total_out_args = entry->size_of_parameters(); - assert(total_out_args > 0, "receiver arg"); + const int total_out_args = java_lang_invoke_MethodType::ptype_slot_count(type) + 1; // +1 for receiver + bool create_new = true; + TempNewSymbol signature = java_lang_invoke_MethodType::as_signature(type, create_new); BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_out_args); BasicType ret_type; { int i = 0; - SignatureStream ss(entry->signature()); + out_sig_bt[i++] = T_OBJECT; // receiver MH + SignatureStream ss(signature); for (; !ss.at_return_type(); ss.next()) { out_sig_bt[i++] = ss.type(); // Collect remaining bits of signature if (ss.type() == T_LONG || ss.type() == T_DOUBLE) out_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots } - assert(i == total_out_args, ""); + assert(i == total_out_args, "%d != %d", i, total_out_args); ret_type = ss.type(); } return (jlong) UpcallLinker::make_upcall_stub( - mh_j, entry, out_sig_bt, total_out_args, ret_type, + mh_j, signature, out_sig_bt, total_out_args, ret_type, abi, conv, needs_return_buffer, checked_cast(ret_buf_size)); JVM_END diff --git a/src/hotspot/share/prims/upcallLinker.hpp b/src/hotspot/share/prims/upcallLinker.hpp index d80516b256678..765ed63fc5a56 100644 --- a/src/hotspot/share/prims/upcallLinker.hpp +++ b/src/hotspot/share/prims/upcallLinker.hpp @@ -34,10 +34,10 @@ class UpcallLinker { private: static JavaThread* maybe_attach_and_get_thread(); - static JavaThread* on_entry(UpcallStub::FrameData* context, jobject receiver); + static JavaThread* on_entry(UpcallStub::FrameData* context); static void on_exit(UpcallStub::FrameData* context); public: - static address make_upcall_stub(jobject mh, Method* entry, + static address make_upcall_stub(jobject mh, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index e0517c91e957d..65bc6c48fee7b 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -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 @@ -43,7 +43,7 @@ #endif // COMPILER2 #ifdef COMPILER2 -const char* VectorSupport::svmlname[VectorSupport::NUM_SVML_OP] = { +const char* VectorSupport::mathname[VectorSupport::NUM_VECTOR_OP_MATH] = { "tan", "tanh", "sin", diff --git a/src/hotspot/share/prims/vectorSupport.hpp b/src/hotspot/share/prims/vectorSupport.hpp index 7302e0060648b..6f8e52e9ec0c6 100644 --- a/src/hotspot/share/prims/vectorSupport.hpp +++ b/src/hotspot/share/prims/vectorSupport.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -121,9 +121,9 @@ class VectorSupport : AllStatic { VECTOR_OP_EXPM1 = 117, VECTOR_OP_HYPOT = 118, - VECTOR_OP_SVML_START = VECTOR_OP_TAN, - VECTOR_OP_SVML_END = VECTOR_OP_HYPOT, - NUM_SVML_OP = VECTOR_OP_SVML_END - VECTOR_OP_SVML_START + 1 + VECTOR_OP_MATH_START = VECTOR_OP_TAN, + VECTOR_OP_MATH_END = VECTOR_OP_HYPOT, + NUM_VECTOR_OP_MATH = VECTOR_OP_MATH_END - VECTOR_OP_MATH_START + 1 }; enum { @@ -131,7 +131,8 @@ class VectorSupport : AllStatic { VEC_SIZE_128 = 1, VEC_SIZE_256 = 2, VEC_SIZE_512 = 3, - NUM_VEC_SIZES = 4 + VEC_SIZE_SCALABLE = 4, + NUM_VEC_SIZES = 5 }; enum { @@ -139,7 +140,7 @@ class VectorSupport : AllStatic { MODE_BITS_COERCED_LONG_TO_MASK = 1 }; - static const char* svmlname[VectorSupport::NUM_SVML_OP]; + static const char* mathname[VectorSupport::NUM_VECTOR_OP_MATH]; static int vop2ideal(jint vop, BasicType bt); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 87f4a751e809a..24f6156224d26 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -676,7 +676,7 @@ WB_END #endif // INCLUDE_G1GC -// Alloc memory using the test memory type so that we can use that to see if +// Alloc memory using the test memory tag so that we can use that to see if // NMT picks it up correctly WB_ENTRY(jlong, WB_NMTMalloc(JNIEnv* env, jobject o, jlong size)) jlong addr = 0; @@ -692,11 +692,11 @@ WB_ENTRY(jlong, WB_NMTMallocWithPseudoStack(JNIEnv* env, jobject o, jlong size, return (jlong)(uintptr_t)os::malloc(size, mtTest, stack); WB_END -// Alloc memory with pseudo call stack and specific memory type. -WB_ENTRY(jlong, WB_NMTMallocWithPseudoStackAndType(JNIEnv* env, jobject o, jlong size, jint pseudo_stack, jint type)) +// Alloc memory with pseudo call stack and specific memory tag. +WB_ENTRY(jlong, WB_NMTMallocWithPseudoStackAndType(JNIEnv* env, jobject o, jlong size, jint pseudo_stack, jint mem_tag)) address pc = (address)(size_t)pseudo_stack; NativeCallStack stack(&pc, 1); - return (jlong)(uintptr_t)os::malloc(size, (MEMFLAGS)type, stack); + return (jlong)(uintptr_t)os::malloc(size, (MemTag)mem_tag, stack); WB_END // Free the memory allocated by NMTAllocTest @@ -708,21 +708,21 @@ WB_ENTRY(jlong, WB_NMTReserveMemory(JNIEnv* env, jobject o, jlong size)) jlong addr = 0; addr = (jlong)(uintptr_t)os::reserve_memory(size); - MemTracker::record_virtual_memory_type((address)addr, mtTest); + MemTracker::record_virtual_memory_tag((address)addr, mtTest); return addr; WB_END WB_ENTRY(jlong, WB_NMTAttemptReserveMemoryAt(JNIEnv* env, jobject o, jlong addr, jlong size)) addr = (jlong)(uintptr_t)os::attempt_reserve_memory_at((char*)(uintptr_t)addr, (size_t)size); - MemTracker::record_virtual_memory_type((address)addr, mtTest); + MemTracker::record_virtual_memory_tag((address)addr, mtTest); return addr; WB_END WB_ENTRY(void, WB_NMTCommitMemory(JNIEnv* env, jobject o, jlong addr, jlong size)) os::commit_memory((char *)(uintptr_t)addr, size, !ExecMem); - MemTracker::record_virtual_memory_type((address)(uintptr_t)addr, mtTest); + MemTracker::record_virtual_memory_tag((address)(uintptr_t)addr, mtTest); WB_END WB_ENTRY(void, WB_NMTUncommitMemory(JNIEnv* env, jobject o, jlong addr, jlong size)) @@ -2159,7 +2159,8 @@ WB_ENTRY(jboolean, WB_IsJVMCISupportedByGC(JNIEnv* env)) WB_END WB_ENTRY(jboolean, WB_CanWriteJavaHeapArchive(JNIEnv* env)) - return HeapShared::can_write(); + return HeapShared::can_write() + && ArchiveHeapLoader::can_use(); // work-around JDK-8341371 WB_END diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 0d0b58412aefa..fe9641063b33e 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -336,6 +336,11 @@ bool Arguments::is_internal_module_property(const char* property) { return false; } +// Return true if the key matches the --module-path property name ("jdk.module.path"). +bool Arguments::is_module_path_property(const char* key) { + return (strcmp(key, MODULE_PROPERTY_PREFIX PATH) == 0); +} + // Process java launcher properties. void Arguments::process_sun_java_launcher_properties(JavaVMInitArgs* args) { // See if sun.java.launcher or sun.java.launcher.is_altjvm is defined. @@ -1817,17 +1822,6 @@ bool Arguments::check_vm_args_consistency() { } #endif -#if !defined(X86) && !defined(AARCH64) && !defined(RISCV64) && !defined(ARM) && !defined(PPC64) && !defined(S390) - if (LockingMode == LM_LIGHTWEIGHT) { - FLAG_SET_CMDLINE(LockingMode, LM_LEGACY); - warning("New lightweight locking not supported on this platform"); - } - if (UseObjectMonitorTable) { - FLAG_SET_CMDLINE(UseObjectMonitorTable, false); - warning("UseObjectMonitorTable not supported on this platform"); - } -#endif - if (UseObjectMonitorTable && LockingMode != LM_LIGHTWEIGHT) { // ObjectMonitorTable requires lightweight locking. FLAG_SET_CMDLINE(UseObjectMonitorTable, false); diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 8251db3d0d59a..e1bfc0438dc90 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -461,6 +461,7 @@ class Arguments : AllStatic { static int PropertyList_readable_count(SystemProperty* pl); static bool is_internal_module_property(const char* option); + static bool is_module_path_property(const char* key); // Miscellaneous System property value getter and setters. static void set_dll_dir(const char *value) { _sun_boot_library_path->set_value(value); } diff --git a/src/hotspot/share/runtime/basicLock.inline.hpp b/src/hotspot/share/runtime/basicLock.inline.hpp index c04c8e5b11706..1090241c3e1f0 100644 --- a/src/hotspot/share/runtime/basicLock.inline.hpp +++ b/src/hotspot/share/runtime/basicLock.inline.hpp @@ -39,7 +39,7 @@ inline void BasicLock::set_displaced_header(markWord header) { inline ObjectMonitor* BasicLock::object_monitor_cache() const { assert(UseObjectMonitorTable, "must be"); -#if defined(X86) || defined(AARCH64) || defined(RISCV64) +#if !defined(ZERO) && (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) return reinterpret_cast(get_metadata()); #else // Other platforms do not make use of the cache yet, diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 7961e56598f25..2006f340450e5 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -447,7 +447,7 @@ bool Deoptimization::deoptimize_objects_internal(JavaThread* thread, GrowableArr RegisterMap map(chunk->at(0)->register_map()); bool deoptimized_objects = false; - bool const jvmci_enabled = JVMCI_ONLY(UseJVMCICompiler) NOT_JVMCI(false); + bool const jvmci_enabled = JVMCI_ONLY(EnableJVMCI) NOT_JVMCI(false); // Reallocate the non-escaping objects and restore their fields. if (jvmci_enabled COMPILER2_PRESENT(|| (DoEscapeAnalysis && EliminateAllocations) diff --git a/src/hotspot/share/runtime/escapeBarrier.hpp b/src/hotspot/share/runtime/escapeBarrier.hpp index df32deef98639..454e0b555e118 100644 --- a/src/hotspot/share/runtime/escapeBarrier.hpp +++ b/src/hotspot/share/runtime/escapeBarrier.hpp @@ -71,7 +71,7 @@ class EscapeBarrier : StackObj { // Revert ea based optimizations for given deoptee thread EscapeBarrier(bool barrier_active, JavaThread* calling_thread, JavaThread* deoptee_thread) : _calling_thread(calling_thread), _deoptee_thread(deoptee_thread), - _barrier_active(barrier_active && (JVMCI_ONLY(UseJVMCICompiler) NOT_JVMCI(false) + _barrier_active(barrier_active && (JVMCI_ONLY(EnableJVMCI) NOT_JVMCI(false) COMPILER2_PRESENT(|| DoEscapeAnalysis))) { if (_barrier_active) sync_and_suspend_one(); @@ -80,7 +80,7 @@ class EscapeBarrier : StackObj { // Revert ea based optimizations for all java threads EscapeBarrier(bool barrier_active, JavaThread* calling_thread) : _calling_thread(calling_thread), _deoptee_thread(nullptr), - _barrier_active(barrier_active && (JVMCI_ONLY(UseJVMCICompiler) NOT_JVMCI(false) + _barrier_active(barrier_active && (JVMCI_ONLY(EnableJVMCI) NOT_JVMCI(false) COMPILER2_PRESENT(|| DoEscapeAnalysis))) { if (_barrier_active) sync_and_suspend_all(); diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index e193271eff658..17078a69ab991 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -719,6 +719,8 @@ void frame::print_on_error(outputStream* st, char* buf, int buflen, bool verbose st->print("v ~MethodHandlesAdapterBlob " PTR_FORMAT, p2i(pc())); } else if (_cb->is_uncommon_trap_stub()) { st->print("v ~UncommonTrapBlob " PTR_FORMAT, p2i(pc())); + } else if (_cb->is_upcall_stub()) { + st->print("v ~UpcallStub::%s " PTR_FORMAT, _cb->name(), p2i(pc())); } else { st->print("v blob " PTR_FORMAT, p2i(pc())); } @@ -1116,6 +1118,19 @@ void frame::oops_entry_do(OopClosure* f, const RegisterMap* map) const { entry_frame_call_wrapper()->oops_do(f); } +void frame::oops_upcall_do(OopClosure* f, const RegisterMap* map) const { + assert(map != nullptr, "map must be set"); + if (map->include_argument_oops()) { + // Upcall stubs call a MethodHandle impl method of which only the receiver + // is ever an oop. + // Currently we should not be able to get here, since there are no + // safepoints in the one resolve stub we can get into (handle_wrong_method) + // Leave this here as a trap in case we ever do: + ShouldNotReachHere(); // not implemented + } + _cb->as_upcall_stub()->oops_do(f, *this); +} + bool frame::is_deoptimized_frame() const { assert(_deopt_state != unknown, "not answerable"); if (_deopt_state == is_deoptimized) { @@ -1147,7 +1162,7 @@ void frame::oops_do_internal(OopClosure* f, NMethodClosure* cf, } else if (is_entry_frame()) { oops_entry_do(f, map); } else if (is_upcall_stub_frame()) { - _cb->as_upcall_stub()->oops_do(f, *this); + oops_upcall_do(f, map); } else if (CodeCache::contains(pc())) { oops_nmethod_do(f, cf, df, derived_mode, map); } else { diff --git a/src/hotspot/share/runtime/frame.hpp b/src/hotspot/share/runtime/frame.hpp index 1c57e3de4daa6..50aafce3837ac 100644 --- a/src/hotspot/share/runtime/frame.hpp +++ b/src/hotspot/share/runtime/frame.hpp @@ -464,6 +464,7 @@ class frame { const RegisterMap* map, bool use_interpreter_oop_map_cache) const; void oops_entry_do(OopClosure* f, const RegisterMap* map) const; + void oops_upcall_do(OopClosure* f, const RegisterMap* map) const; void oops_nmethod_do(OopClosure* f, NMethodClosure* cf, DerivedOopClosure* df, DerivedPointerIterationMode derived_mode, const RegisterMap* map) const; diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 8dafc9a508d99..b568e76930476 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -289,9 +289,6 @@ const int ObjectAlignmentInBytes = 8; product(bool, UseInlineCaches, true, \ "Use Inline Caches for virtual calls ") \ \ - product(size_t, InlineCacheBufferSize, 10*K, EXPERIMENTAL, \ - "InlineCacheBuffer size") \ - \ product(bool, InlineArrayCopy, true, DIAGNOSTIC, \ "Inline arraycopy native that is known to be part of " \ "base library DLL") \ @@ -1994,6 +1991,10 @@ const int ObjectAlignmentInBytes = 8; \ product(bool, StressSecondarySupers, false, DIAGNOSTIC, \ "Use a terrible hash function in order to generate many collisions.") \ + \ + product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \ + "Use an extra lock during Thread start and exit to alleviate" \ + "contention on Threads_lock.") \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp index 39e59cc1ef0ef..8ed16d33a2f06 100644 --- a/src/hotspot/share/runtime/handles.hpp +++ b/src/hotspot/share/runtime/handles.hpp @@ -187,7 +187,7 @@ class HandleArea: public Arena { HandleArea* _prev; // link to outer (older) area public: // Constructor - HandleArea(MEMFLAGS flags, HandleArea* prev) : Arena(flags, Tag::tag_ha, Chunk::tiny_size) { + HandleArea(MemTag mem_tag, HandleArea* prev) : Arena(mem_tag, Tag::tag_ha, Chunk::tiny_size) { debug_only(_handle_mark_nesting = 0); debug_only(_no_handle_mark_nesting = 0); _prev = prev; diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 416e79d584477..14528f6d908fc 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -409,8 +409,8 @@ void JavaThread::check_for_valid_safepoint_state() { // A JavaThread is a normal Java thread -JavaThread::JavaThread(MEMFLAGS flags) : - Thread(flags), +JavaThread::JavaThread(MemTag mem_tag) : + Thread(mem_tag), // Initialize fields _on_thread_list(false), DEBUG_ONLY(_java_call_counter(0) COMMA) @@ -487,6 +487,7 @@ JavaThread::JavaThread(MEMFLAGS flags) : _cont_fastpath_thread_state(1), _held_monitor_count(0), _jni_monitor_count(0), + _unlocked_inflated_monitor(nullptr), _handshake(this), @@ -634,7 +635,7 @@ void JavaThread::block_if_vm_exited() { } } -JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz, MEMFLAGS flags) : JavaThread(flags) { +JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz, MemTag mem_tag) : JavaThread(mem_tag) { set_entry_point(entry_point); // Create the native thread itself. // %note runtime_23 diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 54a11dad9b832..bda438612e349 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -238,8 +238,6 @@ class JavaThread: public Thread { // Safepoint support public: // Expose _thread_state for SafeFetchInt() volatile JavaThreadState _thread_state; - private: - SafepointMechanism::ThreadData _poll_data; ThreadSafepointState* _safepoint_state; // Holds information about a thread during a safepoint address _saved_exception_pc; // Saved pc of instruction where last implicit exception happened NOT_PRODUCT(bool _requires_cross_modify_fence;) // State used by VerifyCrossModifyFence @@ -464,6 +462,7 @@ class JavaThread: public Thread { // It's signed for error detection. intx _held_monitor_count; // used by continuations for fast lock detection intx _jni_monitor_count; + ObjectMonitor* _unlocked_inflated_monitor; private: @@ -479,8 +478,8 @@ class JavaThread: public Thread { public: // Constructor - JavaThread(MEMFLAGS flags = mtThread); // delegating constructor - JavaThread(ThreadFunction entry_point, size_t stack_size = 0, MEMFLAGS flags = mtThread); + JavaThread(MemTag mem_tag = mtThread); // delegating constructor + JavaThread(ThreadFunction entry_point, size_t stack_size = 0, MemTag mem_tag = mtThread); ~JavaThread(); // Factory method to create a new JavaThread whose attach state is "is attaching" @@ -597,6 +596,22 @@ class JavaThread: public Thread { SafepointMechanism::ThreadData* poll_data() { return &_poll_data; } + static ByteSize polling_word_offset() { + ByteSize offset = byte_offset_of(Thread, _poll_data) + + byte_offset_of(SafepointMechanism::ThreadData, _polling_word); + // At least on x86_64, safepoint polls encode the offset as disp8 imm. + assert(in_bytes(offset) < 128, "Offset >= 128"); + return offset; + } + + static ByteSize polling_page_offset() { + ByteSize offset = byte_offset_of(Thread, _poll_data) + + byte_offset_of(SafepointMechanism::ThreadData, _polling_page); + // At least on x86_64, safepoint polls encode the offset as disp8 imm. + assert(in_bytes(offset) < 128, "Offset >= 128"); + return offset; + } + void set_requires_cross_modify_fence(bool val) PRODUCT_RETURN NOT_PRODUCT({ _requires_cross_modify_fence = val; }) // Continuation support @@ -615,6 +630,12 @@ class JavaThread: public Thread { intx jni_monitor_count() { return _jni_monitor_count; } void clear_jni_monitor_count() { _jni_monitor_count = 0; } + // Support for SharedRuntime::monitor_exit_helper() + ObjectMonitor* unlocked_inflated_monitor() const { return _unlocked_inflated_monitor; } + void clear_unlocked_inflated_monitor() { + _unlocked_inflated_monitor = nullptr; + } + inline bool is_vthread_mounted() const; inline const ContinuationEntry* vthread_continuation() const; @@ -780,8 +801,6 @@ class JavaThread: public Thread { static ByteSize vm_result_offset() { return byte_offset_of(JavaThread, _vm_result); } static ByteSize vm_result_2_offset() { return byte_offset_of(JavaThread, _vm_result_2); } static ByteSize thread_state_offset() { return byte_offset_of(JavaThread, _thread_state); } - static ByteSize polling_word_offset() { return byte_offset_of(JavaThread, _poll_data) + byte_offset_of(SafepointMechanism::ThreadData, _polling_word);} - static ByteSize polling_page_offset() { return byte_offset_of(JavaThread, _poll_data) + byte_offset_of(SafepointMechanism::ThreadData, _polling_page);} static ByteSize saved_exception_pc_offset() { return byte_offset_of(JavaThread, _saved_exception_pc); } static ByteSize osthread_offset() { return byte_offset_of(JavaThread, _osthread); } #if INCLUDE_JVMCI @@ -828,6 +847,7 @@ class JavaThread: public Thread { static ByteSize cont_fastpath_offset() { return byte_offset_of(JavaThread, _cont_fastpath); } static ByteSize held_monitor_count_offset() { return byte_offset_of(JavaThread, _held_monitor_count); } static ByteSize jni_monitor_count_offset() { return byte_offset_of(JavaThread, _jni_monitor_count); } + static ByteSize unlocked_inflated_monitor_offset() { return byte_offset_of(JavaThread, _unlocked_inflated_monitor); } #if INCLUDE_JVMTI static ByteSize is_in_VTMS_transition_offset() { return byte_offset_of(JavaThread, _is_in_VTMS_transition); } diff --git a/src/hotspot/share/runtime/lightweightSynchronizer.cpp b/src/hotspot/share/runtime/lightweightSynchronizer.cpp index 0e360dba97bdd..7cf7c201faf74 100644 --- a/src/hotspot/share/runtime/lightweightSynchronizer.cpp +++ b/src/hotspot/share/runtime/lightweightSynchronizer.cpp @@ -29,7 +29,7 @@ #include "logging/log.hpp" #include "memory/allStatic.hpp" #include "memory/resourceArea.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/basicLock.inline.hpp" @@ -60,14 +60,14 @@ class ObjectMonitorTable : AllStatic { } static void* allocate_node(void* context, size_t size, Value const& value) { ObjectMonitorTable::inc_items_count(); - return AllocateHeap(size, MEMFLAGS::mtObjectMonitor); + return AllocateHeap(size, mtObjectMonitor); }; static void free_node(void* context, void* memory, Value const& value) { ObjectMonitorTable::dec_items_count(); FreeHeap(memory); } }; - using ConcurrentTable = ConcurrentHashTable; + using ConcurrentTable = ConcurrentHashTable; static ConcurrentTable* _table; static volatile size_t _items_count; @@ -635,8 +635,11 @@ void LightweightSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* bool entered = monitor->enter_for(locking_thread); assert(entered, "recursive ObjectMonitor::enter_for must succeed"); } else { - // It is assumed that enter_for must enter on an object without contention. - monitor = inflate_and_enter(obj(), ObjectSynchronizer::inflate_cause_monitor_enter, locking_thread, current); + do { + // It is assumed that enter_for must enter on an object without contention. + monitor = inflate_and_enter(obj(), ObjectSynchronizer::inflate_cause_monitor_enter, locking_thread, current); + // But there may still be a race with deflation. + } while (monitor == nullptr); } assert(monitor != nullptr, "LightweightSynchronizer::enter_for must succeed"); diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index f033f42624987..769c7695192a8 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -45,7 +45,6 @@ Mutex* SharedDictionary_lock = nullptr; Monitor* ClassInitError_lock = nullptr; Mutex* Module_lock = nullptr; Mutex* CompiledIC_lock = nullptr; -Mutex* InlineCacheBuffer_lock = nullptr; Mutex* VMStatistic_lock = nullptr; Mutex* JmethodIdCreation_lock = nullptr; Mutex* JfieldIdCreation_lock = nullptr; @@ -67,6 +66,7 @@ Monitor* CodeCache_lock = nullptr; Mutex* TouchedMethodLog_lock = nullptr; Mutex* RetData_lock = nullptr; Monitor* VMOperation_lock = nullptr; +Monitor* ThreadsLockThrottle_lock = nullptr; Monitor* Threads_lock = nullptr; Mutex* NonJavaThreadsList_lock = nullptr; Mutex* NonJavaThreadsListSync_lock = nullptr; @@ -262,7 +262,7 @@ void mutex_init() { MUTEX_DEFN(JfieldIdCreation_lock , PaddedMutex , safepoint); - MUTEX_DEFN(CompiledIC_lock , PaddedMutex , nosafepoint); // locks VtableStubs_lock, InlineCacheBuffer_lock + MUTEX_DEFN(CompiledIC_lock , PaddedMutex , nosafepoint); // locks VtableStubs_lock MUTEX_DEFN(MethodCompileQueue_lock , PaddedMonitor, safepoint); MUTEX_DEFN(CompileStatistics_lock , PaddedMutex , safepoint); MUTEX_DEFN(DirectivesStack_lock , PaddedMutex , nosafepoint); @@ -318,8 +318,9 @@ void mutex_init() { MUTEX_DEFN(JVMCIRuntime_lock , PaddedMonitor, safepoint, true); #endif + MUTEX_DEFN(ThreadsLockThrottle_lock , PaddedMonitor, safepoint); + // These locks have relative rankings, and inherit safepoint checking attributes from that rank. - MUTEX_DEFL(InlineCacheBuffer_lock , PaddedMutex , CompiledIC_lock); MUTEX_DEFL(VtableStubs_lock , PaddedMutex , CompiledIC_lock); // Also holds DumpTimeTable_lock MUTEX_DEFL(CodeCache_lock , PaddedMonitor, VtableStubs_lock); MUTEX_DEFL(NMethodState_lock , PaddedMutex , CodeCache_lock); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 160e6c97db07f..98cb27d0b812d 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -40,7 +40,6 @@ extern Mutex* SharedDictionary_lock; // a lock on the CDS shared dic extern Monitor* ClassInitError_lock; // a lock on the class initialization error table extern Mutex* Module_lock; // a lock on module and package related data structures extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access -extern Mutex* InlineCacheBuffer_lock; // a lock used to guard the InlineCacheBuffer extern Mutex* VMStatistic_lock; // a lock used to guard statistics count increment extern Mutex* JmethodIdCreation_lock; // a lock on creating JNI method identifiers extern Mutex* JfieldIdCreation_lock; // a lock on creating JNI static field identifiers @@ -62,6 +61,8 @@ extern Monitor* CodeCache_lock; // a lock on the CodeCache extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info extern Mutex* RetData_lock; // a lock on installation of RetData inside method data extern Monitor* VMOperation_lock; // a lock on queue of vm_operations waiting to execute +extern Monitor* ThreadsLockThrottle_lock; // used by Thread start/exit to reduce competition for Threads_lock, + // so a VM thread calling a safepoint is prioritized extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads // (also used by Safepoints too to block threads creation/destruction) extern Mutex* NonJavaThreadsList_lock; // a lock on the NonJavaThreads list diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 367d79a5283db..755d49d2c6c58 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -178,7 +178,7 @@ OopStorage* ObjectMonitor::_oop_storage = nullptr; // // Cxq points to the set of Recently Arrived Threads attempting entry. // Because we push threads onto _cxq with CAS, the RATs must take the form of -// a singly-linked LIFO. We drain _cxq into EntryList at unlock-time when +// a singly-linked LIFO. We drain _cxq into EntryList at unlock-time when // the unlocking thread notices that EntryList is null but _cxq is != null. // // The EntryList is ordered by the prevailing queue discipline and @@ -210,19 +210,6 @@ OopStorage* ObjectMonitor::_oop_storage = nullptr; // unpark the notifyee. Unparking a notifee in notify() is inefficient - // it's likely the notifyee would simply impale itself on the lock held // by the notifier. -// -// * An interesting alternative is to encode cxq as (List,LockByte) where -// the LockByte is 0 iff the monitor is owned. _owner is simply an auxiliary -// variable, like _recursions, in the scheme. The threads or Events that form -// the list would have to be aligned in 256-byte addresses. A thread would -// try to acquire the lock or enqueue itself with CAS, but exiting threads -// could use a 1-0 protocol and simply STB to set the LockByte to 0. -// Note that is is *not* word-tearing, but it does presume that full-word -// CAS operations are coherent with intermix with STB operations. That's true -// on most common processors. -// -// * See also http://blogs.sun.com/dave - // Check that object() and set_object() are called from the right context: static void check_object_context() { @@ -257,7 +244,6 @@ ObjectMonitor::ObjectMonitor(oop object) : _EntryList(nullptr), _cxq(nullptr), _succ(nullptr), - _Responsible(nullptr), _SpinDuration(ObjectMonitor::Knob_SpinLimit), _contentions(0), _WaitSet(nullptr), @@ -320,17 +306,11 @@ bool ObjectMonitor::enter_is_async_deflating() { return false; } -void ObjectMonitor::enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark) { - // Used by ObjectSynchronizer::enter_for to enter for another thread. - // The monitor is private to or already owned by locking_thread which must be suspended. - // So this code may only contend with deflation. - assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be"); +bool ObjectMonitor::TryLockWithContentionMark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark) { assert(contention_mark._monitor == this, "must be"); assert(!is_being_async_deflated(), "must be"); - void* prev_owner = try_set_owner_from(nullptr, locking_thread); - bool success = false; if (prev_owner == nullptr) { @@ -343,8 +323,16 @@ void ObjectMonitor::enter_for_with_contention_mark(JavaThread* locking_thread, O // Racing with deflation. prev_owner = try_set_owner_from(DEFLATER_MARKER, locking_thread); if (prev_owner == DEFLATER_MARKER) { - // Cancelled deflation. Increment contentions as part of the deflation protocol. - add_to_contentions(1); + // We successfully cancelled the in-progress async deflation by + // changing owner from DEFLATER_MARKER to current. We now extend + // the lifetime of the contention_mark (e.g. contentions++) here + // to prevent the deflater thread from winning the last part of + // the 2-part async deflation protocol after the regular + // decrement occurs when the contention_mark goes out of + // scope. ObjectMonitor::deflate_monitor() which is called by + // the deflater thread will decrement contentions after it + // recognizes that the async deflation was cancelled. + contention_mark.extend(); success = true; } else if (prev_owner == nullptr) { // At this point we cannot race with deflation as we have both incremented @@ -360,12 +348,28 @@ void ObjectMonitor::enter_for_with_contention_mark(JavaThread* locking_thread, O set_owner_from_BasicLock(prev_owner, locking_thread); success = true; } + assert(!success || owner_raw() == locking_thread, "must be"); + + return success; +} + +void ObjectMonitor::enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark) { + // Used by LightweightSynchronizer::inflate_and_enter in deoptimization path to enter for another thread. + // The monitor is private to or already owned by locking_thread which must be suspended. + // So this code may only contend with deflation. + assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be"); + bool success = TryLockWithContentionMark(locking_thread, contention_mark); + assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT - ", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}, observed owner: " INTPTR_FORMAT, - p2i(locking_thread), p2i(this), p2i(owner_raw()), p2i(prev_owner)); + ", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}", + p2i(locking_thread), p2i(this), p2i(owner_raw())); } bool ObjectMonitor::enter_for(JavaThread* locking_thread) { + // Used by ObjectSynchronizer::enter_for() to enter for another thread. + // The monitor is private to or already owned by locking_thread which must be suspended. + // So this code may only contend with deflation. + assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be"); // Block out deflation as soon as possible. ObjectMonitorContentionMark contention_mark(this); @@ -375,19 +379,29 @@ bool ObjectMonitor::enter_for(JavaThread* locking_thread) { return false; } - enter_for_with_contention_mark(locking_thread, contention_mark); + bool success = TryLockWithContentionMark(locking_thread, contention_mark); + + assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT + ", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}", + p2i(locking_thread), p2i(this), p2i(owner_raw())); assert(owner_raw() == locking_thread, "must be"); return true; } -bool ObjectMonitor::try_enter(JavaThread* current) { - // TryLock avoids the CAS +bool ObjectMonitor::try_enter(JavaThread* current, bool check_for_recursion) { + // TryLock avoids the CAS and handles deflation. TryLockResult r = TryLock(current); if (r == TryLockResult::Success) { assert(_recursions == 0, "invariant"); return true; } + // If called from SharedRuntime::monitor_exit_helper(), we know that + // this thread doesn't already own the lock. + if (!check_for_recursion) { + return false; + } + if (r == TryLockResult::HasOwner && owner() == current) { _recursions++; return true; @@ -400,7 +414,6 @@ bool ObjectMonitor::try_enter(JavaThread* current) { set_owner_from_BasicLock(cur, current); // Convert from BasicLock* to Thread*. return true; } - return false; } @@ -561,16 +574,40 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonito ObjectMonitor::TryLockResult ObjectMonitor::TryLock(JavaThread* current) { void* own = owner_raw(); - if (own != nullptr) return TryLockResult::HasOwner; - if (try_set_owner_from(nullptr, current) == nullptr) { - assert(_recursions == 0, "invariant"); - return TryLockResult::Success; + void* first_own = own; + + for (;;) { + if (own == DEFLATER_MARKER) { + // Block out deflation as soon as possible. + ObjectMonitorContentionMark contention_mark(this); + + // Check for deflation. + if (enter_is_async_deflating()) { + // Treat deflation as interference. + return TryLockResult::Interference; + } + if (TryLockWithContentionMark(current, contention_mark)) { + assert(_recursions == 0, "invariant"); + return TryLockResult::Success; + } else { + // Deflation won or change of owner; dont spin + break; + } + } else if (own == nullptr) { + void* prev_own = try_set_owner_from(nullptr, current); + if (prev_own == nullptr) { + assert(_recursions == 0, "invariant"); + return TryLockResult::Success; + } else { + // The lock had been free momentarily, but we lost the race to the lock. + own = prev_own; + } + } else { + // Retry doesn't make as much sense because the lock was just acquired. + break; + } } - // The lock had been free momentarily, but we lost the race to the lock. - // Interference -- the CAS failed. - // We can either return -1 or retry. - // Retry doesn't make as much sense because the lock was just acquired. - return TryLockResult::Interference; + return first_own == own ? TryLockResult::HasOwner : TryLockResult::Interference; } // Deflate the specified ObjectMonitor if not in-use. Returns true if it @@ -746,8 +783,6 @@ const char* ObjectMonitor::is_busy_to_string(stringStream* ss) { return ss->base(); } -#define MAX_RECHECK_INTERVAL 1000 - void ObjectMonitor::EnterI(JavaThread* current) { assert(current->thread_state() == _thread_blocked, "invariant"); @@ -755,25 +790,6 @@ void ObjectMonitor::EnterI(JavaThread* current) { if (TryLock(current) == TryLockResult::Success) { assert(_succ != current, "invariant"); assert(owner_raw() == current, "invariant"); - assert(_Responsible != current, "invariant"); - return; - } - - if (try_set_owner_from(DEFLATER_MARKER, current) == DEFLATER_MARKER) { - // Cancelled the in-progress async deflation by changing owner from - // DEFLATER_MARKER to current. As part of the contended enter protocol, - // contentions was incremented to a positive value before EnterI() - // was called and that prevents the deflater thread from winning the - // last part of the 2-part async deflation protocol. After EnterI() - // returns to enter(), contentions is decremented because the caller - // now owns the monitor. We bump contentions an extra time here to - // prevent the deflater thread from winning the last part of the - // 2-part async deflation protocol after the regular decrement - // occurs in enter(). The deflater thread will decrement contentions - // after it recognizes that the async deflation was cancelled. - add_to_contentions(1); - assert(_succ != current, "invariant"); - assert(_Responsible != current, "invariant"); return; } @@ -789,14 +805,12 @@ void ObjectMonitor::EnterI(JavaThread* current) { if (TrySpin(current)) { assert(owner_raw() == current, "invariant"); assert(_succ != current, "invariant"); - assert(_Responsible != current, "invariant"); return; } // The Spin failed -- Enqueue and park the thread ... assert(_succ != current, "invariant"); assert(owner_raw() != current, "invariant"); - assert(_Responsible != current, "invariant"); // Enqueue "current" on ObjectMonitor's _cxq. // @@ -826,40 +840,10 @@ void ObjectMonitor::EnterI(JavaThread* current) { if (TryLock(current) == TryLockResult::Success) { assert(_succ != current, "invariant"); assert(owner_raw() == current, "invariant"); - assert(_Responsible != current, "invariant"); return; } } - // Check for cxq|EntryList edge transition to non-null. This indicates - // the onset of contention. While contention persists exiting threads - // will use a ST:MEMBAR:LD 1-1 exit protocol. When contention abates exit - // operations revert to the faster 1-0 mode. This enter operation may interleave - // (race) a concurrent 1-0 exit operation, resulting in stranding, so we - // arrange for one of the contending thread to use a timed park() operations - // to detect and recover from the race. (Stranding is form of progress failure - // where the monitor is unlocked but all the contending threads remain parked). - // That is, at least one of the contended threads will periodically poll _owner. - // One of the contending threads will become the designated "Responsible" thread. - // The Responsible thread uses a timed park instead of a normal indefinite park - // operation -- it periodically wakes and checks for and recovers from potential - // strandings admitted by 1-0 exit operations. We need at most one Responsible - // thread per-monitor at any given moment. Only threads on cxq|EntryList may - // be responsible for a monitor. - // - // Currently, one of the contended threads takes on the added role of "Responsible". - // A viable alternative would be to use a dedicated "stranding checker" thread - // that periodically iterated over all the threads (or active monitors) and unparked - // successors where there was risk of stranding. This would help eliminate the - // timer scalability issues we see on some platforms as we'd only have one thread - // -- the checker -- parked on a timer. - - if (nxt == nullptr && _EntryList == nullptr) { - // Try to assume the role of responsible thread for the monitor. - // CONSIDER: ST vs CAS vs { if (Responsible==null) Responsible=current } - Atomic::replace_if_null(&_Responsible, current); - } - // The lock might have been released while this thread was occupied queueing // itself onto _cxq. To close the race and avoid "stranding" and // progress-liveness failure we must resample-retry _owner before parking. @@ -871,8 +855,6 @@ void ObjectMonitor::EnterI(JavaThread* current) { // to defer the state transitions until absolutely necessary, // and in doing so avoid some transitions ... - int recheckInterval = 1; - for (;;) { if (TryLock(current) == TryLockResult::Success) { @@ -881,37 +863,12 @@ void ObjectMonitor::EnterI(JavaThread* current) { assert(owner_raw() != current, "invariant"); // park self - if (_Responsible == current) { - current->_ParkEvent->park((jlong) recheckInterval); - // Increase the recheckInterval, but clamp the value. - recheckInterval *= 8; - if (recheckInterval > MAX_RECHECK_INTERVAL) { - recheckInterval = MAX_RECHECK_INTERVAL; - } - } else { - current->_ParkEvent->park(); - } + current->_ParkEvent->park(); if (TryLock(current) == TryLockResult::Success) { break; } - if (try_set_owner_from(DEFLATER_MARKER, current) == DEFLATER_MARKER) { - // Cancelled the in-progress async deflation by changing owner from - // DEFLATER_MARKER to current. As part of the contended enter protocol, - // contentions was incremented to a positive value before EnterI() - // was called and that prevents the deflater thread from winning the - // last part of the 2-part async deflation protocol. After EnterI() - // returns to enter(), contentions is decremented because the caller - // now owns the monitor. We bump contentions an extra time here to - // prevent the deflater thread from winning the last part of the - // 2-part async deflation protocol after the regular decrement - // occurs in enter(). The deflater thread will decrement contentions - // after it recognizes that the async deflation was cancelled. - add_to_contentions(1); - break; - } - // The lock is still contested. // Keep a tally of the # of futile wakeups. @@ -953,44 +910,23 @@ void ObjectMonitor::EnterI(JavaThread* current) { assert(owner_raw() == current, "invariant"); UnlinkAfterAcquire(current, &node); - if (_succ == current) _succ = nullptr; - - assert(_succ != current, "invariant"); - if (_Responsible == current) { - _Responsible = nullptr; - OrderAccess::fence(); // Dekker pivot-point - - // We may leave threads on cxq|EntryList without a designated - // "Responsible" thread. This is benign. When this thread subsequently - // exits the monitor it can "see" such preexisting "old" threads -- - // threads that arrived on the cxq|EntryList before the fence, above -- - // by LDing cxq|EntryList. Newly arrived threads -- that is, threads - // that arrive on cxq after the ST:MEMBAR, above -- will set Responsible - // non-null and elect a new "Responsible" timer thread. - // - // This thread executes: - // ST Responsible=null; MEMBAR (in enter epilogue - here) - // LD cxq|EntryList (in subsequent exit) - // - // Entering threads in the slow/contended path execute: - // ST cxq=nonnull; MEMBAR; LD Responsible (in enter prolog) - // The (ST cxq; MEMBAR) is accomplished with CAS(). - // - // The MEMBAR, above, prevents the LD of cxq|EntryList in the subsequent - // exit operation from floating above the ST Responsible=null. + if (_succ == current) { + _succ = nullptr; + // Note that we don't need to do OrderAccess::fence() after clearing + // _succ here, since we own the lock. } // We've acquired ownership with CAS(). // CAS is serializing -- it has MEMBAR/FENCE-equivalent semantics. // But since the CAS() this thread may have also stored into _succ, - // EntryList, cxq or Responsible. These meta-data updates must be + // EntryList or cxq. These meta-data updates must be // visible __before this thread subsequently drops the lock. // Consider what could occur if we didn't enforce this constraint -- // STs to monitor meta-data and user-data could reorder with (become // visible after) the ST in exit that drops ownership of the lock. // Some other thread could then acquire the lock, but observe inconsistent // or old monitor meta-data and heap data. That violates the JMM. - // To that end, the 1-0 exit() operation must have at least STST|LDST + // To that end, the exit() operation must have at least STST|LDST // "release" barrier semantics. Specifically, there must be at least a // STST|LDST barrier in exit() before the ST of null into _owner that drops // the lock. The barrier ensures that changes to monitor meta-data and data @@ -1000,8 +936,7 @@ void ObjectMonitor::EnterI(JavaThread* current) { // // Critically, any prior STs to _succ or EntryList must be visible before // the ST of null into _owner in the *subsequent* (following) corresponding - // monitorexit. Recall too, that in 1-0 mode monitorexit does not necessarily - // execute a serializing instruction. + // monitorexit. return; } @@ -1174,39 +1109,32 @@ void ObjectMonitor::UnlinkAfterAcquire(JavaThread* current, ObjectWaiter* curren // In that case exit() is called with _thread_state == _thread_blocked, // but the monitor's _contentions field is > 0, which inhibits reclamation. // -// 1-0 exit -// ~~~~~~~~ -// ::exit() uses a canonical 1-1 idiom with a MEMBAR although some of -// the fast-path operators have been optimized so the common ::exit() -// operation is 1-0, e.g., see macroAssembler_x86.cpp: fast_unlock(). -// The code emitted by fast_unlock() elides the usual MEMBAR. This -// greatly improves latency -- MEMBAR and CAS having considerable local -// latency on modern processors -- but at the cost of "stranding". Absent the -// MEMBAR, a thread in fast_unlock() can race a thread in the slow -// ::enter() path, resulting in the entering thread being stranding -// and a progress-liveness failure. Stranding is extremely rare. -// We use timers (timed park operations) & periodic polling to detect -// and recover from stranding. Potentially stranded threads periodically -// wake up and poll the lock. See the usage of the _Responsible variable. +// This is the exit part of the locking protocol, often implemented in +// C2_MacroAssembler::fast_unlock() +// +// 1. A release barrier ensures that changes to monitor meta-data +// (_succ, _EntryList, _cxq) and data protected by the lock will be +// visible before we release the lock. +// 2. Release the lock by clearing the owner. +// 3. A storeload MEMBAR is needed between releasing the owner and +// subsequently reading meta-data to safely determine if the lock is +// contended (step 4) without an elected successor (step 5). +// 4. If both _EntryList and _cxq are null, we are done, since there is no +// other thread waiting on the lock to wake up. I.e. there is no +// contention. +// 5. If there is a successor (_succ is non-null), we are done. The +// responsibility for guaranteeing progress-liveness has now implicitly +// been moved from the exiting thread to the successor. +// 6. There are waiters in the entry list (_EntryList and/or cxq are +// non-null), but there is no successor (_succ is null), so we need to +// wake up (unpark) a waiting thread to avoid stranding. // -// The CAS() in enter provides for safety and exclusion, while the CAS or -// MEMBAR in exit provides for progress and avoids stranding. 1-0 locking -// eliminates the CAS/MEMBAR from the exit path, but it admits stranding. -// We detect and recover from stranding with timers. +// Note that since only the current lock owner can manipulate the _EntryList +// or drain _cxq, we need to reacquire the lock before we can wake up +// (unpark) a waiting thread. // -// If a thread transiently strands it'll park until (a) another -// thread acquires the lock and then drops the lock, at which time the -// exiting thread will notice and unpark the stranded thread, or, (b) -// the timer expires. If the lock is high traffic then the stranding latency -// will be low due to (a). If the lock is low traffic then the odds of -// stranding are lower, although the worst-case stranding latency -// is longer. Critically, we don't want to put excessive load in the -// platform's timer subsystem. We want to minimize both the timer injection -// rate (timers created/sec) as well as the number of timers active at -// any one time. (more precisely, we want to minimize timer-seconds, which is -// the integral of the # of active timers at any instant over time). -// Both impinge on OS scalability. Given that, at most one thread parked on -// a monitor will use a timer. +// The CAS() in enter provides for safety and exclusion, while the +// MEMBAR in exit provides for progress and avoids stranding. // // There is also the risk of a futile wake-up. If we drop the lock // another thread can reacquire the lock immediately, and we can @@ -1248,10 +1176,6 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { return; } - // Invariant: after setting Responsible=null an thread must execute - // a MEMBAR or other serializing instruction before fetching EntryList|cxq. - _Responsible = nullptr; - #if INCLUDE_JFR // get the owner's thread id for the MonitorEnter event // if it is enabled and the thread isn't suspended @@ -1278,14 +1202,15 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { // Other threads are blocked trying to acquire the lock. // Normally the exiting thread is responsible for ensuring succession, - // but if other successors are ready or other entering threads are spinning - // then this thread can simply store null into _owner and exit without - // waking a successor. The existence of spinners or ready successors - // guarantees proper succession (liveness). Responsibility passes to the - // ready or running successors. The exiting thread delegates the duty. - // More precisely, if a successor already exists this thread is absolved - // of the responsibility of waking (unparking) one. - // + // but if this thread observes other successors are ready or other + // entering threads are spinning after it has stored null into _owner + // then it can exit without waking a successor. The existence of + // spinners or ready successors guarantees proper succession (liveness). + // Responsibility passes to the ready or running successors. The exiting + // thread delegates the duty. More precisely, if a successor already + // exists this thread is absolved of the responsibility of waking + // (unparking) one. + // The _succ variable is critical to reducing futile wakeup frequency. // _succ identifies the "heir presumptive" thread that has been made // ready (unparked) but that has not yet run. We need only one such @@ -1296,24 +1221,20 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { // Note that spinners in Enter() also set _succ non-null. // In the current implementation spinners opportunistically set // _succ so that exiting threads might avoid waking a successor. - // Another less appealing alternative would be for the exiting thread - // to drop the lock and then spin briefly to see if a spinner managed - // to acquire the lock. If so, the exiting thread could exit - // immediately without waking a successor, otherwise the exiting - // thread would need to dequeue and wake a successor. - // (Note that we'd need to make the post-drop spin short, but no - // shorter than the worst-case round-trip cache-line migration time. - // The dropped lock needs to become visible to the spinner, and then - // the acquisition of the lock by the spinner must become visible to - // the exiting thread). + // Which means that the exiting thread could exit immediately without + // waking a successor, if it observes a successor after it has dropped + // the lock. Note that the dropped lock needs to become visible to the + // spinner. // It appears that an heir-presumptive (successor) must be made ready. // Only the current lock owner can manipulate the EntryList or // drain _cxq, so we need to reacquire the lock. If we fail // to reacquire the lock the responsibility for ensuring succession // falls to the new owner. - // - if (try_set_owner_from(nullptr, current) != nullptr) { + + if (TryLock(current) != TryLockResult::Success) { + // Some other thread acquired the lock (or the monitor was + // deflated). Either way we are done. return; } @@ -1376,7 +1297,7 @@ void ObjectMonitor::exit(JavaThread* current, bool not_suspended) { q = p; } - // In 1-0 mode we need: ST EntryList; MEMBAR #storestore; ST _owner = nullptr + // We need to: ST EntryList; MEMBAR #storestore; ST _owner = nullptr // The MEMBAR is satisfied by the release_store() operation in ExitEpilog(). // See if we can abdicate to a spinner instead of waking a thread. @@ -1566,8 +1487,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { AddWaiter(&node); Thread::SpinRelease(&_WaitSetLock); - _Responsible = nullptr; - intx save = _recursions; // record the old recursion count _waiters++; // increment the number of waiters _recursions = 0; // set the recursion level to be 1 @@ -2245,7 +2164,6 @@ void ObjectMonitor::print() const { print_on(tty); } // _EntryList = 0x0000000000000000 // _cxq = 0x0000000000000000 // _succ = 0x0000000000000000 -// _Responsible = 0x0000000000000000 // _SpinDuration = 5000 // _contentions = 0 // _WaitSet = 0x0000700009756248 @@ -2274,7 +2192,6 @@ void ObjectMonitor::print_debug_style_on(outputStream* st) const { st->print_cr(" _EntryList = " INTPTR_FORMAT, p2i(_EntryList)); st->print_cr(" _cxq = " INTPTR_FORMAT, p2i(_cxq)); st->print_cr(" _succ = " INTPTR_FORMAT, p2i(_succ)); - st->print_cr(" _Responsible = " INTPTR_FORMAT, p2i(_Responsible)); st->print_cr(" _SpinDuration = %d", _SpinDuration); st->print_cr(" _contentions = %d", contentions()); st->print_cr(" _WaitSet = " INTPTR_FORMAT, p2i(_WaitSet)); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index ef85559c2b6c3..30d2e5094162d 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -179,7 +179,6 @@ class ObjectMonitor : public CHeapObj { ObjectWaiter* volatile _cxq; // LL of recently-arrived threads blocked on entry. JavaThread* volatile _succ; // Heir presumptive thread - used for futile wakeup throttling - JavaThread* volatile _Responsible; volatile int _SpinDuration; @@ -348,7 +347,7 @@ class ObjectMonitor : public CHeapObj { void enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark); bool enter_for(JavaThread* locking_thread); bool enter(JavaThread* current); - bool try_enter(JavaThread* current); + bool try_enter(JavaThread* current, bool check_for_recursion = true); bool spin_enter(JavaThread* current); void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark); void exit(JavaThread* current, bool not_suspended = true); @@ -377,6 +376,7 @@ class ObjectMonitor : public CHeapObj { enum class TryLockResult { Interference = -1, HasOwner = 0, Success = 1 }; + bool TryLockWithContentionMark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark); TryLockResult TryLock(JavaThread* current); bool TrySpin(JavaThread* current); @@ -395,12 +395,17 @@ class ObjectMonitorContentionMark : StackObj { DEBUG_ONLY(friend class ObjectMonitor;) ObjectMonitor* _monitor; + bool _extended; NONCOPYABLE(ObjectMonitorContentionMark); public: explicit ObjectMonitorContentionMark(ObjectMonitor* monitor); ~ObjectMonitorContentionMark(); + + // Extends the contention scope beyond this objects lifetime. + // Requires manual decrement of the contentions counter. + void extend(); }; #endif // SHARE_RUNTIME_OBJECTMONITOR_HPP diff --git a/src/hotspot/share/runtime/objectMonitor.inline.hpp b/src/hotspot/share/runtime/objectMonitor.inline.hpp index d26c459b1b415..6d3c6ff24c38b 100644 --- a/src/hotspot/share/runtime/objectMonitor.inline.hpp +++ b/src/hotspot/share/runtime/objectMonitor.inline.hpp @@ -206,15 +206,32 @@ inline void ObjectMonitor::set_next_om(ObjectMonitor* new_value) { Atomic::store(&_next_om, new_value); } +// Block out deflation. inline ObjectMonitorContentionMark::ObjectMonitorContentionMark(ObjectMonitor* monitor) - : _monitor(monitor) { + : _monitor(monitor), _extended(false) { + // Contentions is incremented to a positive value as part of the + // contended enter protocol, which prevents the deflater thread from + // winning the last part of the 2-part async deflation + // protocol. See: ObjectMonitor::deflate_monitor() and + // ObjectMonitor::TryLockWithContentionMark(). _monitor->add_to_contentions(1); } inline ObjectMonitorContentionMark::~ObjectMonitorContentionMark() { + // Decrement contentions when the contention mark goes out of + // scope. This opens up for deflation, if the contention mark + // hasn't been extended. _monitor->add_to_contentions(-1); } +inline void ObjectMonitorContentionMark::extend() { + // Used by ObjectMonitor::TryLockWithContentionMark() to "extend the + // lifetime" of the contention mark. + assert(!_extended, "extending twice is probably a bad design"); + _monitor->add_to_contentions(1); + _extended = true; +} + inline oop ObjectMonitor::object_peek() const { if (_object.is_null()) { return nullptr; diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index a8f3bc5c1d55a..8b1c52a5d33cc 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -32,6 +32,7 @@ #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" #include "gc/shared/gcVMOperations.hpp" +#include "gc/shared/oopStorageSet.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "logging/log.hpp" @@ -600,16 +601,16 @@ bool os::find_builtin_agent(JvmtiAgent* agent, const char *syms[], // --------------------- heap allocation utilities --------------------- -char *os::strdup(const char *str, MEMFLAGS flags) { +char *os::strdup(const char *str, MemTag mem_tag) { size_t size = strlen(str); - char *dup_str = (char *)malloc(size + 1, flags); + char *dup_str = (char *)malloc(size + 1, mem_tag); if (dup_str == nullptr) return nullptr; strcpy(dup_str, str); return dup_str; } -char* os::strdup_check_oom(const char* str, MEMFLAGS flags) { - char* p = os::strdup(str, flags); +char* os::strdup_check_oom(const char* str, MemTag mem_tag) { + char* p = os::strdup(str, mem_tag); if (p == nullptr) { vm_exit_out_of_memory(strlen(str) + 1, OOM_MALLOC_ERROR, "os::strdup_check_oom"); } @@ -629,11 +630,11 @@ static void break_if_ptr_caught(void* ptr) { } #endif // ASSERT -void* os::malloc(size_t size, MEMFLAGS flags) { - return os::malloc(size, flags, CALLER_PC); +void* os::malloc(size_t size, MemTag mem_tag) { + return os::malloc(size, mem_tag, CALLER_PC); } -void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { +void* os::malloc(size_t size, MemTag mem_tag, const NativeCallStack& stack) { // Special handling for NMT preinit phase before arguments are parsed void* rc = nullptr; @@ -651,7 +652,7 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { size = MAX2((size_t)1, size); // Observe MallocLimit - if (MemTracker::check_exceeds_limit(size, memflags)) { + if (MemTracker::check_exceeds_limit(size, mem_tag)) { return nullptr; } @@ -667,7 +668,7 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { return nullptr; } - void* const inner_ptr = MemTracker::record_malloc((address)outer_ptr, size, memflags, stack); + void* const inner_ptr = MemTracker::record_malloc((address)outer_ptr, size, mem_tag, stack); if (CDSConfig::is_dumping_static_archive()) { // Need to deterministically fill all the alignment gaps in C++ structures. @@ -679,20 +680,20 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { return inner_ptr; } -void* os::realloc(void *memblock, size_t size, MEMFLAGS flags) { - return os::realloc(memblock, size, flags, CALLER_PC); +void* os::realloc(void *memblock, size_t size, MemTag mem_tag) { + return os::realloc(memblock, size, mem_tag, CALLER_PC); } -void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { +void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallStack& stack) { // Special handling for NMT preinit phase before arguments are parsed void* rc = nullptr; - if (NMTPreInit::handle_realloc(&rc, memblock, size, memflags)) { + if (NMTPreInit::handle_realloc(&rc, memblock, size, mem_tag)) { return rc; } if (memblock == nullptr) { - return os::malloc(size, memflags, stack); + return os::malloc(size, mem_tag, stack); } DEBUG_ONLY(check_crash_protection()); @@ -715,15 +716,15 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa const size_t old_size = MallocTracker::malloc_header(memblock)->size(); // Observe MallocLimit - if ((size > old_size) && MemTracker::check_exceeds_limit(size - old_size, memflags)) { + if ((size > old_size) && MemTracker::check_exceeds_limit(size - old_size, mem_tag)) { return nullptr; } // Perform integrity checks on and mark the old block as dead *before* calling the real realloc(3) since it // may invalidate the old block, including its header. MallocHeader* header = MallocHeader::resolve_checked(memblock); - assert(memflags == header->flags(), "weird NMT flags mismatch (new:\"%s\" != old:\"%s\")\n", - NMTUtil::flag_to_name(memflags), NMTUtil::flag_to_name(header->flags())); + assert(mem_tag == header->mem_tag(), "weird NMT type mismatch (new:\"%s\" != old:\"%s\")\n", + NMTUtil::tag_to_name(mem_tag), NMTUtil::tag_to_name(header->mem_tag())); const MallocHeader::FreeInfo free_info = header->free_info(); header->mark_block_as_dead(); @@ -742,7 +743,7 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa // After a successful realloc(3), we account the resized block with its new size // to NMT. - void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, memflags, stack); + void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, mem_tag, stack); #ifdef ASSERT assert(old_size == free_info.size, "Sanity"); @@ -1317,6 +1318,11 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { } #endif + // Ask if any OopStorage knows about this address. + if (OopStorageSet::print_containing(addr, st)) { + return; + } + // Still nothing? If NMT is enabled, we can ask what it thinks... if (MemTracker::print_containing_region(addr, st)) { return; @@ -1871,10 +1877,10 @@ bool os::create_stack_guard_pages(char* addr, size_t bytes) { return os::pd_create_stack_guard_pages(addr, bytes); } -char* os::reserve_memory(size_t bytes, bool executable, MEMFLAGS flags) { +char* os::reserve_memory(size_t bytes, bool executable, MemTag mem_tag) { char* result = pd_reserve_memory(bytes, executable); if (result != nullptr) { - MemTracker::record_virtual_memory_reserve(result, bytes, CALLER_PC, flags); + MemTracker::record_virtual_memory_reserve(result, bytes, CALLER_PC, mem_tag); log_debug(os, map)("Reserved " RANGEFMT, RANGEFMTARGS(result, bytes)); } else { log_info(os, map)("Reserve failed (%zu bytes)", bytes); @@ -1882,10 +1888,10 @@ char* os::reserve_memory(size_t bytes, bool executable, MEMFLAGS flags) { return result; } -char* os::attempt_reserve_memory_at(char* addr, size_t bytes, bool executable, MEMFLAGS flag) { +char* os::attempt_reserve_memory_at(char* addr, size_t bytes, bool executable, MemTag mem_tag) { char* result = SimulateFullAddressSpace ? nullptr : pd_attempt_reserve_memory_at(addr, bytes, executable); if (result != nullptr) { - MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC, flag); + MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC, mem_tag); log_debug(os, map)("Reserved " RANGEFMT, RANGEFMTARGS(result, bytes)); } else { log_info(os, map)("Attempt to reserve " RANGEFMT " failed", @@ -2235,31 +2241,31 @@ void os::pretouch_memory(void* start, void* end, size_t page_size) { } } -char* os::map_memory_to_file(size_t bytes, int file_desc, MEMFLAGS flag) { +char* os::map_memory_to_file(size_t bytes, int file_desc, MemTag mem_tag) { // Could have called pd_reserve_memory() followed by replace_existing_mapping_with_file_mapping(), // but AIX may use SHM in which case its more trouble to detach the segment and remap memory to the file. // On all current implementations null is interpreted as any available address. char* result = os::map_memory_to_file(nullptr /* addr */, bytes, file_desc); if (result != nullptr) { - MemTracker::record_virtual_memory_reserve_and_commit(result, bytes, CALLER_PC, flag); + MemTracker::record_virtual_memory_reserve_and_commit(result, bytes, CALLER_PC, mem_tag); } return result; } -char* os::attempt_map_memory_to_file_at(char* addr, size_t bytes, int file_desc, MEMFLAGS flag) { +char* os::attempt_map_memory_to_file_at(char* addr, size_t bytes, int file_desc, MemTag mem_tag) { char* result = pd_attempt_map_memory_to_file_at(addr, bytes, file_desc); if (result != nullptr) { - MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC, flag); + MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC, mem_tag); } return result; } char* os::map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, - bool allow_exec, MEMFLAGS flags) { + bool allow_exec, MemTag mem_tag) { char* result = pd_map_memory(fd, file_name, file_offset, addr, bytes, read_only, allow_exec); if (result != nullptr) { - MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC, flags); + MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC, mem_tag); } return result; } diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 1d81612c033b7..65071769bc4cb 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -450,14 +450,14 @@ class os: AllStatic { inline static size_t cds_core_region_alignment(); // Reserves virtual memory. - static char* reserve_memory(size_t bytes, bool executable = false, MEMFLAGS flags = mtNone); + static char* reserve_memory(size_t bytes, bool executable = false, MemTag mem_tag = mtNone); // Reserves virtual memory that starts at an address that is aligned to 'alignment'. static char* reserve_memory_aligned(size_t size, size_t alignment, bool executable = false); // Attempts to reserve the virtual memory at [addr, addr + bytes). // Does not overwrite existing mappings. - static char* attempt_reserve_memory_at(char* addr, size_t bytes, bool executable = false, MEMFLAGS flag = mtNone); + static char* attempt_reserve_memory_at(char* addr, size_t bytes, bool executable = false, MemTag mem_tag = mtNone); // Given an address range [min, max), attempts to reserve memory within this area, with the given alignment. // If randomize is true, the location will be randomized. @@ -509,16 +509,16 @@ class os: AllStatic { static int create_file_for_heap(const char* dir); // Map memory to the file referred by fd. This function is slightly different from map_memory() // and is added to be used for implementation of -XX:AllocateHeapAt - static char* map_memory_to_file(size_t size, int fd, MEMFLAGS flag = mtNone); - static char* map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MEMFLAGS flag = mtNone); + static char* map_memory_to_file(size_t size, int fd, MemTag mem_tag = mtNone); + static char* map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag = mtNone); static char* map_memory_to_file(char* base, size_t size, int fd); - static char* attempt_map_memory_to_file_at(char* base, size_t size, int fd, MEMFLAGS flag = mtNone); + static char* attempt_map_memory_to_file_at(char* base, size_t size, int fd, MemTag mem_tag = mtNone); // Replace existing reserved memory with file mapping static char* replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd); static char* map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only = false, - bool allow_exec = false, MEMFLAGS flags = mtNone); + bool allow_exec = false, MemTag mem_tag = mtNone); static bool unmap_memory(char *addr, size_t bytes); static void disclaim_memory(char *addr, size_t bytes); static void realign_memory(char *addr, size_t bytes, size_t alignment_hint); @@ -900,16 +900,16 @@ class os: AllStatic { static int get_native_stack(address* stack, int size, int toSkip = 0); // General allocation (must be MT-safe) - static void* malloc (size_t size, MEMFLAGS flags, const NativeCallStack& stack); - static void* malloc (size_t size, MEMFLAGS flags); - static void* realloc (void *memblock, size_t size, MEMFLAGS flag, const NativeCallStack& stack); - static void* realloc (void *memblock, size_t size, MEMFLAGS flag); + static void* malloc (size_t size, MemTag mem_tag, const NativeCallStack& stack); + static void* malloc (size_t size, MemTag mem_tag); + static void* realloc (void *memblock, size_t size, MemTag mem_tag, const NativeCallStack& stack); + static void* realloc (void *memblock, size_t size, MemTag mem_tag); // handles null pointers static void free (void *memblock); - static char* strdup(const char *, MEMFLAGS flags = mtInternal); // Like strdup + static char* strdup(const char *, MemTag mem_tag = mtInternal); // Like strdup // Like strdup, but exit VM when strdup() returns null - static char* strdup_check_oom(const char*, MEMFLAGS flags = mtInternal); + static char* strdup_check_oom(const char*, MemTag mem_tag = mtInternal); // SocketInterface (ex HPI SocketInterface ) static int socket_close(int fd); @@ -939,7 +939,7 @@ class os: AllStatic { // provided buffer as a scratch buffer. The status message which will be written // into the error log either is file location or a short error message, depending // on the checking result. - static void check_dump_limit(char* buffer, size_t bufferSize); + static void check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only = false); // Get the default path to the core file // Returns the length of the string diff --git a/src/hotspot/share/runtime/osThread.hpp b/src/hotspot/share/runtime/osThread.hpp index b0e0588a6a2b6..597cf8e4d3fcb 100644 --- a/src/hotspot/share/runtime/osThread.hpp +++ b/src/hotspot/share/runtime/osThread.hpp @@ -25,111 +25,8 @@ #ifndef SHARE_RUNTIME_OSTHREAD_HPP #define SHARE_RUNTIME_OSTHREAD_HPP -#include "runtime/frame.hpp" -#include "runtime/handles.hpp" -#include "runtime/javaFrameAnchor.hpp" -#include "runtime/objectMonitor.hpp" -#include "runtime/suspendedThreadTask.hpp" #include "utilities/macros.hpp" - -#if defined(LINUX) || defined(AIX) || defined(BSD) -#include "suspendResume_posix.hpp" -#endif - -class Monitor; - -// The OSThread class holds OS-specific thread information. It is equivalent -// to the sys_thread_t structure of the classic JVM implementation. - -// The thread states represented by the ThreadState values are platform-specific -// and are likely to be only approximate, because most OSes don't give you access -// to precise thread state information. - -// Note: the ThreadState is legacy code and is not correctly implemented. -// Uses of ThreadState need to be replaced by the state in the JavaThread. - -enum ThreadState { - ALLOCATED, // Memory has been allocated but not initialized - INITIALIZED, // The thread has been initialized but yet started - RUNNABLE, // Has been started and is runnable, but not necessarily running - MONITOR_WAIT, // Waiting on a contended monitor lock - CONDVAR_WAIT, // Waiting on a condition variable - OBJECT_WAIT, // Waiting on an Object.wait() call - BREAKPOINTED, // Suspended at breakpoint - SLEEPING, // Thread.sleep() - ZOMBIE // All done, but not reclaimed yet -}; - -typedef int (*OSThreadStartFunc)(void*); - -class OSThread: public CHeapObj { - friend class VMStructs; - friend class JVMCIVMStructs; - private: - volatile ThreadState _state; // Thread state *hint* - - // Methods - public: - void set_state(ThreadState state) { _state = state; } - ThreadState get_state() { return _state; } - - OSThread(); - ~OSThread(); - - // Printing - void print_on(outputStream* st) const; - void print() const; - - // Platform dependent stuff +// The actual class declaration is platform specific. #include OS_HEADER(osThread) - public: - - thread_id_t thread_id() const { return _thread_id; } - - void set_thread_id(thread_id_t id) { _thread_id = id; } - - private: - // _thread_id is kernel thread id (similar to LWP id on Solaris). Each - // thread has a unique thread_id (BsdThreads or NPTL). It can be used - // to access /proc. - thread_id_t _thread_id; -}; - - -// Utility class for use with condition variables: -class OSThreadWaitState : public StackObj { - OSThread* _osthread; - ThreadState _old_state; - public: - OSThreadWaitState(OSThread* osthread, bool is_object_wait) { - _osthread = osthread; - _old_state = osthread->get_state(); - if (is_object_wait) { - osthread->set_state(OBJECT_WAIT); - } else { - osthread->set_state(CONDVAR_WAIT); - } - } - ~OSThreadWaitState() { - _osthread->set_state(_old_state); - } -}; - - -// Utility class for use with contended monitors: -class OSThreadContendState : public StackObj { - OSThread* _osthread; - ThreadState _old_state; - public: - OSThreadContendState(OSThread* osthread) { - _osthread = osthread; - _old_state = osthread->get_state(); - osthread->set_state(MONITOR_WAIT); - } - ~OSThreadContendState() { - _osthread->set_state(_old_state); - } -}; - #endif // SHARE_RUNTIME_OSTHREAD_HPP diff --git a/src/hotspot/share/runtime/osThread.cpp b/src/hotspot/share/runtime/osThreadBase.cpp similarity index 87% rename from src/hotspot/share/runtime/osThread.cpp rename to src/hotspot/share/runtime/osThreadBase.cpp index edaefaa1070d2..7bb7ae6aa69f8 100644 --- a/src/hotspot/share/runtime/osThread.cpp +++ b/src/hotspot/share/runtime/osThreadBase.cpp @@ -24,19 +24,11 @@ #include "precompiled.hpp" #include "oops/oop.inline.hpp" -#include "runtime/osThread.hpp" - -OSThread::OSThread() { - pd_initialize(); -} - -OSThread::~OSThread() { - pd_destroy(); -} +#include "runtime/osThreadBase.hpp" // Printing -void OSThread::print_on(outputStream *st) const { - st->print("nid=" UINT64_FORMAT " ", (uint64_t)thread_id()); +void OSThreadBase::print_on(outputStream *st) const { + st->print("nid=" UINTX_FORMAT " ", thread_id_for_printing()); switch (_state) { case ALLOCATED: st->print("allocated "); break; case INITIALIZED: st->print("initialized "); break; @@ -51,4 +43,4 @@ void OSThread::print_on(outputStream *st) const { } } -void OSThread::print() const { print_on(tty); } +void OSThreadBase::print() const { print_on(tty); } diff --git a/src/hotspot/share/runtime/osThreadBase.hpp b/src/hotspot/share/runtime/osThreadBase.hpp new file mode 100644 index 0000000000000..4063da18519d5 --- /dev/null +++ b/src/hotspot/share/runtime/osThreadBase.hpp @@ -0,0 +1,115 @@ +/* + * 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 + * 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_RUNTIME_OSTHREAD_BASE_HPP +#define SHARE_RUNTIME_OSTHREAD_BASE_HPP + +#include "memory/allocation.hpp" + +class Monitor; + +// The OSThread class holds OS-specific thread information. It is equivalent +// to the sys_thread_t structure of the classic JVM implementation. + +// The thread states represented by the ThreadState values are platform-specific +// and are likely to be only approximate, because most OSes don't give you access +// to precise thread state information. + +// Note: the ThreadState is legacy code and is not correctly implemented. +// Uses of ThreadState need to be replaced by the state in the JavaThread. + +enum ThreadState { + ALLOCATED, // Memory has been allocated but not initialized + INITIALIZED, // The thread has been initialized but yet started + RUNNABLE, // Has been started and is runnable, but not necessarily running + MONITOR_WAIT, // Waiting on a contended monitor lock + CONDVAR_WAIT, // Waiting on a condition variable + OBJECT_WAIT, // Waiting on an Object.wait() call + BREAKPOINTED, // Suspended at breakpoint + SLEEPING, // Thread.sleep() + ZOMBIE // All done, but not reclaimed yet +}; + +typedef int (*OSThreadStartFunc)(void*); + +class OSThreadBase: public CHeapObj { + friend class VMStructs; + friend class JVMCIVMStructs; + private: + volatile ThreadState _state; // Thread state *hint* + + // Methods + public: + OSThreadBase() {} + virtual ~OSThreadBase() {} + NONCOPYABLE(OSThreadBase); + + void set_state(ThreadState state) { _state = state; } + ThreadState get_state() { return _state; } + + + virtual uintx thread_id_for_printing() const = 0; + + // Printing + void print_on(outputStream* st) const; + void print() const; +}; + + +// Utility class for use with condition variables: +class OSThreadWaitState : public StackObj { + OSThreadBase* _osthread; + ThreadState _old_state; + public: + OSThreadWaitState(OSThreadBase* osthread, bool is_object_wait) { + _osthread = osthread; + _old_state = osthread->get_state(); + if (is_object_wait) { + osthread->set_state(OBJECT_WAIT); + } else { + osthread->set_state(CONDVAR_WAIT); + } + } + ~OSThreadWaitState() { + _osthread->set_state(_old_state); + } +}; + + +// Utility class for use with contended monitors: +class OSThreadContendState : public StackObj { + OSThreadBase* _osthread; + ThreadState _old_state; + public: + OSThreadContendState(OSThreadBase* osthread) { + _osthread = osthread; + _old_state = osthread->get_state(); + osthread->set_state(MONITOR_WAIT); + } + ~OSThreadContendState() { + _osthread->set_state(_old_state); + } +}; + +#endif // SHARE_RUNTIME_OSTHREAD_BASE_HPP diff --git a/src/hotspot/share/runtime/reflection.cpp b/src/hotspot/share/runtime/reflection.cpp index ab3d82ad7e2a4..15172b7f4c3d8 100644 --- a/src/hotspot/share/runtime/reflection.cpp +++ b/src/hotspot/share/runtime/reflection.cpp @@ -766,10 +766,10 @@ static Handle new_type(Symbol* signature, Klass* k, TRAPS) { } oop Reflection::new_method(const methodHandle& method, bool for_constant_pool_access, TRAPS) { - // Allow sun.reflect.ConstantPool to refer to methods as java.lang.reflect.Methods. - assert(!method()->is_initializer() || - (for_constant_pool_access && method()->is_static()), - "should call new_constructor instead"); + // Allow jdk.internal.reflect.ConstantPool to refer to methods as java.lang.reflect.Methods. + assert(!method()->is_object_initializer() && + (for_constant_pool_access || !method()->is_static_initializer()), + "Should not be the initializer"); InstanceKlass* holder = method->method_holder(); int slot = method->method_idnum(); @@ -817,7 +817,7 @@ oop Reflection::new_method(const methodHandle& method, bool for_constant_pool_ac oop Reflection::new_constructor(const methodHandle& method, TRAPS) { - assert(method()->is_initializer(), "should call new_method instead"); + assert(method()->is_object_initializer(), "Should be the initializer"); InstanceKlass* holder = method->method_holder(); int slot = method->method_idnum(); diff --git a/src/hotspot/share/runtime/safepointMechanism.cpp b/src/hotspot/share/runtime/safepointMechanism.cpp index 624583db3d130..a6aadf5ebc400 100644 --- a/src/hotspot/share/runtime/safepointMechanism.cpp +++ b/src/hotspot/share/runtime/safepointMechanism.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, 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 @@ -60,7 +60,7 @@ void SafepointMechanism::default_initialize() { const size_t allocation_size = 2 * page_size; char* polling_page = os::reserve_memory(allocation_size); os::commit_memory_or_exit(polling_page, allocation_size, false, "Unable to commit Safepoint polling page"); - MemTracker::record_virtual_memory_type((address)polling_page, mtSafepoint); + MemTracker::record_virtual_memory_tag((address)polling_page, mtSafepoint); char* bad_page = polling_page; char* good_page = polling_page + page_size; diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index d9b38133f9944..e4d4e6aea0f8c 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -651,7 +651,7 @@ void SharedRuntime::throw_and_post_jvmti_exception(JavaThread* current, Handle h } #if INCLUDE_JVMCI - if (EnableJVMCI && UseJVMCICompiler) { + if (EnableJVMCI) { vframeStream vfst(current, true); methodHandle method = methodHandle(current, vfst.method()); int bci = vfst.bci(); @@ -1963,6 +1963,26 @@ void SharedRuntime::monitor_exit_helper(oopDesc* obj, BasicLock* lock, JavaThrea assert(JavaThread::current() == current, "invariant"); // Exit must be non-blocking, and therefore no exceptions can be thrown. ExceptionMark em(current); + + // Check if C2_MacroAssembler::fast_unlock() or + // C2_MacroAssembler::fast_unlock_lightweight() unlocked an inflated + // monitor before going slow path. Since there is no safepoint + // polling when calling into the VM, we can be sure that the monitor + // hasn't been deallocated. + ObjectMonitor* m = current->unlocked_inflated_monitor(); + if (m != nullptr) { + assert(m->owner_raw() != current, "must be"); + current->clear_unlocked_inflated_monitor(); + + // We need to reacquire the lock before we can call ObjectSynchronizer::exit(). + if (!m->try_enter(current, /*check_for_recursion*/ false)) { + // Some other thread acquired the lock (or the monitor was + // deflated). Either way we are done. + current->dec_held_monitor_count(); + return; + } + } + // The object could become unlocked through a JNI call, which we have no other checks for. // Give a fatal message if CheckJNICalls. Otherwise we ignore it. if (obj->is_unlocked()) { diff --git a/src/hotspot/share/runtime/statSampler.cpp b/src/hotspot/share/runtime/statSampler.cpp index 5fd038bf845c1..bbd8d3096bba0 100644 --- a/src/hotspot/share/runtime/statSampler.cpp +++ b/src/hotspot/share/runtime/statSampler.cpp @@ -201,7 +201,8 @@ void StatSampler::assert_system_property(const char* name, const char* value, TR // convert Java String to utf8 string char* system_value = java_lang_String::as_utf8_string(value_oop); - assert(strcmp(value, system_value) == 0, "property value mustn't differ from System.getProperty"); + assert(strcmp(value, system_value) == 0, "property value mustn't differ from System.getProperty. Our value is: %s, System.getProperty is: %s", + value, system_value); #endif // ASSERT } diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index c13f64fca4bed..c881b64b59280 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -171,12 +171,13 @@ address StubRoutines::_dlibm_sin_cos_huge = nullptr; address StubRoutines::_dlibm_reduce_pi04l = nullptr; address StubRoutines::_dlibm_tan_cot_huge = nullptr; address StubRoutines::_dtan = nullptr; +address StubRoutines::_dtanh = nullptr; address StubRoutines::_f2hf = nullptr; address StubRoutines::_hf2f = nullptr; -address StubRoutines::_vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}}; -address StubRoutines::_vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}}; +address StubRoutines::_vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH] = {{nullptr}, {nullptr}}; +address StubRoutines::_vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH] = {{nullptr}, {nullptr}}; address StubRoutines::_method_entry_barrier = nullptr; address StubRoutines::_array_sort = nullptr; @@ -187,6 +188,7 @@ address StubRoutines::_cont_returnBarrier = nullptr; address StubRoutines::_cont_returnBarrierExc = nullptr; address StubRoutines::_upcall_stub_exception_handler = nullptr; +address StubRoutines::_upcall_stub_load_target = nullptr; address StubRoutines::_lookup_secondary_supers_table_slow_path_stub = nullptr; address StubRoutines::_lookup_secondary_supers_table_stubs[Klass::SECONDARY_SUPERS_TABLE_SIZE] = { nullptr }; diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index f5b932569be81..f025742b60585 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -281,6 +281,7 @@ class StubRoutines: AllStatic { static address _dlibm_reduce_pi04l; static address _dlibm_tan_cot_huge; static address _dtan; + static address _dtanh; static address _fmod; static address _f2hf; @@ -293,10 +294,11 @@ class StubRoutines: AllStatic { static address _cont_returnBarrierExc; // Vector Math Routines - static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; - static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; + static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH]; + static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH]; static address _upcall_stub_exception_handler; + static address _upcall_stub_load_target; static address _lookup_secondary_supers_table_stubs[]; static address _lookup_secondary_supers_table_slow_path_stub; @@ -472,6 +474,7 @@ class StubRoutines: AllStatic { static address dlibm_sin_cos_huge() { return _dlibm_sin_cos_huge; } static address dlibm_tan_cot_huge() { return _dlibm_tan_cot_huge; } static address dtan() { return _dtan; } + static address dtanh() { return _dtanh; } // These are versions of the java.lang.Float::floatToFloat16() and float16ToFloat() // methods which perform the same operations as the intrinsic version. @@ -504,6 +507,11 @@ class StubRoutines: AllStatic { return _upcall_stub_exception_handler; } + static address upcall_stub_load_target() { + assert(_upcall_stub_load_target != nullptr, "not implemented"); + return _upcall_stub_load_target; + } + static address lookup_secondary_supers_table_stub(u1 slot) { assert(slot < Klass::SECONDARY_SUPERS_TABLE_SIZE, "out of bounds"); assert(_lookup_secondary_supers_table_stubs[slot] != nullptr, "not implemented"); diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index e72077adabf3b..df6a660a0aa21 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -64,7 +64,7 @@ THREAD_LOCAL Thread* Thread::_thr_current = nullptr; DEBUG_ONLY(Thread* Thread::_starting_thread = nullptr;) -Thread::Thread(MEMFLAGS flags) { +Thread::Thread(MemTag mem_tag) { DEBUG_ONLY(_run_state = PRE_CALL_RUN;) @@ -78,12 +78,11 @@ Thread::Thread(MEMFLAGS flags) { // allocated data structures set_osthread(nullptr); - set_resource_area(new (flags) ResourceArea(flags)); + set_resource_area(new (mem_tag) ResourceArea(mem_tag)); DEBUG_ONLY(_current_resource_mark = nullptr;) - set_handle_area(new (flags) HandleArea(flags, nullptr)); + set_handle_area(new (mem_tag) HandleArea(mem_tag, nullptr)); set_metadata_handles(new (mtClass) GrowableArray(30, mtClass)); set_last_handle_mark(nullptr); - DEBUG_ONLY(_missed_ic_stub_refill_verifier = nullptr); // Initial value of zero ==> never claimed. _threads_do_token = 0; @@ -145,6 +144,16 @@ Thread::Thread(MEMFLAGS flags) { MACOS_AARCH64_ONLY(DEBUG_ONLY(_wx_init = false)); } +#ifdef ASSERT +address Thread::stack_base() const { + // Note: can't report Thread::name() here as that can require a ResourceMark which we + // can't use because this gets called too early in the thread initialization. + assert(_stack_base != nullptr, "Stack base not yet set for thread id:%d (0 if not set)", + osthread() != nullptr ? osthread()->thread_id() : 0); + return _stack_base; +} +#endif + void Thread::initialize_tlab() { if (UseTLAB) { tlab().initialize(); diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index e9fee4d113aaa..e2dfce7b2555b 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -33,6 +33,7 @@ #include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" +#include "runtime/safepointMechanism.hpp" #include "runtime/threadHeapSampler.hpp" #include "runtime/threadLocalStorage.hpp" #include "runtime/threadStatisticalInfo.hpp" @@ -46,7 +47,6 @@ class CompilerThread; class HandleArea; class HandleMark; -class ICRefillVerifier; class JvmtiRawMonitor; class NMethodClosure; class Metadata; @@ -110,6 +110,7 @@ class Thread: public ThreadShadow { friend class VMErrorCallbackMark; friend class VMStructs; friend class JVMCIVMStructs; + friend class JavaThread; private: #ifndef USE_LIBRARY_BASED_TLS_ONLY @@ -136,6 +137,11 @@ class Thread: public ThreadShadow { } private: + // Poll data is used in generated code for safepoint polls. + // It is important for performance to put this at lower offset + // in Thread. The accessors are in JavaThread. + SafepointMechanism::ThreadData _poll_data; + // Thread local data area available to the GC. The internal // structure and contents of this data area is GC-specific. // Only GC and GC barrier code should access this data area. @@ -242,20 +248,6 @@ class Thread: public ThreadShadow { public: void set_last_handle_mark(HandleMark* mark) { _last_handle_mark = mark; } HandleMark* last_handle_mark() const { return _last_handle_mark; } - private: - -#ifdef ASSERT - ICRefillVerifier* _missed_ic_stub_refill_verifier; - - public: - ICRefillVerifier* missed_ic_stub_refill_verifier() { - return _missed_ic_stub_refill_verifier; - } - - void set_missed_ic_stub_refill_verifier(ICRefillVerifier* verifier) { - _missed_ic_stub_refill_verifier = verifier; - } -#endif // ASSERT private: // Used by SkipGCALot class. @@ -277,7 +269,7 @@ class Thread: public ThreadShadow { // is waiting to lock public: // Constructor - Thread(MEMFLAGS flag = mtThread); + Thread(MemTag mem_tag = mtThread); virtual ~Thread() = 0; // Thread is abstract. // Manage Thread::current() @@ -532,7 +524,7 @@ class Thread: public ThreadShadow { public: // Stack overflow support - address stack_base() const { assert(_stack_base != nullptr,"Sanity check"); return _stack_base; } + address stack_base() const DEBUG_ONLY(;) NOT_DEBUG({ return _stack_base; }) void set_stack_base(address base) { _stack_base = base; } size_t stack_size() const { return _stack_size; } void set_stack_size(size_t size) { _stack_size = size; } diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 8266bd86a9682..b2edad395cef0 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -65,6 +65,7 @@ #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/flags/jvmFlagLimit.hpp" #include "runtime/globals.hpp" +#include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/java.hpp" @@ -665,6 +666,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { log_info(os)("Initialized VM with process ID %d", os::current_process_id()); + if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && CreateCoredumpOnCrash) { + char buffer[2*JVM_MAXPATHLEN]; + os::check_core_dump_prerequisites(buffer, sizeof(buffer), true); + } + // Signal Dispatcher needs to be started before VMInit event is posted os::initialize_jdk_signal_support(CHECK_JNI_ERR); @@ -1028,7 +1034,9 @@ void Threads::add(JavaThread* p, bool force_daemon) { void Threads::remove(JavaThread* p, bool is_daemon) { // Extra scope needed for Thread_lock, so we can check // that we do not remove thread without safepoint code notice - { MonitorLocker ml(Threads_lock); + { + ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); + MonitorLocker ml(Threads_lock); if (ThreadIdTable::is_initialized()) { // This cleanup must be done before the current thread's GC barrier @@ -1076,7 +1084,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) { // Notify threads waiting in EscapeBarriers EscapeBarrier::thread_removed(p); - } // unlock Threads_lock + } // unlock Threads_lock and ThreadsLockThrottle_lock // Reduce the ObjectMonitor ceiling for the exiting thread. ObjectSynchronizer::dec_in_use_list_ceiling(); diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index 532a9231b70e8..eede52f00d566 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -109,7 +109,6 @@ template(PrintCompileQueue) \ template(PrintClassHierarchy) \ template(PrintClasses) \ - template(ICBufferFull) \ template(PrintMetadata) \ template(GTestExecuteAtSafepoint) \ template(GTestStopSafepoint) \ diff --git a/src/hotspot/share/runtime/vmOperations.hpp b/src/hotspot/share/runtime/vmOperations.hpp index 5ccf689eaf3d9..ea7f62df37db8 100644 --- a/src/hotspot/share/runtime/vmOperations.hpp +++ b/src/hotspot/share/runtime/vmOperations.hpp @@ -60,12 +60,6 @@ class VM_ForceSafepoint: public VM_EmptyOperation { VMOp_Type type() const { return VMOp_ForceSafepoint; } }; -// empty vm op, when forcing a safepoint due to inline cache buffers being full -class VM_ICBufferFull: public VM_EmptyOperation { - public: - VMOp_Type type() const { return VMOp_ICBufferFull; } -}; - class VM_ClearICs: public VM_Operation { private: bool _preserve_static_stubs; diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 483b96fccf382..3060e225427a8 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -373,9 +373,9 @@ /* CompressedOops */ \ /******************/ \ \ - static_field(CompressedOops, _narrow_oop._base, address) \ - static_field(CompressedOops, _narrow_oop._shift, int) \ - static_field(CompressedOops, _narrow_oop._use_implicit_null_checks, bool) \ + static_field(CompressedOops, _base, address) \ + static_field(CompressedOops, _shift, int) \ + static_field(CompressedOops, _use_implicit_null_checks, bool) \ \ /***************************/ \ /* CompressedKlassPointers */ \ @@ -1590,7 +1590,6 @@ declare_c2_type(StorePNode, StoreNode) \ declare_c2_type(StoreNNode, StoreNode) \ declare_c2_type(StoreNKlassNode, StoreNode) \ - declare_c2_type(StoreCMNode, StoreNode) \ declare_c2_type(SCMemProjNode, ProjNode) \ declare_c2_type(LoadStoreNode, Node) \ declare_c2_type(CompareAndSwapNode, LoadStoreConditionalNode) \ diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 7a2c083814614..78c655a43fcf2 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -138,9 +138,11 @@ void DCmd::register_dcmds(){ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); +#endif // LINUX +#if defined(LINUX) || defined(_WIN64) DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true,false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true,false)); -#endif // LINUX +#endif // LINUX or WINDOWS DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); @@ -899,7 +901,6 @@ void EventLogDCmd::execute(DCmdSource source, TRAPS) { int max = -1; if (max_value != nullptr) { char* endptr = nullptr; - int max; if (!parse_integer(max_value, &max)) { output()->print_cr("Invalid max option: \"%s\".", max_value); return; @@ -1173,7 +1174,7 @@ void CompilationMemoryStatisticDCmd::execute(DCmdSource source, TRAPS) { CompilationMemoryStatistic::print_all_by_size(output(), human_readable, minsize); } -#ifdef LINUX +#if defined(LINUX) || defined(_WIN64) SystemMapDCmd::SystemMapDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} @@ -1191,16 +1192,22 @@ SystemDumpMapDCmd::SystemDumpMapDCmd(outputStream* output, bool heap) : void SystemDumpMapDCmd::execute(DCmdSource source, TRAPS) { const char* name = _filename.value(); + if (name == nullptr || name[0] == 0) { + output()->print_cr("filename is empty or not specified. No file written"); + return; + } fileStream fs(name); if (fs.is_open()) { if (!MemTracker::enabled()) { output()->print_cr("(NMT is disabled, will not annotate mappings)."); } MemMapPrinter::print_all_mappings(&fs); +#ifndef _WIN64 // For the readers convenience, resolve path name. char tmp[JVM_MAXPATHLEN]; const char* absname = os::Posix::realpath(name, tmp, sizeof(tmp)); name = absname != nullptr ? absname : name; +#endif output()->print_cr("Memory map dumped to \"%s\".", name); } else { output()->print_cr("Failed to open \"%s\" for writing (%s).", name, os::strerror(errno)); diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index ec3469c8d1523..99b5a3510645a 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -981,14 +981,14 @@ class CompilationMemoryStatisticDCmd: public DCmdWithParser { virtual void execute(DCmdSource source, TRAPS); }; -#ifdef LINUX +#if defined(LINUX) || defined(_WIN64) class SystemMapDCmd : public DCmd { public: SystemMapDCmd(outputStream* output, bool heap); static const char* name() { return "System.map"; } static const char* description() { - return "Prints an annotated process memory map of the VM process (linux only)."; + return "Prints an annotated process memory map of the VM process (linux and Windows only)."; } static const char* impact() { return "Medium; can be high for very large java heaps."; } static const JavaPermission permission() { @@ -1006,7 +1006,7 @@ class SystemDumpMapDCmd : public DCmdWithParser { SystemDumpMapDCmd(outputStream* output, bool heap); static const char* name() { return "System.dump_map"; } static const char* description() { - return "Dumps an annotated process memory map to an output file (linux only)."; + return "Dumps an annotated process memory map to an output file (linux and Windows only)."; } static const char* impact() { return "Medium; can be high for very large java heaps."; } static const JavaPermission permission() { @@ -1017,6 +1017,6 @@ class SystemDumpMapDCmd : public DCmdWithParser { virtual void execute(DCmdSource source, TRAPS); }; -#endif // LINUX +#endif // LINUX or WINDOWS #endif // SHARE_SERVICES_DIAGNOSTICCOMMAND_HPP diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index 5b3749381a01b..10e1a804ad213 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -1512,6 +1512,38 @@ class ClassDumper : public KlassClosure { } }; +// Support class used to generate HPROF_LOAD_CLASS records + +class LoadedClassDumper : public LockedClassesDo { + private: + AbstractDumpWriter* _writer; + GrowableArray* _klass_map; + u4 _class_serial_num; + AbstractDumpWriter* writer() const { return _writer; } + void add_class_serial_number(Klass* k, int serial_num) { + _klass_map->at_put_grow(serial_num, k); + } + public: + LoadedClassDumper(AbstractDumpWriter* writer, GrowableArray* klass_map) + : _writer(writer), _klass_map(klass_map), _class_serial_num(0) {} + + void do_klass(Klass* k) { + // len of HPROF_LOAD_CLASS record + u4 remaining = 2 * oopSize + 2 * sizeof(u4); + DumperSupport::write_header(writer(), HPROF_LOAD_CLASS, remaining); + // class serial number is just a number + writer()->write_u4(++_class_serial_num); + // class ID + writer()->write_classID(k); + // add the Klass* and class serial number pair + add_class_serial_number(k, _class_serial_num); + writer()->write_u4(STACK_TRACE_ID); + // class name ID + Symbol* name = k->name(); + writer()->write_symbolID(name); + } +}; + // Support class used to generate HPROF_GC_ROOT_JNI_LOCAL records class JNILocalsDumper : public OopClosure { @@ -2190,9 +2222,7 @@ void DumpMerger::do_merge() { // The VM operation that performs the heap dump class VM_HeapDumper : public VM_GC_Operation, public WorkerTask, public UnmountedVThreadDumper { private: - static VM_HeapDumper* _global_dumper; - static DumpWriter* _global_writer; - DumpWriter* _local_writer; + DumpWriter* _writer; JavaThread* _oome_thread; Method* _oome_constructor; bool _gc_before_heap_dump; @@ -2218,33 +2248,13 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask, public Unmounte return Atomic::fetch_then_add(&_dump_seq, 1); } - // accessors and setters - static VM_HeapDumper* dumper() { assert(_global_dumper != nullptr, "Error"); return _global_dumper; } - static DumpWriter* writer() { assert(_global_writer != nullptr, "Error"); return _global_writer; } - - void set_global_dumper() { - assert(_global_dumper == nullptr, "Error"); - _global_dumper = this; - } - void set_global_writer() { - assert(_global_writer == nullptr, "Error"); - _global_writer = _local_writer; - } - void clear_global_dumper() { _global_dumper = nullptr; } - void clear_global_writer() { _global_writer = nullptr; } + DumpWriter* writer() const { return _writer; } bool skip_operation() const; - // writes a HPROF_LOAD_CLASS record to global writer - static void do_load_class(Klass* k); - // HPROF_GC_ROOT_THREAD_OBJ records for platform and mounted virtual threads void dump_threads(AbstractDumpWriter* writer); - void add_class_serial_number(Klass* k, int serial_num) { - _klass_map->at_put_grow(serial_num, k); - } - bool is_oom_thread(JavaThread* thread) const { return thread == _oome_thread && _oome_constructor != nullptr; } @@ -2259,7 +2269,7 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask, public Unmounte 0 /* total full collections, dummy, ignored */, gc_before_heap_dump), WorkerTask("dump heap") { - _local_writer = writer; + _writer = writer; _gc_before_heap_dump = gc_before_heap_dump; _klass_map = new (mtServiceability) GrowableArray(INITIAL_CLASS_COUNT, mtServiceability); @@ -2313,9 +2323,6 @@ class VM_HeapDumper : public VM_GC_Operation, public WorkerTask, public Unmounte void dump_vthread(oop vt, AbstractDumpWriter* segment_writer); }; -VM_HeapDumper* VM_HeapDumper::_global_dumper = nullptr; -DumpWriter* VM_HeapDumper::_global_writer = nullptr; - bool VM_HeapDumper::skip_operation() const { return false; } @@ -2329,31 +2336,6 @@ void DumperSupport::end_of_dump(AbstractDumpWriter* writer) { writer->write_u4(0); } -// writes a HPROF_LOAD_CLASS record for the class -void VM_HeapDumper::do_load_class(Klass* k) { - static u4 class_serial_num = 0; - - // len of HPROF_LOAD_CLASS record - u4 remaining = 2*oopSize + 2*sizeof(u4); - - DumperSupport::write_header(writer(), HPROF_LOAD_CLASS, remaining); - - // class serial number is just a number - writer()->write_u4(++class_serial_num); - - // class ID - writer()->write_classID(k); - - // add the Klass* and class serial number pair - dumper()->add_class_serial_number(k, class_serial_num); - - writer()->write_u4(STACK_TRACE_ID); - - // class name ID - Symbol* name = k->name(); - writer()->write_symbolID(name); -} - // Write a HPROF_GC_ROOT_THREAD_OBJ record for platform/carrier and mounted virtual threads. // Then walk the stack so that locals and JNI locals are dumped. void VM_HeapDumper::dump_threads(AbstractDumpWriter* writer) { @@ -2430,11 +2412,6 @@ void VM_HeapDumper::doit() { } } - // At this point we should be the only dumper active, so - // the following should be safe. - set_global_dumper(); - set_global_writer(); - WorkerThreads* workers = ch->safepoint_workers(); prepare_parallel_dump(workers); @@ -2446,10 +2423,6 @@ void VM_HeapDumper::doit() { workers->run_task(this, _num_dumper_threads); _poi = nullptr; } - - // Now we clear the global variables, so that a future dumper can run. - clear_global_dumper(); - clear_global_writer(); } void VM_HeapDumper::work(uint worker_id) { @@ -2480,8 +2453,8 @@ void VM_HeapDumper::work(uint worker_id) { // write HPROF_LOAD_CLASS records { - LockedClassesDo locked_load_classes(&do_load_class); - ClassLoaderDataGraph::classes_do(&locked_load_classes); + LoadedClassDumper loaded_class_dumper(writer(), _klass_map); + ClassLoaderDataGraph::classes_do(&loaded_class_dumper); } // write HPROF_FRAME and HPROF_TRACE records diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 09cb7ffb25a71..a1ad0a910f6d6 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -32,7 +32,7 @@ #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "oops/instanceKlass.hpp" #include "oops/klass.inline.hpp" #include "oops/objArrayKlass.hpp" diff --git a/src/hotspot/share/utilities/bitMap.cpp b/src/hotspot/share/utilities/bitMap.cpp index 6ea473dda48f4..423bc22cd9d6e 100644 --- a/src/hotspot/share/utilities/bitMap.cpp +++ b/src/hotspot/share/utilities/bitMap.cpp @@ -173,8 +173,8 @@ bm_word_t* ResourceBitMap::reallocate(bm_word_t* old_map, size_t old_size_in_wor return pseudo_reallocate(*this, old_map, old_size_in_words, new_size_in_words); } -CHeapBitMap::CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags, bool clear) - : GrowableBitMap(), _flags(flags) { +CHeapBitMap::CHeapBitMap(idx_t size_in_bits, MemTag mem_tag, bool clear) + : GrowableBitMap(), _mem_tag(mem_tag) { initialize(size_in_bits, clear); } @@ -183,7 +183,7 @@ CHeapBitMap::~CHeapBitMap() { } bm_word_t* CHeapBitMap::allocate(idx_t size_in_words) const { - return MallocArrayAllocator::allocate(size_in_words, _flags); + return MallocArrayAllocator::allocate(size_in_words, _mem_tag); } // GrowableBitMap::resize uses free(ptr, size) for T as CHeapBitMap, ArenaBitMap and ResourceBitMap allocators. @@ -193,7 +193,7 @@ void CHeapBitMap::free(bm_word_t* map, idx_t size_in_words) const { } bm_word_t* CHeapBitMap::reallocate(bm_word_t* map, size_t old_size_in_words, size_t new_size_in_words) const { - return MallocArrayAllocator::reallocate(map, new_size_in_words, _flags); + return MallocArrayAllocator::reallocate(map, new_size_in_words, _mem_tag); } #ifdef ASSERT diff --git a/src/hotspot/share/utilities/bitMap.hpp b/src/hotspot/share/utilities/bitMap.hpp index 3363653648557..1c69a0d43f832 100644 --- a/src/hotspot/share/utilities/bitMap.hpp +++ b/src/hotspot/share/utilities/bitMap.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_UTILITIES_BITMAP_HPP #define SHARE_UTILITIES_BITMAP_HPP -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" @@ -638,16 +638,16 @@ class ResourceBitMap : public GrowableBitMap { // A BitMap with storage in the CHeap. class CHeapBitMap : public GrowableBitMap { - // NMT memory type - const MEMFLAGS _flags; + // NMT memory tag + const MemTag _mem_tag; // Don't allow copy or assignment, to prevent the // allocated memory from leaking out to other instances. NONCOPYABLE(CHeapBitMap); public: - explicit CHeapBitMap(MEMFLAGS flags) : GrowableBitMap(), _flags(flags) {} - CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags, bool clear = true); + explicit CHeapBitMap(MemTag mem_tag) : GrowableBitMap(), _mem_tag(mem_tag) {} + CHeapBitMap(idx_t size_in_bits, MemTag mem_tag, bool clear = true); ~CHeapBitMap(); bm_word_t* allocate(idx_t size_in_words) const; diff --git a/src/hotspot/share/utilities/chunkedList.hpp b/src/hotspot/share/utilities/chunkedList.hpp index 9a600e4ce1b50..3269c305e783c 100644 --- a/src/hotspot/share/utilities/chunkedList.hpp +++ b/src/hotspot/share/utilities/chunkedList.hpp @@ -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 @@ -28,7 +28,7 @@ #include "memory/allocation.hpp" #include "utilities/debug.hpp" -template class ChunkedList : public CHeapObj { +template class ChunkedList : public CHeapObj { template friend class TestChunkedList; static const size_t BufferSize = 64; @@ -36,8 +36,8 @@ template class ChunkedList : public CHeapObj { T _values[BufferSize]; T* _top; - ChunkedList* _next_used; - ChunkedList* _next_free; + ChunkedList* _next_used; + ChunkedList* _next_free; T const * end() const { return &_values[BufferSize]; @@ -62,11 +62,11 @@ template class ChunkedList : public CHeapObj { _top++; } - void set_next_used(ChunkedList* buffer) { _next_used = buffer; } - void set_next_free(ChunkedList* buffer) { _next_free = buffer; } + void set_next_used(ChunkedList* buffer) { _next_used = buffer; } + void set_next_free(ChunkedList* buffer) { _next_free = buffer; } - ChunkedList* next_used() const { return _next_used; } - ChunkedList* next_free() const { return _next_free; } + ChunkedList* next_used() const { return _next_used; } + ChunkedList* next_free() const { return _next_free; } size_t size() const { return pointer_delta(_top, _values, sizeof(T)); diff --git a/src/hotspot/share/utilities/concurrentHashTable.hpp b/src/hotspot/share/utilities/concurrentHashTable.hpp index 4e506d5fe84f1..402a767c0b8ab 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.hpp @@ -40,8 +40,8 @@ class Thread; class Mutex; -template -class ConcurrentHashTable : public CHeapObj { +template +class ConcurrentHashTable : public CHeapObj { typedef typename CONFIG::Value VALUE; private: // _stats_rate is null if statistics are not enabled. @@ -61,7 +61,7 @@ class ConcurrentHashTable : public CHeapObj { TableStatistics statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f); // This is the internal node structure. - // Only constructed with placement new from memory allocated with MEMFLAGS of + // Only constructed with placement new from memory allocated with MemTag of // the InternalTable or user-defined memory. class Node { private: @@ -105,7 +105,7 @@ class ConcurrentHashTable : public CHeapObj { } }; - // Only constructed with placement new from an array allocated with MEMFLAGS + // Only constructed with placement new from an array allocated with MemTag // of InternalTable. class Bucket { private: @@ -202,7 +202,7 @@ class ConcurrentHashTable : public CHeapObj { // - Re-size can only change the size into half or double // (any pow 2 would also be possible). // - Use masking of hash for bucket index. - class InternalTable : public CHeapObj { + class InternalTable : public CHeapObj { private: Bucket* _buckets; // Bucket array. public: @@ -277,10 +277,10 @@ class ConcurrentHashTable : public CHeapObj { class ScopedCS: public StackObj { protected: Thread* _thread; - ConcurrentHashTable* _cht; + ConcurrentHashTable* _cht; GlobalCounter::CSContext _cs_context; public: - ScopedCS(Thread* thread, ConcurrentHashTable* cht); + ScopedCS(Thread* thread, ConcurrentHashTable* cht); ~ScopedCS(); }; @@ -372,7 +372,7 @@ class ConcurrentHashTable : public CHeapObj { // Check for dead items in a bucket. template size_t delete_check_nodes(Bucket* bucket, EVALUATE_FUNC& eval_f, - size_t num_del, Node** ndel, GrowableArrayCHeap& ndel_heap); + size_t num_del, Node** ndel, GrowableArrayCHeap& ndel_heap); // Check for dead items in this table. During shrink/grow we cannot guarantee // that we only visit nodes once. To keep it simple caller will have locked @@ -539,12 +539,12 @@ class ConcurrentHashTable : public CHeapObj { // Moves all nodes from this table to to_cht with new hash code. // Must be done at a safepoint. - void rehash_nodes_to(Thread* thread, ConcurrentHashTable* to_cht); + void rehash_nodes_to(Thread* thread, ConcurrentHashTable* to_cht); // Scoped multi getter. class MultiGetHandle : private ScopedCS { public: - MultiGetHandle(Thread* thread, ConcurrentHashTable* cht) + MultiGetHandle(Thread* thread, ConcurrentHashTable* cht) : ScopedCS(thread, cht) {} // In the MultiGetHandle scope you can lookup items matching LOOKUP_FUNC. // The VALUEs are safe as long as you never save the VALUEs outside the diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index f035aeae44847..5656655275adb 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -58,28 +58,28 @@ static void* const POISON_PTR = (void*)0xffbadbac; #endif // Node -template -inline typename ConcurrentHashTable::Node* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::Node* +ConcurrentHashTable:: Node::next() const { return Atomic::load_acquire(&_next); } // Bucket -template -inline typename ConcurrentHashTable::Node* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::Node* +ConcurrentHashTable:: Bucket::first_raw() const { return Atomic::load_acquire(&_first); } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: Bucket::release_assign_node_ptr( - typename ConcurrentHashTable::Node* const volatile * dst, - typename ConcurrentHashTable::Node* node) const + typename ConcurrentHashTable::Node* const volatile * dst, + typename ConcurrentHashTable::Node* node) const { // Due to this assert this methods is not static. assert(is_locked(), "Must be locked."); @@ -87,31 +87,31 @@ inline void ConcurrentHashTable:: Atomic::release_store(tmp, clear_set_state(node, *dst)); } -template -inline typename ConcurrentHashTable::Node* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::Node* +ConcurrentHashTable:: Bucket::first() const { // We strip the states bit before returning the ptr. return clear_state(Atomic::load_acquire(&_first)); } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: Bucket::have_redirect() const { return is_state(first_raw(), STATE_REDIRECT_BIT); } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: Bucket::is_locked() const { return is_state(first_raw(), STATE_LOCK_BIT); } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: Bucket::lock() { int i = 0; @@ -128,10 +128,10 @@ inline void ConcurrentHashTable:: } } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: Bucket::release_assign_last_node_next( - typename ConcurrentHashTable::Node* node) + typename ConcurrentHashTable::Node* node) { assert(is_locked(), "Must be locked."); Node* const volatile * ret = first_ptr(); @@ -141,10 +141,10 @@ inline void ConcurrentHashTable:: release_assign_node_ptr(ret, node); } -template -inline bool ConcurrentHashTable:: - Bucket::cas_first(typename ConcurrentHashTable::Node* node, - typename ConcurrentHashTable::Node* expect +template +inline bool ConcurrentHashTable:: + Bucket::cas_first(typename ConcurrentHashTable::Node* node, + typename ConcurrentHashTable::Node* expect ) { if (is_locked()) { @@ -156,8 +156,8 @@ inline bool ConcurrentHashTable:: return false; } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: Bucket::trylock() { if (is_locked()) { @@ -171,8 +171,8 @@ inline bool ConcurrentHashTable:: return false; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: Bucket::unlock() { assert(is_locked(), "Must be locked."); @@ -181,8 +181,8 @@ inline void ConcurrentHashTable:: Atomic::release_store(&_first, clear_state(first())); } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: Bucket::redirect() { assert(is_locked(), "Must be locked."); @@ -190,15 +190,15 @@ inline void ConcurrentHashTable:: } // InternalTable -template -inline ConcurrentHashTable:: +template +inline ConcurrentHashTable:: InternalTable::InternalTable(size_t log2_size) : _log2_size(log2_size), _size(((size_t)1ul) << _log2_size), _hash_mask(~(~((size_t)0) << _log2_size)) { assert(_log2_size >= SIZE_SMALL_LOG2 && _log2_size <= SIZE_BIG_LOG2, "Bad size"); - _buckets = NEW_C_HEAP_ARRAY(Bucket, _size, F); + _buckets = NEW_C_HEAP_ARRAY(Bucket, _size, MT); // Use placement new for each element instead of new[] which could use more // memory than allocated. for (size_t i = 0; i < _size; ++i) { @@ -206,17 +206,17 @@ inline ConcurrentHashTable:: } } -template -inline ConcurrentHashTable:: +template +inline ConcurrentHashTable:: InternalTable::~InternalTable() { FREE_C_HEAP_ARRAY(Bucket, _buckets); } // ScopedCS -template -inline ConcurrentHashTable:: - ScopedCS::ScopedCS(Thread* thread, ConcurrentHashTable* cht) +template +inline ConcurrentHashTable:: + ScopedCS::ScopedCS(Thread* thread, ConcurrentHashTable* cht) : _thread(thread), _cht(cht), _cs_context(GlobalCounter::critical_section_begin(_thread)) @@ -227,25 +227,25 @@ inline ConcurrentHashTable:: } } -template -inline ConcurrentHashTable:: +template +inline ConcurrentHashTable:: ScopedCS::~ScopedCS() { GlobalCounter::critical_section_end(_thread, _cs_context); } -template +template template -inline typename CONFIG::Value* ConcurrentHashTable:: +inline typename CONFIG::Value* ConcurrentHashTable:: MultiGetHandle::get(LOOKUP_FUNC& lookup_f, bool* grow_hint) { return ScopedCS::_cht->internal_get(ScopedCS::_thread, lookup_f, grow_hint); } // HaveDeletables -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: HaveDeletables::have_deletable(Bucket* bucket, EVALUATE_FUNC& eval_f, Bucket* prefetch_bucket) @@ -271,9 +271,9 @@ inline bool ConcurrentHashTable:: return false; } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: HaveDeletables::have_deletable(Bucket* bucket, EVALUATE_FUNC& eval_f, Bucket* preb) @@ -287,8 +287,8 @@ inline bool ConcurrentHashTable:: } // ConcurrentHashTable -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: write_synchonize_on_visible_epoch(Thread* thread) { assert(_resize_lock_owner == thread, "Re-size lock not held"); @@ -304,8 +304,8 @@ inline void ConcurrentHashTable:: GlobalCounter::write_synchronize(); } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: try_resize_lock(Thread* locker) { if (_resize_lock->try_lock()) { @@ -323,8 +323,8 @@ inline bool ConcurrentHashTable:: return true; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: lock_resize_lock(Thread* locker) { size_t i = 0; @@ -348,8 +348,8 @@ inline void ConcurrentHashTable:: _invisible_epoch = nullptr; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: unlock_resize_lock(Thread* locker) { _invisible_epoch = nullptr; @@ -358,8 +358,8 @@ inline void ConcurrentHashTable:: _resize_lock->unlock(); } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: free_nodes() { // We assume we are not MT during freeing. @@ -374,25 +374,25 @@ inline void ConcurrentHashTable:: } } -template -inline typename ConcurrentHashTable::InternalTable* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::InternalTable* +ConcurrentHashTable:: get_table() const { return Atomic::load_acquire(&_table); } -template -inline typename ConcurrentHashTable::InternalTable* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::InternalTable* +ConcurrentHashTable:: get_new_table() const { return Atomic::load_acquire(&_new_table); } -template -inline typename ConcurrentHashTable::InternalTable* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::InternalTable* +ConcurrentHashTable:: set_table_from_new() { InternalTable* old_table = _table; @@ -406,8 +406,8 @@ ConcurrentHashTable:: return old_table; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: internal_grow_range(Thread* thread, size_t start, size_t stop) { assert(stop <= _table->_size, "Outside backing array"); @@ -446,9 +446,9 @@ inline void ConcurrentHashTable:: } } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: internal_remove(Thread* thread, LOOKUP_FUNC& lookup_f, DELETE_FUNC& delete_f) { Bucket* bucket = get_bucket_locked(thread, lookup_f.get_hash()); @@ -478,9 +478,9 @@ inline bool ConcurrentHashTable:: return true; } -template +template template -inline void ConcurrentHashTable:: +inline void ConcurrentHashTable:: do_bulk_delete_locked_for(Thread* thread, size_t start_idx, size_t stop_idx, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f, bool is_mt) { @@ -513,7 +513,7 @@ inline void ConcurrentHashTable:: // We left critical section but the bucket cannot be removed while we hold // the _resize_lock. bucket->lock(); - GrowableArrayCHeap extra(0); // use this buffer if StackBufferSize is not enough + GrowableArrayCHeap extra(0); // use this buffer if StackBufferSize is not enough size_t nd = delete_check_nodes(bucket, eval_f, StackBufferSize, ndel_stack, extra); bucket->unlock(); if (is_mt) { @@ -533,9 +533,9 @@ inline void ConcurrentHashTable:: GlobalCounter::critical_section_end(thread, cs_context); } -template +template template -inline void ConcurrentHashTable:: +inline void ConcurrentHashTable:: delete_in_bucket(Thread* thread, Bucket* bucket, LOOKUP_FUNC& lookup_f) { assert(bucket->is_locked(), "Must be locked."); @@ -568,9 +568,9 @@ inline void ConcurrentHashTable:: } } -template -inline typename ConcurrentHashTable::Bucket* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::Bucket* +ConcurrentHashTable:: get_bucket(uintx hash) const { InternalTable* table = get_table(); @@ -582,9 +582,9 @@ ConcurrentHashTable:: return bucket; } -template -inline typename ConcurrentHashTable::Bucket* -ConcurrentHashTable:: +template +inline typename ConcurrentHashTable::Bucket* +ConcurrentHashTable:: get_bucket_locked(Thread* thread, const uintx hash) { Bucket* bucket; @@ -613,10 +613,10 @@ ConcurrentHashTable:: } // Always called within critical section -template +template template -typename ConcurrentHashTable::Node* -ConcurrentHashTable:: +typename ConcurrentHashTable::Node* +ConcurrentHashTable:: get_node(const Bucket* const bucket, LOOKUP_FUNC& lookup_f, bool* have_dead, size_t* loops) const { @@ -638,8 +638,8 @@ ConcurrentHashTable:: return node; } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: unzip_bucket(Thread* thread, InternalTable* old_table, InternalTable* new_table, size_t even_index, size_t odd_index) { @@ -698,8 +698,8 @@ inline bool ConcurrentHashTable:: return true; } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: internal_shrink_prolog(Thread* thread, size_t log2_size) { if (!try_resize_lock(thread)) { @@ -715,8 +715,8 @@ inline bool ConcurrentHashTable:: return true; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: internal_shrink_epilog(Thread* thread) { assert(_resize_lock_owner == thread, "Re-size lock not held"); @@ -734,8 +734,8 @@ inline void ConcurrentHashTable:: delete old_table; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: internal_shrink_range(Thread* thread, size_t start, size_t stop) { // The state is also copied here. @@ -771,8 +771,8 @@ inline void ConcurrentHashTable:: } } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: internal_shrink(Thread* thread, size_t log2_size) { if (!internal_shrink_prolog(thread, log2_size)) { @@ -786,8 +786,8 @@ inline bool ConcurrentHashTable:: return true; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: internal_reset(size_t log2_size) { assert(_table != nullptr, "table failed"); @@ -800,8 +800,8 @@ inline void ConcurrentHashTable:: Atomic::release_store(&_table, table); } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: internal_grow_prolog(Thread* thread, size_t log2_size) { // This double checking of _size_limit_reached/is_max_size_reached() @@ -826,8 +826,8 @@ inline bool ConcurrentHashTable:: return true; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: internal_grow_epilog(Thread* thread) { assert(_resize_lock_owner == thread, "Should be locked"); @@ -844,8 +844,8 @@ inline void ConcurrentHashTable:: delete old_table; } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: internal_grow(Thread* thread, size_t log2_size) { if (!internal_grow_prolog(thread, log2_size)) { @@ -860,9 +860,9 @@ inline bool ConcurrentHashTable:: } // Always called within critical section -template +template template -inline typename CONFIG::Value* ConcurrentHashTable:: +inline typename CONFIG::Value* ConcurrentHashTable:: internal_get(Thread* thread, LOOKUP_FUNC& lookup_f, bool* grow_hint) { bool clean = false; @@ -881,9 +881,9 @@ inline typename CONFIG::Value* ConcurrentHashTable:: return ret; } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: internal_insert_get(Thread* thread, LOOKUP_FUNC& lookup_f, const VALUE& value, FOUND_FUNC& foundf, bool* grow_hint, bool* clean_hint) { @@ -949,9 +949,9 @@ inline bool ConcurrentHashTable:: return ret; } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: visit_nodes(Bucket* bucket, FUNC& visitor_f) { Node* current_node = bucket->first(); @@ -965,9 +965,9 @@ inline bool ConcurrentHashTable:: return true; } -template +template template -inline void ConcurrentHashTable:: +inline void ConcurrentHashTable:: do_scan_locked(Thread* thread, FUNC& scan_f) { assert(_resize_lock_owner == thread, "Re-size lock not held"); @@ -982,11 +982,11 @@ inline void ConcurrentHashTable:: } /* ends critical section */ } -template +template template -inline size_t ConcurrentHashTable:: +inline size_t ConcurrentHashTable:: delete_check_nodes(Bucket* bucket, EVALUATE_FUNC& eval_f, - size_t num_del, Node** ndel, GrowableArrayCHeap& extra) + size_t num_del, Node** ndel, GrowableArrayCHeap& extra) { size_t dels = 0; Node* const volatile * rem_n_prev = bucket->first_ptr(); @@ -1013,8 +1013,8 @@ inline size_t ConcurrentHashTable:: } // Constructor -template -inline ConcurrentHashTable:: +template +inline ConcurrentHashTable:: ConcurrentHashTable(size_t log2size, size_t log2size_limit, size_t grow_hint, bool enable_statistics, Mutex::Rank rank, void* context) : _context(context), _new_table(nullptr), _log2_size_limit(log2size_limit), _log2_start_size(log2size), _grow_hint(grow_hint), @@ -1032,8 +1032,8 @@ ConcurrentHashTable(size_t log2size, size_t log2size_limit, size_t grow_hint, bo _size_limit_reached = _table->_log2_size == _log2_size_limit; } -template -inline ConcurrentHashTable:: +template +inline ConcurrentHashTable:: ~ConcurrentHashTable() { delete _resize_lock; @@ -1042,24 +1042,24 @@ inline ConcurrentHashTable:: delete _stats_rate; } -template -inline size_t ConcurrentHashTable:: +template +inline size_t ConcurrentHashTable:: get_mem_size(Thread* thread) { ScopedCS cs(thread, this); return sizeof(*this) + _table->get_mem_size(); } -template -inline size_t ConcurrentHashTable:: +template +inline size_t ConcurrentHashTable:: get_size_log2(Thread* thread) { ScopedCS cs(thread, this); return _table->_log2_size; } -template -inline size_t ConcurrentHashTable:: +template +inline size_t ConcurrentHashTable:: get_dynamic_node_size(size_t value_size) { assert(Node::is_dynamic_sized_value_compatible(), "VALUE must be compatible"); @@ -1067,8 +1067,8 @@ inline size_t ConcurrentHashTable:: return sizeof(Node) - sizeof(VALUE) + value_size; } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: shrink(Thread* thread, size_t size_limit_log2) { size_t tmp = size_limit_log2 == 0 ? _log2_start_size : size_limit_log2; @@ -1076,25 +1076,25 @@ inline bool ConcurrentHashTable:: return ret; } -template -inline void ConcurrentHashTable:: +template +inline void ConcurrentHashTable:: unsafe_reset(size_t size_log2) { size_t tmp = size_log2 == 0 ? _log2_start_size : size_log2; internal_reset(tmp); } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: grow(Thread* thread, size_t size_limit_log2) { size_t tmp = size_limit_log2 == 0 ? _log2_size_limit : size_limit_log2; return internal_grow(thread, tmp); } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: get(Thread* thread, LOOKUP_FUNC& lookup_f, FOUND_FUNC& found_f, bool* grow_hint) { bool ret = false; @@ -1107,8 +1107,8 @@ inline bool ConcurrentHashTable:: return ret; } -template -inline bool ConcurrentHashTable:: +template +inline bool ConcurrentHashTable:: unsafe_insert(const VALUE& value) { bool dead_hash = false; size_t hash = CONFIG::get_hash(value, &dead_hash); @@ -1128,9 +1128,9 @@ inline bool ConcurrentHashTable:: return true; } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: try_scan(Thread* thread, SCAN_FUNC& scan_f) { if (!try_resize_lock(thread)) { @@ -1141,9 +1141,9 @@ inline bool ConcurrentHashTable:: return true; } -template +template template -inline void ConcurrentHashTable:: +inline void ConcurrentHashTable:: do_scan(Thread* thread, SCAN_FUNC& scan_f) { assert(!SafepointSynchronize::is_at_safepoint(), @@ -1155,9 +1155,9 @@ inline void ConcurrentHashTable:: assert(_resize_lock_owner != thread, "Re-size lock held"); } -template +template template -inline void ConcurrentHashTable:: +inline void ConcurrentHashTable:: do_safepoint_scan(SCAN_FUNC& scan_f) { // We only allow this method to be used during a safepoint. @@ -1179,9 +1179,9 @@ inline void ConcurrentHashTable:: do_scan_for_range(scan_f, 0, table->_size, table); } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: do_scan_for_range(FUNC& scan_f, size_t start_idx, size_t stop_idx, InternalTable* table) { assert(start_idx < stop_idx, "Must be"); @@ -1204,9 +1204,9 @@ inline bool ConcurrentHashTable:: return true; } -template +template template -inline bool ConcurrentHashTable:: +inline bool ConcurrentHashTable:: try_bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f) { if (!try_resize_lock(thread)) { @@ -1218,9 +1218,9 @@ inline bool ConcurrentHashTable:: return true; } -template +template template -inline void ConcurrentHashTable:: +inline void ConcurrentHashTable:: bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f) { assert(!SafepointSynchronize::is_at_safepoint(), @@ -1230,9 +1230,9 @@ inline void ConcurrentHashTable:: unlock_resize_lock(thread); } -template +template template -inline TableStatistics ConcurrentHashTable:: +inline TableStatistics ConcurrentHashTable:: statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f) { constexpr size_t batch_size = 128; @@ -1268,9 +1268,9 @@ inline TableStatistics ConcurrentHashTable:: } } -template +template template -inline TableStatistics ConcurrentHashTable:: +inline TableStatistics ConcurrentHashTable:: statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old) { if (!try_resize_lock(thread)) { @@ -1283,9 +1283,9 @@ inline TableStatistics ConcurrentHashTable:: return ts; } -template +template template -inline void ConcurrentHashTable:: +inline void ConcurrentHashTable:: statistics_to(Thread* thread, VALUE_SIZE_FUNC& vs_f, outputStream* st, const char* table_name) { @@ -1300,9 +1300,9 @@ inline void ConcurrentHashTable:: ts.print(st, table_name); } -template -inline void ConcurrentHashTable:: - rehash_nodes_to(Thread* thread, ConcurrentHashTable* to_cht) +template +inline void ConcurrentHashTable:: + rehash_nodes_to(Thread* thread, ConcurrentHashTable* to_cht) { assert(is_safepoint_safe(), "rehashing is at a safepoint - cannot be resizing"); assert(_new_table == nullptr || _new_table == POISON_PTR, "Must be null"); diff --git a/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp b/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp index db9dcae987c3f..44b2f91c3f231 100644 --- a/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp @@ -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 @@ -35,10 +35,10 @@ // operations, which they are serialized with each other. // Base class for pause and/or parallel bulk operations. -template -class ConcurrentHashTable::BucketsOperation { +template +class ConcurrentHashTable::BucketsOperation { protected: - ConcurrentHashTable* _cht; + ConcurrentHashTable* _cht; class InternalTableClaimer { volatile size_t _next; @@ -88,7 +88,7 @@ class ConcurrentHashTable::BucketsOperation { InternalTableClaimer _table_claimer; bool _is_mt; - BucketsOperation(ConcurrentHashTable* cht, bool is_mt = false) + BucketsOperation(ConcurrentHashTable* cht, bool is_mt = false) : _cht(cht), _table_claimer(DEFAULT_TASK_SIZE_LOG2, _cht->_table), _is_mt(is_mt) {} // Returns true if you succeeded to claim the range start -> (stop-1). @@ -146,12 +146,12 @@ class ConcurrentHashTable::BucketsOperation { }; // For doing pausable/parallel bulk delete. -template -class ConcurrentHashTable::BulkDeleteTask : +template +class ConcurrentHashTable::BulkDeleteTask : public BucketsOperation { public: - BulkDeleteTask(ConcurrentHashTable* cht, bool is_mt = false) + BulkDeleteTask(ConcurrentHashTable* cht, bool is_mt = false) : BucketsOperation(cht, is_mt) { } // Before start prepare must be called. @@ -190,12 +190,12 @@ class ConcurrentHashTable::BulkDeleteTask : } }; -template -class ConcurrentHashTable::GrowTask : +template +class ConcurrentHashTable::GrowTask : public BucketsOperation { public: - GrowTask(ConcurrentHashTable* cht) : BucketsOperation(cht) { + GrowTask(ConcurrentHashTable* cht) : BucketsOperation(cht) { } // Before start prepare must be called. bool prepare(Thread* thread) { @@ -229,8 +229,8 @@ class ConcurrentHashTable::GrowTask : } }; -template -class ConcurrentHashTable::ScanTask : +template +class ConcurrentHashTable::ScanTask : public BucketsOperation { // If there is a paused resize, we need to scan items already @@ -255,11 +255,11 @@ class ConcurrentHashTable::ScanTask : } public: - ScanTask(ConcurrentHashTable* cht, size_t claim_size) : BucketsOperation(cht), _new_table_claimer() { + ScanTask(ConcurrentHashTable* cht, size_t claim_size) : BucketsOperation(cht), _new_table_claimer() { set(cht, claim_size); } - void set(ConcurrentHashTable* cht, size_t claim_size) { + void set(ConcurrentHashTable* cht, size_t claim_size) { this->_table_claimer.set(claim_size, cht->get_table()); InternalTable* new_table = cht->get_new_table(); diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 9f839fc1a136b..119d1cf17fd2d 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -711,7 +711,7 @@ static ucontext_t g_stored_assertion_context; void initialize_assert_poison() { char* page = os::reserve_memory(os::vm_page_size()); if (page) { - MemTracker::record_virtual_memory_type(page, mtInternal); + MemTracker::record_virtual_memory_tag(page, mtInternal); if (os::commit_memory(page, os::vm_page_size(), false) && os::protect_memory(page, os::vm_page_size(), os::MEM_PROT_NONE)) { g_assert_poison = page; diff --git a/src/hotspot/share/utilities/growableArray.cpp b/src/hotspot/share/utilities/growableArray.cpp index 8e1057dd9f846..60ed0a477e8e0 100644 --- a/src/hotspot/share/utilities/growableArray.cpp +++ b/src/hotspot/share/utilities/growableArray.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,13 +42,13 @@ void* GrowableArrayArenaAllocator::allocate(int max, int element_size, Arena* ar return arena->Amalloc(byte_size); } -void* GrowableArrayCHeapAllocator::allocate(int max, int element_size, MEMFLAGS memflags) { +void* GrowableArrayCHeapAllocator::allocate(int max, int element_size, MemTag mem_tag) { assert(max >= 0, "integer overflow"); size_t byte_size = element_size * (size_t) max; - // memory type has to be specified for C heap allocation - assert(memflags != mtNone, "memory type not specified for C heap object"); - return (void*)AllocateHeap(byte_size, memflags); + // memory tag has to be specified for C heap allocation + assert(mem_tag != mtNone, "memory tag not specified for C heap object"); + return (void*)AllocateHeap(byte_size, mem_tag); } void GrowableArrayCHeapAllocator::deallocate(void* elements) { diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 79d03f58a9e7d..2eb8e6fd09e12 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -595,7 +595,7 @@ class GrowableArrayArenaAllocator { // CHeap allocator class GrowableArrayCHeapAllocator { public: - static void* allocate(int max, int element_size, MEMFLAGS memflags); + static void* allocate(int max, int element_size, MemTag mem_tag); static void deallocate(void* mem); }; @@ -628,9 +628,9 @@ class GrowableArrayMetadata { } // CHeap allocation - static uintptr_t bits(MEMFLAGS memflags) { - assert(memflags != mtNone, "Must provide a proper MEMFLAGS"); - return (uintptr_t(memflags) << 1) | 1; + static uintptr_t bits(MemTag mem_tag) { + assert(mem_tag != mtNone, "Must provide a proper MemTag"); + return (uintptr_t(mem_tag) << 1) | 1; } // Arena allocation @@ -653,8 +653,8 @@ class GrowableArrayMetadata { } // CHeap allocation - GrowableArrayMetadata(MEMFLAGS memflags) : - _bits(bits(memflags)) + GrowableArrayMetadata(MemTag mem_tag) : + _bits(bits(mem_tag)) debug_only(COMMA _nesting_check(false)) { } @@ -683,14 +683,14 @@ class GrowableArrayMetadata { bool on_arena() const { return (_bits & 1) == 0 && _bits != 0; } Arena* arena() const { return (Arena*)_bits; } - MEMFLAGS memflags() const { return MEMFLAGS(_bits >> 1); } + MemTag mem_tag() const { return MemTag(_bits >> 1); } }; // THE GrowableArray. // // Supports multiple allocation strategies: // - Resource stack allocation: if no extra argument is provided -// - CHeap allocation: if memflags is provided +// - CHeap allocation: if mem_tag is provided // - Arena allocation: if an arena is provided // // There are some drawbacks of using GrowableArray, that are removed in some @@ -712,8 +712,8 @@ class GrowableArray : public GrowableArrayWithAllocator> { return (E*)GrowableArrayResourceAllocator::allocate(max, sizeof(E)); } - static E* allocate(int max, MEMFLAGS memflags) { - return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), memflags); + static E* allocate(int max, MemTag mem_tag) { + return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag); } static E* allocate(int max, Arena* arena) { @@ -736,7 +736,7 @@ class GrowableArray : public GrowableArrayWithAllocator> { } if (on_C_heap()) { - return allocate(this->_capacity, _metadata.memflags()); + return allocate(this->_capacity, _metadata.mem_tag()); } assert(on_arena(), "Sanity"); @@ -760,11 +760,11 @@ class GrowableArray : public GrowableArrayWithAllocator> { init_checks(); } - GrowableArray(int initial_capacity, MEMFLAGS memflags) : + GrowableArray(int initial_capacity, MemTag mem_tag) : GrowableArrayWithAllocator( - allocate(initial_capacity, memflags), + allocate(initial_capacity, mem_tag), initial_capacity), - _metadata(memflags) { + _metadata(mem_tag) { init_checks(); } @@ -776,11 +776,11 @@ class GrowableArray : public GrowableArrayWithAllocator> { init_checks(); } - GrowableArray(int initial_capacity, int initial_len, const E& filler, MEMFLAGS memflags) : + GrowableArray(int initial_capacity, int initial_len, const E& filler, MemTag mem_tag) : GrowableArrayWithAllocator( - allocate(initial_capacity, memflags), + allocate(initial_capacity, mem_tag), initial_capacity, initial_len, filler), - _metadata(memflags) { + _metadata(mem_tag) { init_checks(); } @@ -799,25 +799,25 @@ class GrowableArray : public GrowableArrayWithAllocator> { } }; -// Leaner GrowableArray for CHeap backed data arrays, with compile-time decided MEMFLAGS. -template -class GrowableArrayCHeap : public GrowableArrayWithAllocator > { - friend class GrowableArrayWithAllocator >; +// Leaner GrowableArray for CHeap backed data arrays, with compile-time decided MemTag. +template +class GrowableArrayCHeap : public GrowableArrayWithAllocator > { + friend class GrowableArrayWithAllocator >; - STATIC_ASSERT(F != mtNone); + STATIC_ASSERT(MT != mtNone); - static E* allocate(int max, MEMFLAGS flags) { + static E* allocate(int max, MemTag mem_tag) { if (max == 0) { return nullptr; } - return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), flags); + return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag); } NONCOPYABLE(GrowableArrayCHeap); E* allocate() { - return allocate(this->_capacity, F); + return allocate(this->_capacity, MT); } void deallocate(E* mem) { @@ -826,13 +826,13 @@ class GrowableArrayCHeap : public GrowableArrayWithAllocator >( - allocate(initial_capacity, F), + GrowableArrayWithAllocator >( + allocate(initial_capacity, MT), initial_capacity) {} GrowableArrayCHeap(int initial_capacity, int initial_len, const E& filler) : - GrowableArrayWithAllocator >( - allocate(initial_capacity, F), + GrowableArrayWithAllocator >( + allocate(initial_capacity, MT), initial_capacity, initial_len, filler) {} ~GrowableArrayCHeap() { @@ -840,11 +840,11 @@ class GrowableArrayCHeap : public GrowableArrayWithAllocator class LinkedList : public AnyObj { // A linked list implementation. // The linked list can be allocated in various type of memory: C heap, arena and resource area, etc. template + MemTag MT = mtNMT, AllocFailType alloc_failmode = AllocFailStrategy::RETURN_NULL> class LinkedListImpl : public LinkedList { protected: Arena* _arena; @@ -342,9 +342,9 @@ template (e); + return new(std::nothrow, MT) LinkedListNode(e); } else { - return new(F) LinkedListNode(e); + return new(MT) LinkedListNode(e); } } default: @@ -365,14 +365,14 @@ template - class SortedLinkedList : public LinkedListImpl { + MemTag MT = mtNMT, AllocFailType alloc_failmode = AllocFailStrategy::RETURN_NULL> + class SortedLinkedList : public LinkedListImpl { public: SortedLinkedList() { } - SortedLinkedList(Arena* a) : LinkedListImpl(a) { } + SortedLinkedList(Arena* a) : LinkedListImpl(a) { } virtual LinkedListNode* add(const E& e) { - return LinkedListImpl::add(e); + return LinkedListImpl::add(e); } virtual void move(LinkedList* list) { @@ -409,7 +409,7 @@ template * list) { - return LinkedListImpl::add(list); + return LinkedListImpl::add(list); } virtual LinkedListNode* find_node(const E& e) { diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index 1034dec0d9aaa..23094c9e8c4ac 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -338,6 +338,7 @@ #define NOT_PRODUCT_ARG(arg) #define PRODUCT_RETURN {} #define PRODUCT_RETURN0 { return 0; } +#define PRODUCT_RETURN_NULL { return nullptr; } #define PRODUCT_RETURN_(code) { code } #else // PRODUCT #define PRODUCT_ONLY(code) @@ -345,6 +346,7 @@ #define NOT_PRODUCT_ARG(arg) arg, #define PRODUCT_RETURN /*next token must be ;*/ #define PRODUCT_RETURN0 /*next token must be ;*/ +#define PRODUCT_RETURN_NULL /* next token must be ;*/ #define PRODUCT_RETURN_(code) /*next token must be ;*/ #endif // PRODUCT diff --git a/src/hotspot/share/utilities/objectBitSet.hpp b/src/hotspot/share/utilities/objectBitSet.hpp index 002e107972c89..124188cd32150 100644 --- a/src/hotspot/share/utilities/objectBitSet.hpp +++ b/src/hotspot/share/utilities/objectBitSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, 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,8 +39,8 @@ class MemRegion; * allocated on-demand only, in fragments covering 64M heap ranges. Fragments are never deleted * during the lifetime of the ObjectBitSet. The underlying memory is allocated from C-Heap. */ -template -class ObjectBitSet : public CHeapObj { +template +class ObjectBitSet : public CHeapObj { const static size_t _bitmap_granularity_shift = 26; // 64M const static size_t _bitmap_granularity_size = (size_t)1 << _bitmap_granularity_shift; const static size_t _bitmap_granularity_mask = _bitmap_granularity_size - 1; @@ -52,7 +52,7 @@ class ObjectBitSet : public CHeapObj { return hash ^ (hash >> 3); } - typedef ResizeableResourceHashtable BitMapFragmentTable; CHeapBitMap* get_fragment_bits(uintptr_t addr); @@ -81,8 +81,8 @@ class ObjectBitSet : public CHeapObj { } }; -template -class ObjectBitSet::BitMapFragment : public CHeapObj { +template +class ObjectBitSet::BitMapFragment : public CHeapObj { CHeapBitMap _bits; BitMapFragment* _next; diff --git a/src/hotspot/share/utilities/objectBitSet.inline.hpp b/src/hotspot/share/utilities/objectBitSet.inline.hpp index 144f886cbd9b2..482a97bc2d137 100644 --- a/src/hotspot/share/utilities/objectBitSet.inline.hpp +++ b/src/hotspot/share/utilities/objectBitSet.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -30,22 +30,22 @@ #include "memory/memRegion.hpp" #include "utilities/bitMap.inline.hpp" -template -ObjectBitSet::BitMapFragment::BitMapFragment(uintptr_t granule, BitMapFragment* next) : - _bits(_bitmap_granularity_size >> LogMinObjAlignmentInBytes, F, true /* clear */), +template +ObjectBitSet::BitMapFragment::BitMapFragment(uintptr_t granule, BitMapFragment* next) : + _bits(_bitmap_granularity_size >> LogMinObjAlignmentInBytes, MT, true /* clear */), _next(next) { } -template -ObjectBitSet::ObjectBitSet() : +template +ObjectBitSet::ObjectBitSet() : _bitmap_fragments(32, 8*K), _fragment_list(nullptr), _last_fragment_bits(nullptr), _last_fragment_granule(UINTPTR_MAX) { } -template -ObjectBitSet::~ObjectBitSet() { +template +ObjectBitSet::~ObjectBitSet() { BitMapFragment* current = _fragment_list; while (current != nullptr) { BitMapFragment* next = current->next(); @@ -56,13 +56,13 @@ ObjectBitSet::~ObjectBitSet() { // ResizeableResourceHashtableStorage deletes the table. } -template -inline BitMap::idx_t ObjectBitSet::addr_to_bit(uintptr_t addr) const { +template +inline BitMap::idx_t ObjectBitSet::addr_to_bit(uintptr_t addr) const { return (addr & _bitmap_granularity_mask) >> LogMinObjAlignmentInBytes; } -template -inline CHeapBitMap* ObjectBitSet::get_fragment_bits(uintptr_t addr) { +template +inline CHeapBitMap* ObjectBitSet::get_fragment_bits(uintptr_t addr) { uintptr_t granule = addr >> _bitmap_granularity_shift; if (granule == _last_fragment_granule) { return _last_fragment_bits; @@ -86,15 +86,15 @@ inline CHeapBitMap* ObjectBitSet::get_fragment_bits(uintptr_t addr) { return bits; } -template -inline void ObjectBitSet::mark_obj(uintptr_t addr) { +template +inline void ObjectBitSet::mark_obj(uintptr_t addr) { CHeapBitMap* bits = get_fragment_bits(addr); const BitMap::idx_t bit = addr_to_bit(addr); bits->set_bit(bit); } -template -inline bool ObjectBitSet::is_marked(uintptr_t addr) { +template +inline bool ObjectBitSet::is_marked(uintptr_t addr) { CHeapBitMap* bits = get_fragment_bits(addr); const BitMap::idx_t bit = addr_to_bit(addr); return bits->at(bit); diff --git a/src/hotspot/share/utilities/resizeableResourceHash.hpp b/src/hotspot/share/utilities/resizeableResourceHash.hpp index 03ae8cec4c168..b0c992bf1ef2b 100644 --- a/src/hotspot/share/utilities/resizeableResourceHash.hpp +++ b/src/hotspot/share/utilities/resizeableResourceHash.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,7 @@ template< typename K, typename V, AnyObj::allocation_type ALLOC_TYPE, - MEMFLAGS MEM_TYPE> + MemTag MEM_TAG> class ResizeableResourceHashtableStorage : public AnyObj { using Node = ResourceHashtableNode; @@ -52,7 +52,7 @@ class ResizeableResourceHashtableStorage : public AnyObj { Node** alloc_table(unsigned table_size) { Node** table; if (ALLOC_TYPE == C_HEAP) { - table = NEW_C_HEAP_ARRAY(Node*, table_size, MEM_TYPE); + table = NEW_C_HEAP_ARRAY(Node*, table_size, MEM_TAG); } else { table = NEW_RESOURCE_ARRAY(Node*, table_size); } @@ -72,17 +72,17 @@ class ResizeableResourceHashtableStorage : public AnyObj { template< typename K, typename V, AnyObj::allocation_type ALLOC_TYPE = AnyObj::RESOURCE_AREA, - MEMFLAGS MEM_TYPE = mtInternal, + MemTag MEM_TAG = mtInternal, unsigned (*HASH) (K const&) = primitive_hash, bool (*EQUALS)(K const&, K const&) = primitive_equals > class ResizeableResourceHashtable : public ResourceHashtableBase< - ResizeableResourceHashtableStorage, - K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS> { + ResizeableResourceHashtableStorage, + K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS> { unsigned _max_size; - using BASE = ResourceHashtableBase, - K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS>; + using BASE = ResourceHashtableBase, + K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS>; using Node = ResourceHashtableNode; NONCOPYABLE(ResizeableResourceHashtable); diff --git a/src/hotspot/share/utilities/resourceHash.hpp b/src/hotspot/share/utilities/resourceHash.hpp index b449dc6ea6c10..a99239b21a038 100644 --- a/src/hotspot/share/utilities/resourceHash.hpp +++ b/src/hotspot/share/utilities/resourceHash.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ template< class STORAGE, typename K, typename V, AnyObj::allocation_type ALLOC_TYPE, - MEMFLAGS MEM_TYPE, + MemTag MEM_TAG, unsigned (*HASH) (K const&), bool (*EQUALS)(K const&, K const&) > @@ -153,7 +153,7 @@ class ResourceHashtableBase : public STORAGE { assert(*lookup_node(hv, key) == nullptr, "use put_if_absent"); Node** ptr = bucket_at(index); if (ALLOC_TYPE == AnyObj::C_HEAP) { - *ptr = new (MEM_TYPE) Node(hv, key, value, *ptr); + *ptr = new (MEM_TAG) Node(hv, key, value, *ptr); } else { *ptr = new Node(hv, key, value, *ptr); } @@ -174,7 +174,7 @@ class ResourceHashtableBase : public STORAGE { return false; } else { if (ALLOC_TYPE == AnyObj::C_HEAP) { - *ptr = new (MEM_TYPE) Node(hv, key, value); + *ptr = new (MEM_TAG) Node(hv, key, value); } else { *ptr = new Node(hv, key, value); } @@ -193,7 +193,7 @@ class ResourceHashtableBase : public STORAGE { Node** ptr = lookup_node(hv, key); if (*ptr == nullptr) { if (ALLOC_TYPE == AnyObj::C_HEAP) { - *ptr = new (MEM_TYPE) Node(hv, key); + *ptr = new (MEM_TAG) Node(hv, key); } else { *ptr = new Node(hv, key); } @@ -215,7 +215,7 @@ class ResourceHashtableBase : public STORAGE { Node** ptr = lookup_node(hv, key); if (*ptr == nullptr) { if (ALLOC_TYPE == AnyObj::C_HEAP) { - *ptr = new (MEM_TYPE) Node(hv, key, value); + *ptr = new (MEM_TAG) Node(hv, key, value); } else { *ptr = new Node(hv, key, value); } @@ -364,17 +364,17 @@ template< typename K, typename V, unsigned SIZE = 256, AnyObj::allocation_type ALLOC_TYPE = AnyObj::RESOURCE_AREA, - MEMFLAGS MEM_TYPE = mtInternal, + MemTag MEM_TAG = mtInternal, unsigned (*HASH) (K const&) = primitive_hash, bool (*EQUALS)(K const&, K const&) = primitive_equals > class ResourceHashtable : public ResourceHashtableBase< FixedResourceHashtableStorage, - K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS> { + K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS> { NONCOPYABLE(ResourceHashtable); public: ResourceHashtable() : ResourceHashtableBase, - K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS>() {} + K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS>() {} }; #endif // SHARE_UTILITIES_RESOURCEHASH_HPP diff --git a/src/hotspot/share/utilities/stack.hpp b/src/hotspot/share/utilities/stack.hpp index 8b2ce3802e02d..1a9df130dff0c 100644 --- a/src/hotspot/share/utilities/stack.hpp +++ b/src/hotspot/share/utilities/stack.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -51,11 +51,11 @@ // implementation in class Stack assumes that alloc() will terminate the process // if the allocation fails. -template class StackIterator; +template class StackIterator; // StackBase holds common data/methods that don't depend on the element type, // factored out to reduce template code duplication. -template class StackBase +template class StackBase { public: size_t segment_size() const { return _seg_size; } // Elements per segment. @@ -85,11 +85,11 @@ template class StackBase size_t _cache_size; // Number of segments in the cache. }; -template -class Stack: public StackBase +template +class Stack: public StackBase { public: - friend class StackIterator; + friend class StackIterator; // Number of elements that fit in 4K bytes minus the size of two pointers // (link field and malloc header). @@ -160,13 +160,13 @@ class Stack: public StackBase E* _cache; // Segment cache to avoid ping-ponging. }; -template +template class StackIterator: public StackObj { public: - StackIterator(Stack& stack): _stack(stack) { sync(); } + StackIterator(Stack& stack): _stack(stack) { sync(); } - Stack& stack() const { return _stack; } + Stack& stack() const { return _stack; } bool is_empty() const { return _cur_seg == nullptr; } @@ -176,7 +176,7 @@ class StackIterator: public StackObj void sync(); // Sync the iterator's state to the stack's current state. private: - Stack& _stack; + Stack& _stack; size_t _cur_seg_size; E* _cur_seg; size_t _full_seg_size; diff --git a/src/hotspot/share/utilities/stack.inline.hpp b/src/hotspot/share/utilities/stack.inline.hpp index bed33bd6652ca..49ccf4166290c 100644 --- a/src/hotspot/share/utilities/stack.inline.hpp +++ b/src/hotspot/share/utilities/stack.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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,7 +31,7 @@ #include "utilities/align.hpp" #include "utilities/copy.hpp" -template StackBase::StackBase(size_t segment_size, size_t max_cache_size, +template StackBase::StackBase(size_t segment_size, size_t max_cache_size, size_t max_size): _seg_size(segment_size), _max_size(adjust_max_size(max_size, segment_size)), @@ -40,7 +40,7 @@ template StackBase::StackBase(size_t segment_size, size_t max_ca assert(_max_size % _seg_size == 0, "not a multiple"); } -template size_t StackBase::adjust_max_size(size_t max_size, size_t seg_size) +template size_t StackBase::adjust_max_size(size_t max_size, size_t seg_size) { assert(seg_size > 0, "cannot be 0"); assert(max_size >= seg_size || max_size == 0, "max_size too small"); @@ -51,15 +51,15 @@ template size_t StackBase::adjust_max_size(size_t max_size, size return (max_size + seg_size - 1) / seg_size * seg_size; } -template -Stack::Stack(size_t segment_size, size_t max_cache_size, size_t max_size): - StackBase(adjust_segment_size(segment_size), max_cache_size, max_size) +template +Stack::Stack(size_t segment_size, size_t max_cache_size, size_t max_size): + StackBase(adjust_segment_size(segment_size), max_cache_size, max_size) { reset(true); } -template -void Stack::push(E item) +template +void Stack::push(E item) { assert(!is_full(), "pushing onto a full stack"); size_t index = this->_cur_seg_size; @@ -71,8 +71,8 @@ void Stack::push(E item) this->_cur_seg_size = index + 1; } -template -E Stack::pop() +template +E Stack::pop() { assert(!is_empty(), "popping from an empty stack"); // _cur_seg_size is never 0 if not empty. pop that empties a @@ -85,16 +85,16 @@ E Stack::pop() return result; } -template -void Stack::clear(bool clear_cache) +template +void Stack::clear(bool clear_cache) { free_segments(_cur_seg); if (clear_cache) free_segments(_cache); reset(clear_cache); } -template -size_t Stack::adjust_segment_size(size_t seg_size) +template +size_t Stack::adjust_segment_size(size_t seg_size) { const size_t elem_sz = sizeof(E); const size_t ptr_sz = sizeof(E*); @@ -105,45 +105,45 @@ size_t Stack::adjust_segment_size(size_t seg_size) return seg_size; } -template -size_t Stack::link_offset() const +template +size_t Stack::link_offset() const { return align_up(this->_seg_size * sizeof(E), sizeof(E*)); } -template -size_t Stack::segment_bytes() const +template +size_t Stack::segment_bytes() const { return link_offset() + sizeof(E*); } -template -E** Stack::link_addr(E* seg) const +template +E** Stack::link_addr(E* seg) const { return (E**) ((char*)seg + link_offset()); } -template -E* Stack::get_link(E* seg) const +template +E* Stack::get_link(E* seg) const { return *link_addr(seg); } -template -E* Stack::set_link(E* new_seg, E* old_seg) +template +E* Stack::set_link(E* new_seg, E* old_seg) { *link_addr(new_seg) = old_seg; return new_seg; } -template -E* Stack::alloc(size_t bytes) +template +E* Stack::alloc(size_t bytes) { - return (E*) NEW_C_HEAP_ARRAY(char, bytes, F); + return (E*) NEW_C_HEAP_ARRAY(char, bytes, MT); } -template -void Stack::free(E* addr, size_t bytes) +template +void Stack::free(E* addr, size_t bytes) { FREE_C_HEAP_ARRAY(char, (char*) addr); } @@ -152,8 +152,8 @@ void Stack::free(E* addr, size_t bytes) // code gets inlined. This is generally good, but when too much code has // been inlined, further inlining in the caller might be inhibited. So // prevent infrequent slow path segment manipulation from being inlined. -template -NOINLINE void Stack::push_segment() +template +NOINLINE void Stack::push_segment() { assert(this->_cur_seg_size == this->_seg_size, "current segment is not full"); E* next; @@ -173,8 +173,8 @@ NOINLINE void Stack::push_segment() DEBUG_ONLY(verify(at_empty_transition);) } -template -NOINLINE void Stack::pop_segment() +template +NOINLINE void Stack::pop_segment() { assert(this->_cur_seg_size == 0, "current segment is not empty"); E* const prev = get_link(_cur_seg); @@ -194,8 +194,8 @@ NOINLINE void Stack::pop_segment() DEBUG_ONLY(verify(at_empty_transition);) } -template -void Stack::free_segments(E* seg) +template +void Stack::free_segments(E* seg) { const size_t bytes = segment_bytes(); while (seg != nullptr) { @@ -205,8 +205,8 @@ void Stack::free_segments(E* seg) } } -template -void Stack::reset(bool reset_cache) +template +void Stack::reset(bool reset_cache) { this->_cur_seg_size = this->_seg_size; // So push() will alloc a new segment. this->_full_seg_size = 0; @@ -218,8 +218,8 @@ void Stack::reset(bool reset_cache) } #ifdef ASSERT -template -void Stack::verify(bool at_empty_transition) const +template +void Stack::verify(bool at_empty_transition) const { assert(size() <= this->max_size(), "stack exceeded bounds"); assert(this->cache_size() <= this->max_cache_size(), "cache exceeded bounds"); @@ -234,8 +234,8 @@ void Stack::verify(bool at_empty_transition) const } } -template -void Stack::zap_segment(E* seg, bool zap_link_field) const +template +void Stack::zap_segment(E* seg, bool zap_link_field) const { if (!ZapStackSegments) return; const size_t zap_bytes = segment_bytes() - (zap_link_field ? 0 : sizeof(E*)); @@ -243,16 +243,16 @@ void Stack::zap_segment(E* seg, bool zap_link_field) const } #endif -template -void StackIterator::sync() +template +void StackIterator::sync() { _full_seg_size = _stack._full_seg_size; _cur_seg_size = _stack._cur_seg_size; _cur_seg = _stack._cur_seg; } -template -E* StackIterator::next_addr() +template +E* StackIterator::next_addr() { assert(!is_empty(), "no items left"); if (_cur_seg_size == 1) { diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 0b3ccf9c747aa..7bbb978b8064f 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -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) 2017, 2020 SAP SE. All rights reserved. * Copyright (c) 2023, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -1696,7 +1696,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt ShowMessageBoxOnError = false; } - os::check_dump_limit(buffer, sizeof(buffer)); + os::check_core_dump_prerequisites(buffer, sizeof(buffer)); // reset signal handlers or exception filter; make sure recursive crashes // are handled properly. diff --git a/src/java.base/macosx/native/libjli/java_md_macosx.m b/src/java.base/macosx/native/libjli/java_md_macosx.m index 4ac2f2c10a215..7aeb32be859f2 100644 --- a/src/java.base/macosx/native/libjli/java_md_macosx.m +++ b/src/java.base/macosx/native/libjli/java_md_macosx.m @@ -60,115 +60,79 @@ #define LD_LIBRARY_PATH "DYLD_FALLBACK_LIBRARY_PATH" /* - * If a processor / os combination has the ability to run binaries of - * two data models and cohabitation of jre/jdk bits with both data - * models is supported, then DUAL_MODE is defined. MacOSX is a hybrid - * system in that, the universal library can contain all types of libraries - * 32/64 and client/server, thus the spawn is capable of linking with the - * appropriate library as requested. + * Following is the high level flow of the launcher + * code residing in the common java.c and this + * macosx specific java_md_macosx file: * - * Notes: - * 1. VM. DUAL_MODE is disabled, and not supported, however, it is left here in - * for experimentation and perhaps enable it in the future. - * 2. At the time of this writing, the universal library contains only - * a server 64-bit server JVM. - * 3. "-client" command line option is supported merely as a command line flag, - * for, compatibility reasons, however, a server VM will be launched. - */ - -/* - * Flowchart of launcher execs and options processing on unix + * - JLI_Launch function, which is the entry point + * to the launcher, calls CreateExecutionEnvironment. + * + * - CreateExecutionEnvironment does the following + * (not necessarily in this order): + * - determines the relevant JVM type that needs + * to be ultimately created + * - determines the path and asserts the presence + * of libjava and relevant libjvm library + * - removes any JVM selection options from the + * arguments that were passed to the launcher + * + * - CreateExecutionEnvironment then creates a new + * thread, within the same process, to launch the + * application's main() Java method and parks the + * current thread, on which CreateExecutionEnvironment + * was invoked, in Apple's Cocoa event loop. Before + * doing so, CreateExecutionEnvironment maintains a + * state flag to keep note that a new thread has + * been spawned. + * + * - The newly created thread (in which the application's + * main() method will ultimately run) starts right from + * the beginning of the current process' main function, + * which effectively means that JLI_Launch is re-invoked + * on this new thread and the same above sequence of code + * flow repeats again. During this "recursive" call, when + * at the point of creating a new thread in + * CreateExecutionEnvironment, the CreateExecutionEnvironment + * will check for the state flag to see if a new thread + * has already been spawned and upon noticing that it + * has, it will skip spawning any more threads and will + * return back from CreateExecutionEnvironment. + * + * - The control returns back from CreateExecutionEnvironment + * to JLI_Launch, and the thread on which the control + * returns is the thread on which the application's main() + * Java method will be invoked. + * + * - JLI_Launch then invokes LoadJavaVM which dlopen()s the + * JVM library and asserts the presence of JNI Invocation + * Functions "JNI_CreateJavaVM", "JNI_GetDefaultJavaVMInitArgs" + * and "JNI_GetCreatedJavaVMs" in that library. It then sets + * internal function pointers in the launcher to point to + * those functions. + * + * - JLI_Launch then translates any -J options by invoking + * TranslateApplicationArgs. + * + * - JLI_Launch then invokes ParseArguments to parse/process + * the launcher arguments. * - * The selection of the proper vm shared library to open depends on - * several classes of command line options, including vm "flavor" - * options (-client, -server) and the data model options, -d32 and - * -d64, as well as a version specification which may have come from - * the command line or from the manifest of an executable jar file. - * The vm selection options are not passed to the running - * virtual machine; they must be screened out by the launcher. + * - JLI_Launch then ultimately calls JVMInit. * - * The version specification (if any) is processed first by the - * platform independent routine SelectVersion. This may result in - * the exec of the specified launcher version. + * - JVMInit then invokes JavaMain. * - * Now, in most cases,the launcher will dlopen the target libjvm.so. All - * required libraries are loaded by the runtime linker, using the known paths - * baked into the shared libraries at compile time. Therefore, - * in most cases, the launcher will only exec, if the data models are - * mismatched, and will not set any environment variables, regardless of the - * data models. + * - JavaMain, before launching the application, invokes + * PostJVMInit. * + * - PostJVMInit invokes ShowSplashScreen which displays + * a splash screen for the application, if applicable. * + * - Control then returns back from PostJVMInit into + * JavaMain, which then loads the application's main + * class and invokes the relevant main() Java method. * - * Main - * (incoming argv) - * | - * \|/ - * CreateExecutionEnvironment - * (determines desired data model) - * | - * | - * \|/ - * Have Desired Model ? --> NO --> Is Dual-Mode ? --> NO --> Exit(with error) - * | | - * | | - * | \|/ - * | YES - * | | - * | | - * | \|/ - * | CheckJvmType - * | (removes -client, -server etc.) - * | | - * | | - * \|/ \|/ - * YES Find the desired executable/library - * | | - * | | - * \|/ \|/ - * CheckJvmType POINT A - * (removes -client, -server, etc.) - * | - * | - * \|/ - * TranslateDashJArgs... - * (Prepare to pass args to vm) - * | - * | - * \|/ - * ParseArguments - * (processes version options, - * creates argument list for vm, - * etc.) - * | - * | - * \|/ - * POINT A - * | - * | - * \|/ - * Path is desired JRE ? YES --> Continue - * NO - * | - * | - * \|/ - * Paths have well known - * jvm paths ? --> NO --> Continue - * YES - * | - * | - * \|/ - * Does libjvm.so exist - * in any of them ? --> NO --> Continue - * YES - * | - * | - * \|/ - * Re-exec / Spawn - * | - * | - * \|/ - * Main + * - JavaMain then returns back an integer result which + * then gets propagated as a return value all the way + * out of the JLI_Launch function. */ /* Store the name of the executable once computed */ @@ -333,6 +297,7 @@ static void ParkEventLoop() { static void MacOSXStartup(int argc, char *argv[]) { // Thread already started? static jboolean started = false; + int rc; if (started) { return; } @@ -345,12 +310,14 @@ static void MacOSXStartup(int argc, char *argv[]) { // Fire up the main thread pthread_t main_thr; - if (pthread_create(&main_thr, NULL, &apple_main, &args) != 0) { - JLI_ReportErrorMessageSys("Could not create main thread: %s\n", strerror(errno)); + rc = pthread_create(&main_thr, NULL, &apple_main, &args); + if (rc != 0) { + JLI_ReportErrorMessageSys("Could not create main thread, return code: %d\n", rc); exit(1); } - if (pthread_detach(main_thr)) { - JLI_ReportErrorMessageSys("pthread_detach() failed: %s\n", strerror(errno)); + rc = pthread_detach(main_thr); + if (rc != 0) { + JLI_ReportErrorMessage("pthread_detach() failed, return code: %d\n", rc); exit(1); } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java index 0d5363e866a95..fcdc3fbea49e6 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,7 @@ final class DHPrivateKey implements PrivateKey, private final BigInteger x; // the key bytes, without the algorithm information + // cannot be final as it's re-assigned for deserialization private byte[] key; // the encoded key @@ -70,115 +71,85 @@ final class DHPrivateKey implements PrivateKey, // the private-value length (optional) private final int l; - /** - * Make a DH private key out of a private value x, a prime - * modulus p, and a base generator g. - * - * @param x the private value - * @param p the prime modulus - * @param g the base generator - */ - DHPrivateKey(BigInteger x, BigInteger p, BigInteger g) - throws InvalidKeyException { - this(x, p, g, 0); - } - - /** - * Make a DH private key out of a private value x, a prime - * modulus p, a base generator g, and a - * private-value length l. - * - * @param x the private value - * @param p the prime modulus - * @param g the base generator - * @param l the private-value length - */ - DHPrivateKey(BigInteger x, BigInteger p, BigInteger g, int l) { - this.x = x; - this.p = p; - this.g = g; - this.l = l; - byte[] xbytes = x.toByteArray(); - DerValue val = new DerValue(DerValue.tag_Integer, xbytes); - this.key = val.toByteArray(); - val.clear(); - Arrays.fill(xbytes, (byte) 0); - encode(); + private static class DHComponents { + final BigInteger x; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger x, BigInteger p, BigInteger g, int l, + byte[] key) { + this.x = x; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } } - /** - * Make a DH private key from its DER encoding (PKCS #8). - * - * @param encodedKey the encoded key - * - * @throws InvalidKeyException if the encoded key does not represent - * a Diffie-Hellman private key - */ - DHPrivateKey(byte[] encodedKey) throws InvalidKeyException { + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { DerValue val = null; + try { val = new DerValue(encodedKey); if (val.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Key not a SEQUENCE"); + throw new IOException("Key not a SEQUENCE"); } - // // version - // BigInteger parsedVersion = val.data.getBigInteger(); if (!parsedVersion.equals(PKCS8_VERSION)) { throw new IOException("version mismatch: (supported: " + - PKCS8_VERSION + ", parsed: " + - parsedVersion); + PKCS8_VERSION + ", parsed: " + parsedVersion); } - // // privateKeyAlgorithm - // DerValue algid = val.data.getDerValue(); if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); + throw new IOException("AlgId is not a SEQUENCE"); } DerInputStream derInStream = algid.toDerInputStream(); ObjectIdentifier oid = derInStream.getOID(); if (oid == null) { - throw new InvalidKeyException("Null OID"); + throw new IOException("Null OID"); } if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); + throw new IOException("Parameters missing"); } // parse the parameters DerValue params = derInStream.getDerValue(); if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); + throw new IOException("Null parameters"); } if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); + throw new IOException("Parameters not a SEQUENCE"); } params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? + params.data.getInteger() : 0); + // should have no trailing data if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } else { - this.l = 0; - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); + throw new IOException("Extra parameter data"); } - // // privateKey - // - this.key = val.data.getOctetString(); - - DerInputStream in = new DerInputStream(this.key); - this.x = in.getBigInteger(); + byte[] key = val.data.getOctetString(); + DerInputStream in = new DerInputStream(key); + BigInteger x = in.getBigInteger(); - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); + // should have no trailing data + if (val.data.available() != 0) { + throw new IOException("Excess trailing data"); + } + return new DHComponents(x, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); } finally { if (val != null) { val.clear(); @@ -186,6 +157,108 @@ final class DHPrivateKey implements PrivateKey, } } + // Generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) { + DerOutputStream tmp = new DerOutputStream(); + + // version + tmp.putInteger(PKCS8_VERSION); + + // privateKeyAlgorithm + DerOutputStream algid = new DerOutputStream(); + + // store OID + algid.putOID(DHPublicKey.DH_OID); + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + // wrap algid into SEQUENCE + tmp.write(DerValue.tag_Sequence, algid); + + // privateKey + tmp.putOctetString(key); + + // make it a SEQUENCE + DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); + byte[] encoded = val.toByteArray(); + val.clear(); + + return encoded; + } + + /** + * Make a DH private key out of a private value x, a prime + * modulus p, and a base generator g. + * + * @param x the private value + * @param p the prime modulus + * @param g the base generator + */ + DHPrivateKey(BigInteger x, BigInteger p, BigInteger g) + throws InvalidKeyException { + this(x, p, g, 0); + } + + /** + * Make a DH private key out of a private value x, a prime + * modulus p, a base generator g, and a + * private-value length l. + * + * @param x the private value + * @param p the prime modulus + * @param g the base generator + * @param l the private-value length + */ + DHPrivateKey(BigInteger x, BigInteger p, BigInteger g, int l) { + this.x = x; + this.p = p; + this.g = g; + this.l = l; + + byte[] xbytes = x.toByteArray(); + DerValue val = new DerValue(DerValue.tag_Integer, xbytes); + try { + this.key = val.toByteArray(); + } finally { + val.clear(); + Arrays.fill(xbytes, (byte) 0); + } + this.encodedKey = encode(p, g, l, key); + } + + /** + * Make a DH private key from its DER encoding (PKCS #8). + * + * @param encodedKey the encoded key + * + * @throws InvalidKeyException if the encoded key does not represent + * a Diffie-Hellman private key + */ + DHPrivateKey(byte[] encodedKey) throws InvalidKeyException { + this.encodedKey = encodedKey.clone(); + DHComponents dc; + try { + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); + } + this.x = dc.x; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; + } + /** * Returns the encoding format of this key: "PKCS#8" */ @@ -204,55 +277,9 @@ public String getAlgorithm() { * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - encode(); return encodedKey.clone(); } - /** - * Generate the encodedKey field if it has not been calculated. - * Could generate null. - */ - private void encode() { - if (this.encodedKey == null) { - DerOutputStream tmp = new DerOutputStream(); - - // - // version - // - tmp.putInteger(PKCS8_VERSION); - - // - // privateKeyAlgorithm - // - DerOutputStream algid = new DerOutputStream(); - - // store OID - algid.putOID(DHPublicKey.DH_OID); - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - // wrap algid into SEQUENCE - tmp.write(DerValue.tag_Sequence, algid); - - // privateKey - tmp.putOctetString(this.key); - - // make it a SEQUENCE - DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp); - this.encodedKey = val.toByteArray(); - val.clear(); - } - } - /** * Returns the private value, x. * @@ -307,10 +334,7 @@ public boolean equals(Object obj) { */ @java.io.Serial private Object writeReplace() throws java.io.ObjectStreamException { - encode(); - return new KeyRep(KeyRep.Type.PRIVATE, - getAlgorithm(), - getFormat(), + return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(), encodedKey); } @@ -330,11 +354,28 @@ private void readObject(ObjectInputStream stream) if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + throw new InvalidObjectException("Invalid encoding", e); + } + if (!Arrays.equals(c.key, key) || !c.x.equals(x) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java index 47727c432a672..c95b40482d5bd 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/DHPublicKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package com.sun.crypto.provider; import java.io.*; +import java.util.Arrays; import java.util.Objects; import java.math.BigInteger; import java.security.KeyRep; @@ -70,6 +71,116 @@ final class DHPublicKey implements PublicKey, static final ObjectIdentifier DH_OID = ObjectIdentifier.of(KnownOIDs.DiffieHellman); + private static class DHComponents { + final BigInteger y; + final BigInteger p; + final BigInteger g; + final int l; + final byte[] key; + + DHComponents(BigInteger y, BigInteger p, BigInteger g, int l, + byte[] key) { + this.y = y; + this.p = p; + this.g = g; + this.l = l; + this.key = key; + } + } + + // parses the specified encoding into a DHComponents object + private static DHComponents decode(byte[] encodedKey) + throws IOException { + DerValue val = null; + + try { + val = new DerValue(encodedKey); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid key format"); + } + + // algorithm identifier + DerValue algid = val.data.getDerValue(); + if (algid.tag != DerValue.tag_Sequence) { + throw new IOException("AlgId is not a SEQUENCE"); + } + DerInputStream derInStream = algid.toDerInputStream(); + ObjectIdentifier oid = derInStream.getOID(); + if (oid == null) { + throw new IOException("Null OID"); + } + if (derInStream.available() == 0) { + throw new IOException("Parameters missing"); + } + + // parse the parameters + DerValue params = derInStream.getDerValue(); + if (params.tag == DerValue.tag_Null) { + throw new IOException("Null parameters"); + } + if (params.tag != DerValue.tag_Sequence) { + throw new IOException("Parameters not a SEQUENCE"); + } + params.data.reset(); + + BigInteger p = params.data.getBigInteger(); + BigInteger g = params.data.getBigInteger(); + // Private-value length is OPTIONAL + int l = (params.data.available() != 0 ? params.data.getInteger() : + 0); + if (params.data.available() != 0) { + throw new IOException("Extra parameter data"); + } + + // publickey + byte[] key = val.data.getBitString(); + DerInputStream in = new DerInputStream(key); + BigInteger y = in.getBigInteger(); + + if (val.data.available() != 0) { + throw new IOException("Excess key data"); + } + return new DHComponents(y, p, g, l, key); + } catch (NumberFormatException e) { + throw new IOException("Error parsing key encoding", e); + } + } + + // generates the ASN.1 encoding + private static byte[] encode(BigInteger p, BigInteger g, int l, + byte[] key) { + DerOutputStream algid = new DerOutputStream(); + + // store oid in algid + algid.putOID(DH_OID); + + // encode parameters + DerOutputStream params = new DerOutputStream(); + params.putInteger(p); + params.putInteger(g); + if (l != 0) { + params.putInteger(l); + } + + // wrap parameters into SEQUENCE + DerValue paramSequence = new DerValue(DerValue.tag_Sequence, + params.toByteArray()); + // store parameter SEQUENCE in algid + algid.putDerValue(paramSequence); + + // wrap algid into SEQUENCE, and store it in key encoding + DerOutputStream tmpDerKey = new DerOutputStream(); + tmpDerKey.write(DerValue.tag_Sequence, algid); + + // store key data + tmpDerKey.putBitString(key); + + // wrap algid and key into SEQUENCE + DerOutputStream derKey = new DerOutputStream(); + derKey.write(DerValue.tag_Sequence, tmpDerKey); + return derKey.toByteArray(); + } + /** * Make a DH public key out of a public value y, a prime * modulus p, and a base generator g. @@ -102,7 +213,7 @@ final class DHPublicKey implements PublicKey, this.l = l; this.key = new DerValue(DerValue.tag_Integer, this.y.toByteArray()).toByteArray(); - this.encodedKey = getEncoded(); + this.encodedKey = encode(p, g, l, key); } /** @@ -114,68 +225,19 @@ final class DHPublicKey implements PublicKey, * a Diffie-Hellman public key */ DHPublicKey(byte[] encodedKey) throws InvalidKeyException { - InputStream inStream = new ByteArrayInputStream(encodedKey); - try { - DerValue derKeyVal = new DerValue(inStream); - if (derKeyVal.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException ("Invalid key format"); - } - - /* - * Parse the algorithm identifier - */ - DerValue algid = derKeyVal.data.getDerValue(); - if (algid.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("AlgId is not a SEQUENCE"); - } - DerInputStream derInStream = algid.toDerInputStream(); - ObjectIdentifier oid = derInStream.getOID(); - if (oid == null) { - throw new InvalidKeyException("Null OID"); - } - if (derInStream.available() == 0) { - throw new InvalidKeyException("Parameters missing"); - } - - /* - * Parse the parameters - */ - DerValue params = derInStream.getDerValue(); - if (params.tag == DerValue.tag_Null) { - throw new InvalidKeyException("Null parameters"); - } - if (params.tag != DerValue.tag_Sequence) { - throw new InvalidKeyException("Parameters not a SEQUENCE"); - } - params.data.reset(); - this.p = params.data.getBigInteger(); - this.g = params.data.getBigInteger(); - // Private-value length is OPTIONAL - if (params.data.available() != 0) { - this.l = params.data.getInteger(); - } else { - this.l = 0; - } - if (params.data.available() != 0) { - throw new InvalidKeyException("Extra parameter data"); - } - - /* - * Parse the key - */ - this.key = derKeyVal.data.getBitString(); - - DerInputStream in = new DerInputStream(this.key); - this.y = in.getBigInteger(); - - if (derKeyVal.data.available() != 0) { - throw new InvalidKeyException("Excess key data"); - } + this.encodedKey = encodedKey.clone(); - this.encodedKey = encodedKey.clone(); - } catch (IOException | NumberFormatException e) { - throw new InvalidKeyException("Error parsing key encoding", e); + DHComponents dc; + try { + dc = decode(this.encodedKey); + } catch (IOException e) { + throw new InvalidKeyException("Invalid encoding", e); } + this.y = dc.y; + this.p = dc.p; + this.g = dc.g; + this.l = dc.l; + this.key = dc.key; } /** @@ -196,37 +258,6 @@ public String getAlgorithm() { * Get the encoding of the key. */ public synchronized byte[] getEncoded() { - if (this.encodedKey == null) { - DerOutputStream algid = new DerOutputStream(); - - // store oid in algid - algid.putOID(DH_OID); - - // encode parameters - DerOutputStream params = new DerOutputStream(); - params.putInteger(this.p); - params.putInteger(this.g); - if (this.l != 0) { - params.putInteger(this.l); - } - // wrap parameters into SEQUENCE - DerValue paramSequence = new DerValue(DerValue.tag_Sequence, - params.toByteArray()); - // store parameter SEQUENCE in algid - algid.putDerValue(paramSequence); - - // wrap algid into SEQUENCE, and store it in key encoding - DerOutputStream tmpDerKey = new DerOutputStream(); - tmpDerKey.write(DerValue.tag_Sequence, algid); - - // store key data - tmpDerKey.putBitString(this.key); - - // wrap algid and key into SEQUENCE - DerOutputStream derKey = new DerOutputStream(); - derKey.write(DerValue.tag_Sequence, tmpDerKey); - this.encodedKey = derKey.toByteArray(); - } return this.encodedKey.clone(); } @@ -263,8 +294,9 @@ public String toString() { + Debug.toHexString(this.p) + LINE_SEP + "g:" + LINE_SEP + Debug.toHexString(this.g)); - if (this.l != 0) + if (this.l != 0) { sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l); + } return sb.toString(); } @@ -304,7 +336,7 @@ private Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(), - getEncoded()); + encodedKey); } /** @@ -323,11 +355,28 @@ private void readObject(ObjectInputStream stream) if ((key == null) || (key.length == 0)) { throw new InvalidObjectException("key not deserializable"); } - this.key = key.clone(); if ((encodedKey == null) || (encodedKey.length == 0)) { throw new InvalidObjectException( "encoded key not deserializable"); } - this.encodedKey = encodedKey.clone(); + // check if the "encodedKey" value matches the deserialized fields + DHComponents c; + byte[] encodedKeyIntern = encodedKey.clone(); + try { + c = decode(encodedKeyIntern); + } catch (IOException e) { + throw new InvalidObjectException("Invalid encoding", e); + } + if (!Arrays.equals(c.key, key) || !c.y.equals(y) || !c.p.equals(p) + || !c.g.equals(g) || c.l != l) { + throw new InvalidObjectException( + "encoded key not matching internal fields"); + } + // zero out external arrays + Arrays.fill(key, (byte)0x00); + Arrays.fill(encodedKey, (byte)0x00); + // use self-created internal copies + this.key = c.key; + this.encodedKey = encodedKeyIntern; } } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java index 44cfb76d1628e..478593dfac1ac 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java @@ -72,7 +72,7 @@ abstract class GaloisCounterMode extends CipherSpi { // data size when buffer is divided up to aid in intrinsics private static final int TRIGGERLEN = 65536; // 64k // x86-64 parallel intrinsic data size - private static final int PARALLEL_LEN = 7680; + private static final int PARALLEL_LEN = 512; // max data size for x86-64 intrinsic private static final int SPLIT_LEN = 1048576; // 1MB 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 14ada1699c18c..2762fb3751c1b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -194,22 +194,24 @@ public byte[] getEncoded() { return key.clone(); } - /** - * Restores the state of this object from the stream. - * - * @param stream the {@code ObjectInputStream} from which data is read - * @throws IOException if an I/O error occurs - * @throws ClassNotFoundException if a serialized class cannot be loaded - */ - @java.io.Serial - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - if ((key == null) || (key.length == 0)) { - throw new InvalidObjectException("TlsMasterSecretKey is null"); - } - key = key.clone(); - } - } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + if (key == null || key.length == 0) { + throw new InvalidObjectException("TlsMasterSecretKey is null"); + } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte)0); + } + } } diff --git a/src/java.base/share/classes/java/io/BufferedInputStream.java b/src/java.base/share/classes/java/io/BufferedInputStream.java index cfc2a3d2c75b9..c401873ce12e4 100644 --- a/src/java.base/share/classes/java/io/BufferedInputStream.java +++ b/src/java.base/share/classes/java/io/BufferedInputStream.java @@ -50,6 +50,11 @@ * reread before new bytes are taken from * the contained input stream. * + * @apiNote + * Once wrapped in a {@code BufferedInputStream}, the underlying + * {@code InputStream} should not be used directly nor wrapped with + * another stream. + * * @author Arthur van Hoff * @since 1.0 */ diff --git a/src/java.base/share/classes/java/io/BufferedOutputStream.java b/src/java.base/share/classes/java/io/BufferedOutputStream.java index ecc1e8a8a4845..687f0c91bc4f7 100644 --- a/src/java.base/share/classes/java/io/BufferedOutputStream.java +++ b/src/java.base/share/classes/java/io/BufferedOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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,11 @@ * output stream without necessarily causing a call to the underlying * system for each byte written. * + * @apiNote + * Once wrapped in a {@code BufferedOutputStream}, the underlying + * {@code OutputStream} should not be used directly nor wrapped with + * another stream. + * * @author Arthur van Hoff * @since 1.0 */ diff --git a/src/java.base/share/classes/java/io/BufferedReader.java b/src/java.base/share/classes/java/io/BufferedReader.java index 2cd027c8bd8a2..c2f6b89e08622 100644 --- a/src/java.base/share/classes/java/io/BufferedReader.java +++ b/src/java.base/share/classes/java/io/BufferedReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, 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 @@ -43,8 +43,9 @@ * *

In general, each read request made of a Reader causes a corresponding * read request to be made of the underlying character or byte stream. It is - * therefore advisable to wrap a BufferedReader around any Reader whose read() - * operations may be costly, such as FileReaders and InputStreamReaders. For + * therefore advisable to wrap a {@code BufferedReader} around any + * {@code Reader} whose {@code read()} operations may be costly, such as + * {@code FileReader}s and {@code InputStreamReader}s. For * example, * * {@snippet lang=java : @@ -52,12 +53,18 @@ * } * * will buffer the input from the specified file. Without buffering, each - * invocation of read() or readLine() could cause bytes to be read from the - * file, converted into characters, and then returned, which can be very - * inefficient. + * invocation of {@code read()} or {@code readLine()} could cause bytes to be + * read from the file, converted into characters, and then returned, which can + * be very inefficient. * - *

Programs that use DataInputStreams for textual input can be localized by - * replacing each DataInputStream with an appropriate BufferedReader. + *

Programs that use {@code DataInputStream}s for textual input can be + * localized by replacing each {@code DataInputStream} with an appropriate + * {@code BufferedReader}. + * + * @apiNote + * Once wrapped in a {@code BufferedReader}, the underlying + * {@code Reader} should not be used directly nor wrapped with + * another reader. * * @see FileReader * @see InputStreamReader diff --git a/src/java.base/share/classes/java/io/BufferedWriter.java b/src/java.base/share/classes/java/io/BufferedWriter.java index 4904f7180724a..17862a265ae82 100644 --- a/src/java.base/share/classes/java/io/BufferedWriter.java +++ b/src/java.base/share/classes/java/io/BufferedWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, 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 @@ -37,25 +37,31 @@ *

The buffer size may be specified, or the default size may be accepted. * The default is large enough for most purposes. * - *

A newLine() method is provided, which uses the platform's own notion of - * line separator as defined by the system property {@code line.separator}. - * Not all platforms use the newline character ('\n') to terminate lines. - * Calling this method to terminate each output line is therefore preferred to - * writing a newline character directly. + *

A {@code newLine()} method is provided, which uses the platform's own + * notion of line separator as defined by the system property + * {@linkplain System#lineSeparator() line.separator}. Not all platforms use the newline character ('\n') + * to terminate lines. Calling this method to terminate each output line is + * therefore preferred to writing a newline character directly. * - *

In general, a Writer sends its output immediately to the underlying - * character or byte stream. Unless prompt output is required, it is advisable - * to wrap a BufferedWriter around any Writer whose write() operations may be - * costly, such as FileWriters and OutputStreamWriters. For example, + *

In general, a {@code Writer} sends its output immediately to the + * underlying character or byte stream. Unless prompt output is required, it + * is advisable to wrap a {@code BufferedWriter} around any {@code Writer} whose + * {@code write()} operations may be costly, such as {@code FileWriter}s and + * {@code OutputStreamWriter}s. For example, * * {@snippet lang=java : * PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out"))); * } * - * will buffer the PrintWriter's output to the file. Without buffering, each - * invocation of a print() method would cause characters to be converted into - * bytes that would then be written immediately to the file, which can be very - * inefficient. + * will buffer the {@code PrintWriter}'s output to the file. Without buffering, + * each invocation of a {@code print()} method would cause characters to be + * converted into bytes that would then be written immediately to the file, + * which can be very inefficient. + * + * @apiNote + * Once wrapped in a {@code BufferedWriter}, the underlying + * {@code Writer} should not be used directly nor wrapped with + * another writer. * * @see PrintWriter * @see FileWriter diff --git a/src/java.base/share/classes/java/io/DataInputStream.java b/src/java.base/share/classes/java/io/DataInputStream.java index eab7a6e2f189a..59377aca429ca 100644 --- a/src/java.base/share/classes/java/io/DataInputStream.java +++ b/src/java.base/share/classes/java/io/DataInputStream.java @@ -1,5 +1,6 @@ /* * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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,8 +26,11 @@ package java.io; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.util.ByteArray; +import java.nio.charset.StandardCharsets; import java.util.Objects; /** @@ -45,6 +49,7 @@ * @since 1.0 */ public class DataInputStream extends FilterInputStream implements DataInput { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private static final char[] EMPTY_CHAR_ARRAY = new char[0]; @@ -573,18 +578,16 @@ public final String readUTF() throws IOException { */ public static final String readUTF(DataInput in) throws IOException { int utflen = in.readUnsignedShort(); - byte[] bytearr; - char[] chararr; + byte[] bytearr = null; if (in instanceof DataInputStream dis) { - if (dis.bytearr.length < utflen) { - dis.bytearr = new byte[utflen*2]; - dis.chararr = new char[utflen*2]; + if (dis.bytearr.length >= utflen) { + bytearr = dis.bytearr; } - chararr = dis.chararr; - bytearr = dis.bytearr; - } else { + } + boolean trusted = false; + if (bytearr == null) { bytearr = new byte[utflen]; - chararr = new char[utflen]; + trusted = true; } int c, char2, char3; @@ -592,12 +595,35 @@ public static final String readUTF(DataInput in) throws IOException { int chararr_count=0; in.readFully(bytearr, 0, utflen); + int ascii = JLA.countPositives(bytearr, 0, utflen); + if (ascii == utflen) { + String str; + if (trusted) { + str = JLA.newStringNoRepl(bytearr, StandardCharsets.ISO_8859_1); + } else { + str = new String(bytearr, 0, utflen, StandardCharsets.ISO_8859_1); + } + return str; + } + if (trusted && in instanceof DataInputStream dis) { + dis.bytearr = bytearr; + trusted = false; + } - while (count < utflen) { - c = (int) bytearr[count] & 0xff; - if (c > 127) break; - count++; - chararr[chararr_count++]=(char)c; + char[] chararr; + if (in instanceof DataInputStream dis) { + if (dis.chararr.length < (utflen << 1)) { + dis.chararr = new char[utflen << 1]; + } + chararr = dis.chararr; + } else { + chararr = new char[utflen]; + } + + if (ascii != 0) { + JLA.inflateBytesToChars(bytearr, 0, chararr, 0, ascii); + count += ascii; + chararr_count += ascii; } while (count < utflen) { diff --git a/src/java.base/share/classes/java/io/DataOutputStream.java b/src/java.base/share/classes/java/io/DataOutputStream.java index d16ae73f913bf..4b22d65bd39fa 100644 --- a/src/java.base/share/classes/java/io/DataOutputStream.java +++ b/src/java.base/share/classes/java/io/DataOutputStream.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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,8 +26,13 @@ package java.io; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.util.ByteArray; +import static jdk.internal.util.ModifiedUtf.putChar; +import static jdk.internal.util.ModifiedUtf.utfLen; + /** * A data output stream lets an application write primitive Java data * types to an output stream in a portable way. An application can @@ -44,6 +50,8 @@ * @since 1.0 */ public class DataOutputStream extends FilterOutputStream implements DataOutput { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + /** * The number of bytes written to the data output stream so far. * If this counter overflows, it will be wrapped to Integer.MAX_VALUE. @@ -352,15 +360,11 @@ public final void writeUTF(String str) throws IOException { * {@code str} would exceed 65535 bytes in length * @throws IOException if some other I/O error occurs. */ + @SuppressWarnings("deprecation") static int writeUTF(String str, DataOutput out) throws IOException { final int strlen = str.length(); - int utflen = strlen; // optimized for ASCII - - for (int i = 0; i < strlen; i++) { - int c = str.charAt(i); - if (c >= 0x80 || c == 0) - utflen += (c >= 0x800) ? 2 : 1; - } + int countNonZeroAscii = JLA.countNonZeroAscii(str); + int utflen = utfLen(str, countNonZeroAscii); if (utflen > 65535 || /* overflow */ utflen < strlen) throw new UTFDataFormatException(tooLongMsg(str, utflen)); @@ -377,25 +381,11 @@ static int writeUTF(String str, DataOutput out) throws IOException { int count = 0; ByteArray.setUnsignedShort(bytearr, count, utflen); count += 2; - int i = 0; - for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII - int c = str.charAt(i); - if (c >= 0x80 || c == 0) break; - bytearr[count++] = (byte) c; - } + str.getBytes(0, countNonZeroAscii, bytearr, count); + count += countNonZeroAscii; - for (; i < strlen; i++) { - int c = str.charAt(i); - if (c < 0x80 && c != 0) { - bytearr[count++] = (byte) c; - } else if (c >= 0x800) { - bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); - bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); - bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); - } else { - bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); - bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); - } + for (int i = countNonZeroAscii; i < strlen;) { + count = putChar(bytearr, count, str.charAt(i++)); } out.write(bytearr, 0, utflen + 2); return utflen + 2; diff --git a/src/java.base/share/classes/java/io/FileInputStream.java b/src/java.base/share/classes/java/io/FileInputStream.java index 60b289637fdfc..180b2e416a914 100644 --- a/src/java.base/share/classes/java/io/FileInputStream.java +++ b/src/java.base/share/classes/java/io/FileInputStream.java @@ -82,10 +82,9 @@ public class FileInputStream extends InputStream private volatile boolean closed; /** - * Creates a {@code FileInputStream} by - * opening a connection to an actual file, - * the file named by the path name {@code name} - * in the file system. {@linkplain java.nio.file##links Symbolic links} + * Creates a {@code FileInputStream} to read from an existing file + * named by the path name {@code name}. + * {@linkplain java.nio.file##links Symbolic links} * are automatically redirected to the target of the link. * A new {@code FileDescriptor} * object is created to represent this file @@ -115,10 +114,8 @@ public FileInputStream(String name) throws FileNotFoundException { } /** - * Creates a {@code FileInputStream} by - * opening a connection to an actual file, - * the file named by the {@code File} - * object {@code file} in the file system. + * Creates a {@code FileInputStream} to read from an existing file + * represented by the {@code File} object {@code file}. * {@linkplain java.nio.file##links Symbolic links} * are automatically redirected to the target of the link. * A new {@code FileDescriptor} object diff --git a/src/java.base/share/classes/java/io/InputStream.java b/src/java.base/share/classes/java/io/InputStream.java index 736b6ebd90435..a87870bfce0dd 100644 --- a/src/java.base/share/classes/java/io/InputStream.java +++ b/src/java.base/share/classes/java/io/InputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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,6 +25,8 @@ package java.io; +import jdk.internal.util.ArraysSupport; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -305,12 +307,9 @@ public int read(byte[] b, int off, int len) throws IOException { } /** - * The maximum size of array to allocate. - * Some VMs reserve some header words in an array. - * Attempts to allocate larger arrays may result in - * OutOfMemoryError: Requested array size exceeds VM limit + * The maximum size of array to allocate */ - private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + private static final int MAX_BUFFER_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; /** * Reads all remaining bytes from the input stream. This method blocks until diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java index 3650b10135356..bde069a1774d1 100644 --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java @@ -1,5 +1,6 @@ /* * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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,8 +35,13 @@ import java.util.StringJoiner; import jdk.internal.util.ByteArray; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import sun.reflect.misc.ReflectUtil; +import static jdk.internal.util.ModifiedUtf.putChar; +import static jdk.internal.util.ModifiedUtf.utfLen; + /** * An ObjectOutputStream writes primitive data types and graphs of Java objects * to an OutputStream. The objects can be read (reconstituted) using an @@ -169,6 +175,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static class Caches { /** cache of subclass security audit results */ @@ -885,7 +892,7 @@ public void writeChars(String str) throws IOException { * stream */ public void writeUTF(String str) throws IOException { - bout.writeUTF(str); + bout.writeUTFInternal(str, false); } /** @@ -1317,14 +1324,7 @@ private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) */ private void writeString(String str, boolean unshared) throws IOException { handles.assign(unshared ? null : str); - long utflen = bout.getUTFLength(str); - if (utflen <= 0xFFFF) { - bout.writeByte(TC_STRING); - bout.writeUTF(str, utflen); - } else { - bout.writeByte(TC_LONGSTRING); - bout.writeLongUTF(str, utflen); - } + bout.writeUTFInternal(str, true); } /** @@ -1994,26 +1994,27 @@ public void writeDouble(double v) throws IOException { } } - public void writeBytes(String s) throws IOException { - int endoff = s.length(); - int cpos = 0; - int csize = 0; - for (int off = 0; off < endoff; ) { - if (cpos >= csize) { - cpos = 0; - csize = Math.min(endoff - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - } - if (pos >= MAX_BLOCK_SIZE) { + @SuppressWarnings("deprecation") + void writeBytes(String s, int len) throws IOException { + int pos = this.pos; + for (int strpos = 0; strpos < len;) { + int rem = MAX_BLOCK_SIZE - pos; + int csize = Math.min(len - strpos, rem); + s.getBytes(strpos, strpos + csize, buf, pos); + pos += csize; + strpos += csize; + + if (pos == MAX_BLOCK_SIZE) { + this.pos = pos; drain(); + pos = 0; } - int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos); - int stop = pos + n; - while (pos < stop) { - buf[pos++] = (byte) cbuf[cpos++]; - } - off += n; } + this.pos = pos; + } + + public void writeBytes(String s) throws IOException { + writeBytes(s, s.length()); } public void writeChars(String s) throws IOException { @@ -2026,8 +2027,47 @@ public void writeChars(String s) throws IOException { } } - public void writeUTF(String s) throws IOException { - writeUTF(s, getUTFLength(s)); + public void writeUTF(String str) throws IOException { + writeUTFInternal(str, false); + } + + private void writeUTFInternal(String str, boolean writeHeader) throws IOException { + int strlen = str.length(); + int countNonZeroAscii = JLA.countNonZeroAscii(str); + int utflen = utfLen(str, countNonZeroAscii); + if (utflen <= 0xFFFF) { + if(writeHeader) { + writeByte(TC_STRING); + } + writeShort(utflen); + } else { + if(writeHeader) { + writeByte(TC_LONGSTRING); + } + writeLong(utflen); + } + + if (countNonZeroAscii != 0) { + writeBytes(str, countNonZeroAscii); + } + if (countNonZeroAscii != strlen) { + writeMoreUTF(str, countNonZeroAscii); + } + } + + private void writeMoreUTF(String str, int stroff) throws IOException { + int pos = this.pos; + for (int strlen = str.length(); stroff < strlen;) { + char c = str.charAt(stroff++); + int csize = c != 0 && c < 0x80 ? 1 : c >= 0x800 ? 3 : 2; + if (pos + csize >= MAX_BLOCK_SIZE) { + this.pos = pos; + drain(); + pos = 0; + } + pos = putChar(buf, pos, c); + } + this.pos = pos; } @@ -2153,112 +2193,6 @@ void writeDoubles(double[] v, int off, int len) throws IOException { } } } - - /** - * Returns the length in bytes of the UTF encoding of the given string. - */ - long getUTFLength(String s) { - int len = s.length(); - long utflen = 0; - for (int off = 0; off < len; ) { - int csize = Math.min(len - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - for (int cpos = 0; cpos < csize; cpos++) { - char c = cbuf[cpos]; - if (c >= 0x0001 && c <= 0x007F) { - utflen++; - } else if (c > 0x07FF) { - utflen += 3; - } else { - utflen += 2; - } - } - off += csize; - } - return utflen; - } - - /** - * Writes the given string in UTF format. This method is used in - * situations where the UTF encoding length of the string is already - * known; specifying it explicitly avoids a prescan of the string to - * determine its UTF length. - */ - void writeUTF(String s, long utflen) throws IOException { - if (utflen > 0xFFFFL) { - throw new UTFDataFormatException(); - } - writeShort((int) utflen); - if (utflen == (long) s.length()) { - writeBytes(s); - } else { - writeUTFBody(s); - } - } - - /** - * Writes given string in "long" UTF format. "Long" UTF format is - * identical to standard UTF, except that it uses an 8 byte header - * (instead of the standard 2 bytes) to convey the UTF encoding length. - */ - void writeLongUTF(String s) throws IOException { - writeLongUTF(s, getUTFLength(s)); - } - - /** - * Writes given string in "long" UTF format, where the UTF encoding - * length of the string is already known. - */ - void writeLongUTF(String s, long utflen) throws IOException { - writeLong(utflen); - if (utflen == (long) s.length()) { - writeBytes(s); - } else { - writeUTFBody(s); - } - } - - /** - * Writes the "body" (i.e., the UTF representation minus the 2-byte or - * 8-byte length header) of the UTF encoding for the given string. - */ - private void writeUTFBody(String s) throws IOException { - int limit = MAX_BLOCK_SIZE - 3; - int len = s.length(); - for (int off = 0; off < len; ) { - int csize = Math.min(len - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - for (int cpos = 0; cpos < csize; cpos++) { - char c = cbuf[cpos]; - if (pos <= limit) { - if (c <= 0x007F && c != 0) { - buf[pos++] = (byte) c; - } else if (c > 0x07FF) { - buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F)); - buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F)); - buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F)); - pos += 3; - } else { - buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F)); - buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F)); - pos += 2; - } - } else { // write one byte at a time to normalize block - if (c <= 0x007F && c != 0) { - write(c); - } else if (c > 0x07FF) { - write(0xE0 | ((c >> 12) & 0x0F)); - write(0x80 | ((c >> 6) & 0x3F)); - write(0x80 | ((c >> 0) & 0x3F)); - } else { - write(0xC0 | ((c >> 6) & 0x1F)); - write(0x80 | ((c >> 0) & 0x3F)); - } - } - } - off += csize; - } - } } /** diff --git a/src/java.base/share/classes/java/lang/Boolean.java b/src/java.base/share/classes/java/lang/Boolean.java index ba88157dc923e..2c0925a979016 100644 --- a/src/java.base/share/classes/java/lang/Boolean.java +++ b/src/java.base/share/classes/java/lang/Boolean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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,10 +37,10 @@ import static java.lang.constant.ConstantDescs.CD_Boolean; /** - * The Boolean class wraps a value of the primitive type - * {@code boolean} in an object. An object of type - * {@code Boolean} contains a single field whose type is - * {@code boolean}. + * The {@code Boolean} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code boolean}. An object of type {@code Boolean} contains a + * single field whose type is {@code boolean}. * *

In addition, this class provides many methods for * converting a {@code boolean} to a {@code String} and a diff --git a/src/java.base/share/classes/java/lang/Byte.java b/src/java.base/share/classes/java/lang/Byte.java index 18502abf69c4d..ade75a7a99eeb 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, 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 @@ -39,10 +39,10 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; /** - * - * The {@code Byte} class wraps a value of primitive type {@code byte} - * in an object. An object of type {@code Byte} contains a single - * field whose type is {@code byte}. + * The {@code Byte} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code byte}. An object of type {@code Byte} contains a + * single field whose type is {@code byte}. * *

In addition, this class provides several methods for converting * a {@code byte} to a {@code String} and a {@code String} to a {@code diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index a829d71e11367..84db550d7cc4d 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.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 @@ -43,12 +43,12 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; /** - * The {@code Character} class wraps a value of the primitive - * type {@code char} in an object. An object of class - * {@code Character} contains a single field whose type is - * {@code char}. - *

- * In addition, this class provides a large number of static methods for + * The {@code Character} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code char}. An object of type {@code Character} contains a + * single field whose type is {@code char}. + * + *

In addition, this class provides a large number of static methods for * determining a character's category (lowercase letter, digit, etc.) * and for converting characters from uppercase to lowercase and vice * versa. diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 48ffeea5289ff..79cd57011b0ed 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -140,22 +140,24 @@ * }} * * It is also possible to get the {@code Class} object for a named - * class or interface (or for {@code void}) using a class literal. + * class or interface (or for {@code void}) using a class literal + * (JLS {@jls 15.8.2}). * For example: * * {@snippet lang="java" : - * System.out.println("The name of class Foo is: "+Foo.class.getName()); + * System.out.println("The name of class Foo is: " + Foo.class.getName()); // @highlight substring="Foo.class" * } * *

Some methods of class {@code Class} expose whether the declaration of * a class or interface in Java source code was enclosed within * another declaration. Other methods describe how a class or interface - * is situated in a nest. A nest is a set of + * is situated in a {@index "nest"}. A nest is a set of * classes and interfaces, in the same run-time package, that * allow mutual access to their {@code private} members. - * The classes and interfaces are known as nestmates. + * The classes and interfaces are known as {@index "nestmates"} + * (JVMS {@jvms 4.7.29}). * One nestmate acts as the - * nest host, and enumerates the other nestmates which + * nest host (JVMS {@jvms 4.7.28}), and enumerates the other nestmates which * belong to the nest; each of them in turn records it as the nest host. * The classes and interfaces which belong to a nest, including its host, are * determined when @@ -167,7 +169,7 @@ *

Hidden Classes

* A class or interface created by the invocation of * {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...) - * Lookup::defineHiddenClass} is a {@linkplain Class#isHidden() hidden} + * Lookup::defineHiddenClass} is a {@linkplain Class#isHidden() hidden} * class or interface. * All kinds of class, including enum classes and record classes, may be * hidden classes; all kinds of interface, including annotation interfaces, @@ -216,7 +218,6 @@ * * @see java.lang.ClassLoader#defineClass(byte[], int, int) * @since 1.0 - * @jls 15.8.2 Class Literals */ public final class Class implements java.io.Serializable, GenericDeclaration, diff --git a/src/java.base/share/classes/java/lang/Double.java b/src/java.base/share/classes/java/lang/Double.java index 9b11964d9e6fa..68fff6a2fbdd2 100644 --- a/src/java.base/share/classes/java/lang/Double.java +++ b/src/java.base/share/classes/java/lang/Double.java @@ -36,10 +36,10 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; /** - * The {@code Double} class wraps a value of the primitive type - * {@code double} in an object. An object of type - * {@code Double} contains a single field whose type is - * {@code double}. + * The {@code Double} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code double}. An object of type {@code Double} contains a + * single field whose type is {@code double}. * *

In addition, this class provides several methods for converting a * {@code double} to a {@code String} and a @@ -148,7 +148,7 @@ * relations that can be defined over floating-point values: * *

- *
numerical equality ({@code ==} + *
{@index "numerical equality"} ({@code ==} * operator): (Not an equivalence relation)
*
Two floating-point values represent the same extended real * number. The extended real numbers are the real numbers augmented @@ -158,7 +158,7 @@ * number and is not equal to any value, including itself. *
* - *
bit-wise equivalence:
+ *
{@index "bit-wise equivalence"}:
*
The bits of the two floating-point values are the same. This * equivalence relation for {@code double} values {@code a} and {@code * b} is implemented by the expression @@ -168,7 +168,7 @@ * is distinguished from every other bit pattern encoding a NaN. *
* - *
representation equivalence:
+ *
{@index "representation equivalence"}:
*
The two floating-point values represent the same IEEE 754 * datum. In particular, for {@linkplain #isFinite(double) * finite} values, the sign, {@linkplain Math#getExponent(double) diff --git a/src/java.base/share/classes/java/lang/Float.java b/src/java.base/share/classes/java/lang/Float.java index 02b6600777331..470eb71cf701f 100644 --- a/src/java.base/share/classes/java/lang/Float.java +++ b/src/java.base/share/classes/java/lang/Float.java @@ -36,10 +36,10 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; /** - * The {@code Float} class wraps a value of primitive type - * {@code float} in an object. An object of type - * {@code Float} contains a single field whose type is - * {@code float}. + * The {@code Float} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code float}. An object of type {@code Float} contains a + * single field whose type is {@code float}. * *

In addition, this class provides several methods for converting a * {@code float} to a {@code String} and a diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 7a65046181f00..eab0a942d9a9a 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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,9 +45,10 @@ import static java.lang.String.UTF16; /** - * The {@code Integer} class wraps a value of the primitive type - * {@code int} in an object. An object of type {@code Integer} - * contains a single field whose type is {@code int}. + * The {@code Integer} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code int}. An object of type {@code Integer} contains a + * single field whose type is {@code int}. * *

In addition, this class provides several methods for converting * an {@code int} to a {@code String} and a {@code String} to an @@ -63,8 +64,9 @@ *

Implementation note: The implementations of the "bit twiddling" * methods (such as {@link #highestOneBit(int) highestOneBit} and * {@link #numberOfTrailingZeros(int) numberOfTrailingZeros}) are - * based on material from Henry S. Warren, Jr.'s Hacker's - * Delight, (Addison Wesley, 2002). + * based on material from Henry S. Warren, Jr.'s Hacker's + * Delight, (Addison Wesley, 2002) and Hacker's + * Delight, Second Edition, (Pearson Education, 2013). * * @author Lee Boynton * @author Arthur van Hoff @@ -1736,7 +1738,7 @@ public static int reverse(int i) { * compress(expand(x, m), m) == x & compress(m, m) * } *

- * The Sheep And Goats (SAG) operation (see Hacker's Delight, section 7.7) + * The Sheep And Goats (SAG) operation (see Hacker's Delight, Second Edition, section 7.7) * can be implemented as follows: * {@snippet lang="java" : * int compressLeft(int i, int mask) { diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index ee9533b29eb53..f86e1622b3836 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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,8 +45,9 @@ import static java.lang.String.UTF16; /** - * The {@code Long} class wraps a value of the primitive type {@code - * long} in an object. An object of type {@code Long} contains a + * The {@code Long} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code long}. An object of type {@code Long} contains a * single field whose type is {@code long}. * *

In addition, this class provides several methods for converting @@ -63,8 +64,9 @@ *

Implementation note: The implementations of the "bit twiddling" * methods (such as {@link #highestOneBit(long) highestOneBit} and * {@link #numberOfTrailingZeros(long) numberOfTrailingZeros}) are - * based on material from Henry S. Warren, Jr.'s Hacker's - * Delight, (Addison Wesley, 2002). + * based on material from Henry S. Warren, Jr.'s Hacker's + * Delight, (Addison Wesley, 2002) and Hacker's + * Delight, Second Edition, (Pearson Education, 2013). * * @author Lee Boynton * @author Arthur van Hoff @@ -1749,7 +1751,7 @@ public static long reverse(long i) { * compress(expand(x, m), m) == x & compress(m, m) * } *

- * The Sheep And Goats (SAG) operation (see Hacker's Delight, section 7.7) + * The Sheep And Goats (SAG) operation (see Hacker's Delight, Second Edition, section 7.7) * can be implemented as follows: * {@snippet lang="java" : * long compressLeft(long i, long mask) { diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 044982a588af8..6b576c88b4710 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -2737,6 +2737,7 @@ public static double cosh(double x) { * @return The hyperbolic tangent of {@code x}. * @since 1.5 */ + @IntrinsicCandidate public static double tanh(double x) { return StrictMath.tanh(x); } diff --git a/src/java.base/share/classes/java/lang/Object.java b/src/java.base/share/classes/java/lang/Object.java index d9813df57a4f0..7909f05304268 100644 --- a/src/java.base/share/classes/java/lang/Object.java +++ b/src/java.base/share/classes/java/lang/Object.java @@ -109,7 +109,7 @@ public Object() {} /** * Indicates whether some other object is "equal to" this one. *

- * The {@code equals} method implements an equivalence relation + * The {@code equals} method implements an {@index "equivalence relation"} * on non-null object references: *

    *
  • It is reflexive: for any non-null reference value diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index 6a148d1edac00..ec792de47e2ac 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, 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 @@ -39,8 +39,9 @@ import static java.lang.constant.ConstantDescs.DEFAULT_NAME; /** - * The {@code Short} class wraps a value of primitive type {@code - * short} in an object. An object of type {@code Short} contains a + * The {@code Short} class is the {@linkplain + * java.lang##wrapperClass wrapper class} for values of the primitive + * type {@code short}. An object of type {@code Short} contains a * single field whose type is {@code short}. * *

    In addition, this class provides several methods for converting diff --git a/src/java.base/share/classes/java/lang/ThreadBuilders.java b/src/java.base/share/classes/java/lang/ThreadBuilders.java index ca29dc4f53589..234807d4cc2a7 100644 --- a/src/java.base/share/classes/java/lang/ThreadBuilders.java +++ b/src/java.base/share/classes/java/lang/ThreadBuilders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; import jdk.internal.misc.Unsafe; +import jdk.internal.invoke.MhUtil; import jdk.internal.vm.ContinuationSupport; /** @@ -273,15 +274,9 @@ public ThreadFactory factory() { * Base ThreadFactory implementation. */ private abstract static class BaseThreadFactory implements ThreadFactory { - private static final VarHandle COUNT; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - COUNT = l.findVarHandle(BaseThreadFactory.class, "count", long.class); - } catch (Exception e) { - throw new InternalError(e); - } - } + private static final VarHandle COUNT = MhUtil.findVarHandle( + MethodHandles.lookup(), "count", long.class); + private final String name; private final int characteristics; private final UncaughtExceptionHandler uhe; diff --git a/src/java.base/share/classes/java/lang/TypeNotPresentException.java b/src/java.base/share/classes/java/lang/TypeNotPresentException.java index fdf42548c0371..c5a899ef2851d 100644 --- a/src/java.base/share/classes/java/lang/TypeNotPresentException.java +++ b/src/java.base/share/classes/java/lang/TypeNotPresentException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * 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,9 +28,9 @@ /** * Thrown when an application tries to access a type using a string * representing the type's name, but no definition for the type with - * the specified name can be found. This exception differs from - * {@link ClassNotFoundException} in that {@code ClassNotFoundException} is a - * checked exception, whereas this exception is unchecked. + * the specified name can be found. This exception differs from + * {@link ClassNotFoundException} in that {@code ClassNotFoundException} + * is a checked exception, whereas this exception is unchecked. * *

    Note that this exception may be used when undefined type variables * are accessed as well as when types (e.g., classes, interfaces or @@ -48,15 +48,15 @@ public class TypeNotPresentException extends RuntimeException { private static final long serialVersionUID = -5101214195716534496L; /** - * The type name. + * The type name or the name of a type variable. */ private String typeName; /** - * Constructs a {@code TypeNotPresentException} for the named type - * with the specified cause. + * Constructs a {@code TypeNotPresentException} for the named type or + * type variable with the specified cause. * - * @param typeName the fully qualified name of the unavailable type + * @param typeName the fully qualified name of the unavailable type or type variable * @param cause the exception that was thrown when the system attempted to * load the named type, or {@code null} if unavailable or inapplicable */ @@ -66,9 +66,9 @@ public TypeNotPresentException(String typeName, Throwable cause) { } /** - * Returns the fully qualified name of the unavailable type. + * Returns the fully qualified name of the unavailable type or type variable name. * - * @return the fully qualified name of the unavailable type + * @return the fully qualified name of the unavailable type or type variable name */ public String typeName() { return typeName;} } diff --git a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java index c4474a248b520..fe768e93b1308 100644 --- a/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java +++ b/src/java.base/share/classes/java/lang/classfile/AnnotationValue.java @@ -42,6 +42,8 @@ import jdk.internal.classfile.impl.Util; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * Models an {@code element_value} structure, or a value of an element-value * pair of an annotation, as defined in JVMS {@jvms 4.7.16.1}. @@ -60,7 +62,7 @@ public sealed interface AnnotationValue { /** * Models an annotation value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_ANNOTATION}. + * The {@linkplain #tag tag} of this value is {@value TAG_ANNOTATION}. * * @since 22 */ @@ -73,7 +75,7 @@ sealed interface OfAnnotation extends AnnotationValue /** * Models an array value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_ARRAY}. + * The {@linkplain #tag tag} of this value is {@value TAG_ARRAY}. * * @since 22 */ @@ -131,7 +133,7 @@ sealed interface OfConstant extends AnnotationValue { /** * Models a string value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_STRING}. + * The {@linkplain #tag tag} of this value is {@value TAG_STRING}. * * @since 22 */ @@ -159,7 +161,7 @@ default String resolvedValue() { /** * Models a double value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_DOUBLE}. + * The {@linkplain #tag tag} of this value is {@value TAG_DOUBLE}. * * @since 22 */ @@ -187,7 +189,7 @@ default Double resolvedValue() { /** * Models a float value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_FLOAT}. + * The {@linkplain #tag tag} of this value is {@value TAG_FLOAT}. * * @since 22 */ @@ -215,7 +217,7 @@ default Float resolvedValue() { /** * Models a long value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_LONG}. + * The {@linkplain #tag tag} of this value is {@value TAG_LONG}. * * @since 22 */ @@ -243,7 +245,7 @@ default Long resolvedValue() { /** * Models an int value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_INT}. + * The {@linkplain #tag tag} of this value is {@value TAG_INT}. * * @since 22 */ @@ -271,7 +273,7 @@ default Integer resolvedValue() { /** * Models a short value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_SHORT}. + * The {@linkplain #tag tag} of this value is {@value TAG_SHORT}. * * @since 22 */ @@ -302,7 +304,7 @@ default Short resolvedValue() { /** * Models a char value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_CHAR}. + * The {@linkplain #tag tag} of this value is {@value TAG_CHAR}. * * @since 22 */ @@ -333,7 +335,7 @@ default Character resolvedValue() { /** * Models a byte value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_BYTE}. + * The {@linkplain #tag tag} of this value is {@value TAG_BYTE}. * * @since 22 */ @@ -364,7 +366,7 @@ default Byte resolvedValue() { /** * Models a boolean value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_BOOLEAN}. + * The {@linkplain #tag tag} of this value is {@value TAG_BOOLEAN}. * * @since 22 */ @@ -395,7 +397,7 @@ default Boolean resolvedValue() { /** * Models a class value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_CLASS}. + * The {@linkplain #tag tag} of this value is {@value TAG_CLASS}. * * @since 22 */ @@ -413,7 +415,7 @@ default ClassDesc classSymbol() { /** * Models an enum value of an element-value pair. - * The {@linkplain #tag tag} of this value is {@value ClassFile#AEV_ENUM}. + * The {@linkplain #tag tag} of this value is {@value TAG_ENUM}. * * @since 22 */ @@ -432,9 +434,52 @@ default ClassDesc classSymbol() { Utf8Entry constantName(); } + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfByte}. */ + int TAG_BYTE = 'B'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfChar}. */ + int TAG_CHAR = 'C'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfDouble}. */ + int TAG_DOUBLE = 'D'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfFloat}. */ + int TAG_FLOAT = 'F'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfInt}. */ + int TAG_INT = 'I'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfLong}. */ + int TAG_LONG = 'J'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfShort}. */ + int TAG_SHORT = 'S'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfBoolean}. */ + int TAG_BOOLEAN = 'Z'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfString}. */ + int TAG_STRING = 's'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfEnum}. */ + int TAG_ENUM = 'e'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfClass}. */ + int TAG_CLASS = 'c'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfAnnotation}. */ + int TAG_ANNOTATION = '@'; + + /** The {@link #tag() tag} indicating the value of an element-value pair is {@link OfArray}. */ + int TAG_ARRAY = '['; + /** * {@return the tag character for this value as per JVMS {@jvms 4.7.16.1}} * The tag characters have a one-to-one mapping to the types of annotation element values. + * + * @apiNote + * {@code TAG_}-prefixed constants in this class, such as {@link #TAG_INT}, + * describe the possible return values of this method. */ char tag(); @@ -445,6 +490,8 @@ default ClassDesc classSymbol() { */ static OfEnum ofEnum(Utf8Entry className, Utf8Entry constantName) { + requireNonNull(className); + requireNonNull(constantName); return new AnnotationImpl.OfEnumImpl(className, constantName); } @@ -463,6 +510,7 @@ static OfEnum ofEnum(ClassDesc className, String constantName) { * @param className the descriptor string of the class */ static OfClass ofClass(Utf8Entry className) { + requireNonNull(className); return new AnnotationImpl.OfClassImpl(className); } @@ -479,6 +527,7 @@ static OfClass ofClass(ClassDesc className) { * @param value the string */ static OfString ofString(Utf8Entry value) { + requireNonNull(value); return new AnnotationImpl.OfStringImpl(value); } @@ -495,6 +544,7 @@ static OfString ofString(String value) { * @param value the double value */ static OfDouble ofDouble(DoubleEntry value) { + requireNonNull(value); return new AnnotationImpl.OfDoubleImpl(value); } @@ -511,6 +561,7 @@ static OfDouble ofDouble(double value) { * @param value the float value */ static OfFloat ofFloat(FloatEntry value) { + requireNonNull(value); return new AnnotationImpl.OfFloatImpl(value); } @@ -527,6 +578,7 @@ static OfFloat ofFloat(float value) { * @param value the long value */ static OfLong ofLong(LongEntry value) { + requireNonNull(value); return new AnnotationImpl.OfLongImpl(value); } @@ -543,6 +595,7 @@ static OfLong ofLong(long value) { * @param value the int value */ static OfInt ofInt(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfIntImpl(value); } @@ -559,6 +612,7 @@ static OfInt ofInt(int value) { * @param value the short value */ static OfShort ofShort(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfShortImpl(value); } @@ -575,6 +629,7 @@ static OfShort ofShort(short value) { * @param value the char value */ static OfChar ofChar(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfCharImpl(value); } @@ -591,6 +646,7 @@ static OfChar ofChar(char value) { * @param value the byte value */ static OfByte ofByte(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfByteImpl(value); } @@ -607,6 +663,7 @@ static OfByte ofByte(byte value) { * @param value the boolean value */ static OfBoolean ofBoolean(IntegerEntry value) { + requireNonNull(value); return new AnnotationImpl.OfBooleanImpl(value); } @@ -624,6 +681,7 @@ static OfBoolean ofBoolean(boolean value) { * @param value the annotation */ static OfAnnotation ofAnnotation(Annotation value) { + requireNonNull(value); return new AnnotationImpl.OfAnnotationImpl(value); } @@ -741,6 +799,6 @@ static AnnotationValue of(Object value) { } else if (value instanceof Enum e) { return ofEnum(ClassDesc.ofDescriptor(e.getDeclaringClass().descriptorString()), e.name()); } - throw new IllegalArgumentException("Illegal annotation constant value type " + (value == null ? null : value.getClass())); + throw new IllegalArgumentException("Illegal annotation constant value type " + requireNonNull(value).getClass()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java index d66806ca93b43..0caf231ec2a10 100644 --- a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java +++ b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java @@ -33,6 +33,8 @@ import jdk.internal.classfile.impl.AbstractUnboundModel; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A {@link ClassFileElement} describing an entity that has attributes, such * as a class, field, method, code attribute, or record component. @@ -58,6 +60,7 @@ public sealed interface AttributedElement extends ClassFileElement * is not present */ default > Optional findAttribute(AttributeMapper attr) { + requireNonNull(attr); for (Attribute la : attributes()) { if (la.attributeMapper() == attr) { @SuppressWarnings("unchecked") @@ -76,6 +79,7 @@ default > Optional findAttribute(AttributeMapper at * is not present */ default > List findAttributes(AttributeMapper attr) { + requireNonNull(attr); var list = new ArrayList(); for (var a : attributes()) { if (a.attributeMapper() == attr) { diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFile.java b/src/java.base/share/classes/java/lang/classfile/ClassFile.java index a305878d3ebf2..284503ee62714 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFile.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFile.java @@ -512,903 +512,75 @@ default List verify(Path path) throws IOException { /** 0xCAFEBABE */ int MAGIC_NUMBER = 0xCAFEBABE; - /** The integer value used to encode the NOP instruction. */ - int NOP = 0; - - /** The integer value used to encode the ACONST_NULL instruction. */ - int ACONST_NULL = 1; - - /** The integer value used to encode the ICONST_M1 instruction. */ - int ICONST_M1 = 2; - - /** The integer value used to encode the ICONST_0 instruction. */ - int ICONST_0 = 3; - - /** The integer value used to encode the ICONST_1 instruction. */ - int ICONST_1 = 4; - - /** The integer value used to encode the ICONST_2 instruction. */ - int ICONST_2 = 5; - - /** The integer value used to encode the ICONST_3 instruction. */ - int ICONST_3 = 6; - - /** The integer value used to encode the ICONST_4 instruction. */ - int ICONST_4 = 7; - - /** The integer value used to encode the ICONST_5 instruction. */ - int ICONST_5 = 8; - - /** The integer value used to encode the LCONST_0 instruction. */ - int LCONST_0 = 9; - - /** The integer value used to encode the LCONST_1 instruction. */ - int LCONST_1 = 10; - - /** The integer value used to encode the FCONST_0 instruction. */ - int FCONST_0 = 11; - - /** The integer value used to encode the FCONST_1 instruction. */ - int FCONST_1 = 12; - - /** The integer value used to encode the FCONST_2 instruction. */ - int FCONST_2 = 13; - - /** The integer value used to encode the DCONST_0 instruction. */ - int DCONST_0 = 14; - - /** The integer value used to encode the DCONST_1 instruction. */ - int DCONST_1 = 15; - - /** The integer value used to encode the BIPUSH instruction. */ - int BIPUSH = 16; - - /** The integer value used to encode the SIPUSH instruction. */ - int SIPUSH = 17; - - /** The integer value used to encode the LDC instruction. */ - int LDC = 18; - - /** The integer value used to encode the LDC_W instruction. */ - int LDC_W = 19; - - /** The integer value used to encode the LDC2_W instruction. */ - int LDC2_W = 20; - - /** The integer value used to encode the ILOAD instruction. */ - int ILOAD = 21; - - /** The integer value used to encode the LLOAD instruction. */ - int LLOAD = 22; - - /** The integer value used to encode the FLOAD instruction. */ - int FLOAD = 23; - - /** The integer value used to encode the DLOAD instruction. */ - int DLOAD = 24; - - /** The integer value used to encode the ALOAD instruction. */ - int ALOAD = 25; - - /** The integer value used to encode the ILOAD_0 instruction. */ - int ILOAD_0 = 26; - - /** The integer value used to encode the ILOAD_1 instruction. */ - int ILOAD_1 = 27; - - /** The integer value used to encode the ILOAD_2 instruction. */ - int ILOAD_2 = 28; - - /** The integer value used to encode the ILOAD_3 instruction. */ - int ILOAD_3 = 29; - - /** The integer value used to encode the LLOAD_0 instruction. */ - int LLOAD_0 = 30; - - /** The integer value used to encode the LLOAD_1 instruction. */ - int LLOAD_1 = 31; - - /** The integer value used to encode the LLOAD_2 instruction. */ - int LLOAD_2 = 32; - - /** The integer value used to encode the LLOAD_3 instruction. */ - int LLOAD_3 = 33; - - /** The integer value used to encode the FLOAD_0 instruction. */ - int FLOAD_0 = 34; - - /** The integer value used to encode the FLOAD_1 instruction. */ - int FLOAD_1 = 35; - - /** The integer value used to encode the FLOAD_2 instruction. */ - int FLOAD_2 = 36; - - /** The integer value used to encode the FLOAD_3 instruction. */ - int FLOAD_3 = 37; - - /** The integer value used to encode the DLOAD_0 instruction. */ - int DLOAD_0 = 38; - - /** The integer value used to encode the DLOAD_1 instruction. */ - int DLOAD_1 = 39; - - /** The integer value used to encode the DLOAD_2 instruction. */ - int DLOAD_2 = 40; - - /** The integer value used to encode the DLOAD_3 instruction. */ - int DLOAD_3 = 41; - - /** The integer value used to encode the ALOAD_0 instruction. */ - int ALOAD_0 = 42; - - /** The integer value used to encode the ALOAD_1 instruction. */ - int ALOAD_1 = 43; - - /** The integer value used to encode the ALOAD_2 instruction. */ - int ALOAD_2 = 44; - - /** The integer value used to encode the ALOAD_3 instruction. */ - int ALOAD_3 = 45; - - /** The integer value used to encode the IALOAD instruction. */ - int IALOAD = 46; - - /** The integer value used to encode the LALOAD instruction. */ - int LALOAD = 47; - - /** The integer value used to encode the FALOAD instruction. */ - int FALOAD = 48; - - /** The integer value used to encode the DALOAD instruction. */ - int DALOAD = 49; - - /** The integer value used to encode the AALOAD instruction. */ - int AALOAD = 50; - - /** The integer value used to encode the BALOAD instruction. */ - int BALOAD = 51; - - /** The integer value used to encode the CALOAD instruction. */ - int CALOAD = 52; - - /** The integer value used to encode the SALOAD instruction. */ - int SALOAD = 53; - - /** The integer value used to encode the ISTORE instruction. */ - int ISTORE = 54; - - /** The integer value used to encode the LSTORE instruction. */ - int LSTORE = 55; - - /** The integer value used to encode the FSTORE instruction. */ - int FSTORE = 56; - - /** The integer value used to encode the DSTORE instruction. */ - int DSTORE = 57; - - /** The integer value used to encode the ASTORE instruction. */ - int ASTORE = 58; - - /** The integer value used to encode the ISTORE_0 instruction. */ - int ISTORE_0 = 59; - - /** The integer value used to encode the ISTORE_1 instruction. */ - int ISTORE_1 = 60; - - /** The integer value used to encode the ISTORE_2 instruction. */ - int ISTORE_2 = 61; - - /** The integer value used to encode the ISTORE_3 instruction. */ - int ISTORE_3 = 62; - - /** The integer value used to encode the LSTORE_0 instruction. */ - int LSTORE_0 = 63; - - /** The integer value used to encode the LSTORE_1 instruction. */ - int LSTORE_1 = 64; - - /** The integer value used to encode the LSTORE_2 instruction. */ - int LSTORE_2 = 65; - - /** The integer value used to encode the LSTORE_3 instruction. */ - int LSTORE_3 = 66; - - /** The integer value used to encode the FSTORE_0 instruction. */ - int FSTORE_0 = 67; - - /** The integer value used to encode the FSTORE_1 instruction. */ - int FSTORE_1 = 68; - - /** The integer value used to encode the FSTORE_2 instruction. */ - int FSTORE_2 = 69; - - /** The integer value used to encode the FSTORE_3 instruction. */ - int FSTORE_3 = 70; - - /** The integer value used to encode the DSTORE_0 instruction. */ - int DSTORE_0 = 71; - - /** The integer value used to encode the DSTORE_1 instruction. */ - int DSTORE_1 = 72; - - /** The integer value used to encode the DSTORE_2 instruction. */ - int DSTORE_2 = 73; - - /** The integer value used to encode the DSTORE_3 instruction. */ - int DSTORE_3 = 74; - - /** The integer value used to encode the ASTORE_0 instruction. */ - int ASTORE_0 = 75; - - /** The integer value used to encode the ASTORE_1 instruction. */ - int ASTORE_1 = 76; - - /** The integer value used to encode the ASTORE_2 instruction. */ - int ASTORE_2 = 77; - - /** The integer value used to encode the ASTORE_3 instruction. */ - int ASTORE_3 = 78; - - /** The integer value used to encode the IASTORE instruction. */ - int IASTORE = 79; - - /** The integer value used to encode the LASTORE instruction. */ - int LASTORE = 80; - - /** The integer value used to encode the FASTORE instruction. */ - int FASTORE = 81; - - /** The integer value used to encode the DASTORE instruction. */ - int DASTORE = 82; - - /** The integer value used to encode the AASTORE instruction. */ - int AASTORE = 83; - - /** The integer value used to encode the BASTORE instruction. */ - int BASTORE = 84; - - /** The integer value used to encode the CASTORE instruction. */ - int CASTORE = 85; - - /** The integer value used to encode the SASTORE instruction. */ - int SASTORE = 86; - - /** The integer value used to encode the POP instruction. */ - int POP = 87; - - /** The integer value used to encode the POP2 instruction. */ - int POP2 = 88; - - /** The integer value used to encode the DUP instruction. */ - int DUP = 89; - - /** The integer value used to encode the DUP_X1 instruction. */ - int DUP_X1 = 90; - - /** The integer value used to encode the DUP_X2 instruction. */ - int DUP_X2 = 91; - - /** The integer value used to encode the DUP2 instruction. */ - int DUP2 = 92; - - /** The integer value used to encode the DUP2_X1 instruction. */ - int DUP2_X1 = 93; - - /** The integer value used to encode the DUP2_X2 instruction. */ - int DUP2_X2 = 94; - - /** The integer value used to encode the SWAP instruction. */ - int SWAP = 95; - - /** The integer value used to encode the IADD instruction. */ - int IADD = 96; - - /** The integer value used to encode the LADD instruction. */ - int LADD = 97; - - /** The integer value used to encode the FADD instruction. */ - int FADD = 98; - - /** The integer value used to encode the DADD instruction. */ - int DADD = 99; - - /** The integer value used to encode the ISUB instruction. */ - int ISUB = 100; - - /** The integer value used to encode the LSUB instruction. */ - int LSUB = 101; - - /** The integer value used to encode the FSUB instruction. */ - int FSUB = 102; - - /** The integer value used to encode the DSUB instruction. */ - int DSUB = 103; - - /** The integer value used to encode the IMUL instruction. */ - int IMUL = 104; - - /** The integer value used to encode the LMUL instruction. */ - int LMUL = 105; - - /** The integer value used to encode the FMUL instruction. */ - int FMUL = 106; - - /** The integer value used to encode the DMUL instruction. */ - int DMUL = 107; - - /** The integer value used to encode the IDIV instruction. */ - int IDIV = 108; - - /** The integer value used to encode the LDIV instruction. */ - int LDIV = 109; - - /** The integer value used to encode the FDIV instruction. */ - int FDIV = 110; - - /** The integer value used to encode the DDIV instruction. */ - int DDIV = 111; - - /** The integer value used to encode the IREM instruction. */ - int IREM = 112; - - /** The integer value used to encode the LREM instruction. */ - int LREM = 113; - - /** The integer value used to encode the FREM instruction. */ - int FREM = 114; - - /** The integer value used to encode the DREM instruction. */ - int DREM = 115; - - /** The integer value used to encode the INEG instruction. */ - int INEG = 116; - - /** The integer value used to encode the LNEG instruction. */ - int LNEG = 117; - - /** The integer value used to encode the FNEG instruction. */ - int FNEG = 118; - - /** The integer value used to encode the DNEG instruction. */ - int DNEG = 119; - - /** The integer value used to encode the ISHL instruction. */ - int ISHL = 120; - - /** The integer value used to encode the LSHL instruction. */ - int LSHL = 121; - - /** The integer value used to encode the ISHR instruction. */ - int ISHR = 122; - - /** The integer value used to encode the LSHR instruction. */ - int LSHR = 123; - - /** The integer value used to encode the IUSHR instruction. */ - int IUSHR = 124; - - /** The integer value used to encode the LUSHR instruction. */ - int LUSHR = 125; - - /** The integer value used to encode the IAND instruction. */ - int IAND = 126; - - /** The integer value used to encode the LAND instruction. */ - int LAND = 127; - - /** The integer value used to encode the IOR instruction. */ - int IOR = 128; - - /** The integer value used to encode the LOR instruction. */ - int LOR = 129; - - /** The integer value used to encode the IXOR instruction. */ - int IXOR = 130; - - /** The integer value used to encode the LXOR instruction. */ - int LXOR = 131; - - /** The integer value used to encode the IINC instruction. */ - int IINC = 132; - - /** The integer value used to encode the I2L instruction. */ - int I2L = 133; - - /** The integer value used to encode the I2F instruction. */ - int I2F = 134; - - /** The integer value used to encode the I2D instruction. */ - int I2D = 135; - - /** The integer value used to encode the L2I instruction. */ - int L2I = 136; - - /** The integer value used to encode the L2F instruction. */ - int L2F = 137; - - /** The integer value used to encode the L2D instruction. */ - int L2D = 138; - - /** The integer value used to encode the F2I instruction. */ - int F2I = 139; - - /** The integer value used to encode the F2L instruction. */ - int F2L = 140; - - /** The integer value used to encode the F2D instruction. */ - int F2D = 141; - - /** The integer value used to encode the D2I instruction. */ - int D2I = 142; - - /** The integer value used to encode the D2L instruction. */ - int D2L = 143; - - /** The integer value used to encode the D2F instruction. */ - int D2F = 144; - - /** The integer value used to encode the I2B instruction. */ - int I2B = 145; - - /** The integer value used to encode the I2C instruction. */ - int I2C = 146; - - /** The integer value used to encode the I2S instruction. */ - int I2S = 147; - - /** The integer value used to encode the LCMP instruction. */ - int LCMP = 148; - - /** The integer value used to encode the FCMPL instruction. */ - int FCMPL = 149; - - /** The integer value used to encode the FCMPG instruction. */ - int FCMPG = 150; - - /** The integer value used to encode the DCMPL instruction. */ - int DCMPL = 151; - - /** The integer value used to encode the DCMPG instruction. */ - int DCMPG = 152; - - /** The integer value used to encode the IFEQ instruction. */ - int IFEQ = 153; - - /** The integer value used to encode the IFNE instruction. */ - int IFNE = 154; - - /** The integer value used to encode the IFLT instruction. */ - int IFLT = 155; - - /** The integer value used to encode the IFGE instruction. */ - int IFGE = 156; - - /** The integer value used to encode the IFGT instruction. */ - int IFGT = 157; - - /** The integer value used to encode the IFLE instruction. */ - int IFLE = 158; - - /** The integer value used to encode the IF_ICMPEQ instruction. */ - int IF_ICMPEQ = 159; - - /** The integer value used to encode the IF_ICMPNE instruction. */ - int IF_ICMPNE = 160; - - /** The integer value used to encode the IF_ICMPLT instruction. */ - int IF_ICMPLT = 161; - - /** The integer value used to encode the IF_ICMPGE instruction. */ - int IF_ICMPGE = 162; - - /** The integer value used to encode the IF_ICMPGT instruction. */ - int IF_ICMPGT = 163; - - /** The integer value used to encode the IF_ICMPLE instruction. */ - int IF_ICMPLE = 164; - - /** The integer value used to encode the IF_ACMPEQ instruction. */ - int IF_ACMPEQ = 165; - - /** The integer value used to encode the IF_ACMPNE instruction. */ - int IF_ACMPNE = 166; - - /** The integer value used to encode the GOTO instruction. */ - int GOTO = 167; - - /** The integer value used to encode the JSR instruction. */ - int JSR = 168; - - /** The integer value used to encode the RET instruction. */ - int RET = 169; - - /** The integer value used to encode the TABLESWITCH instruction. */ - int TABLESWITCH = 170; - - /** The integer value used to encode the LOOKUPSWITCH instruction. */ - int LOOKUPSWITCH = 171; - - /** The integer value used to encode the IRETURN instruction. */ - int IRETURN = 172; - - /** The integer value used to encode the LRETURN instruction. */ - int LRETURN = 173; - - /** The integer value used to encode the FRETURN instruction. */ - int FRETURN = 174; - - /** The integer value used to encode the DRETURN instruction. */ - int DRETURN = 175; - - /** The integer value used to encode the ARETURN instruction. */ - int ARETURN = 176; - - /** The integer value used to encode the RETURN instruction. */ - int RETURN = 177; - - /** The integer value used to encode the GETSTATIC instruction. */ - int GETSTATIC = 178; - - /** The integer value used to encode the PUTSTATIC instruction. */ - int PUTSTATIC = 179; - - /** The integer value used to encode the GETFIELD instruction. */ - int GETFIELD = 180; - - /** The integer value used to encode the PUTFIELD instruction. */ - int PUTFIELD = 181; - - /** The integer value used to encode the INVOKEVIRTUAL instruction. */ - int INVOKEVIRTUAL = 182; - - /** The integer value used to encode the INVOKESPECIAL instruction. */ - int INVOKESPECIAL = 183; - - /** The integer value used to encode the INVOKESTATIC instruction. */ - int INVOKESTATIC = 184; - - /** The integer value used to encode the INVOKEINTERFACE instruction. */ - int INVOKEINTERFACE = 185; - - /** The integer value used to encode the INVOKEDYNAMIC instruction. */ - int INVOKEDYNAMIC = 186; - - /** The integer value used to encode the NEW instruction. */ - int NEW = 187; - - /** The integer value used to encode the NEWARRAY instruction. */ - int NEWARRAY = 188; - - /** The integer value used to encode the ANEWARRAY instruction. */ - int ANEWARRAY = 189; - - /** The integer value used to encode the ARRAYLENGTH instruction. */ - int ARRAYLENGTH = 190; - - /** The integer value used to encode the ATHROW instruction. */ - int ATHROW = 191; - - /** The integer value used to encode the CHECKCAST instruction. */ - int CHECKCAST = 192; - - /** The integer value used to encode the INSTANCEOF instruction. */ - int INSTANCEOF = 193; - - /** The integer value used to encode the MONITORENTER instruction. */ - int MONITORENTER = 194; - - /** The integer value used to encode the MONITOREXIT instruction. */ - int MONITOREXIT = 195; - - /** The integer value used to encode the WIDE instruction. */ - int WIDE = 196; - - /** The integer value used to encode the MULTIANEWARRAY instruction. */ - int MULTIANEWARRAY = 197; - - /** The integer value used to encode the IFNULL instruction. */ - int IFNULL = 198; - - /** The integer value used to encode the IFNONNULL instruction. */ - int IFNONNULL = 199; - - /** The integer value used to encode the GOTO_W instruction. */ - int GOTO_W = 200; - - /** The integer value used to encode the JSR_W instruction. */ - int JSR_W = 201; - - /** The value of PUBLIC access and property modifier. */ + /** The bit mask of PUBLIC access and property modifier. */ int ACC_PUBLIC = 0x0001; - /** The value of PROTECTED access and property modifier. */ + /** The bit mask of PROTECTED access and property modifier. */ int ACC_PROTECTED = 0x0004; - /** The value of PRIVATE access and property modifier. */ + /** The bit mask of PRIVATE access and property modifier. */ int ACC_PRIVATE = 0x0002; - /** The value of INTERFACE access and property modifier. */ + /** The bit mask of INTERFACE access and property modifier. */ int ACC_INTERFACE = 0x0200; - /** The value of ENUM access and property modifier. */ + /** The bit mask of ENUM access and property modifier. */ int ACC_ENUM = 0x4000; - /** The value of ANNOTATION access and property modifier. */ + /** The bit mask of ANNOTATION access and property modifier. */ int ACC_ANNOTATION = 0x2000; - /** The value of SUPER access and property modifier. */ + /** The bit mask of SUPER access and property modifier. */ int ACC_SUPER = 0x0020; - /** The value of ABSTRACT access and property modifier. */ + /** The bit mask of ABSTRACT access and property modifier. */ int ACC_ABSTRACT = 0x0400; - /** The value of VOLATILE access and property modifier. */ + /** The bit mask of VOLATILE access and property modifier. */ int ACC_VOLATILE = 0x0040; - /** The value of TRANSIENT access and property modifier. */ + /** The bit mask of TRANSIENT access and property modifier. */ int ACC_TRANSIENT = 0x0080; - /** The value of SYNTHETIC access and property modifier. */ + /** The bit mask of SYNTHETIC access and property modifier. */ int ACC_SYNTHETIC = 0x1000; - /** The value of STATIC access and property modifier. */ + /** The bit mask of STATIC access and property modifier. */ int ACC_STATIC = 0x0008; - /** The value of FINAL access and property modifier. */ + /** The bit mask of FINAL access and property modifier. */ int ACC_FINAL = 0x0010; - /** The value of SYNCHRONIZED access and property modifier. */ + /** The bit mask of SYNCHRONIZED access and property modifier. */ int ACC_SYNCHRONIZED = 0x0020; - /** The value of BRIDGE access and property modifier. */ + /** The bit mask of BRIDGE access and property modifier. */ int ACC_BRIDGE = 0x0040; - /** The value of VARARGS access and property modifier. */ + /** The bit mask of VARARGS access and property modifier. */ int ACC_VARARGS = 0x0080; - /** The value of NATIVE access and property modifier. */ + /** The bit mask of NATIVE access and property modifier. */ int ACC_NATIVE = 0x0100; - /** The value of STRICT access and property modifier. */ + /** The bit mask of STRICT access and property modifier. */ int ACC_STRICT = 0x0800; - /** The value of MODULE access and property modifier. */ + /** The bit mask of MODULE access and property modifier. */ int ACC_MODULE = 0x8000; - /** The value of OPEN access and property modifier. */ + /** The bit mask of OPEN access and property modifier. */ int ACC_OPEN = 0x20; - /** The value of MANDATED access and property modifier. */ + /** The bit mask of MANDATED access and property modifier. */ int ACC_MANDATED = 0x8000; - /** The value of TRANSITIVE access and property modifier. */ + /** The bit mask of TRANSITIVE access and property modifier. */ int ACC_TRANSITIVE = 0x20; - /** The value of STATIC_PHASE access and property modifier. */ + /** The bit mask of STATIC_PHASE access and property modifier. */ int ACC_STATIC_PHASE = 0x40; - /** The value of STATEMENT {@link CharacterRangeInfo} kind. */ - int CRT_STATEMENT = 0x0001; - - /** The value of BLOCK {@link CharacterRangeInfo} kind. */ - int CRT_BLOCK = 0x0002; - - /** The value of ASSIGNMENT {@link CharacterRangeInfo} kind. */ - int CRT_ASSIGNMENT = 0x0004; - - /** The value of FLOW_CONTROLLER {@link CharacterRangeInfo} kind. */ - int CRT_FLOW_CONTROLLER = 0x0008; - - /** The value of FLOW_TARGET {@link CharacterRangeInfo} kind. */ - int CRT_FLOW_TARGET = 0x0010; - - /** The value of INVOKE {@link CharacterRangeInfo} kind. */ - int CRT_INVOKE = 0x0020; - - /** The value of CREATE {@link CharacterRangeInfo} kind. */ - int CRT_CREATE = 0x0040; - - /** The value of BRANCH_TRUE {@link CharacterRangeInfo} kind. */ - int CRT_BRANCH_TRUE = 0x0080; - - /** The value of BRANCH_FALSE {@link CharacterRangeInfo} kind. */ - int CRT_BRANCH_FALSE = 0x0100; - - /** The value of constant pool tag CLASS. */ - int TAG_CLASS = 7; - - /** The value of constant pool tag CONSTANTDYNAMIC. */ - int TAG_CONSTANTDYNAMIC = 17; - - /** The value of constant pool tag DOUBLE. */ - int TAG_DOUBLE = 6; - - /** The value of constant pool tag FIELDREF. */ - int TAG_FIELDREF = 9; - - /** The value of constant pool tag FLOAT. */ - int TAG_FLOAT = 4; - - /** The value of constant pool tag INTEGER. */ - int TAG_INTEGER = 3; - - /** The value of constant pool tag INTERFACEMETHODREF. */ - int TAG_INTERFACEMETHODREF = 11; - - /** The value of constant pool tag INVOKEDYNAMIC. */ - int TAG_INVOKEDYNAMIC = 18; - - /** The value of constant pool tag LONG. */ - int TAG_LONG = 5; - - /** The value of constant pool tag METHODHANDLE. */ - int TAG_METHODHANDLE = 15; - - /** The value of constant pool tag METHODREF. */ - int TAG_METHODREF = 10; - - /** The value of constant pool tag METHODTYPE. */ - int TAG_METHODTYPE = 16; - - /** The value of constant pool tag MODULE. */ - int TAG_MODULE = 19; - - /** The value of constant pool tag NAMEANDTYPE. */ - int TAG_NAMEANDTYPE = 12; - - /** The value of constant pool tag PACKAGE. */ - int TAG_PACKAGE = 20; - - /** The value of constant pool tag STRING. */ - int TAG_STRING = 8; - - /** The value of constant pool tag UNICODE. */ - int TAG_UNICODE = 2; - - /** The value of constant pool tag UTF8. */ - int TAG_UTF8 = 1; - - // annotation element values - - /** The value of annotation element value type AEV_BYTE. */ - int AEV_BYTE = 'B'; - - /** The value of annotation element value type AEV_CHAR. */ - int AEV_CHAR = 'C'; - - /** The value of annotation element value type AEV_DOUBLE. */ - int AEV_DOUBLE = 'D'; - - /** The value of annotation element value type AEV_FLOAT. */ - int AEV_FLOAT = 'F'; - - /** The value of annotation element value type AEV_INT. */ - int AEV_INT = 'I'; - - /** The value of annotation element value type AEV_LONG. */ - int AEV_LONG = 'J'; - - /** The value of annotation element value type AEV_SHORT. */ - int AEV_SHORT = 'S'; - - /** The value of annotation element value type AEV_BOOLEAN. */ - int AEV_BOOLEAN = 'Z'; - - /** The value of annotation element value type AEV_STRING. */ - int AEV_STRING = 's'; - - /** The value of annotation element value type AEV_ENUM. */ - int AEV_ENUM = 'e'; - - /** The value of annotation element value type AEV_CLASS. */ - int AEV_CLASS = 'c'; - - /** The value of annotation element value type AEV_ANNOTATION. */ - int AEV_ANNOTATION = '@'; - - /** The value of annotation element value type AEV_ARRAY. */ - int AEV_ARRAY = '['; - - //type annotations - - /** The value of type annotation target type CLASS_TYPE_PARAMETER. */ - int TAT_CLASS_TYPE_PARAMETER = 0x00; - - /** The value of type annotation target type METHOD_TYPE_PARAMETER. */ - int TAT_METHOD_TYPE_PARAMETER = 0x01; - - /** The value of type annotation target type CLASS_EXTENDS. */ - int TAT_CLASS_EXTENDS = 0x10; - - /** The value of type annotation target type CLASS_TYPE_PARAMETER_BOUND. */ - int TAT_CLASS_TYPE_PARAMETER_BOUND = 0x11; - - /** The value of type annotation target type METHOD_TYPE_PARAMETER_BOUND. */ - int TAT_METHOD_TYPE_PARAMETER_BOUND = 0x12; - - /** The value of type annotation target type FIELD. */ - int TAT_FIELD = 0x13; - - /** The value of type annotation target type METHOD_RETURN. */ - int TAT_METHOD_RETURN = 0x14; - - /** The value of type annotation target type METHOD_RECEIVER. */ - int TAT_METHOD_RECEIVER = 0x15; - - /** The value of type annotation target type METHOD_FORMAL_PARAMETER. */ - int TAT_METHOD_FORMAL_PARAMETER = 0x16; - - /** The value of type annotation target type THROWS. */ - int TAT_THROWS = 0x17; - - /** The value of type annotation target type LOCAL_VARIABLE. */ - int TAT_LOCAL_VARIABLE = 0x40; - - /** The value of type annotation target type RESOURCE_VARIABLE. */ - int TAT_RESOURCE_VARIABLE = 0x41; - - /** The value of type annotation target type EXCEPTION_PARAMETER. */ - int TAT_EXCEPTION_PARAMETER = 0x42; - - /** The value of type annotation target type INSTANCEOF. */ - int TAT_INSTANCEOF = 0x43; - - /** The value of type annotation target type NEW. */ - int TAT_NEW = 0x44; - - /** The value of type annotation target type CONSTRUCTOR_REFERENCE. */ - int TAT_CONSTRUCTOR_REFERENCE = 0x45; - - /** The value of type annotation target type METHOD_REFERENCE. */ - int TAT_METHOD_REFERENCE = 0x46; - - /** The value of type annotation target type CAST. */ - int TAT_CAST = 0x47; - - /** The value of type annotation target type CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT. */ - int TAT_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; - - /** The value of type annotation target type METHOD_INVOCATION_TYPE_ARGUMENT. */ - int TAT_METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; - - /** The value of type annotation target type CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT. */ - int TAT_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; - - /** The value of type annotation target type METHOD_REFERENCE_TYPE_ARGUMENT. */ - int TAT_METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; - - //stackmap verification types - - /** The value of verification type TOP. */ - int VT_TOP = 0; - - /** The value of verification type INTEGER. */ - int VT_INTEGER = 1; - - /** The value of verification type FLOAT. */ - int VT_FLOAT = 2; - - /** The value of verification type DOUBLE. */ - int VT_DOUBLE = 3; - - /** The value of verification type LONG. */ - int VT_LONG = 4; - - /** The value of verification type NULL. */ - int VT_NULL = 5; - - /** The value of verification type UNINITIALIZED_THIS. */ - int VT_UNINITIALIZED_THIS = 6; - - /** The value of verification type OBJECT. */ - int VT_OBJECT = 7; - - /** The value of verification type UNINITIALIZED. */ - int VT_UNINITIALIZED = 8; - - /** The value of default class access flags */ - int DEFAULT_CLASS_FLAGS = ACC_PUBLIC; - /** The class major version of JAVA_1. */ int JAVA_1_VERSION = 45; diff --git a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java index 589713c8e95c8..3f16ce8402492 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassHierarchyResolver.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 @@ -39,6 +39,8 @@ import jdk.internal.classfile.impl.Util; import static java.lang.constant.ConstantDescs.CD_Object; +import static java.util.Objects.requireNonNull; + import jdk.internal.javac.PreviewFeature; /** @@ -106,6 +108,7 @@ static ClassHierarchyInfo ofInterface() { * other resolver in cases where this resolver returns {@code null}. */ default ClassHierarchyResolver orElse(ClassHierarchyResolver other) { + requireNonNull(other); return new ClassHierarchyResolver() { @Override public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { @@ -170,7 +173,7 @@ public Map get() { * @return the {@linkplain ClassHierarchyResolver} */ static ClassHierarchyResolver ofResourceParsing(Function classStreamResolver) { - return new ClassHierarchyImpl.ResourceParsingClassHierarchyResolver(classStreamResolver); + return new ClassHierarchyImpl.ResourceParsingClassHierarchyResolver(requireNonNull(classStreamResolver)); } /** @@ -181,6 +184,7 @@ static ClassHierarchyResolver ofResourceParsing(Function * @return the {@linkplain ClassHierarchyResolver} */ static ClassHierarchyResolver ofResourceParsing(ClassLoader loader) { + requireNonNull(loader); return ofResourceParsing(new Function<>() { @Override public InputStream apply(ClassDesc classDesc) { @@ -210,6 +214,7 @@ static ClassHierarchyResolver of(Collection interfaces, * @return the class hierarchy resolver */ static ClassHierarchyResolver ofClassLoading(ClassLoader loader) { + requireNonNull(loader); return new ClassLoadingClassHierarchyResolver(new Function<>() { @Override public Class apply(ClassDesc cd) { @@ -232,6 +237,7 @@ public Class apply(ClassDesc cd) { * @return the class hierarchy resolver */ static ClassHierarchyResolver ofClassLoading(MethodHandles.Lookup lookup) { + requireNonNull(lookup); return new ClassLoadingClassHierarchyResolver(new Function<>() { @Override public Class apply(ClassDesc cd) { diff --git a/src/java.base/share/classes/java/lang/classfile/ClassTransform.java b/src/java.base/share/classes/java/lang/classfile/ClassTransform.java index 743a39851148a..230b436b138ad 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassTransform.java @@ -32,6 +32,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link ClassElement}. * @@ -63,7 +65,7 @@ public void accept(ClassBuilder builder, ClassElement element) { * @return the stateful class transform */ static ClassTransform ofStateful(Supplier supplier) { - return new TransformImpl.SupplierClassTransform(supplier); + return new TransformImpl.SupplierClassTransform(requireNonNull(supplier)); } /** @@ -74,6 +76,7 @@ static ClassTransform ofStateful(Supplier supplier) { * @return the class transform */ static ClassTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new ClassTransform() { @Override public void accept(ClassBuilder builder, ClassElement element) { @@ -95,6 +98,7 @@ public void atEnd(ClassBuilder builder) { * @return the class transform */ static ClassTransform dropping(Predicate filter) { + requireNonNull(filter); return (b, e) -> { if (!filter.test(e)) b.with(e); @@ -111,7 +115,7 @@ static ClassTransform dropping(Predicate filter) { */ static ClassTransform transformingMethods(Predicate filter, MethodTransform xform) { - return new TransformImpl.ClassMethodTransform(xform, filter); + return new TransformImpl.ClassMethodTransform(requireNonNull(xform), requireNonNull(filter)); } /** @@ -122,7 +126,7 @@ static ClassTransform transformingMethods(Predicate filter, * @return the class transform */ static ClassTransform transformingMethods(MethodTransform xform) { - return transformingMethods(mm -> true, xform); + return transformingMethods(_ -> true, xform); } /** @@ -157,7 +161,7 @@ static ClassTransform transformingMethodBodies(CodeTransform xform) { * @return the class transform */ static ClassTransform transformingFields(FieldTransform xform) { - return new TransformImpl.ClassFieldTransform(xform, f -> true); + return new TransformImpl.ClassFieldTransform(requireNonNull(xform), _ -> true); } /** @@ -169,6 +173,6 @@ static ClassTransform transformingFields(FieldTransform xform) { */ @Override default ClassTransform andThen(ClassTransform t) { - return new TransformImpl.ChainedClassTransform(this, t); + return new TransformImpl.ChainedClassTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java index 5f4e5b72a1919..7b27238bbdfae 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java @@ -429,6 +429,8 @@ default CodeBuilder trying(Consumer tryHandler, * @param tk the load type * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID void} + * or {@code slot} is out of range * @since 23 */ default CodeBuilder loadLocal(TypeKind tk, int slot) { @@ -440,6 +442,8 @@ default CodeBuilder loadLocal(TypeKind tk, int slot) { * @param tk the store type * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID void} + * or {@code slot} is out of range * @since 23 */ default CodeBuilder storeLocal(TypeKind tk, int slot) { @@ -615,36 +619,77 @@ default CodeBuilder loadConstant(ConstantDesc value) { if (value == null || value == ConstantDescs.NULL) return aconst_null(); if (value instanceof Number) { - if (value instanceof Integer iVal) - return switch (iVal) { - case -1 -> iconst_m1(); - case 0 -> iconst_0(); - case 1 -> iconst_1(); - case 2 -> iconst_2(); - case 3 -> iconst_3(); - case 4 -> iconst_4(); - case 5 -> iconst_5(); - default -> (iVal >= Byte.MIN_VALUE && iVal <= Byte.MAX_VALUE) ? bipush(iVal) - : (iVal >= Short.MIN_VALUE && iVal <= Short.MAX_VALUE) ? sipush(iVal) - : ldc(constantPool().intEntry(iVal)); - }; - if (value instanceof Long lVal) - return lVal == 0L ? lconst_0() - : lVal == 1L ? lconst_1() - : ldc(constantPool().longEntry(lVal)); - if (value instanceof Float fVal) - return Float.floatToRawIntBits(fVal) == 0 ? fconst_0() - : fVal == 1.0f ? fconst_1() - : fVal == 2.0f ? fconst_2() - : ldc(constantPool().floatEntry(fVal)); - if (value instanceof Double dVal) - return Double.doubleToRawLongBits(dVal) == 0L ? dconst_0() - : dVal == 1.0d ? dconst_1() - : ldc(constantPool().doubleEntry(dVal)); + if (value instanceof Integer) return loadConstant((int) value); + if (value instanceof Long ) return loadConstant((long) value); + if (value instanceof Float ) return loadConstant((float) value); + if (value instanceof Double ) return loadConstant((double) value); } return ldc(value); } + + /** + * Generate an instruction pushing a constant int value onto the operand stack. + * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Integer.valueOf(value))}. + * @param value the int value + * @return this builder + * @since 24 + */ + default CodeBuilder loadConstant(int value) { + return switch (value) { + case -1 -> iconst_m1(); + case 0 -> iconst_0(); + case 1 -> iconst_1(); + case 2 -> iconst_2(); + case 3 -> iconst_3(); + case 4 -> iconst_4(); + case 5 -> iconst_5(); + default -> (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE ) ? bipush(value) + : (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) ? sipush(value) + : ldc(constantPool().intEntry(value)); + }; + } + + /** + * Generate an instruction pushing a constant long value onto the operand stack. + * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Long.valueOf(value))}. + * @param value the long value + * @return this builder + * @since 24 + */ + default CodeBuilder loadConstant(long value) { + return value == 0l ? lconst_0() + : value == 1l ? lconst_1() + : ldc(constantPool().longEntry(value)); + } + + /** + * Generate an instruction pushing a constant float value onto the operand stack. + * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Float.valueOf(value))}. + * @param value the float value + * @return this builder + * @since 24 + */ + default CodeBuilder loadConstant(float value) { + return Float.floatToRawIntBits(value) == 0 ? fconst_0() + : value == 1.0f ? fconst_1() + : value == 2.0f ? fconst_2() + : ldc(constantPool().floatEntry(value)); + } + + /** + * Generate an instruction pushing a constant double value onto the operand stack. + * This is identical to {@link #loadConstant(ConstantDesc) loadConstant(Double.valueOf(value))}. + * @param value the double value + * @return this builder + * @since 24 + */ + default CodeBuilder loadConstant(double value) { + return Double.doubleToRawLongBits(value) == 0l ? dconst_0() + : value == 1.0d ? dconst_1() + : ldc(constantPool().doubleEntry(value)); + } + /** * Generate a do nothing instruction * @return this builder @@ -752,6 +797,7 @@ default CodeBuilder characterRange(Label startScope, Label endScope, int charact * @param startScope the start scope of the variable * @param endScope the end scope of the variable * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder localVariable(int slot, Utf8Entry nameEntry, Utf8Entry descriptorEntry, Label startScope, Label endScope) { return with(LocalVariable.of(slot, nameEntry, descriptorEntry, startScope, endScope)); @@ -765,6 +811,7 @@ default CodeBuilder localVariable(int slot, Utf8Entry nameEntry, Utf8Entry descr * @param startScope the start scope of the variable * @param endScope the end scope of the variable * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder localVariable(int slot, String name, ClassDesc descriptor, Label startScope, Label endScope) { return localVariable(slot, @@ -781,6 +828,7 @@ default CodeBuilder localVariable(int slot, String name, ClassDesc descriptor, L * @param startScope the start scope of the variable * @param endScope the end scope of the variable * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder localVariableType(int slot, Utf8Entry nameEntry, Utf8Entry signatureEntry, Label startScope, Label endScope) { return with(LocalVariableType.of(slot, nameEntry, signatureEntry, startScope, endScope)); @@ -794,6 +842,7 @@ default CodeBuilder localVariableType(int slot, Utf8Entry nameEntry, Utf8Entry s * @param startScope the start scope of the variable * @param endScope the end scope of the variable * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder localVariableType(int slot, String name, Signature signature, Label startScope, Label endScope) { return localVariableType(slot, @@ -836,6 +885,7 @@ default CodeBuilder aastore() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder aload(int slot) { return loadLocal(TypeKind.REFERENCE, slot); @@ -884,6 +934,7 @@ default CodeBuilder arraylength() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder astore(int slot) { return storeLocal(TypeKind.REFERENCE, slot); @@ -917,6 +968,7 @@ default CodeBuilder bastore() { * Generate an instruction pushing an int in the range of byte onto the operand stack. * @param b the int in the range of byte * @return this builder + * @throws IllegalArgumentException if {@code b} is out of range of byte */ default CodeBuilder bipush(int b) { return with(ConstantInstruction.ofArgument(Opcode.BIPUSH, b)); @@ -1053,6 +1105,7 @@ default CodeBuilder ddiv() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder dload(int slot) { return loadLocal(TypeKind.DOUBLE, slot); @@ -1098,6 +1151,7 @@ default CodeBuilder dreturn() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder dstore(int slot) { return storeLocal(TypeKind.DOUBLE, slot); @@ -1265,6 +1319,7 @@ default CodeBuilder fdiv() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder fload(int slot) { return loadLocal(TypeKind.FLOAT, slot); @@ -1310,6 +1365,7 @@ default CodeBuilder freturn() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder fstore(int slot) { return storeLocal(TypeKind.FLOAT, slot); @@ -1685,6 +1741,7 @@ default CodeBuilder ifne(Label target) { * @param slot the local variable slot * @param val the increment value * @return this builder + * @throws IllegalArgumentException if {@code slot} or {@code val} is out of range */ default CodeBuilder iinc(int slot, int val) { return with(IncrementInstruction.of(slot, val)); @@ -1698,6 +1755,7 @@ default CodeBuilder iinc(int slot, int val) { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder iload(int slot) { return loadLocal(TypeKind.INT, slot); @@ -1956,6 +2014,7 @@ default CodeBuilder ishr() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder istore(int slot) { return storeLocal(TypeKind.INT, slot); @@ -2118,6 +2177,7 @@ default CodeBuilder ldiv() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder lload(int slot) { return loadLocal(TypeKind.LONG, slot); @@ -2187,6 +2247,7 @@ default CodeBuilder lshr() { * * @param slot the local variable slot * @return this builder + * @throws IllegalArgumentException if {@code slot} is out of range */ default CodeBuilder lstore(int slot) { return storeLocal(TypeKind.LONG, slot); @@ -2237,6 +2298,7 @@ default CodeBuilder monitorexit() { * @param array the array type * @param dims the number of dimensions * @return this builder + * @throws IllegalArgumentException if {@code dims} is out of range */ default CodeBuilder multianewarray(ClassEntry array, int dims) { return with(NewMultiArrayInstruction.of(array, dims)); @@ -2248,6 +2310,7 @@ default CodeBuilder multianewarray(ClassEntry array, int dims) { * @param dims the number of dimensions * @return this builder * @throws IllegalArgumentException if {@code array} represents a primitive type + * or if {@code dims} is out of range */ default CodeBuilder multianewarray(ClassDesc array, int dims) { return multianewarray(constantPool().classEntry(array), dims); @@ -2286,6 +2349,8 @@ default CodeBuilder new_(ClassDesc clazz) { * Generate an instruction to create a new array of a primitive type * @param typeKind the primitive array type * @return this builder + * @throws IllegalArgumentException when the {@code typeKind} is not a legal + * primitive array component type */ default CodeBuilder newarray(TypeKind typeKind) { return with(NewPrimitiveArrayInstruction.of(typeKind)); @@ -2382,6 +2447,7 @@ default CodeBuilder sastore() { * Generate an instruction pushing an int in the range of short onto the operand stack. * @param s the int in the range of short * @return this builder + * @throws IllegalArgumentException if {@code s} is out of range of short */ default CodeBuilder sipush(int s) { return with(ConstantInstruction.ofArgument(Opcode.SIPUSH, s)); diff --git a/src/java.base/share/classes/java/lang/classfile/CodeTransform.java b/src/java.base/share/classes/java/lang/classfile/CodeTransform.java index cdc7a3b1434d7..0474e0c9c67f7 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeTransform.java @@ -30,6 +30,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link CodeElement}. * @@ -61,7 +63,7 @@ public void accept(CodeBuilder builder, CodeElement element) { * @return the stateful code transform */ static CodeTransform ofStateful(Supplier supplier) { - return new TransformImpl.SupplierCodeTransform(supplier); + return new TransformImpl.SupplierCodeTransform(requireNonNull(supplier)); } /** @@ -72,6 +74,7 @@ static CodeTransform ofStateful(Supplier supplier) { * @return the code transform */ static CodeTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new CodeTransform() { @Override public void accept(CodeBuilder builder, CodeElement element) { @@ -94,6 +97,6 @@ public void atEnd(CodeBuilder builder) { */ @Override default CodeTransform andThen(CodeTransform t) { - return new TransformImpl.ChainedCodeTransform(this, t); + return new TransformImpl.ChainedCodeTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/FieldTransform.java b/src/java.base/share/classes/java/lang/classfile/FieldTransform.java index 4e39f1e9c7fec..78a6f5ead2f79 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldTransform.java @@ -31,6 +31,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link FieldElement}. * @@ -62,7 +64,7 @@ public void accept(FieldBuilder builder, FieldElement element) { * @return the stateful field transform */ static FieldTransform ofStateful(Supplier supplier) { - return new TransformImpl.SupplierFieldTransform(supplier); + return new TransformImpl.SupplierFieldTransform(requireNonNull(supplier)); } /** @@ -73,6 +75,7 @@ static FieldTransform ofStateful(Supplier supplier) { * @return the field transform */ static FieldTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new FieldTransform() { @Override public void accept(FieldBuilder builder, FieldElement element) { @@ -94,6 +97,7 @@ public void atEnd(FieldBuilder builder) { * @return the field transform */ static FieldTransform dropping(Predicate filter) { + requireNonNull(filter); return (b, e) -> { if (!filter.test(e)) b.with(e); @@ -109,6 +113,6 @@ static FieldTransform dropping(Predicate filter) { */ @Override default FieldTransform andThen(FieldTransform t) { - return new TransformImpl.ChainedFieldTransform(this, t); + return new TransformImpl.ChainedFieldTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/MethodTransform.java b/src/java.base/share/classes/java/lang/classfile/MethodTransform.java index e7e024ebc3467..bf5786f3dc74b 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodTransform.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodTransform.java @@ -31,6 +31,8 @@ import jdk.internal.classfile.impl.TransformImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A transformation on streams of {@link MethodElement}. * @@ -62,6 +64,7 @@ public void accept(MethodBuilder builder, MethodElement element) { * @return the stateful method transform */ static MethodTransform ofStateful(Supplier supplier) { + requireNonNull(supplier); return new TransformImpl.SupplierMethodTransform(supplier); } @@ -73,6 +76,7 @@ static MethodTransform ofStateful(Supplier supplier) { * @return the method transform */ static MethodTransform endHandler(Consumer finisher) { + requireNonNull(finisher); return new MethodTransform() { @Override public void accept(MethodBuilder builder, MethodElement element) { @@ -94,6 +98,7 @@ public void atEnd(MethodBuilder builder) { * @return the method transform */ static MethodTransform dropping(Predicate filter) { + requireNonNull(filter); return (b, e) -> { if (!filter.test(e)) b.with(e); @@ -108,7 +113,7 @@ static MethodTransform dropping(Predicate filter) { * @return the class transform */ static MethodTransform transformingCode(CodeTransform xform) { - return new TransformImpl.MethodCodeTransform(xform); + return new TransformImpl.MethodCodeTransform(requireNonNull(xform)); } /** @@ -120,6 +125,6 @@ static MethodTransform transformingCode(CodeTransform xform) { */ @Override default MethodTransform andThen(MethodTransform t) { - return new TransformImpl.ChainedMethodTransform(this, t); + return new TransformImpl.ChainedMethodTransform(this, requireNonNull(t)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/Opcode.java b/src/java.base/share/classes/java/lang/classfile/Opcode.java index ab991d983453d..735510dbcea3b 100644 --- a/src/java.base/share/classes/java/lang/classfile/Opcode.java +++ b/src/java.base/share/classes/java/lang/classfile/Opcode.java @@ -24,6 +24,7 @@ */ package java.lang.classfile; +import jdk.internal.classfile.impl.RawBytecodeHelper; import jdk.internal.javac.PreviewFeature; /** @@ -40,658 +41,658 @@ public enum Opcode { /** Do nothing */ - NOP(ClassFile.NOP, 1, Kind.NOP), + NOP(RawBytecodeHelper.NOP, 1, Kind.NOP), /** Push null */ - ACONST_NULL(ClassFile.ACONST_NULL, 1, Kind.CONSTANT), + ACONST_NULL(RawBytecodeHelper.ACONST_NULL, 1, Kind.CONSTANT), /** Push int constant -1 */ - ICONST_M1(ClassFile.ICONST_M1, 1, Kind.CONSTANT), + ICONST_M1(RawBytecodeHelper.ICONST_M1, 1, Kind.CONSTANT), /** Push int constant 0 */ - ICONST_0(ClassFile.ICONST_0, 1, Kind.CONSTANT), + ICONST_0(RawBytecodeHelper.ICONST_0, 1, Kind.CONSTANT), /** Push int constant 1 */ - ICONST_1(ClassFile.ICONST_1, 1, Kind.CONSTANT), + ICONST_1(RawBytecodeHelper.ICONST_1, 1, Kind.CONSTANT), /** Push int constant 2 */ - ICONST_2(ClassFile.ICONST_2, 1, Kind.CONSTANT), + ICONST_2(RawBytecodeHelper.ICONST_2, 1, Kind.CONSTANT), /** Push int constant 3 */ - ICONST_3(ClassFile.ICONST_3, 1, Kind.CONSTANT), + ICONST_3(RawBytecodeHelper.ICONST_3, 1, Kind.CONSTANT), /** Push int constant 4 */ - ICONST_4(ClassFile.ICONST_4, 1, Kind.CONSTANT), + ICONST_4(RawBytecodeHelper.ICONST_4, 1, Kind.CONSTANT), /** Push int constant 5 */ - ICONST_5(ClassFile.ICONST_5, 1, Kind.CONSTANT), + ICONST_5(RawBytecodeHelper.ICONST_5, 1, Kind.CONSTANT), /** Push long constant 0 */ - LCONST_0(ClassFile.LCONST_0, 1, Kind.CONSTANT), + LCONST_0(RawBytecodeHelper.LCONST_0, 1, Kind.CONSTANT), /** Push long constant 1 */ - LCONST_1(ClassFile.LCONST_1, 1, Kind.CONSTANT), + LCONST_1(RawBytecodeHelper.LCONST_1, 1, Kind.CONSTANT), /** Push float constant 0 */ - FCONST_0(ClassFile.FCONST_0, 1, Kind.CONSTANT), + FCONST_0(RawBytecodeHelper.FCONST_0, 1, Kind.CONSTANT), /** Push float constant 1 */ - FCONST_1(ClassFile.FCONST_1, 1, Kind.CONSTANT), + FCONST_1(RawBytecodeHelper.FCONST_1, 1, Kind.CONSTANT), /** Push float constant 2 */ - FCONST_2(ClassFile.FCONST_2, 1, Kind.CONSTANT), + FCONST_2(RawBytecodeHelper.FCONST_2, 1, Kind.CONSTANT), /** Push double constant 0 */ - DCONST_0(ClassFile.DCONST_0, 1, Kind.CONSTANT), + DCONST_0(RawBytecodeHelper.DCONST_0, 1, Kind.CONSTANT), /** Push double constant 1 */ - DCONST_1(ClassFile.DCONST_1, 1, Kind.CONSTANT), + DCONST_1(RawBytecodeHelper.DCONST_1, 1, Kind.CONSTANT), /** Push byte */ - BIPUSH(ClassFile.BIPUSH, 2, Kind.CONSTANT), + BIPUSH(RawBytecodeHelper.BIPUSH, 2, Kind.CONSTANT), /** Push short */ - SIPUSH(ClassFile.SIPUSH, 3, Kind.CONSTANT), + SIPUSH(RawBytecodeHelper.SIPUSH, 3, Kind.CONSTANT), /** Push item from run-time constant pool */ - LDC(ClassFile.LDC, 2, Kind.CONSTANT), + LDC(RawBytecodeHelper.LDC, 2, Kind.CONSTANT), /** Push item from run-time constant pool (wide index) */ - LDC_W(ClassFile.LDC_W, 3, Kind.CONSTANT), + LDC_W(RawBytecodeHelper.LDC_W, 3, Kind.CONSTANT), /** Push long or double from run-time constant pool (wide index) */ - LDC2_W(ClassFile.LDC2_W, 3, Kind.CONSTANT), + LDC2_W(RawBytecodeHelper.LDC2_W, 3, Kind.CONSTANT), /** Load int from local variable */ - ILOAD(ClassFile.ILOAD, 2, Kind.LOAD), + ILOAD(RawBytecodeHelper.ILOAD, 2, Kind.LOAD), /** Load long from local variable */ - LLOAD(ClassFile.LLOAD, 2, Kind.LOAD), + LLOAD(RawBytecodeHelper.LLOAD, 2, Kind.LOAD), /** Load float from local variable */ - FLOAD(ClassFile.FLOAD, 2, Kind.LOAD), + FLOAD(RawBytecodeHelper.FLOAD, 2, Kind.LOAD), /** Load double from local variable */ - DLOAD(ClassFile.DLOAD, 2, Kind.LOAD), + DLOAD(RawBytecodeHelper.DLOAD, 2, Kind.LOAD), /** Load reference from local variable */ - ALOAD(ClassFile.ALOAD, 2, Kind.LOAD), + ALOAD(RawBytecodeHelper.ALOAD, 2, Kind.LOAD), /** Load int from local variable 0 */ - ILOAD_0(ClassFile.ILOAD_0, 1, Kind.LOAD), + ILOAD_0(RawBytecodeHelper.ILOAD_0, 1, Kind.LOAD), /** Load int from local variable 1 */ - ILOAD_1(ClassFile.ILOAD_1, 1, Kind.LOAD), + ILOAD_1(RawBytecodeHelper.ILOAD_1, 1, Kind.LOAD), /** Load int from local variable 2 */ - ILOAD_2(ClassFile.ILOAD_2, 1, Kind.LOAD), + ILOAD_2(RawBytecodeHelper.ILOAD_2, 1, Kind.LOAD), /** Load int from local variable3 */ - ILOAD_3(ClassFile.ILOAD_3, 1, Kind.LOAD), + ILOAD_3(RawBytecodeHelper.ILOAD_3, 1, Kind.LOAD), /** Load long from local variable 0 */ - LLOAD_0(ClassFile.LLOAD_0, 1, Kind.LOAD), + LLOAD_0(RawBytecodeHelper.LLOAD_0, 1, Kind.LOAD), /** Load long from local variable 1 */ - LLOAD_1(ClassFile.LLOAD_1, 1, Kind.LOAD), + LLOAD_1(RawBytecodeHelper.LLOAD_1, 1, Kind.LOAD), /** Load long from local variable 2 */ - LLOAD_2(ClassFile.LLOAD_2, 1, Kind.LOAD), + LLOAD_2(RawBytecodeHelper.LLOAD_2, 1, Kind.LOAD), /** Load long from local variable 3 */ - LLOAD_3(ClassFile.LLOAD_3, 1, Kind.LOAD), + LLOAD_3(RawBytecodeHelper.LLOAD_3, 1, Kind.LOAD), /** Load float from local variable 0 */ - FLOAD_0(ClassFile.FLOAD_0, 1, Kind.LOAD), + FLOAD_0(RawBytecodeHelper.FLOAD_0, 1, Kind.LOAD), /** Load float from local variable 1 */ - FLOAD_1(ClassFile.FLOAD_1, 1, Kind.LOAD), + FLOAD_1(RawBytecodeHelper.FLOAD_1, 1, Kind.LOAD), /** Load float from local variable 2 */ - FLOAD_2(ClassFile.FLOAD_2, 1, Kind.LOAD), + FLOAD_2(RawBytecodeHelper.FLOAD_2, 1, Kind.LOAD), /** Load float from local variable 3 */ - FLOAD_3(ClassFile.FLOAD_3, 1, Kind.LOAD), + FLOAD_3(RawBytecodeHelper.FLOAD_3, 1, Kind.LOAD), /** Load double from local variable 0 */ - DLOAD_0(ClassFile.DLOAD_0, 1, Kind.LOAD), + DLOAD_0(RawBytecodeHelper.DLOAD_0, 1, Kind.LOAD), /** Load double from local variable 1 */ - DLOAD_1(ClassFile.DLOAD_1, 1, Kind.LOAD), + DLOAD_1(RawBytecodeHelper.DLOAD_1, 1, Kind.LOAD), /** Load double from local variable 2 */ - DLOAD_2(ClassFile.DLOAD_2, 1, Kind.LOAD), + DLOAD_2(RawBytecodeHelper.DLOAD_2, 1, Kind.LOAD), /** Load double from local variable 3 */ - DLOAD_3(ClassFile.DLOAD_3, 1, Kind.LOAD), + DLOAD_3(RawBytecodeHelper.DLOAD_3, 1, Kind.LOAD), /** Load reference from local variable 0 */ - ALOAD_0(ClassFile.ALOAD_0, 1, Kind.LOAD), + ALOAD_0(RawBytecodeHelper.ALOAD_0, 1, Kind.LOAD), /** Load reference from local variable 1 */ - ALOAD_1(ClassFile.ALOAD_1, 1, Kind.LOAD), + ALOAD_1(RawBytecodeHelper.ALOAD_1, 1, Kind.LOAD), /** Load reference from local variable 2 */ - ALOAD_2(ClassFile.ALOAD_2, 1, Kind.LOAD), + ALOAD_2(RawBytecodeHelper.ALOAD_2, 1, Kind.LOAD), /** Load reference from local variable 3 */ - ALOAD_3(ClassFile.ALOAD_3, 1, Kind.LOAD), + ALOAD_3(RawBytecodeHelper.ALOAD_3, 1, Kind.LOAD), /** Load int from array */ - IALOAD(ClassFile.IALOAD, 1, Kind.ARRAY_LOAD), + IALOAD(RawBytecodeHelper.IALOAD, 1, Kind.ARRAY_LOAD), /** Load long from array */ - LALOAD(ClassFile.LALOAD, 1, Kind.ARRAY_LOAD), + LALOAD(RawBytecodeHelper.LALOAD, 1, Kind.ARRAY_LOAD), /** Load float from array */ - FALOAD(ClassFile.FALOAD, 1, Kind.ARRAY_LOAD), + FALOAD(RawBytecodeHelper.FALOAD, 1, Kind.ARRAY_LOAD), /** Load double from array */ - DALOAD(ClassFile.DALOAD, 1, Kind.ARRAY_LOAD), + DALOAD(RawBytecodeHelper.DALOAD, 1, Kind.ARRAY_LOAD), /** Load reference from array */ - AALOAD(ClassFile.AALOAD, 1, Kind.ARRAY_LOAD), + AALOAD(RawBytecodeHelper.AALOAD, 1, Kind.ARRAY_LOAD), /** Load byte from array */ - BALOAD(ClassFile.BALOAD, 1, Kind.ARRAY_LOAD), + BALOAD(RawBytecodeHelper.BALOAD, 1, Kind.ARRAY_LOAD), /** Load char from array */ - CALOAD(ClassFile.CALOAD, 1, Kind.ARRAY_LOAD), + CALOAD(RawBytecodeHelper.CALOAD, 1, Kind.ARRAY_LOAD), /** Load short from array */ - SALOAD(ClassFile.SALOAD, 1, Kind.ARRAY_LOAD), + SALOAD(RawBytecodeHelper.SALOAD, 1, Kind.ARRAY_LOAD), /** Store int into local variable */ - ISTORE(ClassFile.ISTORE, 2, Kind.STORE), + ISTORE(RawBytecodeHelper.ISTORE, 2, Kind.STORE), /** Store long into local variable */ - LSTORE(ClassFile.LSTORE, 2, Kind.STORE), + LSTORE(RawBytecodeHelper.LSTORE, 2, Kind.STORE), /** Store float into local variable */ - FSTORE(ClassFile.FSTORE, 2, Kind.STORE), + FSTORE(RawBytecodeHelper.FSTORE, 2, Kind.STORE), /** Store double into local variable */ - DSTORE(ClassFile.DSTORE, 2, Kind.STORE), + DSTORE(RawBytecodeHelper.DSTORE, 2, Kind.STORE), /** Store reference into local variable */ - ASTORE(ClassFile.ASTORE, 2, Kind.STORE), + ASTORE(RawBytecodeHelper.ASTORE, 2, Kind.STORE), /** Store int into local variable 0 */ - ISTORE_0(ClassFile.ISTORE_0, 1, Kind.STORE), + ISTORE_0(RawBytecodeHelper.ISTORE_0, 1, Kind.STORE), /** Store int into local variable 1 */ - ISTORE_1(ClassFile.ISTORE_1, 1, Kind.STORE), + ISTORE_1(RawBytecodeHelper.ISTORE_1, 1, Kind.STORE), /** Store int into local variable 2 */ - ISTORE_2(ClassFile.ISTORE_2, 1, Kind.STORE), + ISTORE_2(RawBytecodeHelper.ISTORE_2, 1, Kind.STORE), /** Store int into local variable 3 */ - ISTORE_3(ClassFile.ISTORE_3, 1, Kind.STORE), + ISTORE_3(RawBytecodeHelper.ISTORE_3, 1, Kind.STORE), /** Store long into local variable 0 */ - LSTORE_0(ClassFile.LSTORE_0, 1, Kind.STORE), + LSTORE_0(RawBytecodeHelper.LSTORE_0, 1, Kind.STORE), /** Store long into local variable 1 */ - LSTORE_1(ClassFile.LSTORE_1, 1, Kind.STORE), + LSTORE_1(RawBytecodeHelper.LSTORE_1, 1, Kind.STORE), /** Store long into local variable 2 */ - LSTORE_2(ClassFile.LSTORE_2, 1, Kind.STORE), + LSTORE_2(RawBytecodeHelper.LSTORE_2, 1, Kind.STORE), /** Store long into local variable 3 */ - LSTORE_3(ClassFile.LSTORE_3, 1, Kind.STORE), + LSTORE_3(RawBytecodeHelper.LSTORE_3, 1, Kind.STORE), /** Store float into local variable 0 */ - FSTORE_0(ClassFile.FSTORE_0, 1, Kind.STORE), + FSTORE_0(RawBytecodeHelper.FSTORE_0, 1, Kind.STORE), /** Store float into local variable 1 */ - FSTORE_1(ClassFile.FSTORE_1, 1, Kind.STORE), + FSTORE_1(RawBytecodeHelper.FSTORE_1, 1, Kind.STORE), /** Store float into local variable 2 */ - FSTORE_2(ClassFile.FSTORE_2, 1, Kind.STORE), + FSTORE_2(RawBytecodeHelper.FSTORE_2, 1, Kind.STORE), /** Store float into local variable 3 */ - FSTORE_3(ClassFile.FSTORE_3, 1, Kind.STORE), + FSTORE_3(RawBytecodeHelper.FSTORE_3, 1, Kind.STORE), /** Store double into local variable 0 */ - DSTORE_0(ClassFile.DSTORE_0, 1, Kind.STORE), + DSTORE_0(RawBytecodeHelper.DSTORE_0, 1, Kind.STORE), /** Store double into local variable 1 */ - DSTORE_1(ClassFile.DSTORE_1, 1, Kind.STORE), + DSTORE_1(RawBytecodeHelper.DSTORE_1, 1, Kind.STORE), /** Store double into local variable 2 */ - DSTORE_2(ClassFile.DSTORE_2, 1, Kind.STORE), + DSTORE_2(RawBytecodeHelper.DSTORE_2, 1, Kind.STORE), /** Store double into local variable 3 */ - DSTORE_3(ClassFile.DSTORE_3, 1, Kind.STORE), + DSTORE_3(RawBytecodeHelper.DSTORE_3, 1, Kind.STORE), /** Store reference into local variable 0 */ - ASTORE_0(ClassFile.ASTORE_0, 1, Kind.STORE), + ASTORE_0(RawBytecodeHelper.ASTORE_0, 1, Kind.STORE), /** Store reference into local variable 1 */ - ASTORE_1(ClassFile.ASTORE_1, 1, Kind.STORE), + ASTORE_1(RawBytecodeHelper.ASTORE_1, 1, Kind.STORE), /** Store reference into local variable 2 */ - ASTORE_2(ClassFile.ASTORE_2, 1, Kind.STORE), + ASTORE_2(RawBytecodeHelper.ASTORE_2, 1, Kind.STORE), /** Store reference into local variable 3 */ - ASTORE_3(ClassFile.ASTORE_3, 1, Kind.STORE), + ASTORE_3(RawBytecodeHelper.ASTORE_3, 1, Kind.STORE), /** Store into int array */ - IASTORE(ClassFile.IASTORE, 1, Kind.ARRAY_STORE), + IASTORE(RawBytecodeHelper.IASTORE, 1, Kind.ARRAY_STORE), /** Store into long array */ - LASTORE(ClassFile.LASTORE, 1, Kind.ARRAY_STORE), + LASTORE(RawBytecodeHelper.LASTORE, 1, Kind.ARRAY_STORE), /** Store into float array */ - FASTORE(ClassFile.FASTORE, 1, Kind.ARRAY_STORE), + FASTORE(RawBytecodeHelper.FASTORE, 1, Kind.ARRAY_STORE), /** Store into double array */ - DASTORE(ClassFile.DASTORE, 1, Kind.ARRAY_STORE), + DASTORE(RawBytecodeHelper.DASTORE, 1, Kind.ARRAY_STORE), /** Store into reference array */ - AASTORE(ClassFile.AASTORE, 1, Kind.ARRAY_STORE), + AASTORE(RawBytecodeHelper.AASTORE, 1, Kind.ARRAY_STORE), /** Store into byte array */ - BASTORE(ClassFile.BASTORE, 1, Kind.ARRAY_STORE), + BASTORE(RawBytecodeHelper.BASTORE, 1, Kind.ARRAY_STORE), /** Store into char array */ - CASTORE(ClassFile.CASTORE, 1, Kind.ARRAY_STORE), + CASTORE(RawBytecodeHelper.CASTORE, 1, Kind.ARRAY_STORE), /** Store into short array */ - SASTORE(ClassFile.SASTORE, 1, Kind.ARRAY_STORE), + SASTORE(RawBytecodeHelper.SASTORE, 1, Kind.ARRAY_STORE), /** Pop the top operand stack value */ - POP(ClassFile.POP, 1, Kind.STACK), + POP(RawBytecodeHelper.POP, 1, Kind.STACK), /** Pop the top one or two operand stack values */ - POP2(ClassFile.POP2, 1, Kind.STACK), + POP2(RawBytecodeHelper.POP2, 1, Kind.STACK), /** Duplicate the top operand stack value */ - DUP(ClassFile.DUP, 1, Kind.STACK), + DUP(RawBytecodeHelper.DUP, 1, Kind.STACK), /** Duplicate the top operand stack value and insert two values down */ - DUP_X1(ClassFile.DUP_X1, 1, Kind.STACK), + DUP_X1(RawBytecodeHelper.DUP_X1, 1, Kind.STACK), /** Duplicate the top operand stack value and insert two or three values down */ - DUP_X2(ClassFile.DUP_X2, 1, Kind.STACK), + DUP_X2(RawBytecodeHelper.DUP_X2, 1, Kind.STACK), /** Duplicate the top one or two operand stack values */ - DUP2(ClassFile.DUP2, 1, Kind.STACK), + DUP2(RawBytecodeHelper.DUP2, 1, Kind.STACK), /** Duplicate the top one or two operand stack values and insert two or three values down */ - DUP2_X1(ClassFile.DUP2_X1, 1, Kind.STACK), + DUP2_X1(RawBytecodeHelper.DUP2_X1, 1, Kind.STACK), /** Duplicate the top one or two operand stack values and insert two, three, or four values down */ - DUP2_X2(ClassFile.DUP2_X2, 1, Kind.STACK), + DUP2_X2(RawBytecodeHelper.DUP2_X2, 1, Kind.STACK), /** Swap the top two operand stack values */ - SWAP(ClassFile.SWAP, 1, Kind.STACK), + SWAP(RawBytecodeHelper.SWAP, 1, Kind.STACK), /** Add int */ - IADD(ClassFile.IADD, 1, Kind.OPERATOR), + IADD(RawBytecodeHelper.IADD, 1, Kind.OPERATOR), /** Add long */ - LADD(ClassFile.LADD, 1, Kind.OPERATOR), + LADD(RawBytecodeHelper.LADD, 1, Kind.OPERATOR), /** Add float */ - FADD(ClassFile.FADD, 1, Kind.OPERATOR), + FADD(RawBytecodeHelper.FADD, 1, Kind.OPERATOR), /** Add double */ - DADD(ClassFile.DADD, 1, Kind.OPERATOR), + DADD(RawBytecodeHelper.DADD, 1, Kind.OPERATOR), /** Subtract int */ - ISUB(ClassFile.ISUB, 1, Kind.OPERATOR), + ISUB(RawBytecodeHelper.ISUB, 1, Kind.OPERATOR), /** Subtract long */ - LSUB(ClassFile.LSUB, 1, Kind.OPERATOR), + LSUB(RawBytecodeHelper.LSUB, 1, Kind.OPERATOR), /** Subtract float */ - FSUB(ClassFile.FSUB, 1, Kind.OPERATOR), + FSUB(RawBytecodeHelper.FSUB, 1, Kind.OPERATOR), /** Subtract double */ - DSUB(ClassFile.DSUB, 1, Kind.OPERATOR), + DSUB(RawBytecodeHelper.DSUB, 1, Kind.OPERATOR), /** Multiply int */ - IMUL(ClassFile.IMUL, 1, Kind.OPERATOR), + IMUL(RawBytecodeHelper.IMUL, 1, Kind.OPERATOR), /** Multiply long */ - LMUL(ClassFile.LMUL, 1, Kind.OPERATOR), + LMUL(RawBytecodeHelper.LMUL, 1, Kind.OPERATOR), /** Multiply float */ - FMUL(ClassFile.FMUL, 1, Kind.OPERATOR), + FMUL(RawBytecodeHelper.FMUL, 1, Kind.OPERATOR), /** Multiply double */ - DMUL(ClassFile.DMUL, 1, Kind.OPERATOR), + DMUL(RawBytecodeHelper.DMUL, 1, Kind.OPERATOR), /** Divide int */ - IDIV(ClassFile.IDIV, 1, Kind.OPERATOR), + IDIV(RawBytecodeHelper.IDIV, 1, Kind.OPERATOR), /** Divide long */ - LDIV(ClassFile.LDIV, 1, Kind.OPERATOR), + LDIV(RawBytecodeHelper.LDIV, 1, Kind.OPERATOR), /** Divide float */ - FDIV(ClassFile.FDIV, 1, Kind.OPERATOR), + FDIV(RawBytecodeHelper.FDIV, 1, Kind.OPERATOR), /** Divide double */ - DDIV(ClassFile.DDIV, 1, Kind.OPERATOR), + DDIV(RawBytecodeHelper.DDIV, 1, Kind.OPERATOR), /** Remainder int */ - IREM(ClassFile.IREM, 1, Kind.OPERATOR), + IREM(RawBytecodeHelper.IREM, 1, Kind.OPERATOR), /** Remainder long */ - LREM(ClassFile.LREM, 1, Kind.OPERATOR), + LREM(RawBytecodeHelper.LREM, 1, Kind.OPERATOR), /** Remainder float */ - FREM(ClassFile.FREM, 1, Kind.OPERATOR), + FREM(RawBytecodeHelper.FREM, 1, Kind.OPERATOR), /** Remainder double */ - DREM(ClassFile.DREM, 1, Kind.OPERATOR), + DREM(RawBytecodeHelper.DREM, 1, Kind.OPERATOR), /** Negate int */ - INEG(ClassFile.INEG, 1, Kind.OPERATOR), + INEG(RawBytecodeHelper.INEG, 1, Kind.OPERATOR), /** Negate long */ - LNEG(ClassFile.LNEG, 1, Kind.OPERATOR), + LNEG(RawBytecodeHelper.LNEG, 1, Kind.OPERATOR), /** Negate float */ - FNEG(ClassFile.FNEG, 1, Kind.OPERATOR), + FNEG(RawBytecodeHelper.FNEG, 1, Kind.OPERATOR), /** Negate double */ - DNEG(ClassFile.DNEG, 1, Kind.OPERATOR), + DNEG(RawBytecodeHelper.DNEG, 1, Kind.OPERATOR), /** Shift left int */ - ISHL(ClassFile.ISHL, 1, Kind.OPERATOR), + ISHL(RawBytecodeHelper.ISHL, 1, Kind.OPERATOR), /** Shift left long */ - LSHL(ClassFile.LSHL, 1, Kind.OPERATOR), + LSHL(RawBytecodeHelper.LSHL, 1, Kind.OPERATOR), /** Shift right int */ - ISHR(ClassFile.ISHR, 1, Kind.OPERATOR), + ISHR(RawBytecodeHelper.ISHR, 1, Kind.OPERATOR), /** Shift right long */ - LSHR(ClassFile.LSHR, 1, Kind.OPERATOR), + LSHR(RawBytecodeHelper.LSHR, 1, Kind.OPERATOR), /** Logical shift right int */ - IUSHR(ClassFile.IUSHR, 1, Kind.OPERATOR), + IUSHR(RawBytecodeHelper.IUSHR, 1, Kind.OPERATOR), /** Logical shift right long */ - LUSHR(ClassFile.LUSHR, 1, Kind.OPERATOR), + LUSHR(RawBytecodeHelper.LUSHR, 1, Kind.OPERATOR), /** Boolean AND int */ - IAND(ClassFile.IAND, 1, Kind.OPERATOR), + IAND(RawBytecodeHelper.IAND, 1, Kind.OPERATOR), /** Boolean AND long */ - LAND(ClassFile.LAND, 1, Kind.OPERATOR), + LAND(RawBytecodeHelper.LAND, 1, Kind.OPERATOR), /** Boolean OR int */ - IOR(ClassFile.IOR, 1, Kind.OPERATOR), + IOR(RawBytecodeHelper.IOR, 1, Kind.OPERATOR), /** Boolean OR long */ - LOR(ClassFile.LOR, 1, Kind.OPERATOR), + LOR(RawBytecodeHelper.LOR, 1, Kind.OPERATOR), /** Boolean XOR int */ - IXOR(ClassFile.IXOR, 1, Kind.OPERATOR), + IXOR(RawBytecodeHelper.IXOR, 1, Kind.OPERATOR), /** Boolean XOR long */ - LXOR(ClassFile.LXOR, 1, Kind.OPERATOR), + LXOR(RawBytecodeHelper.LXOR, 1, Kind.OPERATOR), /** Increment local variable by constant */ - IINC(ClassFile.IINC, 3, Kind.INCREMENT), + IINC(RawBytecodeHelper.IINC, 3, Kind.INCREMENT), /** Convert int to long */ - I2L(ClassFile.I2L, 1, Kind.CONVERT), + I2L(RawBytecodeHelper.I2L, 1, Kind.CONVERT), /** Convert int to float */ - I2F(ClassFile.I2F, 1, Kind.CONVERT), + I2F(RawBytecodeHelper.I2F, 1, Kind.CONVERT), /** Convert int to double */ - I2D(ClassFile.I2D, 1, Kind.CONVERT), + I2D(RawBytecodeHelper.I2D, 1, Kind.CONVERT), /** Convert long to int */ - L2I(ClassFile.L2I, 1, Kind.CONVERT), + L2I(RawBytecodeHelper.L2I, 1, Kind.CONVERT), /** Convert long to float */ - L2F(ClassFile.L2F, 1, Kind.CONVERT), + L2F(RawBytecodeHelper.L2F, 1, Kind.CONVERT), /** Convert long to double */ - L2D(ClassFile.L2D, 1, Kind.CONVERT), + L2D(RawBytecodeHelper.L2D, 1, Kind.CONVERT), /** Convert float to int */ - F2I(ClassFile.F2I, 1, Kind.CONVERT), + F2I(RawBytecodeHelper.F2I, 1, Kind.CONVERT), /** Convert float to long */ - F2L(ClassFile.F2L, 1, Kind.CONVERT), + F2L(RawBytecodeHelper.F2L, 1, Kind.CONVERT), /** Convert float to double */ - F2D(ClassFile.F2D, 1, Kind.CONVERT), + F2D(RawBytecodeHelper.F2D, 1, Kind.CONVERT), /** Convert double to int */ - D2I(ClassFile.D2I, 1, Kind.CONVERT), + D2I(RawBytecodeHelper.D2I, 1, Kind.CONVERT), /** Convert double to long */ - D2L(ClassFile.D2L, 1, Kind.CONVERT), + D2L(RawBytecodeHelper.D2L, 1, Kind.CONVERT), /** Convert double to float */ - D2F(ClassFile.D2F, 1, Kind.CONVERT), + D2F(RawBytecodeHelper.D2F, 1, Kind.CONVERT), /** Convert int to byte */ - I2B(ClassFile.I2B, 1, Kind.CONVERT), + I2B(RawBytecodeHelper.I2B, 1, Kind.CONVERT), /** Convert int to char */ - I2C(ClassFile.I2C, 1, Kind.CONVERT), + I2C(RawBytecodeHelper.I2C, 1, Kind.CONVERT), /** Convert int to short */ - I2S(ClassFile.I2S, 1, Kind.CONVERT), + I2S(RawBytecodeHelper.I2S, 1, Kind.CONVERT), /** Compare long */ - LCMP(ClassFile.LCMP, 1, Kind.OPERATOR), + LCMP(RawBytecodeHelper.LCMP, 1, Kind.OPERATOR), /** Compare float */ - FCMPL(ClassFile.FCMPL, 1, Kind.OPERATOR), + FCMPL(RawBytecodeHelper.FCMPL, 1, Kind.OPERATOR), /** Compare float */ - FCMPG(ClassFile.FCMPG, 1, Kind.OPERATOR), + FCMPG(RawBytecodeHelper.FCMPG, 1, Kind.OPERATOR), /** Compare double */ - DCMPL(ClassFile.DCMPL, 1, Kind.OPERATOR), + DCMPL(RawBytecodeHelper.DCMPL, 1, Kind.OPERATOR), /** Compare double */ - DCMPG(ClassFile.DCMPG, 1, Kind.OPERATOR), + DCMPG(RawBytecodeHelper.DCMPG, 1, Kind.OPERATOR), /** Branch if int comparison with zero succeeds */ - IFEQ(ClassFile.IFEQ, 3, Kind.BRANCH), + IFEQ(RawBytecodeHelper.IFEQ, 3, Kind.BRANCH), /** Branch if int comparison with zero succeeds */ - IFNE(ClassFile.IFNE, 3, Kind.BRANCH), + IFNE(RawBytecodeHelper.IFNE, 3, Kind.BRANCH), /** Branch if int comparison with zero succeeds */ - IFLT(ClassFile.IFLT, 3, Kind.BRANCH), + IFLT(RawBytecodeHelper.IFLT, 3, Kind.BRANCH), /** Branch if int comparison with zero succeeds */ - IFGE(ClassFile.IFGE, 3, Kind.BRANCH), + IFGE(RawBytecodeHelper.IFGE, 3, Kind.BRANCH), /** Branch if int comparison with zero succeeds */ - IFGT(ClassFile.IFGT, 3, Kind.BRANCH), + IFGT(RawBytecodeHelper.IFGT, 3, Kind.BRANCH), /** Branch if int comparison with zero succeeds */ - IFLE(ClassFile.IFLE, 3, Kind.BRANCH), + IFLE(RawBytecodeHelper.IFLE, 3, Kind.BRANCH), /** Branch if int comparison succeeds */ - IF_ICMPEQ(ClassFile.IF_ICMPEQ, 3, Kind.BRANCH), + IF_ICMPEQ(RawBytecodeHelper.IF_ICMPEQ, 3, Kind.BRANCH), /** Branch if int comparison succeeds */ - IF_ICMPNE(ClassFile.IF_ICMPNE, 3, Kind.BRANCH), + IF_ICMPNE(RawBytecodeHelper.IF_ICMPNE, 3, Kind.BRANCH), /** Branch if int comparison succeeds */ - IF_ICMPLT(ClassFile.IF_ICMPLT, 3, Kind.BRANCH), + IF_ICMPLT(RawBytecodeHelper.IF_ICMPLT, 3, Kind.BRANCH), /** Branch if int comparison succeeds */ - IF_ICMPGE(ClassFile.IF_ICMPGE, 3, Kind.BRANCH), + IF_ICMPGE(RawBytecodeHelper.IF_ICMPGE, 3, Kind.BRANCH), /** Branch if int comparison succeeds */ - IF_ICMPGT(ClassFile.IF_ICMPGT, 3, Kind.BRANCH), + IF_ICMPGT(RawBytecodeHelper.IF_ICMPGT, 3, Kind.BRANCH), /** Branch if int comparison succeeds */ - IF_ICMPLE(ClassFile.IF_ICMPLE, 3, Kind.BRANCH), + IF_ICMPLE(RawBytecodeHelper.IF_ICMPLE, 3, Kind.BRANCH), /** Branch if reference comparison succeeds */ - IF_ACMPEQ(ClassFile.IF_ACMPEQ, 3, Kind.BRANCH), + IF_ACMPEQ(RawBytecodeHelper.IF_ACMPEQ, 3, Kind.BRANCH), /** Branch if reference comparison succeeds */ - IF_ACMPNE(ClassFile.IF_ACMPNE, 3, Kind.BRANCH), + IF_ACMPNE(RawBytecodeHelper.IF_ACMPNE, 3, Kind.BRANCH), /** Branch always */ - GOTO(ClassFile.GOTO, 3, Kind.BRANCH), + GOTO(RawBytecodeHelper.GOTO, 3, Kind.BRANCH), /** * Jump subroutine is discontinued opcode * @see java.lang.classfile.instruction.DiscontinuedInstruction */ - JSR(ClassFile.JSR, 3, Kind.DISCONTINUED_JSR), + JSR(RawBytecodeHelper.JSR, 3, Kind.DISCONTINUED_JSR), /** * Return from subroutine is discontinued opcode * @see java.lang.classfile.instruction.DiscontinuedInstruction */ - RET(ClassFile.RET, 2, Kind.DISCONTINUED_RET), + RET(RawBytecodeHelper.RET, 2, Kind.DISCONTINUED_RET), /** Access jump table by index and jump */ - TABLESWITCH(ClassFile.TABLESWITCH, -1, Kind.TABLE_SWITCH), + TABLESWITCH(RawBytecodeHelper.TABLESWITCH, -1, Kind.TABLE_SWITCH), /** Access jump table by key match and jump */ - LOOKUPSWITCH(ClassFile.LOOKUPSWITCH, -1, Kind.LOOKUP_SWITCH), + LOOKUPSWITCH(RawBytecodeHelper.LOOKUPSWITCH, -1, Kind.LOOKUP_SWITCH), /** Return int from method */ - IRETURN(ClassFile.IRETURN, 1, Kind.RETURN), + IRETURN(RawBytecodeHelper.IRETURN, 1, Kind.RETURN), /** Return long from method */ - LRETURN(ClassFile.LRETURN, 1, Kind.RETURN), + LRETURN(RawBytecodeHelper.LRETURN, 1, Kind.RETURN), /** Return float from method */ - FRETURN(ClassFile.FRETURN, 1, Kind.RETURN), + FRETURN(RawBytecodeHelper.FRETURN, 1, Kind.RETURN), /** Return double from method */ - DRETURN(ClassFile.DRETURN, 1, Kind.RETURN), + DRETURN(RawBytecodeHelper.DRETURN, 1, Kind.RETURN), /** Return reference from method */ - ARETURN(ClassFile.ARETURN, 1, Kind.RETURN), + ARETURN(RawBytecodeHelper.ARETURN, 1, Kind.RETURN), /** Return void from method */ - RETURN(ClassFile.RETURN, 1, Kind.RETURN), + RETURN(RawBytecodeHelper.RETURN, 1, Kind.RETURN), /** Get static field from class */ - GETSTATIC(ClassFile.GETSTATIC, 3, Kind.FIELD_ACCESS), + GETSTATIC(RawBytecodeHelper.GETSTATIC, 3, Kind.FIELD_ACCESS), /** Set static field in class */ - PUTSTATIC(ClassFile.PUTSTATIC, 3, Kind.FIELD_ACCESS), + PUTSTATIC(RawBytecodeHelper.PUTSTATIC, 3, Kind.FIELD_ACCESS), /** Fetch field from object */ - GETFIELD(ClassFile.GETFIELD, 3, Kind.FIELD_ACCESS), + GETFIELD(RawBytecodeHelper.GETFIELD, 3, Kind.FIELD_ACCESS), /** Set field in object */ - PUTFIELD(ClassFile.PUTFIELD, 3, Kind.FIELD_ACCESS), + PUTFIELD(RawBytecodeHelper.PUTFIELD, 3, Kind.FIELD_ACCESS), /** Invoke instance method; dispatch based on class */ - INVOKEVIRTUAL(ClassFile.INVOKEVIRTUAL, 3, Kind.INVOKE), + INVOKEVIRTUAL(RawBytecodeHelper.INVOKEVIRTUAL, 3, Kind.INVOKE), /** * Invoke instance method; direct invocation of instance initialization * methods and methods of the current class and its supertypes */ - INVOKESPECIAL(ClassFile.INVOKESPECIAL, 3, Kind.INVOKE), + INVOKESPECIAL(RawBytecodeHelper.INVOKESPECIAL, 3, Kind.INVOKE), /** Invoke a class (static) method */ - INVOKESTATIC(ClassFile.INVOKESTATIC, 3, Kind.INVOKE), + INVOKESTATIC(RawBytecodeHelper.INVOKESTATIC, 3, Kind.INVOKE), /** Invoke interface method */ - INVOKEINTERFACE(ClassFile.INVOKEINTERFACE, 5, Kind.INVOKE), + INVOKEINTERFACE(RawBytecodeHelper.INVOKEINTERFACE, 5, Kind.INVOKE), /** Invoke a dynamically-computed call site */ - INVOKEDYNAMIC(ClassFile.INVOKEDYNAMIC, 5, Kind.INVOKE_DYNAMIC), + INVOKEDYNAMIC(RawBytecodeHelper.INVOKEDYNAMIC, 5, Kind.INVOKE_DYNAMIC), /** Create new object */ - NEW(ClassFile.NEW, 3, Kind.NEW_OBJECT), + NEW(RawBytecodeHelper.NEW, 3, Kind.NEW_OBJECT), /** Create new array */ - NEWARRAY(ClassFile.NEWARRAY, 2, Kind.NEW_PRIMITIVE_ARRAY), + NEWARRAY(RawBytecodeHelper.NEWARRAY, 2, Kind.NEW_PRIMITIVE_ARRAY), /** Create new array of reference */ - ANEWARRAY(ClassFile.ANEWARRAY, 3, Kind.NEW_REF_ARRAY), + ANEWARRAY(RawBytecodeHelper.ANEWARRAY, 3, Kind.NEW_REF_ARRAY), /** Get length of array */ - ARRAYLENGTH(ClassFile.ARRAYLENGTH, 1, Kind.OPERATOR), + ARRAYLENGTH(RawBytecodeHelper.ARRAYLENGTH, 1, Kind.OPERATOR), /** Throw exception or error */ - ATHROW(ClassFile.ATHROW, 1, Kind.THROW_EXCEPTION), + ATHROW(RawBytecodeHelper.ATHROW, 1, Kind.THROW_EXCEPTION), /** Check whether object is of given type */ - CHECKCAST(ClassFile.CHECKCAST, 3, Kind.TYPE_CHECK), + CHECKCAST(RawBytecodeHelper.CHECKCAST, 3, Kind.TYPE_CHECK), /** Determine if object is of given type */ - INSTANCEOF(ClassFile.INSTANCEOF, 3, Kind.TYPE_CHECK), + INSTANCEOF(RawBytecodeHelper.INSTANCEOF, 3, Kind.TYPE_CHECK), /** Enter monitor for object */ - MONITORENTER(ClassFile.MONITORENTER, 1, Kind.MONITOR), + MONITORENTER(RawBytecodeHelper.MONITORENTER, 1, Kind.MONITOR), /** Exit monitor for object */ - MONITOREXIT(ClassFile.MONITOREXIT, 1, Kind.MONITOR), + MONITOREXIT(RawBytecodeHelper.MONITOREXIT, 1, Kind.MONITOR), /** Create new multidimensional array */ - MULTIANEWARRAY(ClassFile.MULTIANEWARRAY, 4, Kind.NEW_MULTI_ARRAY), + MULTIANEWARRAY(RawBytecodeHelper.MULTIANEWARRAY, 4, Kind.NEW_MULTI_ARRAY), /** Branch if reference is null */ - IFNULL(ClassFile.IFNULL, 3, Kind.BRANCH), + IFNULL(RawBytecodeHelper.IFNULL, 3, Kind.BRANCH), /** Branch if reference not null */ - IFNONNULL(ClassFile.IFNONNULL, 3, Kind.BRANCH), + IFNONNULL(RawBytecodeHelper.IFNONNULL, 3, Kind.BRANCH), /** Branch always (wide index) */ - GOTO_W(ClassFile.GOTO_W, 5, Kind.BRANCH), + GOTO_W(RawBytecodeHelper.GOTO_W, 5, Kind.BRANCH), /** * Jump subroutine (wide index) is discontinued opcode * @see java.lang.classfile.instruction.DiscontinuedInstruction */ - JSR_W(ClassFile.JSR_W, 5, Kind.DISCONTINUED_JSR), + JSR_W(RawBytecodeHelper.JSR_W, 5, Kind.DISCONTINUED_JSR), /** Load int from local variable (wide index) */ - ILOAD_W((ClassFile.WIDE << 8) | ClassFile.ILOAD, 4, Kind.LOAD), + ILOAD_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.ILOAD, 4, Kind.LOAD), /** Load long from local variable (wide index) */ - LLOAD_W((ClassFile.WIDE << 8) | ClassFile.LLOAD, 4, Kind.LOAD), + LLOAD_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.LLOAD, 4, Kind.LOAD), /** Load float from local variable (wide index) */ - FLOAD_W((ClassFile.WIDE << 8) | ClassFile.FLOAD, 4, Kind.LOAD), + FLOAD_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.FLOAD, 4, Kind.LOAD), /** Load double from local variable (wide index) */ - DLOAD_W((ClassFile.WIDE << 8) | ClassFile.DLOAD, 4, Kind.LOAD), + DLOAD_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.DLOAD, 4, Kind.LOAD), /** Load reference from local variable (wide index) */ - ALOAD_W((ClassFile.WIDE << 8) | ClassFile.ALOAD, 4, Kind.LOAD), + ALOAD_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.ALOAD, 4, Kind.LOAD), /** Store int into local variable (wide index) */ - ISTORE_W((ClassFile.WIDE << 8) | ClassFile.ISTORE, 4, Kind.STORE), + ISTORE_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.ISTORE, 4, Kind.STORE), /** Store long into local variable (wide index) */ - LSTORE_W((ClassFile.WIDE << 8) | ClassFile.LSTORE, 4, Kind.STORE), + LSTORE_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.LSTORE, 4, Kind.STORE), /** Store float into local variable (wide index) */ - FSTORE_W((ClassFile.WIDE << 8) | ClassFile.FSTORE, 4, Kind.STORE), + FSTORE_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.FSTORE, 4, Kind.STORE), /** Store double into local variable (wide index) */ - DSTORE_W((ClassFile.WIDE << 8) | ClassFile.DSTORE, 4, Kind.STORE), + DSTORE_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.DSTORE, 4, Kind.STORE), /** Store reference into local variable (wide index) */ - ASTORE_W((ClassFile.WIDE << 8) | ClassFile.ASTORE, 4, Kind.STORE), + ASTORE_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.ASTORE, 4, Kind.STORE), /** * Return from subroutine (wide index) is discontinued opcode * @see java.lang.classfile.instruction.DiscontinuedInstruction */ - RET_W((ClassFile.WIDE << 8) | ClassFile.RET, 4, Kind.DISCONTINUED_RET), + RET_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.RET, 4, Kind.DISCONTINUED_RET), /** Increment local variable by constant (wide index) */ - IINC_W((ClassFile.WIDE << 8) | ClassFile.IINC, 6, Kind.INCREMENT); + IINC_W((RawBytecodeHelper.WIDE << 8) | RawBytecodeHelper.IINC, 6, Kind.INCREMENT); /** * Kinds of opcodes. @@ -1085,13 +1086,13 @@ public static enum Kind { /** * {@return the opcode value} For {@linkplain #isWide() wide} pseudo-opcodes, returns the - * first 2 bytes of the instruction, which are the {@code wide} opcode and the functional - * local variable opcode, as a U2 value. + * first 2 bytes of the instruction, which are the wide opcode {@code 196} ({@code 0xC4}) + * and the functional opcode, as a U2 value. */ public int bytecode() { return bytecode; } /** - * {@return true if this is a pseudo-opcode modified by {@code wide}} + * {@return true if this is a pseudo-opcode modified by wide opcode} * * @see #ILOAD_W * @see #LLOAD_W diff --git a/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java b/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java index b6c9aec76a1a3..139c8eef835dc 100644 --- a/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java +++ b/src/java.base/share/classes/java/lang/classfile/TypeAnnotation.java @@ -31,31 +31,10 @@ import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute; import jdk.internal.classfile.impl.TargetInfoImpl; import jdk.internal.classfile.impl.UnboundAttribute; - -import static java.lang.classfile.ClassFile.TAT_CAST; -import static java.lang.classfile.ClassFile.TAT_CLASS_EXTENDS; -import static java.lang.classfile.ClassFile.TAT_CLASS_TYPE_PARAMETER; -import static java.lang.classfile.ClassFile.TAT_CLASS_TYPE_PARAMETER_BOUND; -import static java.lang.classfile.ClassFile.TAT_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT; -import static java.lang.classfile.ClassFile.TAT_CONSTRUCTOR_REFERENCE; -import static java.lang.classfile.ClassFile.TAT_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT; -import static java.lang.classfile.ClassFile.TAT_EXCEPTION_PARAMETER; -import static java.lang.classfile.ClassFile.TAT_FIELD; -import static java.lang.classfile.ClassFile.TAT_INSTANCEOF; -import static java.lang.classfile.ClassFile.TAT_LOCAL_VARIABLE; -import static java.lang.classfile.ClassFile.TAT_METHOD_FORMAL_PARAMETER; -import static java.lang.classfile.ClassFile.TAT_METHOD_INVOCATION_TYPE_ARGUMENT; -import static java.lang.classfile.ClassFile.TAT_METHOD_RECEIVER; -import static java.lang.classfile.ClassFile.TAT_METHOD_REFERENCE; -import static java.lang.classfile.ClassFile.TAT_METHOD_REFERENCE_TYPE_ARGUMENT; -import static java.lang.classfile.ClassFile.TAT_METHOD_RETURN; -import static java.lang.classfile.ClassFile.TAT_METHOD_TYPE_PARAMETER; -import static java.lang.classfile.ClassFile.TAT_METHOD_TYPE_PARAMETER_BOUND; -import static java.lang.classfile.ClassFile.TAT_NEW; -import static java.lang.classfile.ClassFile.TAT_RESOURCE_VARIABLE; -import static java.lang.classfile.ClassFile.TAT_THROWS; import jdk.internal.javac.PreviewFeature; +import static java.lang.classfile.TypeAnnotation.TargetInfo.*; + /** * Models a {@code type_annotation} structure (JVMS {@jvms 4.7.20}). This model * indicates the annotated type within a declaration or expression and the part @@ -87,70 +66,70 @@ public sealed interface TypeAnnotation @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public enum TargetType { /** For annotations on a class type parameter declaration. */ - CLASS_TYPE_PARAMETER(TAT_CLASS_TYPE_PARAMETER, 1), + CLASS_TYPE_PARAMETER(TARGET_CLASS_TYPE_PARAMETER, 1), /** For annotations on a method type parameter declaration. */ - METHOD_TYPE_PARAMETER(TAT_METHOD_TYPE_PARAMETER, 1), + METHOD_TYPE_PARAMETER(TARGET_METHOD_TYPE_PARAMETER, 1), /** For annotations on the type of an "extends" or "implements" clause. */ - CLASS_EXTENDS(TAT_CLASS_EXTENDS, 2), + CLASS_EXTENDS(TARGET_CLASS_EXTENDS, 2), /** For annotations on a bound of a type parameter of a class. */ - CLASS_TYPE_PARAMETER_BOUND(TAT_CLASS_TYPE_PARAMETER_BOUND, 2), + CLASS_TYPE_PARAMETER_BOUND(TARGET_CLASS_TYPE_PARAMETER_BOUND, 2), /** For annotations on a bound of a type parameter of a method. */ - METHOD_TYPE_PARAMETER_BOUND(TAT_METHOD_TYPE_PARAMETER_BOUND, 2), + METHOD_TYPE_PARAMETER_BOUND(TARGET_METHOD_TYPE_PARAMETER_BOUND, 2), /** For annotations on a field. */ - FIELD(TAT_FIELD, 0), + FIELD(TARGET_FIELD, 0), /** For annotations on a method return type. */ - METHOD_RETURN(TAT_METHOD_RETURN, 0), + METHOD_RETURN(TARGET_METHOD_RETURN, 0), /** For annotations on the method receiver. */ - METHOD_RECEIVER(TAT_METHOD_RECEIVER, 0), + METHOD_RECEIVER(TARGET_METHOD_RECEIVER, 0), /** For annotations on a method parameter. */ - METHOD_FORMAL_PARAMETER(TAT_METHOD_FORMAL_PARAMETER, 1), + METHOD_FORMAL_PARAMETER(TARGET_METHOD_FORMAL_PARAMETER, 1), /** For annotations on a throws clause in a method declaration. */ - THROWS(TAT_THROWS, 2), + THROWS(TARGET_THROWS, 2), /** For annotations on a local variable. */ - LOCAL_VARIABLE(TAT_LOCAL_VARIABLE, -1), + LOCAL_VARIABLE(TARGET_LOCAL_VARIABLE, -1), /** For annotations on a resource variable. */ - RESOURCE_VARIABLE(TAT_RESOURCE_VARIABLE, -1), + RESOURCE_VARIABLE(TARGET_RESOURCE_VARIABLE, -1), /** For annotations on an exception parameter. */ - EXCEPTION_PARAMETER(TAT_EXCEPTION_PARAMETER, 2), + EXCEPTION_PARAMETER(TARGET_EXCEPTION_PARAMETER, 2), /** For annotations on a type test. */ - INSTANCEOF(TAT_INSTANCEOF, 2), + INSTANCEOF(TARGET_INSTANCEOF, 2), /** For annotations on an object creation expression. */ - NEW(TAT_NEW, 2), + NEW(TARGET_NEW, 2), /** For annotations on a constructor reference receiver. */ - CONSTRUCTOR_REFERENCE(TAT_CONSTRUCTOR_REFERENCE, 2), + CONSTRUCTOR_REFERENCE(TARGET_CONSTRUCTOR_REFERENCE, 2), /** For annotations on a method reference receiver. */ - METHOD_REFERENCE(TAT_METHOD_REFERENCE, 2), + METHOD_REFERENCE(TARGET_METHOD_REFERENCE, 2), /** For annotations on a typecast. */ - CAST(TAT_CAST, 3), + CAST(TARGET_CAST, 3), /** For annotations on a type argument of an object creation expression. */ - CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT(TAT_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT, 3), + CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT(TARGET_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT, 3), /** For annotations on a type argument of a method call. */ - METHOD_INVOCATION_TYPE_ARGUMENT(TAT_METHOD_INVOCATION_TYPE_ARGUMENT, 3), + METHOD_INVOCATION_TYPE_ARGUMENT(TARGET_METHOD_INVOCATION_TYPE_ARGUMENT, 3), /** For annotations on a type argument of a constructor reference. */ - CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT(TAT_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT, 3), + CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT(TARGET_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT, 3), /** For annotations on a type argument of a method reference. */ - METHOD_REFERENCE_TYPE_ARGUMENT(TAT_METHOD_REFERENCE_TYPE_ARGUMENT, 3); + METHOD_REFERENCE_TYPE_ARGUMENT(TARGET_METHOD_REFERENCE_TYPE_ARGUMENT, 3); private final int targetTypeValue; private final int sizeIfFixed; @@ -162,6 +141,11 @@ private TargetType(int targetTypeValue, int sizeIfFixed) { /** * {@return the target type value} + * + * @apiNote + * {@code TARGET_}-prefixed constants in {@link TargetInfo}, such as {@link + * TargetInfo#TARGET_CLASS_TYPE_PARAMETER}, describe the possible return + * values of this method. */ public int targetTypeValue() { return targetTypeValue; @@ -214,6 +198,146 @@ static TypeAnnotation of(TargetInfo targetInfo, List targetPa @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) sealed interface TargetInfo { + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}. + */ + int TARGET_CLASS_TYPE_PARAMETER = 0x00; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + */ + int TARGET_METHOD_TYPE_PARAMETER = 0x01; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#CLASS_EXTENDS CLASS_EXTENDS}. + */ + int TARGET_CLASS_EXTENDS = 0x10; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#CLASS_TYPE_PARAMETER_BOUND + * CLASS_TYPE_PARAMETER_BOUND}. + */ + int TARGET_CLASS_TYPE_PARAMETER_BOUND = 0x11; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_TYPE_PARAMETER_BOUND + * METHOD_TYPE_PARAMETER_BOUND}. + */ + int TARGET_METHOD_TYPE_PARAMETER_BOUND = 0x12; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#FIELD FIELD}. + */ + int TARGET_FIELD = 0x13; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_RETURN METHOD_RETURN}. + */ + int TARGET_METHOD_RETURN = 0x14; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_RECEIVER METHOD_RECEIVER}. + */ + int TARGET_METHOD_RECEIVER = 0x15; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_FORMAL_PARAMETER + * METHOD_FORMAL_PARAMETER}. + */ + int TARGET_METHOD_FORMAL_PARAMETER = 0x16; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#THROWS THROWS}. + */ + int TARGET_THROWS = 0x17; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#LOCAL_VARIABLE LOCAL_VARIABLE}. + */ + int TARGET_LOCAL_VARIABLE = 0x40; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#RESOURCE_VARIABLE RESOURCE_VARIABLE}. + */ + int TARGET_RESOURCE_VARIABLE = 0x41; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#EXCEPTION_PARAMETER EXCEPTION_PARAMETER}. + */ + int TARGET_EXCEPTION_PARAMETER = 0x42; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#INSTANCEOF INSTANCEOF}. + */ + int TARGET_INSTANCEOF = 0x43; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#NEW NEW}. + */ + int TARGET_NEW = 0x44; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#CONSTRUCTOR_REFERENCE + * CONSTRUCTOR_REFERENCE}. + */ + int TARGET_CONSTRUCTOR_REFERENCE = 0x45; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_REFERENCE METHOD_REFERENCE}. + */ + int TARGET_METHOD_REFERENCE = 0x46; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#CAST CAST}. + */ + int TARGET_CAST = 0x47; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}. + */ + int TARGET_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}. + */ + int TARGET_METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}. + */ + int TARGET_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; + + /** + * The {@linkplain TargetType#targetTypeValue() value} of type annotation {@linkplain + * #targetType target type} {@link TargetType#METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + */ + int TARGET_METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; + /** * {@return the type of the target} */ diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java index 37abcb027132e..126ba1037cefb 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java @@ -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 @@ -24,6 +24,8 @@ */ package java.lang.classfile.attribute; +import java.lang.classfile.instruction.CharacterRange; + import jdk.internal.classfile.impl.UnboundAttribute; import jdk.internal.javac.PreviewFeature; @@ -70,17 +72,17 @@ public sealed interface CharacterRangeInfo * The value of the flags item describes the kind of range. Multiple flags * may be set within flags. *

      - *
    • {@link java.lang.classfile.ClassFile#CRT_STATEMENT} Range is a Statement + *
    • {@link CharacterRange#FLAG_STATEMENT} Range is a Statement * (except ExpressionStatement), StatementExpression {@jls 14.8}, as well as each * VariableDeclaratorId = VariableInitializer of * LocalVariableDeclarationStatement {@jls 14.4} or FieldDeclaration {@jls 8.3} in the * grammar. - *
    • {@link java.lang.classfile.ClassFile#CRT_BLOCK} Range is a Block in the + *
    • {@link CharacterRange#FLAG_BLOCK} Range is a Block in the * grammar. - *
    • {@link java.lang.classfile.ClassFile#CRT_ASSIGNMENT} Range is an assignment + *
    • {@link CharacterRange#FLAG_ASSIGNMENT} Range is an assignment * expression - Expression1 AssignmentOperator Expression1 in the grammar as * well as increment and decrement expressions (both prefix and postfix). - *
    • {@link java.lang.classfile.ClassFile#CRT_FLOW_CONTROLLER} An expression + *
    • {@link CharacterRange#FLAG_FLOW_CONTROLLER} An expression * whose value will effect control flow. {@code Flowcon} in the following: *
            * if ( Flowcon ) Statement [else Statement]
      @@ -92,7 +94,7 @@ public sealed interface CharacterRangeInfo
            * Flowcon && Expression3
            * Flowcon ? Expression : Expression1
            * 
      - *
    • {@link java.lang.classfile.ClassFile#CRT_FLOW_TARGET} Statement or + *
    • {@link CharacterRange#FLAG_FLOW_TARGET} Statement or * expression effected by a CRT_FLOW_CONTROLLER. {@code Flowtarg} in the following: *
            * if ( Flowcon ) Flowtarg [else Flowtarg]
      @@ -103,11 +105,11 @@ public sealed interface CharacterRangeInfo
            * Flowcon && Flowtarg
            * Flowcon ? Flowtarg : Flowtarg
            * 
      - *
    • {@link java.lang.classfile.ClassFile#CRT_INVOKE} Method invocation. For + *
    • {@link CharacterRange#FLAG_INVOKE} Method invocation. For * example: Identifier Arguments. - *
    • {@link java.lang.classfile.ClassFile#CRT_CREATE} New object creation. For + *
    • {@link CharacterRange#FLAG_CREATE} New object creation. For * example: new Creator. - *
    • {@link java.lang.classfile.ClassFile#CRT_BRANCH_TRUE} A condition encoded + *
    • {@link CharacterRange#FLAG_BRANCH_TRUE} A condition encoded * in the branch instruction immediately contained in the code range for * this item is not inverted towards the corresponding branch condition in * the source code. I.e. actual jump occurs if and only if the the source @@ -119,7 +121,7 @@ public sealed interface CharacterRangeInfo * if<cond>, ifnonull, ifnull or goto. CRT_BRANCH_TRUE and * CRT_BRANCH_FALSE are special kinds of entries that can be used to * determine what branch of a condition was chosen during the runtime. - *
    • {@link java.lang.classfile.ClassFile#CRT_BRANCH_FALSE} A condition encoded + *
    • {@link CharacterRange#FLAG_BRANCH_FALSE} A condition encoded * in the branch instruction immediately contained in the code range for * this item is inverted towards the corresponding branch condition in the * source code. I.e. actual jump occurs if and only if the the source code @@ -134,6 +136,7 @@ public sealed interface CharacterRangeInfo * All bits of the flags item not assigned above are reserved for future use. They should be set to zero in generated class files and should be ignored by Java virtual machine implementations. * * @return the flags + * @see CharacterRange#flags() */ int flags(); diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java index 768019fa1d301..2c91900850120 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java @@ -92,7 +92,7 @@ default Optional enclosingMethodType() { * immediately enclosed by a method or constructor} */ default Optional enclosingMethodTypeSymbol() { - return enclosingMethod().map(Util::methodTypeSymbol); + return enclosingMethodType().map(Util::methodTypeSymbol); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java index 46caa66ef9adf..3415c89174ae9 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/StackMapFrameInfo.java @@ -32,7 +32,6 @@ import java.lang.classfile.constantpool.ClassEntry; import jdk.internal.classfile.impl.StackMapDecoder; import jdk.internal.classfile.impl.TemporaryConstantPool; -import static java.lang.classfile.ClassFile.*; import jdk.internal.javac.PreviewFeature; /** @@ -85,8 +84,39 @@ public static StackMapFrameInfo of(Label target, @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) sealed interface VerificationTypeInfo { + /** The {@link #tag() tag} for verification type info {@link SimpleVerificationTypeInfo#TOP TOP}. */ + int ITEM_TOP = 0; + + /** The {@link #tag() tag} for verification type info {@link SimpleVerificationTypeInfo#INTEGER INTEGER}. */ + int ITEM_INTEGER = 1; + + /** The {@link #tag() tag} for verification type info {@link SimpleVerificationTypeInfo#FLOAT FLOAT}. */ + int ITEM_FLOAT = 2; + + /** The {@link #tag() tag} for verification type info {@link SimpleVerificationTypeInfo#DOUBLE DOUBLE}. */ + int ITEM_DOUBLE = 3; + + /** The {@link #tag() tag} for verification type info {@link SimpleVerificationTypeInfo#LONG LONG}. */ + int ITEM_LONG = 4; + + /** The {@link #tag() tag} for verification type info {@link SimpleVerificationTypeInfo#NULL NULL}. */ + int ITEM_NULL = 5; + + /** The {@link #tag() tag} for verification type info {@link SimpleVerificationTypeInfo#UNINITIALIZED_THIS UNINITIALIZED_THIS}. */ + int ITEM_UNINITIALIZED_THIS = 6; + + /** The {@link #tag() tag} for verification type info {@link ObjectVerificationTypeInfo OBJECT}. */ + int ITEM_OBJECT = 7; + + /** The {@link #tag() tag} for verification type info {@link UninitializedVerificationTypeInfo UNINITIALIZED}. */ + int ITEM_UNINITIALIZED = 8; + /** * {@return the tag of the type info} + * + * @apiNote + * {@code ITEM_}-prefixed constants in this class, such as {@link #ITEM_TOP}, describe the + * possible return values of this method. */ int tag(); } @@ -100,25 +130,25 @@ sealed interface VerificationTypeInfo { public enum SimpleVerificationTypeInfo implements VerificationTypeInfo { /** verification type top */ - ITEM_TOP(VT_TOP), + TOP(ITEM_TOP), /** verification type int */ - ITEM_INTEGER(VT_INTEGER), + INTEGER(ITEM_INTEGER), /** verification type float */ - ITEM_FLOAT(VT_FLOAT), + FLOAT(ITEM_FLOAT), /** verification type double */ - ITEM_DOUBLE(VT_DOUBLE), + DOUBLE(ITEM_DOUBLE), /** verification type long */ - ITEM_LONG(VT_LONG), + LONG(ITEM_LONG), /** verification type null */ - ITEM_NULL(VT_NULL), + NULL(ITEM_NULL), /** verification type uninitializedThis */ - ITEM_UNINITIALIZED_THIS(VT_UNINITIALIZED_THIS); + UNINITIALIZED_THIS(ITEM_UNINITIALIZED_THIS); private final int tag; @@ -134,7 +164,7 @@ public int tag() { } /** - * A stack value for an object type. + * A stack value for an object type. Its {@link #tag() tag} is {@value #ITEM_OBJECT}. * * @since 22 */ @@ -173,7 +203,7 @@ default ClassDesc classSymbol() { } /** - * An uninitialized stack value. + * An uninitialized stack value. Its {@link #tag() tag} is {@value #ITEM_UNINITIALIZED}. * * @since 22 */ diff --git a/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java b/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java index d3ae180dde573..bcda36355870a 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.java +++ b/src/java.base/share/classes/java/lang/classfile/components/ClassRemapper.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 @@ -36,6 +36,8 @@ import jdk.internal.classfile.impl.ClassRemapperImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * {@code ClassRemapper} is a {@link ClassTransform}, {@link FieldTransform}, * {@link MethodTransform} and {@link CodeTransform} @@ -64,6 +66,7 @@ public sealed interface ClassRemapper extends ClassTransform permits ClassRemapp * @return new instance of {@code ClassRemapper} */ static ClassRemapper of(Map classMap) { + requireNonNull(classMap); return of(desc -> classMap.getOrDefault(desc, desc)); } @@ -75,7 +78,7 @@ static ClassRemapper of(Map classMap) { * @return new instance of {@code ClassRemapper} */ static ClassRemapper of(Function mapFunction) { - return new ClassRemapperImpl(mapFunction); + return new ClassRemapperImpl(requireNonNull(mapFunction)); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java b/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java index ca5ad90389c5c..6ec3f9f792b72 100644 --- a/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java +++ b/src/java.base/share/classes/java/lang/classfile/components/CodeRelabeler.java @@ -33,6 +33,8 @@ import jdk.internal.classfile.impl.CodeRelabelerImpl; import jdk.internal.javac.PreviewFeature; +import static java.util.Objects.requireNonNull; + /** * A code relabeler is a {@link CodeTransform} replacing all occurrences * of {@link java.lang.classfile.Label} in the transformed code with new instances. @@ -62,6 +64,7 @@ static CodeRelabeler of() { * @return a new instance of CodeRelabeler */ static CodeRelabeler of(Map map) { + requireNonNull(map); return of((l, cob) -> map.computeIfAbsent(l, ll -> cob.newLabel())); } @@ -72,6 +75,6 @@ static CodeRelabeler of(Map map) { * @return a new instance of CodeRelabeler */ static CodeRelabeler of(BiFunction mapFunction) { - return new CodeRelabelerImpl(mapFunction); + return new CodeRelabelerImpl(requireNonNull(mapFunction)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java index 144c8a539d72c..507ff906274cb 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java @@ -50,7 +50,7 @@ public sealed interface ConstantDynamicEntry * {@return a symbolic descriptor for the dynamic constant's type} */ default ClassDesc typeSymbol() { - return Util.fieldTypeSymbol(nameAndType()); + return Util.fieldTypeSymbol(type()); } @Override diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java index 1c0d6e55e3143..12c9789133bb4 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java @@ -470,10 +470,11 @@ default StringEntry stringEntry(String value) { } /** - * {@return A {@link ConstantValueEntry} descripbing the provided + * {@return A {@link ConstantValueEntry} describing the provided * Integer, Long, Float, Double, or String constant} * * @param c the constant + * @see ConstantValueEntry#constantValue() */ default ConstantValueEntry constantValueEntry(ConstantDesc c) { if (c instanceof Integer i) return intEntry(i); diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java index 340baeb905fd6..720e3fd5d5cc4 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java @@ -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 @@ -24,13 +24,14 @@ */ package java.lang.classfile.constantpool; +import java.lang.classfile.Attributes; import java.lang.constant.ConstantDesc; import jdk.internal.javac.PreviewFeature; /** * Models a constant pool entry that can be used as the constant in a - * {@code ConstantValue} attribute; this includes the four primitive constant - * types and {@linkplain String} constants. + * {@link Attributes#constantValue() ConstantValue} attribute; this includes the four + * primitive constant types and {@linkplain String} constants. * * @sealedGraph * @since 22 @@ -42,6 +43,8 @@ public sealed interface ConstantValueEntry extends LoadableConstantEntry /** * {@return the constant value} The constant value will be an {@link Integer}, * {@link Long}, {@link Float}, {@link Double}, or {@link String}. + * + * @see ConstantPoolBuilder#constantValueEntry(ConstantDesc) */ @Override ConstantDesc constantValue(); diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java index 628abdac6fe43..75533770b3524 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java @@ -44,6 +44,6 @@ public sealed interface FieldRefEntry extends MemberRefEntry * {@return a symbolic descriptor for the field's type} */ default ClassDesc typeSymbol() { - return Util.fieldTypeSymbol(nameAndType()); + return Util.fieldTypeSymbol(type()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java index 43faa488bb915..b97defdc1e148 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java @@ -45,6 +45,6 @@ public sealed interface InterfaceMethodRefEntry * {@return a symbolic descriptor for the interface method's type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(nameAndType()); + return Util.methodTypeSymbol(type()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java index d9a1c29997299..f06c3d4c7828c 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java @@ -47,7 +47,7 @@ public sealed interface InvokeDynamicEntry * {@return a symbolic descriptor for the call site's invocation type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(nameAndType()); + return Util.methodTypeSymbol(type()); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java index 39684db462134..3ff8dfdc0f462 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java @@ -44,6 +44,6 @@ public sealed interface MethodRefEntry extends MemberRefEntry * {@return a symbolic descriptor for the method's type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(nameAndType()); + return Util.methodTypeSymbol(type()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java index c8b74a139280e..d2af4c7c11ae5 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/PoolEntry.java @@ -38,6 +38,57 @@ public sealed interface PoolEntry LoadableConstantEntry, MemberRefEntry, ModuleEntry, NameAndTypeEntry, PackageEntry { + /** The {@linkplain #tag tag} for {@link ClassEntry CONSTANT_Class} constant kind. */ + int TAG_CLASS = 7; + + /** The {@linkplain #tag tag} for {@link DoubleEntry CONSTANT_Double} constant kind. */ + int TAG_DOUBLE = 6; + + /** The {@linkplain #tag tag} for {@link ConstantDynamicEntry CONSTANT_Dynamic} constant kind. */ + int TAG_DYNAMIC = 17; + + /** The {@linkplain #tag tag} for {@link FieldRefEntry CONSTANT_Fieldref} constant kind. */ + int TAG_FIELDREF = 9; + + /** The {@linkplain #tag tag} for {@link FloatEntry CONSTANT_Float} constant kind. */ + int TAG_FLOAT = 4; + + /** The {@linkplain #tag tag} for {@link IntegerEntry CONSTANT_Integer} constant kind. */ + int TAG_INTEGER = 3; + + /** The {@linkplain #tag tag} for {@link InterfaceMethodRefEntry CONSTANT_InterfaceMethodref} constant kind. */ + int TAG_INTERFACE_METHODREF = 11; + + /** The {@linkplain #tag tag} for {@link InvokeDynamicEntry CONSTANT_InvokeDynamic} constant kind. */ + int TAG_INVOKE_DYNAMIC = 18; + + /** The {@linkplain #tag tag} for {@link LongEntry CONSTANT_Long} constant kind. */ + int TAG_LONG = 5; + + /** The {@linkplain #tag tag} for {@link MethodHandleEntry CONSTANT_MethodHandle} constant kind. */ + int TAG_METHOD_HANDLE = 15; + + /** The {@linkplain #tag tag} for {@link MethodRefEntry CONSTANT_Methodref} constant kind. */ + int TAG_METHODREF = 10; + + /** The {@linkplain #tag tag} for {@link MethodTypeEntry CONSTANT_MethodType} constant kind. */ + int TAG_METHOD_TYPE = 16; + + /** The {@linkplain #tag tag} for {@link ModuleEntry CONSTANT_Module} constant kind. */ + int TAG_MODULE = 19; + + /** The {@linkplain #tag tag} for {@link NameAndTypeEntry CONSTANT_NameAndType} constant kind. */ + int TAG_NAME_AND_TYPE = 12; + + /** The {@linkplain #tag tag} for {@link PackageEntry CONSTANT_Package} constant kind. */ + int TAG_PACKAGE = 20; + + /** The {@linkplain #tag tag} for {@link StringEntry CONSTANT_String} constant kind. */ + int TAG_STRING = 8; + + /** The {@linkplain #tag tag} for {@link Utf8Entry CONSTANT_Utf8} constant kind. */ + int TAG_UTF8 = 1; + /** * {@return the constant pool this entry is from} */ @@ -45,6 +96,10 @@ public sealed interface PoolEntry /** * {@return the constant pool tag that describes the type of this entry} + * + * @apiNote + * {@code TAG_}-prefixed constants in this class, such as {@link #TAG_UTF8}, + * describe the possible return values of this method. */ byte tag(); diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/CharacterRange.java b/src/java.base/share/classes/java/lang/classfile/instruction/CharacterRange.java index 89a54caeaf2fe..fca3279cd2228 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/CharacterRange.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/CharacterRange.java @@ -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 @@ -29,6 +29,7 @@ import java.lang.classfile.CodeModel; import java.lang.classfile.Label; import java.lang.classfile.PseudoInstruction; +import java.lang.classfile.attribute.CharacterRangeInfo; import java.lang.classfile.attribute.CharacterRangeTableAttribute; import jdk.internal.classfile.impl.AbstractPseudoInstruction; import jdk.internal.classfile.impl.BoundCharacterRange; @@ -45,6 +46,34 @@ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface CharacterRange extends PseudoInstruction permits AbstractPseudoInstruction.UnboundCharacterRange, BoundCharacterRange { + + /** The bit mask of STATEMENT {@link CharacterRangeInfo} kind. */ + int FLAG_STATEMENT = 0x0001; + + /** The bit mask of BLOCK {@link CharacterRangeInfo} kind. */ + int FLAG_BLOCK = 0x0002; + + /** The bit mask of ASSIGNMENT {@link CharacterRangeInfo} kind. */ + int FLAG_ASSIGNMENT = 0x0004; + + /** The bit mask of FLOW_CONTROLLER {@link CharacterRangeInfo} kind. */ + int FLAG_FLOW_CONTROLLER = 0x0008; + + /** The bit mask of FLOW_TARGET {@link CharacterRangeInfo} kind. */ + int FLAG_FLOW_TARGET = 0x0010; + + /** The bit mask of INVOKE {@link CharacterRangeInfo} kind. */ + int FLAG_INVOKE = 0x0020; + + /** The bit mask of CREATE {@link CharacterRangeInfo} kind. */ + int FLAG_CREATE = 0x0040; + + /** The bit mask of BRANCH_TRUE {@link CharacterRangeInfo} kind. */ + int FLAG_BRANCH_TRUE = 0x0080; + + /** The bit mask of BRANCH_FALSE {@link CharacterRangeInfo} kind. */ + int FLAG_BRANCH_FALSE = 0x0100; + /** * {@return the start of the instruction range} */ @@ -75,15 +104,15 @@ public sealed interface CharacterRange extends PseudoInstruction * A flags word, indicating the kind of range. Multiple flag bits * may be set. Valid flags include: *
        - *
      • {@link java.lang.classfile.ClassFile#CRT_STATEMENT} - *
      • {@link java.lang.classfile.ClassFile#CRT_BLOCK} - *
      • {@link java.lang.classfile.ClassFile#CRT_ASSIGNMENT} - *
      • {@link java.lang.classfile.ClassFile#CRT_FLOW_CONTROLLER} - *
      • {@link java.lang.classfile.ClassFile#CRT_FLOW_TARGET} - *
      • {@link java.lang.classfile.ClassFile#CRT_INVOKE} - *
      • {@link java.lang.classfile.ClassFile#CRT_CREATE} - *
      • {@link java.lang.classfile.ClassFile#CRT_BRANCH_TRUE} - *
      • {@link java.lang.classfile.ClassFile#CRT_BRANCH_FALSE} + *
      • {@link #FLAG_STATEMENT} + *
      • {@link #FLAG_BLOCK} + *
      • {@link #FLAG_ASSIGNMENT} + *
      • {@link #FLAG_FLOW_CONTROLLER} + *
      • {@link #FLAG_FLOW_TARGET} + *
      • {@link #FLAG_INVOKE} + *
      • {@link #FLAG_CREATE} + *
      • {@link #FLAG_BRANCH_TRUE} + *
      • {@link #FLAG_BRANCH_FALSE} *
      * * @see java.lang.classfile.attribute.CharacterRangeInfo#flags() diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java index 61ea14c459884..fc87dd274d36e 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/DiscontinuedInstruction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -30,6 +30,7 @@ import java.lang.classfile.Label; import java.lang.classfile.Opcode; import jdk.internal.classfile.impl.AbstractInstruction; +import jdk.internal.classfile.impl.BytecodeHelpers; import jdk.internal.classfile.impl.Util; import jdk.internal.javac.PreviewFeature; @@ -112,10 +113,10 @@ sealed interface RetInstruction extends DiscontinuedInstruction * which must be of kind {@link Opcode.Kind#DISCONTINUED_RET} * @param slot the local variable slot to load return address from * @throws IllegalArgumentException if the opcode kind is not - * {@link Opcode.Kind#DISCONTINUED_RET}. + * {@link Opcode.Kind#DISCONTINUED_RET} or if {@code slot} is out of range */ static RetInstruction of(Opcode op, int slot) { - Util.checkKind(op, Opcode.Kind.DISCONTINUED_RET); + BytecodeHelpers.validateRet(op, slot); return new AbstractInstruction.UnboundRetInstruction(op, slot); } @@ -123,6 +124,7 @@ static RetInstruction of(Opcode op, int slot) { * {@return a RET instruction} * * @param slot the local variable slot to load return address from + * @throws IllegalArgumentException if {@code slot} is out of range */ static RetInstruction of(int slot) { return of(slot < 256 ? Opcode.RET : Opcode.RET_W, slot); diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java index 3bde87ee48dbc..bebb101d7f326 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java @@ -58,6 +58,7 @@ public sealed interface IncrementInstruction extends Instruction * * @param slot the local variable slot to increment * @param constant the value to increment by + * @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range */ static IncrementInstruction of(int slot, int constant) { return new AbstractInstruction.UnboundIncrementInstruction(slot, constant); diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java index ff68abce3d21e..74b8dc942a271 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java @@ -94,7 +94,7 @@ default Utf8Entry type() { * {@return a symbolic descriptor for the method type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(method().nameAndType()); + return Util.methodTypeSymbol(method().type()); } diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/LoadInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/LoadInstruction.java index f4cc8bab794fb..ea10ba6a0d0fb 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/LoadInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/LoadInstruction.java @@ -62,9 +62,12 @@ public sealed interface LoadInstruction extends Instruction * * @param kind the type of the value to be loaded * @param slot the local variable slot to load from + * @throws IllegalArgumentException if {@code kind} is + * {@link TypeKind#VOID void} or {@code slot} is out of range */ static LoadInstruction of(TypeKind kind, int slot) { - return of(BytecodeHelpers.loadOpcode(kind, slot), slot); + var opcode = BytecodeHelpers.loadOpcode(kind, slot); // validates slot, trusted + return new AbstractInstruction.UnboundLoadInstruction(opcode, slot); } /** @@ -74,10 +77,11 @@ static LoadInstruction of(TypeKind kind, int slot) { * which must be of kind {@link Opcode.Kind#LOAD} * @param slot the local variable slot to load from * @throws IllegalArgumentException if the opcode kind is not - * {@link Opcode.Kind#LOAD}. + * {@link Opcode.Kind#LOAD} or {@code slot} is out of range */ static LoadInstruction of(Opcode op, int slot) { Util.checkKind(op, Opcode.Kind.LOAD); + BytecodeHelpers.validateSlot(op, slot, true); return new AbstractInstruction.UnboundLoadInstruction(op, slot); } } diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java index 84a8599315218..390034bd6663d 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariable.java @@ -92,6 +92,7 @@ default ClassDesc typeSymbol() { * @param descriptorEntry the local variable descriptor * @param startScope the start range of the local variable scope * @param endScope the end range of the local variable scope + * @throws IllegalArgumentException if {@code slot} is out of range */ static LocalVariable of(int slot, Utf8Entry nameEntry, Utf8Entry descriptorEntry, Label startScope, Label endScope) { return new AbstractPseudoInstruction.UnboundLocalVariable(slot, nameEntry, descriptorEntry, @@ -106,6 +107,7 @@ static LocalVariable of(int slot, Utf8Entry nameEntry, Utf8Entry descriptorEntry * @param descriptor the local variable descriptor * @param startScope the start range of the local variable scope * @param endScope the end range of the local variable scope + * @throws IllegalArgumentException if {@code slot} is out of range */ static LocalVariable of(int slot, String name, ClassDesc descriptor, Label startScope, Label endScope) { return of(slot, diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java index 4409abe6db272..d0d2cd1581fe0 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/LocalVariableType.java @@ -89,6 +89,7 @@ default Signature signatureSymbol() { * @param signatureEntry the local variable signature * @param startScope the start range of the local variable scope * @param endScope the end range of the local variable scope + * @throws IllegalArgumentException if {@code slot} is out of range */ static LocalVariableType of(int slot, Utf8Entry nameEntry, Utf8Entry signatureEntry, Label startScope, Label endScope) { return new AbstractPseudoInstruction.UnboundLocalVariableType(slot, nameEntry, signatureEntry, @@ -103,6 +104,7 @@ static LocalVariableType of(int slot, Utf8Entry nameEntry, Utf8Entry signatureEn * @param signature the local variable signature * @param startScope the start range of the local variable scope * @param endScope the end range of the local variable scope + * @throws IllegalArgumentException if {@code slot} is out of range */ static LocalVariableType of(int slot, String name, Signature signature, Label startScope, Label endScope) { return of(slot, diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/NewMultiArrayInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/NewMultiArrayInstruction.java index 2f45b96760361..2c572c607b4f4 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/NewMultiArrayInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/NewMultiArrayInstruction.java @@ -29,6 +29,7 @@ import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.Instruction; import jdk.internal.classfile.impl.AbstractInstruction; +import jdk.internal.classfile.impl.BytecodeHelpers; import jdk.internal.javac.PreviewFeature; /** @@ -58,9 +59,11 @@ public sealed interface NewMultiArrayInstruction extends Instruction * * @param arrayTypeEntry the type of the array * @param dimensions the number of dimensions of the array + * @throws IllegalArgumentException if {@code dimensions} is out of range */ static NewMultiArrayInstruction of(ClassEntry arrayTypeEntry, int dimensions) { + BytecodeHelpers.validateMultiArrayDimensions(dimensions); return new AbstractInstruction.UnboundNewMultidimensionalArrayInstruction(arrayTypeEntry, dimensions); } } diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/StoreInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/StoreInstruction.java index 278bf8c0ec399..5bfe13421da35 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/StoreInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/StoreInstruction.java @@ -61,9 +61,12 @@ public sealed interface StoreInstruction extends Instruction * * @param kind the type of the value to be stored * @param slot the local variable slot to store to + * @throws IllegalArgumentException if {@code kind} is {@link + * TypeKind#VOID void} or {@code slot} is out of range */ static StoreInstruction of(TypeKind kind, int slot) { - return of(BytecodeHelpers.storeOpcode(kind, slot), slot); + var opcode = BytecodeHelpers.storeOpcode(kind, slot); // validates slot + return new AbstractInstruction.UnboundStoreInstruction(opcode, slot); } /** @@ -73,10 +76,11 @@ static StoreInstruction of(TypeKind kind, int slot) { * which must be of kind {@link Opcode.Kind#STORE} * @param slot the local variable slot to store to * @throws IllegalArgumentException if the opcode kind is not - * {@link Opcode.Kind#STORE}. + * {@link Opcode.Kind#STORE} or {@code slot} is out of range */ static StoreInstruction of(Opcode op, int slot) { Util.checkKind(op, Opcode.Kind.STORE); + BytecodeHelpers.validateSlot(op, slot, false); return new AbstractInstruction.UnboundStoreInstruction(op, slot); } } diff --git a/src/java.base/share/classes/java/lang/doc-files/RestrictedMethods.html b/src/java.base/share/classes/java/lang/doc-files/RestrictedMethods.html new file mode 100644 index 0000000000000..413adf8e0630d --- /dev/null +++ b/src/java.base/share/classes/java/lang/doc-files/RestrictedMethods.html @@ -0,0 +1,53 @@ + + + + + Restricted methods + + +

      Restricted methods

      +

      Various methods in the Java SE API allow Java code to interoperate with resources outside the Java runtime + in such a way that the runtime cannot prove correct or safe use of the resources. These methods can, + when used incorrectly, violate the integrity of the Java Virtual Machine, but are conditionally made available + to users, as they provide essential functionality. They are known as restricted methods.

      +

      Given the potential danger of restricted methods, the Java runtime issues a warning on + the standard error stream every time a restricted method is invoked. Such warnings can + be disabled by granting access to restricted methods to selected modules. This can be + done either via implementation-specific command line options or programmatically, e.g. + by calling ModuleLayer.Controller.enableNativeAccess(java.lang.Module).

      +

      When a restricted method is invoked by JNI code, + or from an upcall stub + and there is no caller class on the stack, it is as if the restricted method call occurred in an unnamed module.

      +

      In the reference implementation, access to restricted methods can be granted to + specific modules using the command line option --enable-native-access=M1,M2, ... Mn, + where M1, M2, ... Mn are module names (for the unnamed module, + the special value ALL-UNNAMED can be used). Access to restricted methods + from modules not listed by that option is deemed illegal. Clients can + control how access to restricted methods is handled, using the command line + option --illegal-native-access. If this option is not specified, + illegal access to restricted methods will result in runtime warnings.

      + + diff --git a/src/java.base/share/classes/java/lang/doc-files/threadPrimitiveDeprecation.html b/src/java.base/share/classes/java/lang/doc-files/threadPrimitiveDeprecation.html index c680c0d2745a5..9f3ad156727a0 100644 --- a/src/java.base/share/classes/java/lang/doc-files/threadPrimitiveDeprecation.html +++ b/src/java.base/share/classes/java/lang/doc-files/threadPrimitiveDeprecation.html @@ -71,7 +71,7 @@

      What should I use instead of Thread.stop?

      communication of the stop-request, the variable must be volatile (or access to the variable must be synchronized).

      -

      For example, suppose your applet contains the following +

      For example, suppose your application contains the following start, stop and run methods:

      @@ -92,12 +92,12 @@ 

      What should I use instead of Thread.stop?

      Thread.sleep(interval); } catch (InterruptedException e){ } - repaint(); + blink(); } }
      You can avoid the use of Thread.stop by replacing the -applet's stop and run methods with: +application's stop and run methods with:
           private volatile Thread blinker;
       
      @@ -112,7 +112,7 @@ 

      What should I use instead of Thread.stop?

      Thread.sleep(interval); } catch (InterruptedException e){ } - repaint(); + blink(); } }
      diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index 1f31301638e05..18419ba1877d8 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -128,49 +128,9 @@ * {@linkplain java.lang.foreign.SegmentAllocator#allocateFrom(java.lang.String) converting} * Java strings into zero-terminated, UTF-8 strings, as demonstrated in the above example. * - *

      Restricted methods

      - * - * Some methods in this package are considered restricted. Restricted methods - * are typically used to bind native foreign data and/or functions to first-class - * Java API elements which can then be used directly by clients. For instance the - * restricted method {@link java.lang.foreign.MemorySegment#reinterpret(long)} can be - * used to create a fresh segment with the same address and temporal bounds, but with - * the provided size. This can be useful to resize memory segments obtained when - * interacting with native functions. - *

      - * Binding foreign data and/or functions is generally unsafe and, if done incorrectly, - * can result in VM crashes, or memory corruption when the bound Java API element - * is accessed. For instance, incorrectly resizing a native memory segment using - * {@link java.lang.foreign.MemorySegment#reinterpret(long)} can lead to a JVM crash, or, - * worse, lead to silent memory corruption when attempting to access the resized segment. - * For these reasons, it is crucial for code that calls a restricted method to never pass - * arguments that might cause incorrect binding of foreign data and/or functions to - * a Java API. - *

      - * Given the potential danger of restricted methods, the Java runtime issues a warning on - * the standard error stream every time a restricted method is invoked. Such warnings can - * be disabled by granting access to restricted methods to selected modules. This can be - * done either via implementation-specific command line options or programmatically, e.g. - * by calling {@link java.lang.ModuleLayer.Controller#enableNativeAccess(java.lang.Module)}. - *

      - * For every class in this package, unless specified otherwise, any method arguments of - * reference type must not be {@code null}, and any null argument will elicit a - * {@code NullPointerException}. This fact is not individually documented for methods of - * this API. - * * @apiNote Usual memory model guarantees (see {@jls 17.4}) do not apply when accessing * native memory segments as these segments are backed by off-heap regions of memory. * - * @implNote - * In the reference implementation, access to restricted methods can be granted to - * specific modules using the command line option {@code --enable-native-access=M1,M2, ... Mn}, - * where {@code M1}, {@code M2}, {@code ... Mn} are module names (for the unnamed module, - * the special value {@code ALL-UNNAMED} can be used). Access to restricted methods - * from modules not listed by that option is deemed illegal. Clients can - * control how access to restricted methods is handled, using the command line - * option {@code --illegal-native-access}. If this option is not specified, - * illegal access to restricted methods will result in runtime warnings. - * * @spec jni/index.html Java Native Interface Specification * * @since 22 diff --git a/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java index 44bca025b8b6d..90da523dec831 100644 --- a/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java @@ -143,7 +143,7 @@ static LambdaForm makeReinvokerForm(MethodHandle target, final int PRE_ACTION = hasPreAction ? nameCursor++ : -1; final int NEXT_MH = customized ? -1 : nameCursor++; final int REINVOKE = nameCursor++; - LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + LambdaForm.Name[] names = LambdaForm.invokeArguments(nameCursor - ARG_LIMIT, mtype); assert(names.length == nameCursor); names[THIS_DMH] = names[THIS_DMH].withConstraint(constraint); Object[] targetArgs; diff --git a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java index 850ed2f53f91f..8316c867dc2c7 100644 --- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -250,11 +250,17 @@ static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) { default: throw new InternalError("which="+which); } - MethodType mtypeWithArg = mtype.appendParameterTypes(MemberName.class); - if (doesAlloc) - mtypeWithArg = mtypeWithArg - .insertParameterTypes(0, Object.class) // insert newly allocated obj - .changeReturnType(void.class); // returns void + MethodType mtypeWithArg; + if (doesAlloc) { + var ptypes = mtype.ptypes(); + var newPtypes = new Class[ptypes.length + 2]; + newPtypes[0] = Object.class; // insert newly allocated obj + System.arraycopy(ptypes, 0, newPtypes, 1, ptypes.length); + newPtypes[newPtypes.length - 1] = MemberName.class; + mtypeWithArg = MethodType.methodType(void.class, newPtypes, true); + } else { + mtypeWithArg = mtype.appendParameterTypes(MemberName.class); + } MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic); try { linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, LM_TRUSTED, @@ -270,7 +276,7 @@ static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) { final int GET_MEMBER = nameCursor++; final int CHECK_RECEIVER = (needsReceiverCheck ? nameCursor++ : -1); final int LINKER_CALL = nameCursor++; - Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, mtype); assert(names.length == nameCursor); if (doesAlloc) { // names = { argx,y,z,... new C, init method } @@ -786,7 +792,7 @@ static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, i final int LINKER_CALL = nameCursor++; final int POST_CAST = (needsCast && isGetter ? nameCursor++ : -1); final int RESULT = nameCursor-1; // either the call or the cast - Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, mtype); if (needsInit) names[INIT_BAR] = new Name(getFunction(NF_ensureInitialized), names[DMH_THIS]); if (needsCast && !isGetter) diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 92c02c433c524..10f282065fd8f 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -34,12 +34,10 @@ import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; -import java.lang.classfile.FieldBuilder; import java.lang.classfile.MethodBuilder; import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; import java.lang.constant.ClassDesc; -import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodTypeDesc; import java.lang.reflect.Modifier; import java.util.LinkedHashSet; @@ -51,16 +49,15 @@ import java.lang.classfile.attribute.ExceptionsAttribute; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPoolBuilder; -import java.lang.classfile.constantpool.MethodRefEntry; + import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandleNatives.Constants.NESTMATE_CLASS; import static java.lang.invoke.MethodHandleNatives.Constants.STRONG_LOADER_LINK; -import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; -import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; import static java.lang.invoke.MethodType.methodType; import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.MethodTypeDescImpl; import jdk.internal.constant.ReferenceClassDescImpl; +import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; /** @@ -71,7 +68,7 @@ */ /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$"; - private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final @Stable String[] ARG_NAME_CACHE = {"arg$1", "arg$2", "arg$3", "arg$4", "arg$5", "arg$6", "arg$7", "arg$8"}; private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC; // For dumping generated classes to disk, for debugging purposes @@ -96,7 +93,6 @@ private final MethodTypeDesc implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" private final MethodType constructorType; // Generated class constructor type "(CC)void" private final MethodTypeDesc constructorTypeDesc;// Type descriptor for the generated class constructor type "(CC)void" - private final String[] argNames; // Generated names for the constructor arguments private final ClassDesc[] argDescs; // Type descriptors for the constructor arguments private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" private final ConstantPoolBuilder pool = ConstantPoolBuilder.of(); @@ -174,18 +170,24 @@ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, implKind == MethodHandleInfo.REF_invokeSpecial || implKind == MethodHandleInfo.REF_invokeStatic && implClass.isHidden(); int parameterCount = factoryType.parameterCount(); + ClassDesc[] argDescs; + MethodTypeDesc constructorTypeDesc; if (parameterCount > 0) { - argNames = new String[parameterCount]; argDescs = new ClassDesc[parameterCount]; for (int i = 0; i < parameterCount; i++) { - argNames[i] = "arg$" + (i + 1); argDescs[i] = classDesc(factoryType.parameterType(i)); } + constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); } else { - argNames = EMPTY_STRING_ARRAY; argDescs = EMPTY_CLASSDESC_ARRAY; + constructorTypeDesc = MTD_void; } - constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); + this.argDescs = argDescs; + this.constructorTypeDesc = constructorTypeDesc; + } + + private static String argName(int i) { + return i < ARG_NAME_CACHE.length ? ARG_NAME_CACHE[i] : "arg$" + (i + 1); } private static String lambdaClassName(Class targetClass) { @@ -313,7 +315,7 @@ public void accept(ClassBuilder clb) { .withInterfaceSymbols(interfaces); // Generate final fields to be filled in by constructor for (int i = 0; i < argDescs.length; i++) { - clb.withField(argNames[i], argDescs[i], ACC_PRIVATE | ACC_FINAL); + clb.withField(argName(i), argDescs[i], ACC_PRIVATE | ACC_FINAL); } generateConstructor(clb); @@ -394,10 +396,9 @@ public void accept(CodeBuilder cob) { .invokespecial(CD_Object, INIT_NAME, MTD_void); int parameterCount = factoryType.parameterCount(); for (int i = 0; i < parameterCount; i++) { - cob.aload(0); - Class argType = factoryType.parameterType(i); - cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); - cob.putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); + cob.aload(0) + .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i)) + .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); } cob.return_(); } @@ -449,7 +450,7 @@ public void accept(CodeBuilder cob) { cob.dup() .loadConstant(i) .aload(0) - .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); + .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); cob.aastore(); } @@ -506,9 +507,9 @@ public void accept(CodeBuilder cob) { cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA), List.of()), cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle))); } - for (int i = 0; i < argNames.length; i++) { + for (int i = 0; i < argDescs.length; i++) { cob.aload(0) - .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); + .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); } convertArgumentTypes(cob, methodType); diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index 6031b55a107b2..5d307a9ff04f1 100644 --- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -891,10 +891,9 @@ private Name emitSelectAlternative(CodeBuilder cob, Name selectAlternativeName, emitStaticInvoke(cob, invokeBasicName); // goto L_done - cob.goto_w(L_done); - - // L_fallback: - cob.labelBinding(L_fallback); + cob.goto_w(L_done) + // L_fallback: + .labelBinding(L_fallback); // invoke selectAlternativeName.arguments[2] System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); @@ -945,26 +944,23 @@ private Name emitGuardWithCatch(CodeBuilder cob, int pos) { .dropParameterTypes(0,1) .changeReturnType(returnType); - cob.exceptionCatch(L_startBlock, L_endBlock, L_handler, CD_Throwable); - - // Normal case - cob.labelBinding(L_startBlock); + cob.exceptionCatch(L_startBlock, L_endBlock, L_handler, CD_Throwable) + // Normal case + .labelBinding(L_startBlock); // load target emitPushArgument(cob, invoker, 0); emitPushArguments(cob, args, 1); // skip 1st argument: method handle - cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); - cob.labelBinding(L_endBlock); - cob.goto_w(L_done); - - // Exceptional case - cob.labelBinding(L_handler); - - // Check exception's type - cob.dup(); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())) + .labelBinding(L_endBlock) + .goto_w(L_done) + // Exceptional case + .labelBinding(L_handler) + // Check exception's type + .dup(); // load exception class emitPushArgument(cob, invoker, 1); - cob.swap(); - cob.invokevirtual(CD_Class, "isInstance", MTD_boolean_Object); + cob.swap() + .invokevirtual(CD_Class, "isInstance", MTD_boolean_Object); Label L_rethrow = cob.newLabel(); cob.ifeq(L_rethrow); @@ -974,13 +970,11 @@ private Name emitGuardWithCatch(CodeBuilder cob, int pos) { cob.swap(); emitPushArguments(cob, args, 1); // skip 1st argument: method handle MethodType catcherType = type.insertParameterTypes(0, Throwable.class); - cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(catcherType.basicType())); - cob.goto_w(L_done); - - cob.labelBinding(L_rethrow); - cob.athrow(); - - cob.labelBinding(L_done); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(catcherType.basicType())) + .goto_w(L_done) + .labelBinding(L_rethrow) + .athrow() + .labelBinding(L_done); return result; } @@ -1075,8 +1069,8 @@ private Name emitTryFinally(CodeBuilder cob, int pos) { cob.labelBinding(lFrom); emitPushArgument(cob, invoker, 0); // load target emitPushArguments(cob, args, 1); // load args (skip 0: method handle) - cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); - cob.labelBinding(lTo); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())) + .labelBinding(lTo); // FINALLY_NORMAL: int index = extendLocalsMap(new Class[]{ returnType }); @@ -1084,17 +1078,16 @@ private Name emitTryFinally(CodeBuilder cob, int pos) { emitStoreInsn(cob, basicReturnType.basicTypeKind(), index); } emitPushArgument(cob, invoker, 1); // load cleanup - cob.loadConstant(null); + cob.aconst_null(); if (isNonVoid) { emitLoadInsn(cob, basicReturnType.basicTypeKind(), index); } emitPushArguments(cob, args, 1); // load args (skip 0: method handle) - cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc); - cob.goto_w(lDone); - - // CATCH: - cob.labelBinding(lCatch); - cob.dup(); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc) + .goto_w(lDone) + // CATCH: + .labelBinding(lCatch) + .dup(); // FINALLY_EXCEPTIONAL: emitPushArgument(cob, invoker, 1); // load cleanup @@ -1107,10 +1100,9 @@ private Name emitTryFinally(CodeBuilder cob, int pos) { if (isNonVoid) { emitPopInsn(cob, basicReturnType); } - cob.athrow(); - - // DONE: - cob.labelBinding(lDone); + cob.athrow() + // DONE: + .labelBinding(lDone); return result; } @@ -1147,26 +1139,24 @@ private Name emitTableSwitch(CodeBuilder cob, int pos, int numCases) { } emitPushArgument(cob, invoker, 0); // push switch input - cob.tableswitch(0, numCases - 1, defaultLabel, cases); - - cob.labelBinding(defaultLabel); + cob.tableswitch(0, numCases - 1, defaultLabel, cases) + .labelBinding(defaultLabel); emitPushArgument(cob, invoker, 1); // push default handle emitPushArguments(cob, args, 1); // again, skip collector - cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); - cob.goto_(endLabel); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor) + .goto_(endLabel); for (int i = 0; i < numCases; i++) { cob.labelBinding(cases.get(i).target()); // Load the particular case: emitLoadInsn(cob, TypeKind.REFERENCE, casesLocal); - cob.loadConstant(i); - cob.aaload(); + cob.loadConstant(i) + .aaload(); // invoke it: emitPushArguments(cob, args, 1); // again, skip collector - cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); - - cob.goto_(endLabel); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor) + .goto_(endLabel); } cob.labelBinding(endLabel); @@ -1335,16 +1325,14 @@ private Name emitLoop(CodeBuilder cob, int pos) { // invoke fini emitLoopHandleInvoke(cob, invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); - cob.goto_w(lDone); - - // this is the beginning of the next loop clause - cob.labelBinding(lNext); + cob.goto_w(lDone) + // this is the beginning of the next loop clause + .labelBinding(lNext); } - cob.goto_w(lLoop); - - // DONE: - cob.labelBinding(lDone); + cob.goto_w(lLoop) + // DONE: + .labelBinding(lDone); return result; } @@ -1370,8 +1358,8 @@ private void emitLoopHandleInvoke(CodeBuilder cob, Name holder, int handles, int int firstLoopStateSlot) { // load handle for clause emitPushClauseArray(cob, clauseDataSlot, handles); - cob.loadConstant(clause); - cob.aaload(); + cob.loadConstant(clause) + .aaload(); // load loop state (preceding the other arguments) if (pushLocalState) { for (int s = 0; s < loopLocalStateTypes.length; ++s) { @@ -1385,8 +1373,8 @@ private void emitLoopHandleInvoke(CodeBuilder cob, Name holder, int handles, int private void emitPushClauseArray(CodeBuilder cob, int clauseDataSlot, int which) { emitLoadInsn(cob, TypeKind.REFERENCE, clauseDataSlot); - cob.loadConstant(which - 1); - cob.aaload(); + cob.loadConstant(which - 1) + .aaload(); } private void emitZero(CodeBuilder cob, BasicType type) { @@ -1519,14 +1507,14 @@ public void accept(MethodBuilder mb) { @Override public void accept(CodeBuilder cob) { // create parameter array - cob.loadConstant(invokerType.parameterCount()); - cob.anewarray(CD_Object); + cob.loadConstant(invokerType.parameterCount()) + .anewarray(CD_Object); // fill parameter array for (int i = 0; i < invokerType.parameterCount(); i++) { Class ptype = invokerType.parameterType(i); - cob.dup(); - cob.loadConstant(i); + cob.dup() + .loadConstant(i); emitLoadInsn(cob, basicType(ptype).basicTypeKind(), i); // box if primitive type if (ptype.isPrimitive()) { @@ -1535,10 +1523,10 @@ public void accept(CodeBuilder cob) { cob.aastore(); } // invoke - cob.aload(0); - cob.getfield(CD_MethodHandle, "form", CD_LambdaForm); - cob.swap(); // swap form and array; avoid local variable - cob.invokevirtual(CD_LambdaForm, "interpretWithArguments", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object_array)); + cob.aload(0) + .getfield(CD_MethodHandle, "form", CD_LambdaForm) + .swap() // swap form and array; avoid local variable + .invokevirtual(CD_LambdaForm, "interpretWithArguments", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object_array)); // maybe unbox Class rtype = invokerType.returnType(); @@ -1592,9 +1580,9 @@ public void accept(CodeBuilder cob) { // Load arguments from array for (int i = 0; i < dstType.parameterCount(); i++) { - cob.aload(1); - cob.loadConstant(i); - cob.aaload(); + cob.aload(1) + .loadConstant(i) + .aaload(); // Maybe unbox Class dptype = dstType.parameterType(i); @@ -1645,9 +1633,9 @@ private void bogusMethod(ClassBuilder clb, Object os) { clb.withMethodBody("dummy", MTD_void, ACC_STATIC, new Consumer<>() { @Override public void accept(CodeBuilder cob) { - cob.loadConstant(os.toString()); - cob.pop(); - cob.return_(); + cob.ldc(os.toString()) + .pop() + .return_(); } }); } diff --git a/src/java.base/share/classes/java/lang/invoke/Invokers.java b/src/java.base/share/classes/java/lang/invoke/Invokers.java index 6c383dd3f2ca3..9990370ae25b6 100644 --- a/src/java.base/share/classes/java/lang/invoke/Invokers.java +++ b/src/java.base/share/classes/java/lang/invoke/Invokers.java @@ -25,6 +25,7 @@ package java.lang.invoke; +import jdk.internal.invoke.MhUtil; import jdk.internal.vm.annotation.DontInline; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Hidden; @@ -314,14 +315,14 @@ static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int whi final int CHECK_TYPE = nameCursor++; final int CHECK_CUSTOM = (CUSTOMIZE_THRESHOLD >= 0) ? nameCursor++ : -1; final int LINKER_CALL = nameCursor++; - MethodType invokerFormType = mtype.invokerType(); + MethodType invokerFormType = mtype; if (isLinker) { if (!customized) invokerFormType = invokerFormType.appendParameterTypes(MemberName.class); } else { invokerFormType = invokerFormType.invokerType(); } - Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType); + Name[] names = invokeArguments(nameCursor - INARG_LIMIT, invokerFormType); assert(names.length == nameCursor) : Arrays.asList(mtype, customized, which, nameCursor, names.length); if (MTYPE_ARG >= INARG_LIMIT) { @@ -390,11 +391,11 @@ private static LambdaForm varHandleMethodGenericLinkerHandleForm(MethodType mtyp final int LINKER_CALL = nameCursor++; Name[] names = new Name[LINKER_CALL + 1]; - names[THIS_VH] = argument(THIS_VH, BasicType.basicType(Object.class)); + names[THIS_VH] = argument(THIS_VH, BasicType.L_TYPE); for (int i = 0; i < mtype.parameterCount(); i++) { names[ARG_BASE + i] = argument(ARG_BASE + i, BasicType.basicType(mtype.parameterType(i))); } - names[VAD_ARG] = new Name(ARG_LIMIT, BasicType.basicType(Object.class)); + names[VAD_ARG] = new Name(ARG_LIMIT, BasicType.L_TYPE); names[UNBOUND_VH] = new Name(getFunction(NF_directVarHandleTarget), names[THIS_VH]); @@ -446,8 +447,8 @@ private static LambdaForm varHandleMethodInvokerHandleForm(MethodType mtype, boo final int LINKER_CALL = nameCursor++; Name[] names = new Name[LINKER_CALL + 1]; - names[THIS_MH] = argument(THIS_MH, BasicType.basicType(Object.class)); - names[CALL_VH] = argument(CALL_VH, BasicType.basicType(Object.class)); + names[THIS_MH] = argument(THIS_MH, BasicType.L_TYPE); + names[CALL_VH] = argument(CALL_VH, BasicType.L_TYPE); for (int i = 0; i < mtype.parameterCount(); i++) { names[ARG_BASE + i] = argument(ARG_BASE + i, BasicType.basicType(mtype.parameterType(i))); } @@ -589,17 +590,16 @@ static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) { final int CSITE_ARG = skipCallSite ? -1 : APPENDIX_ARG; final int CALL_MH = skipCallSite ? APPENDIX_ARG : nameCursor++; // result of getTarget final int LINKER_CALL = nameCursor++; - MethodType invokerFormType = mtype.appendParameterTypes(skipCallSite ? MethodHandle.class : CallSite.class); - Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType); - assert(names.length == nameCursor); - assert(names[APPENDIX_ARG] != null); + Name[] names = arguments(nameCursor - INARG_LIMIT + 1, mtype); + assert(names.length == nameCursor && names[APPENDIX_ARG] == null); + names[APPENDIX_ARG] = argument(APPENDIX_ARG, BasicType.L_TYPE); if (!skipCallSite) names[CALL_MH] = new Name(getFunction(NF_getCallSiteTarget), names[CSITE_ARG]); // (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*) final int PREPEND_MH = 0, PREPEND_COUNT = 1; - Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, OUTARG_LIMIT + PREPEND_COUNT, Object[].class); + Object[] outArgs = new Object[OUTARG_LIMIT + PREPEND_COUNT]; + System.arraycopy(names, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT); // prepend MH argument: - System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT); outArgs[PREPEND_MH] = names[CALL_MH]; names[LINKER_CALL] = new Name(mtype, outArgs); lform = LambdaForm.create(INARG_LIMIT, names, @@ -682,16 +682,9 @@ private static NamedFunction getNamedFunction(String name, MethodType type) } private static class Lazy { - private static final MethodHandle MH_asSpreader; - - static { - try { - MH_asSpreader = IMPL_LOOKUP.findVirtual(MethodHandle.class, "asSpreader", - MethodType.methodType(MethodHandle.class, Class.class, int.class)); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } + private static final MethodHandle MH_asSpreader = MhUtil.findVirtual( + IMPL_LOOKUP, MethodHandle.class, "asSpreader", + MethodType.methodType(MethodHandle.class, Class.class, int.class)); } static { diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java index 0132cc6edf589..7109f35f069ce 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -1636,6 +1636,16 @@ static Name[] arguments(int extra, MethodType types) { names[i] = argument(i, basicType(types.parameterType(i))); return names; } + + static Name[] invokeArguments(int extra, MethodType types) { + int length = types.parameterCount(); + Name[] names = new Name[length + extra + 1]; + names[0] = argument(0, L_TYPE); + for (int i = 0; i < length; i++) + names[i + 1] = argument(i + 1, basicType(types.parameterType(i))); + return names; + } + static final int INTERNED_ARGUMENT_LIMIT = 10; private static final Name[][] INTERNED_ARGUMENTS = new Name[ARG_TYPE_LIMIT][INTERNED_ARGUMENT_LIMIT]; diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java index edcecce37e05d..d327060070741 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, 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 @@ -27,6 +27,8 @@ import jdk.internal.loader.ClassLoaders; +import jdk.internal.vm.annotation.DontInline; +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import java.lang.constant.ClassDesc; @@ -856,6 +858,7 @@ public Object invokeWithArguments(java.util.List arguments) throws Throwable * @throws WrongMethodTypeException if the conversion cannot be made * @see MethodHandles#explicitCastArguments */ + @ForceInline public final MethodHandle asType(MethodType newType) { // Fast path alternative to a heavyweight {@code asType} call. // Return 'this' if the conversion will be a no-op. @@ -867,7 +870,7 @@ public final MethodHandle asType(MethodType newType) { if (at != null) { return at; } - return setAsTypeCache(asTypeUncached(newType)); + return setAsTypeCache(newType); } private MethodHandle asTypeCached(MethodType newType) { @@ -885,7 +888,16 @@ private MethodHandle asTypeCached(MethodType newType) { return null; } - private MethodHandle setAsTypeCache(MethodHandle at) { + /* + * We disable inlining here to prevent complex code in the slow path + * of MethodHandle::asType from being inlined into that method. + * Excessive inlining into MethodHandle::asType can cause that method + * to become too big, which will then cause performance issues during + * var handle and method handle calls. + */ + @DontInline + private MethodHandle setAsTypeCache(MethodType newType) { + MethodHandle at = asTypeUncached(newType); // Don't introduce a strong reference in the cache if newType depends on any class loader other than // current method handle already does to avoid class loader leaks. if (isSafeToCache(at.type)) { @@ -1879,8 +1891,7 @@ void updateForm(Function updater) { if (oldForm != newForm) { assert (newForm.customized == null || newForm.customized == this); newForm.prepare(); // as in MethodHandle. - UNSAFE.putReference(this, FORM_OFFSET, newForm); - UNSAFE.fullFence(); + UNSAFE.putReferenceRelease(this, FORM_OFFSET, newForm); // properly publish newForm } } finally { updateInProgress = false; diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index f17bbd17c9467..992ef38768484 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -810,8 +810,7 @@ static LambdaForm makeGuardWithTestForm(MethodType basicType) { final int CALL_TARGET = nameCursor++; assert(CALL_TARGET == SELECT_ALT+1); // must be true to trigger IBG.emitSelectAlternative - MethodType lambdaType = basicType.invokerType(); - Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, basicType); BoundMethodHandle.SpeciesData data = (GET_COUNTERS != -1) ? BoundMethodHandle.speciesData_LLLL() @@ -843,7 +842,7 @@ static LambdaForm makeGuardWithTestForm(MethodType basicType) { invokeArgs[0] = names[SELECT_ALT]; names[CALL_TARGET] = new Name(basicType, invokeArgs); - lform = LambdaForm.create(lambdaType.parameterCount(), names, /*forceInline=*/true, Kind.GUARD); + lform = LambdaForm.create(basicType.parameterCount() + 1, names, /*forceInline=*/true, Kind.GUARD); return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWT, lform); } @@ -870,8 +869,6 @@ static LambdaForm makeGuardWithTestForm(MethodType basicType) { * among catchException combinators with the same basic type. */ private static LambdaForm makeGuardWithCatchForm(MethodType basicType) { - MethodType lambdaType = basicType.invokerType(); - LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWC); if (lform != null) { return lform; @@ -890,7 +887,7 @@ private static LambdaForm makeGuardWithCatchForm(MethodType basicType) { final int TRY_CATCH = nameCursor++; final int UNBOX_RESULT = nameCursor++; - Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, basicType); BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); @@ -919,7 +916,7 @@ private static LambdaForm makeGuardWithCatchForm(MethodType basicType) { Object[] unboxArgs = new Object[] {names[GET_UNBOX_RESULT], names[TRY_CATCH]}; names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs); - lform = LambdaForm.create(lambdaType.parameterCount(), names, Kind.GUARD_WITH_CATCH); + lform = LambdaForm.create(basicType.parameterCount() + 1, names, Kind.GUARD_WITH_CATCH); return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWC, lform); } @@ -1733,8 +1730,6 @@ private static MethodHandle[] toArray(List l) { * bytecode generation}. */ private static LambdaForm makeLoopForm(MethodType basicType, BasicType[] localVarTypes) { - MethodType lambdaType = basicType.invokerType(); - final int THIS_MH = 0; // the BMH_LLL final int ARG_BASE = 1; // start of incoming arguments final int ARG_LIMIT = ARG_BASE + basicType.parameterCount(); @@ -1749,7 +1744,7 @@ private static LambdaForm makeLoopForm(MethodType basicType, BasicType[] localVa LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_LOOP); if (lform == null) { - Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, basicType); BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); @@ -1777,7 +1772,7 @@ private static LambdaForm makeLoopForm(MethodType basicType, BasicType[] localVa names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs); lform = basicType.form().setCachedLambdaForm(MethodTypeForm.LF_LOOP, - LambdaForm.create(lambdaType.parameterCount(), names, Kind.LOOP)); + LambdaForm.create(basicType.parameterCount() + 1, names, Kind.LOOP)); } // BOXED_ARGS is the index into the names array where the loop idiom starts @@ -1966,8 +1961,6 @@ static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Cl * forms among tryFinally combinators with the same basic type. */ private static LambdaForm makeTryFinallyForm(MethodType basicType) { - MethodType lambdaType = basicType.invokerType(); - LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_TF); if (lform != null) { return lform; @@ -1985,7 +1978,7 @@ private static LambdaForm makeTryFinallyForm(MethodType basicType) { final int TRY_FINALLY = nameCursor++; final int UNBOX_RESULT = nameCursor++; - Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, basicType); BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); @@ -2011,7 +2004,7 @@ private static LambdaForm makeTryFinallyForm(MethodType basicType) { Object[] unboxArgs = new Object[] {names[GET_UNBOX_RESULT], names[TRY_FINALLY]}; names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs); - lform = LambdaForm.create(lambdaType.parameterCount(), names, Kind.TRY_FINALLY); + lform = LambdaForm.create(basicType.parameterCount() + 1, names, Kind.TRY_FINALLY); return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_TF, lform); } @@ -2055,7 +2048,6 @@ private static MethodHandle makeCollector(Class arrayType, int parameterCount } private static LambdaForm makeCollectorForm(MethodType basicType, Class arrayType) { - MethodType lambdaType = basicType.invokerType(); int parameterCount = basicType.parameterCount(); // Only share the lambda form for empty arrays and reference types. @@ -2088,7 +2080,7 @@ private static LambdaForm makeCollectorForm(MethodType basicType, Class array final int STORE_ELEMENT_LIMIT = STORE_ELEMENT_BASE + parameterCount; nameCursor = STORE_ELEMENT_LIMIT; - Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, basicType); BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_L(); names[THIS_MH] = names[THIS_MH].withConstraint(data); @@ -2106,7 +2098,7 @@ private static LambdaForm makeCollectorForm(MethodType basicType, Class array names[CALL_NEW_ARRAY], storeIndex, names[argCursor]); } - LambdaForm lform = LambdaForm.create(lambdaType.parameterCount(), names, CALL_NEW_ARRAY, Kind.COLLECTOR); + LambdaForm lform = LambdaForm.create(basicType.parameterCount() + 1, names, CALL_NEW_ARRAY, Kind.COLLECTOR); if (isSharedLambdaForm) { lform = basicType.form().setCachedLambdaForm(MethodTypeForm.LF_COLLECTOR, lform); } @@ -2169,8 +2161,6 @@ public int hashCode() { private static LambdaForm makeTableSwitchForm(MethodType basicType, BoundMethodHandle.SpeciesData data, int numCases) { - MethodType lambdaType = basicType.invokerType(); - // We need to cache based on the basic type X number of cases, // since the number of cases is used when generating bytecode. // This also means that we can't use the cache in MethodTypeForm, @@ -2202,7 +2192,7 @@ private static LambdaForm makeTableSwitchForm(MethodType basicType, BoundMethodH final int FIELD_UNBOX_RESULT = fieldCursor++; final int FIELD_CASES = fieldCursor++; - Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + Name[] names = invokeArguments(nameCursor - ARG_LIMIT, basicType); names[THIS_MH] = names[THIS_MH].withConstraint(data); names[GET_DEFAULT_CASE] = new Name(data.getterFunction(FIELD_DEFAULT_CASE), names[THIS_MH]); @@ -2231,7 +2221,7 @@ private static LambdaForm makeTableSwitchForm(MethodType basicType, BoundMethodH names[UNBOXED_RESULT] = new Name(invokeBasic, unboxArgs); } - lform = LambdaForm.create(lambdaType.parameterCount(), names, Kind.TABLE_SWITCH); + lform = LambdaForm.create(basicType.parameterCount() + 1, names, Kind.TABLE_SWITCH); LambdaForm prev = TableSwitchCacheKey.CACHE.putIfAbsent(key, lform); return prev != null ? prev : lform; } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index c6a5c2763f4a6..9709c881863ef 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -54,6 +54,7 @@ import java.lang.classfile.TypeKind; import jdk.internal.constant.ConstantUtils; +import jdk.internal.loader.ClassLoaders; import jdk.internal.module.Modules; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -371,48 +372,46 @@ private static Class getProxyClass(Class intfc) { */ private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, ClassDesc ifaceDesc, String methodName, List methods) { - return ClassFile.of(ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(loader))) + return ClassFile.of(ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(loader == null ? + ClassLoaders.platformClassLoader() : loader))) .build(proxyDesc, clb -> { - clb.withSuperclass(CD_Object); - clb.withFlags(ACC_FINAL | ACC_SYNTHETIC); - clb.withInterfaceSymbols(ifaceDesc); - - // static and instance fields - clb.withField(TYPE_NAME, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); - clb.withField(TARGET_NAME, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); + clb.withSuperclass(CD_Object) + .withFlags(ACC_FINAL | ACC_SYNTHETIC) + .withInterfaceSymbols(ifaceDesc) + // static and instance fields + .withField(TYPE_NAME, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL) + .withField(TARGET_NAME, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); for (var mi : methods) { clb.withField(mi.fieldName, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); } // clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> { - cob.loadConstant(ifaceDesc); - cob.putstatic(proxyDesc, TYPE_NAME, CD_Class); - cob.return_(); + cob.loadConstant(ifaceDesc) + .putstatic(proxyDesc, TYPE_NAME, CD_Class) + .return_(); }); // (Lookup, MethodHandle target, MethodHandle callerBoundTarget) clb.withMethodBody(INIT_NAME, MTD_void_Lookup_MethodHandle_MethodHandle, 0, cob -> { - cob.aload(0); - cob.invokespecial(CD_Object, INIT_NAME, MTD_void); - - // call ensureOriginalLookup to verify the given Lookup has access - cob.aload(1); - cob.invokestatic(proxyDesc, "ensureOriginalLookup", MTD_void_Lookup); - - // this.target = target; - cob.aload(0); - cob.aload(2); - cob.putfield(proxyDesc, TARGET_NAME, CD_MethodHandle); + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void) + // call ensureOriginalLookup to verify the given Lookup has access + .aload(1) + .invokestatic(proxyDesc, ENSURE_ORIGINAL_LOOKUP, MTD_void_Lookup) + // this.target = target; + .aload(0) + .aload(2) + .putfield(proxyDesc, TARGET_NAME, CD_MethodHandle); // method handles adjusted to the method type of each method for (var mi : methods) { // this.m = callerBoundTarget.asType(xxType); - cob.aload(0); - cob.aload(3); - cob.loadConstant(mi.desc); - cob.invokevirtual(CD_MethodHandle, "asType", MTD_MethodHandle_MethodType); - cob.putfield(proxyDesc, mi.fieldName, CD_MethodHandle); + cob.aload(0) + .aload(3) + .loadConstant(mi.desc) + .invokevirtual(CD_MethodHandle, "asType", MTD_MethodHandle_MethodType) + .putfield(proxyDesc, mi.fieldName, CD_MethodHandle); } // complete @@ -425,26 +424,26 @@ private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, Cl clb.withMethodBody(ENSURE_ORIGINAL_LOOKUP, MTD_void_Lookup, ACC_PRIVATE | ACC_STATIC, cob -> { var failLabel = cob.newLabel(); // check lookupClass - cob.aload(0); - cob.invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class); - cob.loadConstant(proxyDesc); - cob.if_acmpne(failLabel); - // check original access - cob.aload(0); - cob.invokevirtual(CD_MethodHandles_Lookup, "lookupModes", MTD_int); - cob.loadConstant(Lookup.ORIGINAL); - cob.iand(); - cob.ifeq(failLabel); - // success - cob.return_(); - // throw exception - cob.labelBinding(failLabel); - cob.new_(CD_IllegalAccessException); - cob.dup(); - cob.aload(0); // lookup - cob.invokevirtual(CD_Object, "toString", MTD_String); - cob.invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String); - cob.athrow(); + cob.aload(0) + .invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class) + .loadConstant(proxyDesc) + .if_acmpne(failLabel) + // check original access + .aload(0) + .invokevirtual(CD_MethodHandles_Lookup, "lookupModes", MTD_int) + .loadConstant(Lookup.ORIGINAL) + .iand() + .ifeq(failLabel) + // success + .return_() + // throw exception + .labelBinding(failLabel) + .new_(CD_IllegalAccessException) + .dup() + .aload(0) // lookup + .invokevirtual(CD_Object, "toString", MTD_String) + .invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String) + .athrow(); }); // implementation methods @@ -453,14 +452,14 @@ private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, Cl clb.withMethodBody(methodName, mi.desc, ACC_PUBLIC, cob -> cob .trying(bcb -> { // return this.handleField.invokeExact(arguments...); - bcb.aload(0); - bcb.getfield(proxyDesc, mi.fieldName, CD_MethodHandle); + bcb.aload(0) + .getfield(proxyDesc, mi.fieldName, CD_MethodHandle); for (int j = 0; j < mi.desc.parameterCount(); j++) { bcb.loadLocal(TypeKind.from(mi.desc.parameterType(j)), bcb.parameterSlot(j)); } - bcb.invokevirtual(CD_MethodHandle, "invokeExact", mi.desc); - bcb.return_(TypeKind.from(mi.desc.returnType())); + bcb.invokevirtual(CD_MethodHandle, "invokeExact", mi.desc) + .return_(TypeKind.from(mi.desc.returnType())); }, ctb -> ctb // catch (Error | RuntimeException | Declared ex) { throw ex; } .catchingMulti(mi.thrown, CodeBuilder::athrow) diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 6a73266ae80a4..9e292373f9caf 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -39,7 +39,9 @@ import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; +import java.lang.classfile.ClassFile; import java.lang.classfile.ClassModel; +import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.invoke.LambdaForm.BasicType; import java.lang.invoke.MethodHandleImpl.Intrinsic; @@ -2242,85 +2244,70 @@ private static ClassFileDumper defaultDumper() { private static final ClassFileDumper DEFAULT_DUMPER = ClassFileDumper.getInstance( "jdk.invoke.MethodHandle.dumpClassFiles", "DUMP_CLASS_FILES"); - static class ClassFile { - final String name; // internal name - final int accessFlags; - final byte[] bytes; - ClassFile(String name, int accessFlags, byte[] bytes) { - this.name = name; - this.accessFlags = accessFlags; - this.bytes = bytes; + /** + * This method checks the class file version and the structure of `this_class`. + * and checks if the bytes is a class or interface (ACC_MODULE flag not set) + * that is in the named package. + * + * @throws IllegalArgumentException if ACC_MODULE flag is set in access flags + * or the class is not in the given package name. + */ + static String validateAndFindInternalName(byte[] bytes, String pkgName) { + int magic = readInt(bytes, 0); + if (magic != ClassFile.MAGIC_NUMBER) { + throw new ClassFormatError("Incompatible magic value: " + magic); } + // We have to read major and minor this way as ClassFile API throws IAE + // yet we want distinct ClassFormatError and UnsupportedClassVersionError + int minor = readUnsignedShort(bytes, 4); + int major = readUnsignedShort(bytes, 6); - static ClassFile newInstanceNoCheck(String name, byte[] bytes) { - return new ClassFile(name, 0, bytes); + if (!VM.isSupportedClassFileVersion(major, minor)) { + throw new UnsupportedClassVersionError("Unsupported class file version " + major + "." + minor); } - /** - * This method checks the class file version and the structure of `this_class`. - * and checks if the bytes is a class or interface (ACC_MODULE flag not set) - * that is in the named package. - * - * @throws IllegalArgumentException if ACC_MODULE flag is set in access flags - * or the class is not in the given package name. - */ - static ClassFile newInstance(byte[] bytes, String pkgName) { - var cf = readClassFile(bytes); - - // check if it's in the named package - int index = cf.name.lastIndexOf('/'); - String pn = (index == -1) ? "" : cf.name.substring(0, index).replace('/', '.'); - if (!pn.equals(pkgName)) { - throw newIllegalArgumentException(cf.name + " not in same package as lookup class"); - } - return cf; + String name; + ClassDesc sym; + int accessFlags; + try { + ClassModel cm = ClassFile.of().parse(bytes); + var thisClass = cm.thisClass(); + name = thisClass.asInternalName(); + sym = thisClass.asSymbol(); + accessFlags = cm.flags().flagsMask(); + } catch (IllegalArgumentException e) { + ClassFormatError cfe = new ClassFormatError(); + cfe.initCause(e); + throw cfe; + } + // must be a class or interface + if ((accessFlags & ACC_MODULE) != 0) { + throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set"); } - private static ClassFile readClassFile(byte[] bytes) { - int magic = readInt(bytes, 0); - if (magic != 0xCAFEBABE) { - throw new ClassFormatError("Incompatible magic value: " + magic); - } - int minor = readUnsignedShort(bytes, 4); - int major = readUnsignedShort(bytes, 6); - if (!VM.isSupportedClassFileVersion(major, minor)) { - throw new UnsupportedClassVersionError("Unsupported class file version " + major + "." + minor); - } - - String name; - int accessFlags; - try { - ClassModel cm = java.lang.classfile.ClassFile.of().parse(bytes); - name = cm.thisClass().asInternalName(); - accessFlags = cm.flags().flagsMask(); - } catch (IllegalArgumentException e) { - ClassFormatError cfe = new ClassFormatError(); - cfe.initCause(e); - throw cfe; - } - // must be a class or interface - if ((accessFlags & ACC_MODULE) != 0) { - throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set"); - } - return new ClassFile(name, accessFlags, bytes); + String pn = sym.packageName(); + if (!pn.equals(pkgName)) { + throw newIllegalArgumentException(name + " not in same package as lookup class"); } - private static int readInt(byte[] bytes, int offset) { - if ((offset+4) > bytes.length) { - throw new ClassFormatError("Invalid ClassFile structure"); - } - return ((bytes[offset] & 0xFF) << 24) - | ((bytes[offset + 1] & 0xFF) << 16) - | ((bytes[offset + 2] & 0xFF) << 8) - | (bytes[offset + 3] & 0xFF); + return name; + } + + private static int readInt(byte[] bytes, int offset) { + if ((offset + 4) > bytes.length) { + throw new ClassFormatError("Invalid ClassFile structure"); } + return ((bytes[offset] & 0xFF) << 24) + | ((bytes[offset + 1] & 0xFF) << 16) + | ((bytes[offset + 2] & 0xFF) << 8) + | (bytes[offset + 3] & 0xFF); + } - private static int readUnsignedShort(byte[] bytes, int offset) { - if ((offset+2) > bytes.length) { - throw new ClassFormatError("Invalid ClassFile structure"); - } - return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF); + private static int readUnsignedShort(byte[] bytes, int offset) { + if ((offset+2) > bytes.length) { + throw new ClassFormatError("Invalid ClassFile structure"); } + return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF); } /* @@ -2334,23 +2321,22 @@ private static int readUnsignedShort(byte[] bytes, int offset) { * {@code bytes} denotes a class in a different package than the lookup class */ private ClassDefiner makeClassDefiner(byte[] bytes) { - ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName()); - return new ClassDefiner(this, cf, STRONG_LOADER_LINK, defaultDumper()); + var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName()); + return new ClassDefiner(this, internalName, bytes, STRONG_LOADER_LINK, defaultDumper()); } /** * Returns a ClassDefiner that creates a {@code Class} object of a normal class * from the given bytes. No package name check on the given bytes. * - * @param name internal name + * @param internalName internal name * @param bytes class bytes * @param dumper dumper to write the given bytes to the dumper's output directory * @return ClassDefiner that defines a normal class of the given bytes. */ - ClassDefiner makeClassDefiner(String name, byte[] bytes, ClassFileDumper dumper) { + ClassDefiner makeClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper) { // skip package name validation - ClassFile cf = ClassFile.newInstanceNoCheck(name, bytes); - return new ClassDefiner(this, cf, STRONG_LOADER_LINK, dumper); + return new ClassDefiner(this, internalName, bytes, STRONG_LOADER_LINK, dumper); } /** @@ -2368,8 +2354,8 @@ ClassDefiner makeClassDefiner(String name, byte[] bytes, ClassFileDumper dumper) * {@code bytes} denotes a class in a different package than the lookup class */ ClassDefiner makeHiddenClassDefiner(byte[] bytes, ClassFileDumper dumper) { - ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName()); - return makeHiddenClassDefiner(cf, false, dumper, 0); + var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName()); + return makeHiddenClassDefiner(internalName, bytes, false, dumper, 0); } /** @@ -2391,51 +2377,53 @@ ClassDefiner makeHiddenClassDefiner(byte[] bytes, ClassFileDumper dumper) { private ClassDefiner makeHiddenClassDefiner(byte[] bytes, boolean accessVmAnnotations, int flags) { - ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName()); - return makeHiddenClassDefiner(cf, accessVmAnnotations, defaultDumper(), flags); + var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName()); + return makeHiddenClassDefiner(internalName, bytes, accessVmAnnotations, defaultDumper(), flags); } /** * Returns a ClassDefiner that creates a {@code Class} object of a hidden class * from the given bytes and the given options. No package name check on the given bytes. * - * @param name internal name that specifies the prefix of the hidden class + * @param internalName internal name that specifies the prefix of the hidden class * @param bytes class bytes * @param dumper dumper to write the given bytes to the dumper's output directory * @return ClassDefiner that defines a hidden class of the given bytes and options. */ - ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, ClassFileDumper dumper) { + ClassDefiner makeHiddenClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper) { Objects.requireNonNull(dumper); // skip name and access flags validation - return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), false, dumper, 0); + return makeHiddenClassDefiner(internalName, bytes, false, dumper, 0); } /** * Returns a ClassDefiner that creates a {@code Class} object of a hidden class * from the given bytes and the given options. No package name check on the given bytes. * - * @param name internal name that specifies the prefix of the hidden class + * @param internalName internal name that specifies the prefix of the hidden class * @param bytes class bytes * @param flags class options flag mask * @param dumper dumper to write the given bytes to the dumper's output directory * @return ClassDefiner that defines a hidden class of the given bytes and options. */ - ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, ClassFileDumper dumper, int flags) { + ClassDefiner makeHiddenClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper, int flags) { Objects.requireNonNull(dumper); // skip name and access flags validation - return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), false, dumper, flags); + return makeHiddenClassDefiner(internalName, bytes, false, dumper, flags); } /** * Returns a ClassDefiner that creates a {@code Class} object of a hidden class * from the given class file and options. * - * @param cf ClassFile + * @param internalName internal name + * @param bytes Class byte array * @param flags class option flag mask * @param accessVmAnnotations true to give the hidden class access to VM annotations * @param dumper dumper to write the given bytes to the dumper's output directory */ - private ClassDefiner makeHiddenClassDefiner(ClassFile cf, + private ClassDefiner makeHiddenClassDefiner(String internalName, + byte[] bytes, boolean accessVmAnnotations, ClassFileDumper dumper, int flags) { @@ -2446,27 +2434,12 @@ private ClassDefiner makeHiddenClassDefiner(ClassFile cf, flags |= ACCESS_VM_ANNOTATIONS; } - return new ClassDefiner(this, cf, flags, dumper); + return new ClassDefiner(this, internalName, bytes, flags, dumper); } - static class ClassDefiner { - private final Lookup lookup; - private final String name; // internal name - private final byte[] bytes; - private final int classFlags; - private final ClassFileDumper dumper; - - private ClassDefiner(Lookup lookup, ClassFile cf, int flags, ClassFileDumper dumper) { - assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK); - this.lookup = lookup; - this.bytes = cf.bytes; - this.name = cf.name; - this.classFlags = flags; - this.dumper = dumper; - } - - String internalName() { - return name; + record ClassDefiner(Lookup lookup, String internalName, byte[] bytes, int classFlags, ClassFileDumper dumper) { + ClassDefiner { + assert ((classFlags & HIDDEN_CLASS) != 0 || (classFlags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK); } Class defineClass(boolean initialize) { @@ -2495,7 +2468,7 @@ Class defineClass(boolean initialize, Object classData) { Class c = null; try { c = SharedSecrets.getJavaLangAccess() - .defineClass(loader, lookupClass, name, bytes, pd, initialize, classFlags, classData); + .defineClass(loader, lookupClass, internalName, bytes, pd, initialize, classFlags, classData); assert !isNestmate() || c.getNestHost() == lookupClass.getNestHost(); return c; } finally { diff --git a/src/java.base/share/classes/java/lang/invoke/MutableCallSite.java b/src/java.base/share/classes/java/lang/invoke/MutableCallSite.java index 50ba77d8f96f9..65872e360a73f 100644 --- a/src/java.base/share/classes/java/lang/invoke/MutableCallSite.java +++ b/src/java.base/share/classes/java/lang/invoke/MutableCallSite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, 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,7 +26,8 @@ package java.lang.invoke; import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; + +import static java.lang.invoke.MethodHandleStatics.UNSAFE; /** * A {@code MutableCallSite} is a {@link CallSite} whose target variable @@ -274,11 +275,10 @@ public final MethodHandle dynamicInvoker() { */ public static void syncAll(MutableCallSite[] sites) { if (sites.length == 0) return; - STORE_BARRIER.lazySet(0); + UNSAFE.storeFence(); for (MutableCallSite site : sites) { Objects.requireNonNull(site); // trigger NPE on first null } // FIXME: NYI } - private static final AtomicInteger STORE_BARRIER = new AtomicInteger(); } diff --git a/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java index 4d09b96775dd7..35cc610dad15e 100644 --- a/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/NativeMethodHandle.java @@ -108,7 +108,7 @@ private static LambdaForm makePreparedLambdaForm(MethodType mtype) { final int GET_NEP = nameCursor++; final int LINKER_CALL = nameCursor++; - LambdaForm.Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + LambdaForm.Name[] names = invokeArguments(nameCursor - ARG_LIMIT, mtype); assert (names.length == nameCursor); names[GET_NEP] = new LambdaForm.Name(Lazy.NF_internalNativeEntryPoint, names[NMH_THIS]); diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index 4fbbec1769364..97848e1fcb3e5 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -1526,7 +1526,7 @@ public void accept(CodeBuilder cb) { * length = length(this.length, arg0, arg1, ..., argN); */ if (staticConcat) { - cb.ldc(length); + cb.loadConstant(length); } else { cb.aload(thisSlot) .getfield(concatClass, "length", CD_int); @@ -1549,7 +1549,7 @@ public void accept(CodeBuilder cb) { * length -= suffix.length(); */ if (staticConcat) { - cb.ldc(constants[paramCount].length()) + cb.loadConstant(constants[paramCount].length()) .isub() .istore(lengthSlot); } else { @@ -1557,7 +1557,7 @@ public void accept(CodeBuilder cb) { .getfield(concatClass, "constants", CD_Array_String) .dup() .astore(constantsSlot) - .ldc(paramCount) + .loadConstant(paramCount) .aaload() .dup() .astore(suffixSlot) @@ -1572,7 +1572,7 @@ public void accept(CodeBuilder cb) { * buf = newArrayWithSuffix(suffix, length, coder) */ if (staticConcat) { - cb.ldc(constants[paramCount]); + cb.loadConstant(constants[paramCount]); } else { cb.aload(suffixSlot); } @@ -1759,10 +1759,10 @@ public void accept(CodeBuilder cb) { .loadLocal(kind, cb.parameterSlot(i)); if (staticConcat) { - cb.ldc(constants[i - 3]); + cb.loadConstant(constants[i - 3]); } else { cb.aload(constantsSlot) - .ldc(i - 4) + .loadConstant(i - 4) .aaload(); } diff --git a/src/java.base/share/classes/java/lang/package-info.java b/src/java.base/share/classes/java/lang/package-info.java index 882a610c1a08e..9ca4482c8191f 100644 --- a/src/java.base/share/classes/java/lang/package-info.java +++ b/src/java.base/share/classes/java/lang/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018, 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 @@ -25,35 +25,42 @@ /** * Provides classes that are fundamental to the design of the Java - * programming language. The most important classes are {@code - * Object}, which is the root of the class hierarchy, and {@code + * programming language. The most important classes are {@link + * Object}, which is the root of the class hierarchy, and {@link * Class}, instances of which represent classes at run time. * - *

      Frequently it is necessary to represent a value of primitive - * type as if it were an object. The wrapper classes {@code Boolean}, - * {@code Character}, {@code Integer}, {@code Long}, {@code Float}, - * and {@code Double} serve this purpose. An object of type {@code - * Double}, for example, contains a field whose type is double, - * representing that value in such a way that a reference to it can be - * stored in a variable of reference type. These classes also provide - * a number of methods for converting among primitive values, as well - * as supporting such standard methods as equals and hashCode. The - * {@code Void} class is a non-instantiable class that holds a - * reference to a {@code Class} object representing the type void. + *

      Frequently it is necessary to represent a + * value of primitive type as if it were an object.The {@index + * "wrapper classes"} {@link Boolean}, {@link Byte}, {@link + * Character}, {@link Short}, {@link Integer}, {@link Long}, {@link + * Float}, and {@link Double} serve this purpose. An object of type + * {@code Double}, for example, contains a field whose type is {@code + * double}, representing that value in such a way that a reference to + * it can be stored in a variable of reference type. As discussed in + * The Java Language Specification, the wrapper classes + * are involved in boxing (JLS {@jls 5.1.7}) and unboxing (JLS {@jls + * 5.1.8}) conversions. These classes provide a number of methods for + * converting among primitive values, as well as methods supporting + * such standard functionality as {@code equals} and {@code hashCode}. + * The {@link Void} class is a non-instantiable class that holds a + * reference to a {@code Class} object representing the type {@code + * void}. * - *

      The class {@code Math} provides commonly used mathematical - * functions such as sine, cosine, and square root. The classes {@code - * String}, {@code StringBuffer}, and {@code StringBuilder} similarly - * provide commonly used operations on character strings. + *

      The class {@link Math} provides commonly used mathematical + * functions such as {@linkplain Math#sin(double) sine}, {@linkplain + * Math#cos(double) cosine}, and {@linkplain Math#sqrt(double) square + * root}. The classes {@link String}, {@link StringBuffer}, and {@link + * StringBuilder} similarly provide commonly used operations on + * character strings. * - *

      Classes {@code ClassLoader}, {@code Process}, {@code - * ProcessBuilder}, {@code Runtime}, {@code SecurityManager}, and - * {@code System} provide "system operations" that manage the dynamic + *

      Classes {@link ClassLoader}, {@link Process}, {@link + * ProcessBuilder}, {@link Runtime}, {@link SecurityManager}, and + * {@link System} provide "system operations" that manage the dynamic * loading of classes, creation of external processes, host * environment inquiries such as the time of day, and enforcement of * security policies. * - *

      Class {@code Throwable} encompasses objects that may be thrown + *

      Class {@link Throwable} encompasses objects that may be thrown * by the {@code throw} statement. Subclasses of {@code Throwable} * represent errors and exceptions. * diff --git a/src/java.base/share/classes/java/lang/ref/PhantomReference.java b/src/java.base/share/classes/java/lang/ref/PhantomReference.java index e474748349776..47a989cde4689 100644 --- a/src/java.base/share/classes/java/lang/ref/PhantomReference.java +++ b/src/java.base/share/classes/java/lang/ref/PhantomReference.java @@ -77,6 +77,19 @@ boolean refersToImpl(T obj) { @IntrinsicCandidate private native boolean refersTo0(Object o); + /* Override the implementation of Reference.clear. + * Phantom references are weaker than finalization, so the referent + * access needs to be handled differently for garbage collectors that + * do reference processing concurrently. + */ + @Override + void clearImpl() { + clear0(); + } + + @IntrinsicCandidate + private native void clear0(); + /** * Creates a new phantom reference that refers to the given object and * is registered with the given queue. diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index 747aa64902f8e..88cd2a3190b71 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -403,13 +403,23 @@ boolean refersToImpl(T obj) { * necessary. */ public void clear() { - clear0(); + clearImpl(); } - /* Implementation of clear(), also used by enqueue(). A simple - * assignment of the referent field won't do for some garbage - * collectors. + /* Implementation of clear(). A simple assignment of the referent field + * won't do for some garbage collectors. There is the override for phantom + * references, which requires different semantics. This method is also + * used by enqueue(). + * + *

      This method exists only to avoid making clear0() virtual. Making + * clear0() virtual has the undesirable effect of C2 often preferring + * to call the native implementation over the intrinsic. */ + void clearImpl() { + clear0(); + } + + @IntrinsicCandidate private native void clear0(); /* -- Operations on inactive FinalReferences -- */ @@ -511,7 +521,7 @@ public boolean isEnqueued() { * it was not registered with a queue when it was created */ public boolean enqueue() { - clear0(); // Intentionally clear0() rather than clear() + clearImpl(); // Intentionally clearImpl() to dispatch to overridden method, if needed return this.queue.enqueue(this); } diff --git a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java index 2e56d03c6ad89..abdcaf5ae1fa2 100644 --- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java +++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java @@ -114,11 +114,18 @@ final class ProxyGenerator { private static final Method OBJECT_EQUALS_METHOD; private static final Method OBJECT_TO_STRING_METHOD; + private static final String OBJECT_HASH_CODE_SIG; + private static final String OBJECT_EQUALS_SIG; + private static final String OBJECT_TO_STRING_SIG; + static { try { OBJECT_HASH_CODE_METHOD = Object.class.getMethod("hashCode"); + OBJECT_HASH_CODE_SIG = OBJECT_HASH_CODE_METHOD.toShortSignature(); OBJECT_EQUALS_METHOD = Object.class.getMethod("equals", Object.class); + OBJECT_EQUALS_SIG = OBJECT_EQUALS_METHOD.toShortSignature(); OBJECT_TO_STRING_METHOD = Object.class.getMethod("toString"); + OBJECT_TO_STRING_SIG = OBJECT_TO_STRING_METHOD.toShortSignature(); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } @@ -446,9 +453,9 @@ private byte[] generateClassFile() { * java.lang.Object take precedence over duplicate methods in the * proxy interfaces. */ - addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, "m0")); - addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, "m1")); - addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, "m2")); + addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, OBJECT_HASH_CODE_SIG, "m0")); + addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, OBJECT_EQUALS_SIG, "m1")); + addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, OBJECT_TO_STRING_SIG, "m2")); /* * Accumulate all of the methods from the proxy interfaces. @@ -526,7 +533,7 @@ private void addProxyMethod(Method m, Class fromClass) { return; } } - sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType, + sigmethods.add(new ProxyMethod(m, sig, returnType, exceptionTypes, fromClass, "m" + proxyMethodCount++)); } @@ -617,11 +624,11 @@ private void generateLookupAccessor(ClassBuilder clb) { Label failLabel = cob.newLabel(); ClassEntry mhl = cp.classEntry(CD_MethodHandles_Lookup); ClassEntry iae = cp.classEntry(CD_IllegalAccessException); - cob.aload(cob.parameterSlot(0)) + cob.aload(0) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("lookupClass", MTD_Class))) .ldc(proxyCE) .if_acmpne(failLabel) - .aload(cob.parameterSlot(0)) + .aload(0) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("hasFullPrivilegeAccess", MTD_boolean))) .ifeq(failLabel) .invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup) @@ -629,7 +636,7 @@ private void generateLookupAccessor(ClassBuilder clb) { .labelBinding(failLabel) .new_(iae) .dup() - .aload(cob.parameterSlot(0)) + .aload(0) .invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("toString", MTD_String))) .invokespecial(cp.methodRefEntry(iae, exInit)) .athrow() @@ -650,18 +657,16 @@ private class ProxyMethod { private final Method method; private final String shortSignature; private final Class fromClass; - private final Class[] parameterTypes; private final Class returnType; private final String methodFieldName; private Class[] exceptionTypes; private final FieldRefEntry methodField; - private ProxyMethod(Method method, String sig, Class[] parameterTypes, + private ProxyMethod(Method method, String sig, Class returnType, Class[] exceptionTypes, Class fromClass, String methodFieldName) { this.method = method; this.shortSignature = sig; - this.parameterTypes = parameterTypes; this.returnType = returnType; this.exceptionTypes = exceptionTypes; this.fromClass = fromClass; @@ -670,14 +675,17 @@ private ProxyMethod(Method method, String sig, Class[] parameterTypes, cp.nameAndTypeEntry(methodFieldName, CD_Method)); } + private Class[] parameterTypes() { + return method.getSharedParameterTypes(); + } + /** * Create a new specific ProxyMethod with a specific field name * * @param method The method for which to create a proxy */ - private ProxyMethod(Method method, String methodFieldName) { - this(method, method.toShortSignature(), - method.getSharedParameterTypes(), method.getReturnType(), + private ProxyMethod(Method method, String sig, String methodFieldName) { + this(method, sig, method.getReturnType(), method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName); } @@ -685,17 +693,18 @@ private ProxyMethod(Method method, String methodFieldName) { * Generate this method, including the code and exception table entry. */ private void generateMethod(ClassBuilder clb) { - var desc = methodTypeDesc(returnType, parameterTypes); + var desc = methodTypeDesc(returnType, parameterTypes()); int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL : ACC_PUBLIC | ACC_FINAL; - var catchList = computeUniqueCatchList(exceptionTypes); clb.withMethod(method.getName(), desc, accessFlags, mb -> mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes)))) .withCode(cob -> { + var catchList = computeUniqueCatchList(exceptionTypes); cob.aload(cob.receiverSlot()) .getfield(handlerField) .aload(cob.receiverSlot()) .getstatic(methodField); + Class[] parameterTypes = parameterTypes(); if (parameterTypes.length > 0) { // Create an array and fill with the parameters converting primitives to wrappers cob.loadConstant(parameterTypes.length) @@ -784,6 +793,7 @@ private void codeFieldInitialization(CodeBuilder cob) { var cp = cob.constantPool(); codeClassForName(cob, fromClass); + Class[] parameterTypes = parameterTypes(); cob.ldc(method.getName()) .loadConstant(parameterTypes.length) .anewarray(classCE); @@ -817,10 +827,14 @@ private void codeFieldInitialization(CodeBuilder cob) { * loader is anticipated at local variable index 0. */ private void codeClassForName(CodeBuilder cob, Class cl) { - cob.ldc(cl.getName()) - .iconst_0() // false - .aload(0)// classLoader - .invokestatic(classForName); + if (cl == Object.class) { + cob.ldc(objectCE); + } else { + cob.ldc(cl.getName()) + .iconst_0() // false + .aload(0)// classLoader + .invokestatic(classForName); + } } @Override diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index bfdb76e2ef1b6..0c0144b24dbac 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -29,7 +29,6 @@ import java.lang.classfile.CodeBuilder; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; -import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; @@ -55,6 +54,7 @@ import jdk.internal.misc.PreviewFeatures; import jdk.internal.vm.annotation.Stable; +import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; import java.util.Arrays; @@ -86,15 +86,15 @@ private SwitchBootstraps() {} private static final ClassDesc CD_Objects = ReferenceClassDescImpl.ofValidated("Ljava/util/Objects;"); private static final MethodTypeDesc CHECK_INDEX_DESCRIPTOR = - MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int, ConstantDescs.CD_int, ConstantDescs.CD_int); - private static final MethodTypeDesc MTD_TYPE_SWITCH = MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int, - ConstantDescs.CD_Object, - ConstantDescs.CD_int); - private static final MethodTypeDesc MTD_TYPE_SWITCH_EXTRA = MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int, - ConstantDescs.CD_Object, - ConstantDescs.CD_int, + MethodTypeDescImpl.ofValidated(CD_int, CD_int, CD_int); + private static final MethodTypeDesc MTD_TYPE_SWITCH = MethodTypeDescImpl.ofValidated(CD_int, + CD_Object, + CD_int); + private static final MethodTypeDesc MTD_TYPE_SWITCH_EXTRA = MethodTypeDescImpl.ofValidated(CD_int, + CD_Object, + CD_int, CD_BiPredicate, - ConstantDescs.CD_List); + CD_List); private static final MethodType MT_TYPE_SWITCH_EXTRA = MethodType.methodType(int.class, Object.class, int.class, @@ -484,19 +484,19 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto return cb -> { // Objects.checkIndex(RESTART_IDX, labelConstants + 1) - cb.iload(RESTART_IDX); - cb.loadConstant(labelConstants.length + 1); - cb.invokestatic(CD_Objects, "checkIndex", CHECK_INDEX_DESCRIPTOR); - cb.pop(); - cb.aload(SELECTOR_OBJ); + cb.iload(RESTART_IDX) + .loadConstant(labelConstants.length + 1) + .invokestatic(CD_Objects, "checkIndex", CHECK_INDEX_DESCRIPTOR) + .pop() + .aload(SELECTOR_OBJ); Label nonNullLabel = cb.newLabel(); - cb.ifnonnull(nonNullLabel); - cb.iconst_m1(); - cb.ireturn(); - cb.labelBinding(nonNullLabel); + cb.ifnonnull(nonNullLabel) + .iconst_m1() + .ireturn() + .labelBinding(nonNullLabel); if (labelConstants.length == 0) { cb.loadConstant(0) - .ireturn(); + .ireturn(); return; } cb.iload(RESTART_IDX); @@ -535,132 +535,132 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto if (!selectorType.isPrimitive() && !Wrapper.isWrapperNumericOrBooleanType(selectorType)) { // Object o = ... // o instanceof Wrapped(float) - cb.aload(SELECTOR_OBJ); - cb.instanceOf(Wrapper.forBasicType(classLabel).wrapperClassDescriptor()); - cb.ifeq(next); + cb.aload(SELECTOR_OBJ) + .instanceOf(Wrapper.forBasicType(classLabel).wrapperClassDescriptor()) + .ifeq(next); } else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) { // Integer i = ... or int i = ... // o instanceof float Label notNumber = cb.newLabel(); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Number); + cb.aload(SELECTOR_OBJ) + .instanceOf(CD_Number); if (selectorType == long.class || selectorType == float.class || selectorType == double.class || selectorType == Long.class || selectorType == Float.class || selectorType == Double.class) { cb.ifeq(next); } else { cb.ifeq(notNumber); } - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Number); + cb.aload(SELECTOR_OBJ) + .checkcast(CD_Number); if (selectorType == long.class || selectorType == Long.class) { - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "longValue", - MethodTypeDesc.of(ConstantDescs.CD_long)); + MethodTypeDesc.of(CD_long)); } else if (selectorType == float.class || selectorType == Float.class) { - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "floatValue", - MethodTypeDesc.of(ConstantDescs.CD_float)); + MethodTypeDesc.of(CD_float)); } else if (selectorType == double.class || selectorType == Double.class) { - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "doubleValue", - MethodTypeDesc.of(ConstantDescs.CD_double)); + MethodTypeDesc.of(CD_double)); } else { Label compare = cb.newLabel(); - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "intValue", - MethodTypeDesc.of(ConstantDescs.CD_int)); - cb.goto_(compare); - cb.labelBinding(notNumber); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Character); - cb.ifeq(next); - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Character); - cb.invokevirtual(ConstantDescs.CD_Character, + MethodTypeDesc.of(CD_int)) + .goto_(compare) + .labelBinding(notNumber) + .aload(SELECTOR_OBJ) + .instanceOf(CD_Character) + .ifeq(next) + .aload(SELECTOR_OBJ) + .checkcast(CD_Character) + .invokevirtual(CD_Character, "charValue", - MethodTypeDesc.of(ConstantDescs.CD_char)); - cb.labelBinding(compare); + MethodTypeDesc.of(CD_char)) + .labelBinding(compare); } TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel); String methodName = TypePairs.typePairToName.get(typePair); cb.invokestatic(referenceClassDesc(ExactConversionsSupport.class), methodName, - MethodTypeDesc.of(ConstantDescs.CD_boolean, classDesc(typePair.from))); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, classDesc(typePair.from))) + .ifeq(next); } } else { Optional classLabelConstableOpt = classLabel.describeConstable(); if (classLabelConstableOpt.isPresent()) { - cb.aload(SELECTOR_OBJ); - cb.instanceOf(classLabelConstableOpt.orElseThrow()); - cb.ifeq(next); + cb.aload(SELECTOR_OBJ) + .instanceOf(classLabelConstableOpt.orElseThrow()) + .ifeq(next); } else { - cb.aload(EXTRA_CLASS_LABELS); - cb.loadConstant(extraClassLabels.size()); - cb.invokeinterface(ConstantDescs.CD_List, + cb.aload(EXTRA_CLASS_LABELS) + .loadConstant(extraClassLabels.size()) + .invokeinterface(CD_List, "get", - MethodTypeDesc.of(ConstantDescs.CD_Object, - ConstantDescs.CD_int)); - cb.checkcast(ConstantDescs.CD_Class); - cb.aload(SELECTOR_OBJ); - cb.invokevirtual(ConstantDescs.CD_Class, + MethodTypeDesc.of(CD_Object, + CD_int)) + .checkcast(CD_Class) + .aload(SELECTOR_OBJ) + .invokevirtual(CD_Class, "isInstance", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object)) + .ifeq(next); extraClassLabels.add(classLabel); } } } else if (caseLabel instanceof EnumDesc enumLabel) { int enumIdx = enumDescs.size(); enumDescs.add(enumLabel); - cb.aload(ENUM_CACHE); - cb.loadConstant(enumIdx); - cb.invokestatic(ConstantDescs.CD_Integer, + cb.aload(ENUM_CACHE) + .loadConstant(enumIdx) + .invokestatic(CD_Integer, "valueOf", - MethodTypeDesc.of(ConstantDescs.CD_Integer, - ConstantDescs.CD_int)); - cb.aload(SELECTOR_OBJ); - cb.invokeinterface(CD_BiPredicate, + MethodTypeDesc.of(CD_Integer, + CD_int)) + .aload(SELECTOR_OBJ) + .invokeinterface(CD_BiPredicate, "test", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object, + CD_Object)) + .ifeq(next); } else if (caseLabel instanceof String stringLabel) { - cb.ldc(stringLabel); - cb.aload(SELECTOR_OBJ); - cb.invokevirtual(ConstantDescs.CD_Object, + cb.ldc(stringLabel) + .aload(SELECTOR_OBJ) + .invokevirtual(CD_Object, "equals", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object)) + .ifeq(next); } else if (caseLabel instanceof Integer integerLabel) { Label compare = cb.newLabel(); Label notNumber = cb.newLabel(); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Number); - cb.ifeq(notNumber); - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Number); - cb.invokevirtual(ConstantDescs.CD_Number, + cb.aload(SELECTOR_OBJ) + .instanceOf(CD_Number) + .ifeq(notNumber) + .aload(SELECTOR_OBJ) + .checkcast(CD_Number) + .invokevirtual(CD_Number, "intValue", - MethodTypeDesc.of(ConstantDescs.CD_int)); - cb.goto_(compare); - cb.labelBinding(notNumber); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Character); - cb.ifeq(next); - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Character); - cb.invokevirtual(ConstantDescs.CD_Character, + MethodTypeDesc.of(CD_int)) + .goto_(compare) + .labelBinding(notNumber) + .aload(SELECTOR_OBJ) + .instanceOf(CD_Character) + .ifeq(next) + .aload(SELECTOR_OBJ) + .checkcast(CD_Character) + .invokevirtual(CD_Character, "charValue", - MethodTypeDesc.of(ConstantDescs.CD_char)); - cb.labelBinding(compare); + MethodTypeDesc.of(CD_char)) + .labelBinding(compare) - cb.ldc(integerLabel); - cb.if_icmpne(next); + .loadConstant(integerLabel) + .if_icmpne(next); } else if ((caseLabel instanceof Long || caseLabel instanceof Float || caseLabel instanceof Double || @@ -674,23 +674,23 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto cb.invokestatic(caseLabelWrapper.wrapperClassDescriptor(), "valueOf", MethodTypeDesc.of(caseLabelWrapper.wrapperClassDescriptor(), - caseLabelWrapper.basicClassDescriptor())); - cb.aload(SELECTOR_OBJ); - cb.invokevirtual(ConstantDescs.CD_Object, + caseLabelWrapper.basicClassDescriptor())) + .aload(SELECTOR_OBJ) + .invokevirtual(CD_Object, "equals", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object)) + .ifeq(next); } else { throw new InternalError("Unsupported label type: " + caseLabel.getClass()); } - cb.loadConstant(idx); - cb.ireturn(); + cb.loadConstant(idx) + .ireturn(); } - cb.labelBinding(dflt); - cb.loadConstant(labelConstants.length); - cb.ireturn(); + cb.labelBinding(dflt) + .loadConstant(labelConstants.length) + .ireturn(); }; } diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index b84e50f567eb7..6ff435ba1ed3d 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -597,44 +597,52 @@ void leftShift(int n) { */ if (intLen == 0) return; + int nInts = n >>> 5; - int nBits = n&0x1F; - int bitsInHighWord = BigInteger.bitLengthForInt(value[offset]); + int nBits = n & 0x1F; + int leadingZeros = Integer.numberOfLeadingZeros(value[offset]); // If shift can be done without moving words, do so - if (n <= (32-bitsInHighWord)) { + if (n <= leadingZeros) { primitiveLeftShift(nBits); return; } - int newLen = intLen + nInts +1; - if (nBits <= (32-bitsInHighWord)) - newLen--; - if (value.length < newLen) { - // The array must grow - int[] result = new int[newLen]; - for (int i=0; i < intLen; i++) - result[i] = value[offset+i]; - setValue(result, newLen); - } else if (value.length - offset >= newLen) { - // Use space on right - for(int i=0; i < newLen - intLen; i++) - value[offset+intLen+i] = 0; + int newLen = intLen + nInts; + if (nBits > leadingZeros) + newLen++; + + int[] result; + final int newOffset; + if (value.length < newLen) { // The array must grow + result = new int[newLen]; + newOffset = 0; } else { - // Must use space on left - for (int i=0; i < intLen; i++) - value[i] = value[offset+i]; - for (int i=intLen; i < newLen; i++) - value[i] = 0; - offset = 0; + result = value; + newOffset = value.length - offset >= newLen ? offset : 0; } + + int trailingZerosPos = newOffset + intLen; + if (nBits != 0) { + // Do primitive shift directly for speed + if (nBits <= leadingZeros) { + primitiveLeftShift(nBits, result, newOffset); // newOffset <= offset + } else { + int lastInt = value[offset + intLen - 1]; + primitiveRightShift(32 - nBits, result, newOffset); // newOffset <= offset + result[trailingZerosPos++] = lastInt << nBits; + } + } else if (result != value || newOffset != offset) { + System.arraycopy(value, offset, result, newOffset, intLen); + } + + // Add trailing zeros + if (result == value) + Arrays.fill(result, trailingZerosPos, newOffset + newLen, 0); + + value = result; intLen = newLen; - if (nBits == 0) - return; - if (nBits <= (32-bitsInHighWord)) - primitiveLeftShift(nBits); - else - primitiveRightShift(32 -nBits); + offset = newOffset; } /** @@ -698,15 +706,30 @@ private int mulsubBorrow(int[] q, int[] a, int x, int len, int offset) { * less than 32. * Assumes that intLen > 0, n > 0 for speed */ - private final void primitiveRightShift(int n) { + private void primitiveRightShift(int n) { + primitiveRightShift(n, value, offset); + } + + /** + * Right shift this MutableBigInteger n bits, where n is + * less than 32, placing the result in the specified array. + * Assumes that intLen > 0, n > 0 for speed. + * The result can be the value array of this MutableBigInteger, + * but for speed the copy is not performed safely, so, in that case + * the caller has to make sure that + * {@code (resFrom <= offset || resFrom >= offset + intLen)}. + */ + private void primitiveRightShift(int n, int[] result, int resFrom) { int[] val = value; int n2 = 32 - n; - for (int i=offset+intLen-1, c=val[i]; i > offset; i--) { - int b = c; - c = val[i-1]; - val[i] = (c << n2) | (b >>> n); + + int b = val[offset]; + result[resFrom] = b >>> n; + for (int i = 1; i < intLen; i++) { + int c = b; + b = val[offset + i]; + result[resFrom + i] = (c << n2) | (b >>> n); } - val[offset] >>>= n; } /** @@ -714,15 +737,30 @@ private final void primitiveRightShift(int n) { * less than 32. * Assumes that intLen > 0, n > 0 for speed */ - private final void primitiveLeftShift(int n) { + private void primitiveLeftShift(int n) { + primitiveLeftShift(n, value, offset); + } + + /** + * Left shift this MutableBigInteger n bits, where n is + * less than 32, placing the result in the specified array. + * Assumes that intLen > 0, n > 0 for speed. + * The result can be the value array of this MutableBigInteger, + * but for speed the copy is not performed safely, so, in that case + * the caller has to make sure that + * {@code (resFrom <= offset || resFrom >= offset + intLen)}. + */ + private void primitiveLeftShift(int n, int[] result, int resFrom) { int[] val = value; int n2 = 32 - n; - for (int i=offset, c=val[i], m=i+intLen-1; i < m; i++) { - int b = c; - c = val[i+1]; - val[i] = (b << n) | (c >>> n2); + final int m = intLen - 1; + int b = val[offset]; + for (int i = 0; i < m; i++) { + int c = val[offset + i + 1]; + result[resFrom + i] = (b << n) | (c >>> n2); + b = c; } - val[offset+intLen-1] <<= n; + result[resFrom + m] = b << n; } /** @@ -1511,17 +1549,6 @@ long divide(long v, MutableBigInteger quotient) { } } - private static void copyAndShift(int[] src, int srcFrom, int srcLen, int[] dst, int dstFrom, int shift) { - int n2 = 32 - shift; - int c=src[srcFrom]; - for (int i=0; i < srcLen-1; i++) { - int b = c; - c = src[++srcFrom]; - dst[dstFrom+i] = (b << shift) | (c >>> n2); - } - dst[dstFrom+srcLen-1] = c << shift; - } - /** * Divide this MutableBigInteger by the divisor. * The quotient will be placed into the provided quotient object & @@ -1539,13 +1566,13 @@ private MutableBigInteger divideMagnitude(MutableBigInteger div, MutableBigInteger rem; // Remainder starts as dividend with space for a leading zero if (shift > 0) { divisor = new int[dlen]; - copyAndShift(div.value,div.offset,dlen,divisor,0,shift); + div.primitiveLeftShift(shift, divisor, 0); if (Integer.numberOfLeadingZeros(value[offset]) >= shift) { int[] remarr = new int[intLen + 1]; rem = new MutableBigInteger(remarr); rem.intLen = intLen; rem.offset = 1; - copyAndShift(value,offset,intLen,remarr,1,shift); + this.primitiveLeftShift(shift, remarr, 1); } else { int[] remarr = new int[intLen + 2]; rem = new MutableBigInteger(remarr); diff --git a/src/java.base/share/classes/java/net/HttpURLConnection.java b/src/java.base/share/classes/java/net/HttpURLConnection.java index b405fb10a1655..625fd30424eea 100644 --- a/src/java.base/share/classes/java/net/HttpURLConnection.java +++ b/src/java.base/share/classes/java/net/HttpURLConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, 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 @@ -373,8 +373,7 @@ protected HttpURLConnection (URL u) { /** * Sets whether HTTP redirects (requests with response code 3xx) should - * be automatically followed by this class. True by default. Applets - * cannot change this variable. + * be automatically followed by this class. True by default. *

      * If there is a security manager, this method first calls * the security manager's {@code checkSetFactory} method diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index 23c225fbb2d29..309fa7a80d099 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -27,6 +27,7 @@ import jdk.internal.event.SocketReadEvent; import jdk.internal.event.SocketWriteEvent; +import jdk.internal.invoke.MhUtil; import sun.security.util.SecurityConstants; import java.io.InputStream; @@ -102,14 +103,10 @@ public class Socket implements java.io.Closeable { private static final VarHandle STATE, IN, OUT; static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - STATE = l.findVarHandle(Socket.class, "state", int.class); - IN = l.findVarHandle(Socket.class, "in", InputStream.class); - OUT = l.findVarHandle(Socket.class, "out", OutputStream.class); - } catch (Exception e) { - throw new InternalError(e); - } + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = MhUtil.findVarHandle(l, "state", int.class); + IN = MhUtil.findVarHandle(l, "in", InputStream.class); + OUT = MhUtil.findVarHandle(l, "out", OutputStream.class); } // the underlying SocketImpl, may be null, may be swapped when connecting 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 a61844cca6e09..a67df0c0d0092 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 @@ -253,6 +253,15 @@

      Misc HTTP URL stream protocol handler properties

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

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

    All these properties are checked only once at startup.

    diff --git a/src/java.base/share/classes/java/nio/channels/SelectionKey.java b/src/java.base/share/classes/java/nio/channels/SelectionKey.java index ca6df2a7aa054..79029e6365353 100644 --- a/src/java.base/share/classes/java/nio/channels/SelectionKey.java +++ b/src/java.base/share/classes/java/nio/channels/SelectionKey.java @@ -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 @@ -25,6 +25,8 @@ package java.nio.channels; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -429,15 +431,9 @@ public final boolean isAcceptable() { // -- Attachments -- - private static final VarHandle ATTACHMENT; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - ATTACHMENT = l.findVarHandle(SelectionKey.class, "attachment", Object.class); - } catch (Exception e) { - throw new InternalError(e); - } - } + private static final VarHandle ATTACHMENT = MhUtil.findVarHandle( + MethodHandles.lookup(), "attachment", Object.class); + private volatile Object attachment; /** diff --git a/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectionKey.java b/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectionKey.java index 0a79ca321dd33..3005fc9522c64 100644 --- a/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectionKey.java +++ b/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectionKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, 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,6 +30,7 @@ import java.nio.channels.SelectionKey; import java.nio.channels.Selector; +import jdk.internal.invoke.MhUtil; import sun.nio.ch.SelectionKeyImpl; import sun.nio.ch.SelectorImpl; @@ -46,15 +47,8 @@ public abstract class AbstractSelectionKey extends SelectionKey { - private static final VarHandle INVALID; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - INVALID = l.findVarHandle(AbstractSelectionKey.class, "invalid", boolean.class); - } catch (Exception e) { - throw new InternalError(e); - } - } + private static final VarHandle INVALID = MhUtil.findVarHandle( + MethodHandles.lookup(), "invalid", boolean.class); /** * Initializes a new instance of this class. diff --git a/src/java.base/share/classes/java/nio/channels/spi/AbstractSelector.java b/src/java.base/share/classes/java/nio/channels/spi/AbstractSelector.java index 7ea5f89221834..71dc8620eb804 100644 --- a/src/java.base/share/classes/java/nio/channels/spi/AbstractSelector.java +++ b/src/java.base/share/classes/java/nio/channels/spi/AbstractSelector.java @@ -32,6 +32,8 @@ import java.nio.channels.Selector; import java.util.HashSet; import java.util.Set; + +import jdk.internal.invoke.MhUtil; import sun.nio.ch.Interruptible; import sun.nio.ch.SelectorImpl; @@ -72,15 +74,9 @@ public abstract class AbstractSelector extends Selector { - private static final VarHandle CLOSED; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - CLOSED = l.findVarHandle(AbstractSelector.class, "closed", boolean.class); - } catch (Exception e) { - throw new InternalError(e); - } - } + private static final VarHandle CLOSED = MhUtil.findVarHandle( + MethodHandles.lookup(), "closed", boolean.class); + private volatile boolean closed; // The provider that created this selector diff --git a/src/java.base/share/classes/java/nio/charset/spi/CharsetProvider.java b/src/java.base/share/classes/java/nio/charset/spi/CharsetProvider.java index 87ff0d07854ef..114558d93927f 100644 --- a/src/java.base/share/classes/java/nio/charset/spi/CharsetProvider.java +++ b/src/java.base/share/classes/java/nio/charset/spi/CharsetProvider.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 @@ -33,17 +33,29 @@ * Charset service-provider class. * *

    A charset provider is a concrete subclass of this class that has a - * zero-argument constructor and some number of associated charset - * implementation classes. Charset providers may be installed in an instance - * of the Java platform as extensions. Providers may also be made available by - * adding them to the applet or application class path or by some other - * platform-specific means. Charset providers are looked up via the current - * thread's {@link java.lang.Thread#getContextClassLoader() context class - * loader}. + * zero-argument constructor and some number of associated {@code Charset} + * implementation classes. Charset providers are deployed on the application + * module path or the application class path. In order to be looked up, charset + * providers must be visible to the {@link ClassLoader#getSystemClassLoader() system + * class loader}. See {@link java.util.ServiceLoader##developing-service-providers + * Deploying Service Providers} for further detail on deploying a charset + * provider as a module or on the class path. * - *

    A charset provider identifies itself with a provider-configuration file - * named {@code java.nio.charset.spi.CharsetProvider} in the resource - * directory {@code META-INF/services}. The file should contain a list of + *

    For a charset provider deployed in a module, the provides + * directive must be specified in the module declaration. The provides directive + * specifies both the service and the service provider. In this case, the service + * is {@code java.nio.charset.spi.CharsetProvider}. + * + *

    As an example, a charset provider deployed as a module might specify the + * following directive: + *

    {@code
    + *     provides java.nio.charset.spi.CharsetProvider with com.example.ExternalCharsetProvider;
    + * }
    + * + *

    For a charset provider deployed on the class path, it identifies itself + * with a provider-configuration file named {@code + * java.nio.charset.spi.CharsetProvider} in the resource directory + * {@code META-INF/services}. The file should contain a list of * fully-qualified concrete charset-provider class names, one per line. A line * is terminated by any one of a line feed ({@code '\n'}), a carriage return * ({@code '\r'}), or a carriage return followed immediately by a line feed. diff --git a/src/java.base/share/classes/java/security/Permissions.java b/src/java.base/share/classes/java/security/Permissions.java index 42c1adc90021c..3bdeac6f92945 100644 --- a/src/java.base/share/classes/java/security/Permissions.java +++ b/src/java.base/share/classes/java/security/Permissions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 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 @@ -407,6 +407,11 @@ private void readObject(ObjectInputStream in) throws IOException, @SuppressWarnings("unchecked") Hashtable, PermissionCollection> perms = (Hashtable, PermissionCollection>)gfields.get("perms", null); + + if (perms == null) { + throw new InvalidObjectException("perms can't be null"); + } + permsMap = new ConcurrentHashMap<>(perms.size()*2); permsMap.putAll(perms); diff --git a/src/java.base/share/classes/java/security/Policy.java b/src/java.base/share/classes/java/security/Policy.java index 838366b7e3862..37f7cc3d9a686 100644 --- a/src/java.base/share/classes/java/security/Policy.java +++ b/src/java.base/share/classes/java/security/Policy.java @@ -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 @@ -896,5 +896,15 @@ public UnsupportedEmptyCollection() { @Override public Enumeration elements() { return perms.elements(); } + + /** + * If this object is readonly, no new objects can be added to it using {@code add}. + * + * @return {@code true} if this object is marked as readonly, {@code false} otherwise. + */ + @Override + public boolean isReadOnly() { + return perms.isReadOnly(); + } } } diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 0cdd22340dfbf..6628b717eb073 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,39 @@ package java.security; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.io.*; +import java.net.URI; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import jdk.internal.access.JavaSecurityPropertiesAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.event.EventHelper; import jdk.internal.event.SecurityPropertyModificationEvent; -import jdk.internal.access.SharedSecrets; import jdk.internal.util.StaticProperty; +import sun.security.jca.GetInstance; +import sun.security.jca.ProviderList; +import sun.security.jca.Providers; import sun.security.util.Debug; import sun.security.util.PropertyExpander; -import sun.security.jca.*; - /** *

    This class centralizes all security properties and common security * methods. One of its primary uses is to manage providers. @@ -63,7 +80,17 @@ public final class Security { Debug.getInstance("properties"); /* The java.security properties */ - private static Properties props; + private static final Properties props = new Properties() { + @Override + public synchronized Object put(Object key, Object val) { + if (key instanceof String strKey && val instanceof String strVal && + SecPropLoader.isInclude(strKey)) { + SecPropLoader.loadInclude(strVal); + return null; + } + return super.put(key, val); + } + }; /* cache a copy for recording purposes */ private static Properties initialSecurityProperties; @@ -74,11 +101,220 @@ private static class ProviderProperty { Provider provider; } + private static final class SecPropLoader { + private enum LoadingMode {OVERRIDE, APPEND} + + private static final String OVERRIDE_SEC_PROP = + "security.overridePropertiesFile"; + + private static final String EXTRA_SYS_PROP = + "java.security.properties"; + + private static Path currentPath; + + private static final Set activePaths = new HashSet<>(); + + static void loadAll() { + // first load the master properties file to + // determine the value of OVERRIDE_SEC_PROP + loadMaster(); + loadExtra(); + } + + static boolean isInclude(String key) { + return "include".equals(key); + } + + static void checkReservedKey(String key) + throws IllegalArgumentException { + if (isInclude(key)) { + throw new IllegalArgumentException("Key '" + key + + "' is reserved and cannot be used as a " + + "Security property name."); + } + } + + private static void loadMaster() { + try { + loadFromPath(Path.of(StaticProperty.javaHome(), "conf", + "security", "java.security"), LoadingMode.APPEND); + } catch (IOException e) { + throw new InternalError("Error loading java.security file", e); + } + } + + private static void loadExtra() { + if ("true".equalsIgnoreCase(props.getProperty(OVERRIDE_SEC_PROP))) { + String propFile = System.getProperty(EXTRA_SYS_PROP); + if (propFile != null) { + LoadingMode mode = LoadingMode.APPEND; + if (propFile.startsWith("=")) { + mode = LoadingMode.OVERRIDE; + propFile = propFile.substring(1); + } + try { + loadExtraHelper(propFile, mode); + } catch (Exception e) { + if (sdebug != null) { + sdebug.println("unable to load security " + + "properties from " + propFile); + e.printStackTrace(); + } + } + } + } + } + + private static void loadExtraHelper(String propFile, LoadingMode mode) + throws Exception { + propFile = PropertyExpander.expand(propFile); + if (propFile.isEmpty()) { + throw new IOException("Empty extra properties file path"); + } + + // Try to interpret propFile as a path + Exception error; + if ((error = loadExtraFromPath(propFile, mode)) == null) { + return; + } + + // Try to interpret propFile as a file URL + URI uri = null; + try { + uri = new URI(propFile); + } catch (Exception ignore) {} + if (uri != null && "file".equalsIgnoreCase(uri.getScheme()) && + (error = loadExtraFromFileUrl(uri, mode)) == null) { + return; + } + + // Try to interpret propFile as a URL + URL url; + try { + url = newURL(propFile); + } catch (MalformedURLException ignore) { + // URL has no scheme: previous error is more accurate + throw error; + } + loadFromUrl(url, mode); + } + + private static Exception loadExtraFromPath(String propFile, + LoadingMode mode) throws Exception { + Path path; + try { + path = Path.of(propFile); + if (!Files.exists(path)) { + return new FileNotFoundException(propFile); + } + } catch (InvalidPathException e) { + return e; + } + loadFromPath(path, mode); + return null; + } + + + private static Exception loadExtraFromFileUrl(URI uri, LoadingMode mode) + throws Exception { + Path path; + try { + path = Path.of(uri); + } catch (Exception e) { + return e; + } + loadFromPath(path, mode); + return null; + } + + private static void reset(LoadingMode mode) { + if (mode == LoadingMode.OVERRIDE) { + if (sdebug != null) { + sdebug.println( + "overriding other security properties files!"); + } + props.clear(); + } + } + + static void loadInclude(String propFile) { + String expPropFile = PropertyExpander.expandNonStrict(propFile); + if (sdebug != null) { + sdebug.println("processing include: '" + propFile + "'" + + (propFile.equals(expPropFile) ? "" : + " (expanded to '" + expPropFile + "')")); + } + try { + Path path = Path.of(expPropFile); + if (!path.isAbsolute()) { + if (currentPath == null) { + throw new InternalError("Cannot resolve '" + + expPropFile + "' relative path when included " + + "from a non-regular properties file " + + "(e.g. HTTP served file)"); + } + path = currentPath.resolveSibling(path); + } + loadFromPath(path, LoadingMode.APPEND); + } catch (IOException | InvalidPathException e) { + throw new InternalError("Unable to include '" + expPropFile + + "'", e); + } + } + + private static void loadFromPath(Path path, LoadingMode mode) + throws IOException { + boolean isRegularFile = Files.isRegularFile(path); + if (isRegularFile) { + path = path.toRealPath(); + } else if (Files.isDirectory(path)) { + throw new IOException("Is a directory"); + } else { + path = path.toAbsolutePath(); + } + if (activePaths.contains(path)) { + throw new InternalError("Cyclic include of '" + path + "'"); + } + try (InputStream is = Files.newInputStream(path)) { + reset(mode); + Path previousPath = currentPath; + currentPath = isRegularFile ? path : null; + activePaths.add(path); + try { + debugLoad(true, path); + props.load(is); + debugLoad(false, path); + } finally { + activePaths.remove(path); + currentPath = previousPath; + } + } + } + + private static void loadFromUrl(URL url, LoadingMode mode) + throws IOException { + try (InputStream is = url.openStream()) { + reset(mode); + debugLoad(true, url); + props.load(is); + debugLoad(false, url); + } + } + + private static void debugLoad(boolean start, Object source) { + if (sdebug != null) { + int level = activePaths.isEmpty() ? 1 : activePaths.size(); + sdebug.println((start ? + ">".repeat(level) + " starting to process " : + "<".repeat(level) + " finished processing ") + source); + } + } + } + static { // doPrivileged here because there are multiple // things in initialize that might require privs. - // (the FileInputStream call and the File.exists call, - // the securityPropFile call, etc) + // (the FileInputStream call and the File.exists call, etc) @SuppressWarnings("removal") var dummy = AccessController.doPrivileged((PrivilegedAction) () -> { initialize(); @@ -94,28 +330,7 @@ public Properties getInitialProperties() { } private static void initialize() { - props = new Properties(); - boolean overrideAll = false; - - // first load the system properties file - // to determine the value of security.overridePropertiesFile - File propFile = securityPropFile("java.security"); - boolean success = loadProps(propFile, null, false); - if (!success) { - throw new InternalError("Error loading java.security file"); - } - - if ("true".equalsIgnoreCase(props.getProperty - ("security.overridePropertiesFile"))) { - - String extraPropFile = System.getProperty - ("java.security.properties"); - if (extraPropFile != null && extraPropFile.startsWith("=")) { - overrideAll = true; - extraPropFile = extraPropFile.substring(1); - } - loadProps(null, extraPropFile, overrideAll); - } + SecPropLoader.loadAll(); initialSecurityProperties = (Properties) props.clone(); if (sdebug != null) { for (String key : props.stringPropertyNames()) { @@ -123,63 +338,6 @@ private static void initialize() { props.getProperty(key)); } } - - } - - private static boolean loadProps(File masterFile, String extraPropFile, boolean overrideAll) { - InputStream is = null; - try { - if (masterFile != null && masterFile.exists()) { - is = new FileInputStream(masterFile); - } else if (extraPropFile != null) { - extraPropFile = PropertyExpander.expand(extraPropFile); - File propFile = new File(extraPropFile); - URL propURL; - if (propFile.exists()) { - propURL = newURL - ("file:" + propFile.getCanonicalPath()); - } else { - propURL = newURL(extraPropFile); - } - - is = propURL.openStream(); - if (overrideAll) { - props = new Properties(); - if (sdebug != null) { - sdebug.println - ("overriding other security properties files!"); - } - } - } else { - // unexpected - return false; - } - props.load(is); - if (sdebug != null) { - // ExceptionInInitializerError if masterFile.getName() is - // called here (NPE!). Leave as is (and few lines down) - sdebug.println("reading security properties file: " + - masterFile == null ? extraPropFile : "java.security"); - } - return true; - } catch (IOException | PropertyExpander.ExpandException e) { - if (sdebug != null) { - sdebug.println("unable to load security properties from " + - masterFile == null ? extraPropFile : "java.security"); - e.printStackTrace(); - } - return false; - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException ioe) { - if (sdebug != null) { - sdebug.println("unable to close input stream"); - } - } - } - } } /** @@ -188,14 +346,6 @@ private static boolean loadProps(File masterFile, String extraPropFile, boolean private Security() { } - private static File securityPropFile(String filename) { - // maybe check for a system property which will specify where to - // look. Someday. - String sep = File.separator; - return new File(StaticProperty.javaHome() + sep + "conf" + sep + - "security" + sep + filename); - } - /** * Looks up providers, and returns the property (and its associated * provider) mapping the key, if any. @@ -714,17 +864,16 @@ static Object[] getImpl(String algorithm, String type, Provider provider, * denies * access to retrieve the specified security property value * @throws NullPointerException if key is {@code null} + * @throws IllegalArgumentException if key is reserved and cannot be + * used as a Security property name. Reserved keys are: + * "include". * * @see #setProperty * @see java.security.SecurityPermission */ public static String getProperty(String key) { - @SuppressWarnings("removal") - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new SecurityPermission("getProperty."+ - key)); - } + SecPropLoader.checkReservedKey(key); + check("getProperty." + key); String name = props.getProperty(key); if (name != null) name = name.trim(); // could be a class name with trailing ws @@ -749,11 +898,15 @@ public static String getProperty(String key) { * java.lang.SecurityManager#checkPermission} method * denies access to set the specified security property value * @throws NullPointerException if key or datum is {@code null} + * @throws IllegalArgumentException if key is reserved and cannot be + * used as a Security property name. Reserved keys are: + * "include". * * @see #getProperty * @see java.security.SecurityPermission */ public static void setProperty(String key, String datum) { + SecPropLoader.checkReservedKey(key); check("setProperty." + key); props.put(key, datum); invalidateSMCache(key); /* See below. */ diff --git a/src/java.base/share/classes/java/security/SignedObject.java b/src/java.base/share/classes/java/security/SignedObject.java index e2f9c764ec2da..f65300fc80814 100644 --- a/src/java.base/share/classes/java/security/SignedObject.java +++ b/src/java.base/share/classes/java/security/SignedObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 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 @@ -152,20 +152,20 @@ public final class SignedObject implements Serializable { */ public SignedObject(Serializable object, PrivateKey signingKey, Signature signingEngine) - throws IOException, InvalidKeyException, SignatureException { - // creating a stream pipe-line, from a to b - ByteArrayOutputStream b = new ByteArrayOutputStream(); - ObjectOutput a = new ObjectOutputStream(b); + throws IOException, InvalidKeyException, SignatureException { + // creating a stream pipe-line, from a to b + ByteArrayOutputStream b = new ByteArrayOutputStream(); + ObjectOutput a = new ObjectOutputStream(b); - // write and flush the object content to byte array - a.writeObject(object); - a.flush(); - a.close(); - this.content = b.toByteArray(); - b.close(); + // write and flush the object content to byte array + a.writeObject(object); + a.flush(); + a.close(); + this.content = b.toByteArray(); + b.close(); - // now sign the encapsulated object - this.sign(signingKey, signingEngine); + // now sign the encapsulated object + this.sign(signingKey, signingEngine); } /** @@ -245,12 +245,12 @@ public boolean verify(PublicKey verificationKey, * @throws SignatureException if signing fails. */ private void sign(PrivateKey signingKey, Signature signingEngine) - throws InvalidKeyException, SignatureException { - // initialize the signing engine - signingEngine.initSign(signingKey); - signingEngine.update(this.content.clone()); - this.signature = signingEngine.sign().clone(); - this.thealgorithm = signingEngine.getAlgorithm(); + throws InvalidKeyException, SignatureException { + // initialize the signing engine + signingEngine.initSign(signingKey); + signingEngine.update(this.content.clone()); + this.signature = signingEngine.sign(); + this.thealgorithm = signingEngine.getAlgorithm(); } /** @@ -263,10 +263,16 @@ private void sign(PrivateKey signingKey, Signature signingEngine) */ @Serial private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - ObjectInputStream.GetField fields = s.readFields(); - content = ((byte[])fields.get("content", null)).clone(); - signature = ((byte[])fields.get("signature", null)).clone(); - thealgorithm = (String)fields.get("thealgorithm", null); + throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = s.readFields(); + byte[] c = (byte[]) fields.get("content", null); + byte[] sig = (byte[]) fields.get("signature", null); + String a = (String) fields.get("thealgorithm", null); + if (c == null || sig == null || a == null) { + throw new InvalidObjectException("One or more null fields"); + } + content = c.clone(); + signature = sig.clone(); + thealgorithm = a; } } diff --git a/src/java.base/share/classes/java/security/Timestamp.java b/src/java.base/share/classes/java/security/Timestamp.java index 10a93a9b180f7..96df37a8c1f2d 100644 --- a/src/java.base/share/classes/java/security/Timestamp.java +++ b/src/java.base/share/classes/java/security/Timestamp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.ObjectInputStream; +import java.io.InvalidObjectException; import java.io.Serializable; import java.security.cert.CertPath; import java.security.cert.Certificate; @@ -78,7 +79,7 @@ public final class Timestamp implements Serializable { * {@code null}. */ public Timestamp(Date timestamp, CertPath signerCertPath) { - if (timestamp == null || signerCertPath == null) { + if (isNull(timestamp, signerCertPath)) { throw new NullPointerException(); } this.timestamp = new Date(timestamp.getTime()); // clone @@ -166,9 +167,16 @@ public String toString() { */ @java.io.Serial private void readObject(ObjectInputStream ois) - throws IOException, ClassNotFoundException { + throws IOException, ClassNotFoundException { ois.defaultReadObject(); + if (isNull(timestamp, signerCertPath)) { + throw new InvalidObjectException("Invalid null field(s)"); + } myhash = -1; timestamp = new Date(timestamp.getTime()); } + + private static boolean isNull(Date d, CertPath c) { + return (d == null || c == null); + } } diff --git a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java index a5f4de22d89bc..c0bdf5fc2a1f8 100644 --- a/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java +++ b/src/java.base/share/classes/java/security/UnresolvedPermissionCollection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; +import java.io.InvalidObjectException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; @@ -196,23 +197,32 @@ private void readObject(ObjectInputStream in) throws IOException, ObjectInputStream.GetField gfields = in.readFields(); // Get permissions - @SuppressWarnings("unchecked") // writeObject writes a Hashtable> // for the permissions key, so this cast is safe, unless the data is corrupt. - Hashtable> permissions = - (Hashtable>) - gfields.get("permissions", null); - perms = new ConcurrentHashMap<>(permissions.size()*2); + try { + @SuppressWarnings("unchecked") + Hashtable> permissions = + (Hashtable>) + gfields.get("permissions", null); + + if (permissions == null) { + throw new InvalidObjectException("Invalid null permissions"); + } - // Convert each entry (Vector) into a List - Set>> set = permissions.entrySet(); - for (Map.Entry> e : set) { - // Convert Vector into ArrayList - Vector vec = e.getValue(); - List list = new CopyOnWriteArrayList<>(vec); + perms = new ConcurrentHashMap<>(permissions.size()*2); - // Add to Hashtable being serialized - perms.put(e.getKey(), list); + // Convert each entry (Vector) into a List + Set>> set = permissions.entrySet(); + for (Map.Entry> e : set) { + // Convert Vector into ArrayList + Vector vec = e.getValue(); + List list = new CopyOnWriteArrayList<>(vec); + + // Add to Hashtable being serialized + perms.put(e.getKey(), list); + } + } catch (ClassCastException cce) { + throw new InvalidObjectException("Invalid type for permissions"); } } } diff --git a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java index 70083033fc6e6..6649dcda6ccb2 100644 --- a/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java +++ b/src/java.base/share/classes/java/security/cert/CertificateRevokedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -70,6 +71,13 @@ public class CertificateRevokedException extends CertificateException { private transient Map extensions; + private static boolean isNull(Date revocationDate, + CRLReason reason, X500Principal authority, + Map extensions) { + return (revocationDate == null || reason == null || authority == null + || extensions == null); + } + /** * Constructs a {@code CertificateRevokedException} with * the specified revocation date, reason code, authority name, and map @@ -92,8 +100,7 @@ public class CertificateRevokedException extends CertificateException { */ public CertificateRevokedException(Date revocationDate, CRLReason reason, X500Principal authority, Map extensions) { - if (revocationDate == null || reason == null || authority == null || - extensions == null) { + if (isNull(revocationDate, reason, authority, extensions)) { throw new NullPointerException(); } this.revocationDate = new Date(revocationDate.getTime()); @@ -234,9 +241,6 @@ private void readObject(ObjectInputStream ois) // (revocationDate, reason, authority) ois.defaultReadObject(); - // Defensively copy the revocation date - revocationDate = new Date(revocationDate.getTime()); - // Read in the size (number of mappings) of the extensions map // and create the extensions map int size = ois.readInt(); @@ -247,6 +251,13 @@ private void readObject(ObjectInputStream ois) } else { extensions = HashMap.newHashMap(Math.min(size, 20)); } + // make sure all fields are set before checking + if (isNull(revocationDate, reason, authority, extensions)) { + throw new InvalidObjectException("Invalid null field(s)"); + } + + // Defensively copy the revocation date + revocationDate = new Date(revocationDate.getTime()); // Read in the extensions and put the mappings in the extensions map for (int i = 0; i < size; i++) { diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index 58872834567d5..83cc3ec4647f4 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.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; +import java.io.ObjectStreamException; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; @@ -1181,6 +1182,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; @@ -1459,6 +1462,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 @@ -1639,6 +1645,11 @@ private void setFormatFromPattern(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; @@ -2006,24 +2017,53 @@ private static FormatStyle fromString(String text) { */ @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - boolean isValid = maxOffset >= -1 - && formats.length > maxOffset - && offsets.length > maxOffset - && argumentNumbers.length > maxOffset; + ObjectInputStream.GetField fields = in.readFields(); + if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets") + || fields.defaulted("formats") || fields.defaulted("locale") + || fields.defaulted("pattern") || fields.defaulted("maxOffset")){ + throw new InvalidObjectException("Stream has missing data"); + } + + locale = (Locale) fields.get("locale", null); + String patt = (String) fields.get("pattern", null); + int maxOff = fields.get("maxOffset", -2); + int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone(); + int[] offs = ((int[]) fields.get("offsets", null)).clone(); + Format[] fmts = ((Format[]) fields.get("formats", null)).clone(); + + // Check arrays/maxOffset have correct value/length + boolean isValid = maxOff >= -1 && argNums.length > maxOff + && offs.length > maxOff && fmts.length > maxOff; + + // Check the correctness of arguments and offsets if (isValid) { - int lastOffset = pattern.length() + 1; - for (int i = maxOffset; i >= 0; --i) { - if ((offsets[i] < 0) || (offsets[i] > lastOffset)) { + int lastOffset = patt.length() + 1; + for (int i = maxOff; i >= 0; --i) { + if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX + || offs[i] < 0 || offs[i] > lastOffset) { isValid = false; break; } else { - lastOffset = offsets[i]; + lastOffset = offs[i]; } } } + if (!isValid) { - throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream."); + throw new InvalidObjectException("Stream has invalid data"); } + maxOffset = maxOff; + pattern = patt; + offsets = offs; + formats = fmts; + argumentNumbers = argNums; + } + + /** + * Serialization without data not supported for this class. + */ + @java.io.Serial + private void readObjectNoData() throws ObjectStreamException { + throw new InvalidObjectException("Deserialized MessageFormat objects need data"); } } diff --git a/src/java.base/share/classes/java/time/ZoneId.java b/src/java.base/share/classes/java/time/ZoneId.java index 21c9054761c21..47758b64e54ae 100644 --- a/src/java.base/share/classes/java/time/ZoneId.java +++ b/src/java.base/share/classes/java/time/ZoneId.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 @@ -186,15 +186,12 @@ public abstract sealed class ZoneId implements Serializable permits ZoneOffset, * This map allows the IDs to continue to be used via the * {@link #of(String, Map)} factory method. *

    - * This map contains a mapping of the IDs that is in line with TZDB 2005r and + * This map contains a mapping of the IDs that is in line with TZDB 2024b and * later, where 'EST', 'MST' and 'HST' map to IDs which do not include daylight - * savings. + * savings since 1970. This mapping may change in update releases in support of new versions of TZDB. *

    * This maps as follows: *

      - *
    • EST - -05:00
    • - *
    • HST - -10:00
    • - *
    • MST - -07:00
    • *
    • ACT - Australia/Darwin
    • *
    • AET - Australia/Sydney
    • *
    • AGT - America/Argentina/Buenos_Aires
    • @@ -208,10 +205,13 @@ public abstract sealed class ZoneId implements Serializable permits ZoneOffset, *
    • CTT - Asia/Shanghai
    • *
    • EAT - Africa/Addis_Ababa
    • *
    • ECT - Europe/Paris
    • + *
    • EST - America/Panama
    • + *
    • HST - Pacific/Honolulu
    • *
    • IET - America/Indiana/Indianapolis
    • *
    • IST - Asia/Kolkata
    • *
    • JST - Asia/Tokyo
    • *
    • MIT - Pacific/Apia
    • + *
    • MST - America/Phoenix
    • *
    • NET - Asia/Yerevan
    • *
    • NST - Pacific/Auckland
    • *
    • PLT - Asia/Karachi
    • @@ -249,9 +249,9 @@ public abstract sealed class ZoneId implements Serializable permits ZoneOffset, entry("PST", "America/Los_Angeles"), entry("SST", "Pacific/Guadalcanal"), entry("VST", "Asia/Ho_Chi_Minh"), - entry("EST", "-05:00"), - entry("MST", "-07:00"), - entry("HST", "-10:00") + entry("EST", "America/Panama"), + entry("MST", "America/Phoenix"), + entry("HST", "Pacific/Honolulu") ); /** * Serialization version. diff --git a/src/java.base/share/classes/java/util/ArrayDeque.java b/src/java.base/share/classes/java/util/ArrayDeque.java index 9997110b658ee..60bb3ed4a4572 100644 --- a/src/java.base/share/classes/java/util/ArrayDeque.java +++ b/src/java.base/share/classes/java/util/ArrayDeque.java @@ -38,6 +38,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; import jdk.internal.access.SharedSecrets; +import jdk.internal.util.ArraysSupport; /** * Resizable-array implementation of the {@link Deque} interface. Array @@ -124,12 +125,9 @@ public class ArrayDeque extends AbstractCollection transient int tail; /** - * The maximum size of array to allocate. - * Some VMs reserve some header words in an array. - * Attempts to allocate larger arrays may result in - * OutOfMemoryError: Requested array size exceeds VM limit + * The maximum size of array to allocate */ - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + private static final int MAX_ARRAY_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; /** * Increases the capacity of this deque by at least the given amount. diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java index bcf7b79e78024..c00b130a553a2 100644 --- a/src/java.base/share/classes/java/util/ArrayList.java +++ b/src/java.base/share/classes/java/util/ArrayList.java @@ -1808,6 +1808,7 @@ private void replaceAllRange(UnaryOperator operator, int i, int end) { @Override public void sort(Comparator c) { sortRange(c, 0, size); + modCount++; } @SuppressWarnings("unchecked") @@ -1816,7 +1817,6 @@ private void sortRange(Comparator c, int fromIndex, int toIndex) { Arrays.sort((E[]) elementData, fromIndex, toIndex, c); if (modCount != expectedModCount) throw new ConcurrentModificationException(); - modCount++; } void checkInvariants() { diff --git a/src/java.base/share/classes/java/util/BitSet.java b/src/java.base/share/classes/java/util/BitSet.java index fea9693d7ed0d..de4910b20c6d5 100644 --- a/src/java.base/share/classes/java/util/BitSet.java +++ b/src/java.base/share/classes/java/util/BitSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1184,7 +1184,7 @@ private void readObject(ObjectInputStream s) public String toString() { checkInvariants(); - final int MAX_INITIAL_CAPACITY = Integer.MAX_VALUE - 8; + final int MAX_INITIAL_CAPACITY = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; int numBits = (wordsInUse > 128) ? cardinality() : wordsInUse * BITS_PER_WORD; // Avoid overflow in the case of a humongous numBits diff --git a/src/java.base/share/classes/java/util/Currency.java b/src/java.base/share/classes/java/util/Currency.java index 14d1cf4351d97..b11b774e6142a 100644 --- a/src/java.base/share/classes/java/util/Currency.java +++ b/src/java.base/share/classes/java/util/Currency.java @@ -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 @@ -108,7 +108,7 @@ * with {@code Currency} or monetary values as it provides better handling of floating * point numbers and their operations. * - * @spec http://www.iso.org/iso/home/standards/currency_codes.htm ISO - ISO 4217 - Currency codes + * @spec https://www.iso.org/iso-4217-currency-codes.html ISO - ISO 4217 - Currency codes * @see java.math.BigDecimal * @since 1.4 */ diff --git a/src/java.base/share/classes/java/util/Hashtable.java b/src/java.base/share/classes/java/util/Hashtable.java index 1fd56d5a258e5..ddff8f7a0ab68 100644 --- a/src/java.base/share/classes/java/util/Hashtable.java +++ b/src/java.base/share/classes/java/util/Hashtable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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,6 +30,7 @@ import java.util.function.Function; import java.util.function.BiFunction; import jdk.internal.access.SharedSecrets; +import jdk.internal.util.ArraysSupport; /** * This class implements a hash table, which maps keys to values. Any @@ -390,12 +391,9 @@ public synchronized V get(Object key) { } /** - * The maximum size of array to allocate. - * Some VMs reserve some header words in an array. - * Attempts to allocate larger arrays may result in - * OutOfMemoryError: Requested array size exceeds VM limit + * The maximum size of array to allocate */ - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + private static final int MAX_ARRAY_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; /** * Increases the capacity of and internally reorganizes this diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 73132b81c09a6..5550d399a7fbd 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -2324,12 +2324,11 @@ public String getDisplayName(Locale inLocale) { // If we cannot get the message format pattern, then we use a simple // hard-coded pattern. This should not occur in practice unless the // installation is missing some core files (FormatData etc.). - StringBuilder result = new StringBuilder(); - result.append((String)displayNames[1]); - if (displayNames.length > 2) { - result.append(" ("); - result.append((String)displayNames[2]); - result.append(')'); + StringBuilder result = new StringBuilder((String) displayNames[1]); + if (displayNames[2] != null) { + result.append(" (") + .append((String) displayNames[2]) + .append(')'); } return result.toString(); } diff --git a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java index 8adfd106eebb4..d690494405085 100644 --- a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java +++ b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; @@ -3079,14 +3081,10 @@ static final class MinimalStage extends CompletableFuture { private static final VarHandle STACK; private static final VarHandle NEXT; static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - RESULT = l.findVarHandle(CompletableFuture.class, "result", Object.class); - STACK = l.findVarHandle(CompletableFuture.class, "stack", Completion.class); - NEXT = l.findVarHandle(Completion.class, "next", Completion.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } + MethodHandles.Lookup l = MethodHandles.lookup(); + RESULT = MhUtil.findVarHandle(l, "result", Object.class); + STACK = MhUtil.findVarHandle(l, "stack", Completion.class); + NEXT = MhUtil.findVarHandle(l, Completion.class, "next", Completion.class); // Reduce the risk of rare disastrous classloading in first call to // LockSupport.park: https://bugs.openjdk.org/browse/JDK-8074773 diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index 4df4f73695f62..5a23fdb05a656 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -69,6 +69,7 @@ import java.util.function.ToLongFunction; import java.util.stream.Stream; import jdk.internal.misc.Unsafe; +import jdk.internal.util.ArraysSupport; /** * A hash table supporting full concurrency of retrievals and @@ -517,7 +518,7 @@ public class ConcurrentHashMap extends AbstractMap * The largest possible (non-power of two) array size. * Needed by toArray and related methods. */ - static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + static final int MAX_ARRAY_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; /** * The default concurrency level for this table. Unused but diff --git a/src/java.base/share/classes/java/util/concurrent/Exchanger.java b/src/java.base/share/classes/java/util/concurrent/Exchanger.java index 8674ea9af39bc..75de69b3e5283 100644 --- a/src/java.base/share/classes/java/util/concurrent/Exchanger.java +++ b/src/java.base/share/classes/java/util/concurrent/Exchanger.java @@ -36,6 +36,8 @@ package java.util.concurrent; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; @@ -530,15 +532,11 @@ public V exchange(V x, long timeout, TimeUnit unit) private static final VarHandle ENTRY; private static final VarHandle AA; static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - BOUND = l.findVarHandle(Exchanger.class, "bound", int.class); - MATCH = l.findVarHandle(Node.class, "match", Object.class); - ENTRY = l.findVarHandle(Slot.class, "entry", Node.class); - AA = MethodHandles.arrayElementVarHandle(Slot[].class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } + MethodHandles.Lookup l = MethodHandles.lookup(); + BOUND = MhUtil.findVarHandle(l, "bound", int.class); + MATCH = MhUtil.findVarHandle(l, Node.class, "match", Object.class); + ENTRY = MhUtil.findVarHandle(l, Slot.class, "entry", Node.class); + AA = MethodHandles.arrayElementVarHandle(Slot[].class); } } diff --git a/src/java.base/share/classes/java/util/concurrent/FutureTask.java b/src/java.base/share/classes/java/util/concurrent/FutureTask.java index 627e69559c856..b905e71aeef95 100644 --- a/src/java.base/share/classes/java/util/concurrent/FutureTask.java +++ b/src/java.base/share/classes/java/util/concurrent/FutureTask.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; @@ -582,14 +584,10 @@ public String toString() { private static final VarHandle RUNNER; private static final VarHandle WAITERS; static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - STATE = l.findVarHandle(FutureTask.class, "state", int.class); - RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class); - WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = MhUtil.findVarHandle(l, "state", int.class); + RUNNER = MhUtil.findVarHandle(l, "runner", Thread.class); + WAITERS = MhUtil.findVarHandle(l, "waiters", WaitNode.class); // Reduce the risk of rare disastrous classloading in first call to // LockSupport.park: https://bugs.openjdk.org/browse/JDK-8074773 diff --git a/src/java.base/share/classes/java/util/concurrent/Phaser.java b/src/java.base/share/classes/java/util/concurrent/Phaser.java index 22e2f663cb9a7..9cd2376d84714 100644 --- a/src/java.base/share/classes/java/util/concurrent/Phaser.java +++ b/src/java.base/share/classes/java/util/concurrent/Phaser.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.concurrent.atomic.AtomicReference; @@ -1137,15 +1139,9 @@ public boolean block() { } // VarHandle mechanics - private static final VarHandle STATE; + private static final VarHandle STATE = MhUtil.findVarHandle( + MethodHandles.lookup(), "state", long.class); static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - STATE = l.findVarHandle(Phaser.class, "state", long.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - // Reduce the risk of rare disastrous classloading in first call to // LockSupport.park: https://bugs.openjdk.org/browse/JDK-8074773 Class ensureLoaded = LockSupport.class; diff --git a/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java b/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java index fdb0128b7a755..f51e356736dea 100644 --- a/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java +++ b/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java @@ -53,6 +53,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; import jdk.internal.access.SharedSecrets; +import jdk.internal.invoke.MhUtil; import jdk.internal.util.ArraysSupport; /** @@ -1093,15 +1094,8 @@ public void forEach(Consumer action) { } // VarHandle mechanics - private static final VarHandle ALLOCATIONSPINLOCK; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - ALLOCATIONSPINLOCK = l.findVarHandle(PriorityBlockingQueue.class, - "allocationSpinLock", - int.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle ALLOCATIONSPINLOCK = + MhUtil.findVarHandle( + MethodHandles.lookup(), "allocationSpinLock", int.class); + } diff --git a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java index 31b4cd6c6a613..edb2708934d90 100644 --- a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java +++ b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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.util.function.Supplier; import jdk.internal.javac.PreviewFeature; import jdk.internal.misc.ThreadFlock; +import jdk.internal.invoke.MhUtil; /** * A basic API for structured concurrency. {@code StructuredTaskScope} supports @@ -990,13 +991,9 @@ public static final class ShutdownOnSuccess extends StructuredTaskScope { private static final VarHandle FIRST_RESULT; private static final VarHandle FIRST_EXCEPTION; static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - FIRST_RESULT = l.findVarHandle(ShutdownOnSuccess.class, "firstResult", Object.class); - FIRST_EXCEPTION = l.findVarHandle(ShutdownOnSuccess.class, "firstException", Throwable.class); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } + MethodHandles.Lookup l = MethodHandles.lookup(); + FIRST_RESULT = MhUtil.findVarHandle(l, "firstResult", Object.class); + FIRST_EXCEPTION = MhUtil.findVarHandle(l, "firstException", Throwable.class); } private volatile Object firstResult; private volatile Throwable firstException; @@ -1177,15 +1174,8 @@ public T result(Function esf) thro */ @PreviewFeature(feature = PreviewFeature.Feature.STRUCTURED_CONCURRENCY) public static final class ShutdownOnFailure extends StructuredTaskScope { - private static final VarHandle FIRST_EXCEPTION; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - FIRST_EXCEPTION = l.findVarHandle(ShutdownOnFailure.class, "firstException", Throwable.class); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle FIRST_EXCEPTION = + MhUtil.findVarHandle(MethodHandles.lookup(), "firstException", Throwable.class); private volatile Throwable firstException; /** diff --git a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java index b27c57114d620..a2fc3ac92bdbc 100644 --- a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java +++ b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.ArrayList; @@ -1505,16 +1507,10 @@ else if (timed) static final VarHandle QA; static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - CTL = l.findVarHandle(BufferedSubscription.class, "ctl", - int.class); - DEMAND = l.findVarHandle(BufferedSubscription.class, "demand", - long.class); - QA = MethodHandles.arrayElementVarHandle(Object[].class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } + MethodHandles.Lookup l = MethodHandles.lookup(); + CTL = MhUtil.findVarHandle(l, "ctl", int.class); + DEMAND = MhUtil.findVarHandle(l, "demand", long.class); + QA = MethodHandles.arrayElementVarHandle(Object[].class); // Reduce the risk of rare disastrous classloading in first call to // LockSupport.park: https://bugs.openjdk.org/browse/JDK-8074773 diff --git a/src/java.base/share/classes/java/util/concurrent/ThreadPerTaskExecutor.java b/src/java.base/share/classes/java/util/concurrent/ThreadPerTaskExecutor.java index ca9ce08db7466..e98ece084c0df 100644 --- a/src/java.base/share/classes/java/util/concurrent/ThreadPerTaskExecutor.java +++ b/src/java.base/share/classes/java/util/concurrent/ThreadPerTaskExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -38,6 +38,7 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.invoke.MhUtil; import jdk.internal.vm.ThreadContainer; import jdk.internal.vm.ThreadContainers; @@ -48,15 +49,8 @@ class ThreadPerTaskExecutor extends ThreadContainer implements ExecutorService { private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static final Permission MODIFY_THREAD = new RuntimePermission("modifyThread"); - private static final VarHandle STATE; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - STATE = l.findVarHandle(ThreadPerTaskExecutor.class, "state", int.class); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle STATE = MhUtil.findVarHandle( + MethodHandles.lookup(), "state", int.class); private final ThreadFactory factory; private final Set threads = ConcurrentHashMap.newKeySet(); @@ -506,14 +500,10 @@ private static class AnyResultHolder { private static final VarHandle EXCEPTION; private static final VarHandle EXCEPTION_COUNT; static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - RESULT = l.findVarHandle(AnyResultHolder.class, "result", Object.class); - EXCEPTION = l.findVarHandle(AnyResultHolder.class, "exception", Throwable.class); - EXCEPTION_COUNT = l.findVarHandle(AnyResultHolder.class, "exceptionCount", int.class); - } catch (Exception e) { - throw new InternalError(e); - } + MethodHandles.Lookup l = MethodHandles.lookup(); + RESULT = MhUtil.findVarHandle(l, "result", Object.class); + EXCEPTION = MhUtil.findVarHandle(l, "exception", Throwable.class); + EXCEPTION_COUNT = MhUtil.findVarHandle(l, "exceptionCount", int.class); } private static final Object NULL = new Object(); diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java index c6671deaa2eb5..fb171e9c1d99f 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java @@ -35,6 +35,8 @@ package java.util.concurrent.atomic; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -50,15 +52,8 @@ */ public class AtomicBoolean implements java.io.Serializable { private static final long serialVersionUID = 4654671469794556979L; - private static final VarHandle VALUE; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle VALUE = MhUtil.findVarHandle( + MethodHandles.lookup(), "value", int.class); private volatile int value; diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java index 8e1727c8f1972..d74c3f2ef4f4f 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java @@ -35,6 +35,8 @@ package java.util.concurrent.atomic; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -190,16 +192,8 @@ public boolean attemptMark(V expectedReference, boolean newMark) { } // VarHandle mechanics - private static final VarHandle PAIR; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - PAIR = l.findVarHandle(AtomicMarkableReference.class, "pair", - Pair.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle PAIR = MhUtil.findVarHandle( + MethodHandles.lookup(), "pair", Pair.class); private boolean casPair(Pair cmp, Pair val) { return PAIR.compareAndSet(this, cmp, val); diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java index da445da61badc..e512c91f2dfac 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java @@ -35,6 +35,8 @@ package java.util.concurrent.atomic; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.function.BinaryOperator; @@ -50,15 +52,8 @@ */ public class AtomicReference implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; - private static final VarHandle VALUE; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle VALUE = MhUtil.findVarHandle( + MethodHandles.lookup(), "value", Object.class); @SuppressWarnings("serial") // Conditionally serializable private volatile V value; diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java index b4152c50855c1..bac164b874a1d 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java @@ -35,6 +35,8 @@ package java.util.concurrent.atomic; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -190,16 +192,8 @@ public boolean attemptStamp(V expectedReference, int newStamp) { } // VarHandle mechanics - private static final VarHandle PAIR; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - PAIR = l.findVarHandle(AtomicStampedReference.class, "pair", - Pair.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle PAIR = MhUtil.findVarHandle( + MethodHandles.lookup(), "pair", Pair.class); private boolean casPair(Pair cmp, Pair val) { return PAIR.compareAndSet(this, cmp, val); diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java b/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java index 61a9f4d79e9a3..04ae2b4515825 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java @@ -35,6 +35,8 @@ package java.util.concurrent.atomic; +import jdk.internal.invoke.MhUtil; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.Arrays; @@ -138,15 +140,8 @@ final long getAndSet(long val) { } // VarHandle mechanics - private static final VarHandle VALUE; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - VALUE = l.findVarHandle(Cell.class, "value", long.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } + private static final VarHandle VALUE = MhUtil.findVarHandle( + MethodHandles.lookup(), "value", long.class); } /** Number of CPUS, to place bound on table size */ @@ -381,14 +376,12 @@ else if (casBase(v = base, apply(fn, v, x))) private static final VarHandle CELLSBUSY; private static final VarHandle THREAD_PROBE; static { - try { - MethodHandles.Lookup l1 = MethodHandles.lookup(); - BASE = l1.findVarHandle(Striped64.class, - "base", long.class); - CELLSBUSY = l1.findVarHandle(Striped64.class, - "cellsBusy", int.class); + MethodHandles.Lookup l1 = MethodHandles.lookup(); + + BASE = MhUtil.findVarHandle(l1, "base", long.class); + CELLSBUSY = MhUtil.findVarHandle(l1, "cellsBusy", int.class); @SuppressWarnings("removal") - MethodHandles.Lookup l2 = java.security.AccessController.doPrivileged( + MethodHandles.Lookup l2 = java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<>() { public MethodHandles.Lookup run() { try { @@ -397,11 +390,7 @@ public MethodHandles.Lookup run() { throw new ExceptionInInitializerError(e); } }}); - THREAD_PROBE = l2.findVarHandle(Thread.class, - "threadLocalRandomProbe", int.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } + THREAD_PROBE = MhUtil.findVarHandle(l2, "threadLocalRandomProbe", int.class); } } diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java index 77dfed3b84f2e..3dc3a49dc778e 100644 --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -90,7 +90,7 @@ *

      If the {@code verify} flag is on when opening a signed jar file, the content * of the jar entry is verified against the signature embedded inside the manifest * that is associated with its {@link JarEntry#getRealName() path name}. For a - * multi-release jar file, the content of a versioned entry is verfieid against + * multi-release jar file, the content of a versioned entry is verified against * its own signature and {@link JarEntry#getCodeSigners()} returns its own signers. * * Please note that the verification process does not include validating the diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index 0e87bebdcbfec..654adb5376f48 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -1502,8 +1502,8 @@ public static String quote(String s) { return "\\Q" + s + "\\E"; int lenHint = s.length(); - lenHint = (lenHint < Integer.MAX_VALUE - 8 - lenHint) ? - (lenHint << 1) : (Integer.MAX_VALUE - 8); + lenHint = (lenHint < ArraysSupport.SOFT_MAX_ARRAY_LENGTH - lenHint) ? + (lenHint << 1) : ArraysSupport.SOFT_MAX_ARRAY_LENGTH; StringBuilder sb = new StringBuilder(lenHint); sb.append("\\Q"); diff --git a/src/java.base/share/classes/java/util/stream/ForEachOps.java b/src/java.base/share/classes/java/util/stream/ForEachOps.java index 0c6b8aa665a78..1ba5970915226 100644 --- a/src/java.base/share/classes/java/util/stream/ForEachOps.java +++ b/src/java.base/share/classes/java/util/stream/ForEachOps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,8 @@ */ package java.util.stream; +import jdk.internal.invoke.MhUtil; + import java.util.Objects; import java.util.Spliterator; import java.util.concurrent.CountedCompleter; @@ -370,15 +372,8 @@ static final class ForEachOrderedTask extends CountedCompleter { private Node node; private ForEachOrderedTask next; - private static final VarHandle NEXT; - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - NEXT = l.findVarHandle(ForEachOrderedTask.class, "next", ForEachOrderedTask.class); - } catch (Exception e) { - throw new InternalError(e); - } - } + private static final VarHandle NEXT = MhUtil.findVarHandle( + MethodHandles.lookup(), "next", ForEachOrderedTask.class); protected ForEachOrderedTask(PipelineHelper helper, Spliterator spliterator, diff --git a/src/java.base/share/classes/java/util/stream/GathererOp.java b/src/java.base/share/classes/java/util/stream/GathererOp.java index 8a7073f63ef72..37f01901201eb 100644 --- a/src/java.base/share/classes/java/util/stream/GathererOp.java +++ b/src/java.base/share/classes/java/util/stream/GathererOp.java @@ -24,6 +24,7 @@ */ package java.util.stream; +import jdk.internal.invoke.MhUtil; import jdk.internal.vm.annotation.ForceInline; import java.lang.invoke.MethodHandles; @@ -453,16 +454,8 @@ final class Hybrid extends CountedCompleter { private Spliterator spliterator; private Hybrid next; - private static final VarHandle NEXT; - - static { - try { - MethodHandles.Lookup l = MethodHandles.lookup(); - NEXT = l.findVarHandle(Hybrid.class, "next", Hybrid.class); - } catch (Exception e) { - throw new InternalError(e); - } - } + private static final VarHandle NEXT = MhUtil.findVarHandle( + MethodHandles.lookup(), "next", Hybrid.class); protected Hybrid(Spliterator spliterator) { super(null); diff --git a/src/java.base/share/classes/java/util/stream/Nodes.java b/src/java.base/share/classes/java/util/stream/Nodes.java index 7c06823b925e7..77d700917179e 100644 --- a/src/java.base/share/classes/java/util/stream/Nodes.java +++ b/src/java.base/share/classes/java/util/stream/Nodes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,8 @@ */ package java.util.stream; +import jdk.internal.util.ArraysSupport; + import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; @@ -57,7 +59,7 @@ private Nodes() { /** * The maximum size of an array that can be allocated. */ - static final long MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + static final long MAX_ARRAY_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; // IllegalArgumentException messages static final String BAD_SIZE = "Stream size exceeds max array size"; diff --git a/src/java.base/share/classes/java/util/zip/ZipCoder.java b/src/java.base/share/classes/java/util/zip/ZipCoder.java index 6692703c1b3f0..8d4a05389eede 100644 --- a/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -56,28 +56,27 @@ public static ZipCoder get(Charset charset) { } /** - * This enum represents the three possible return values for + * Constants representing the three possible return values for * {@link #compare(String, byte[], int, int, boolean)} when * this method compares a lookup name to a string encoded in the * CEN byte array. */ - enum Comparison { - /** + static final byte + /* * The lookup string is exactly equal * to the encoded string. - */ - EXACT_MATCH, - /** + */ + EXACT_MATCH = 0, + /* * The lookup string and the encoded string differs only * by the encoded string having a trailing '/' character. */ - DIRECTORY_MATCH, - /** + DIRECTORY_MATCH = 1, + /* * The lookup string and the encoded string do not match. * (They are neither an exact match or a directory match.) */ - NO_MATCH - } + NO_MATCH = 2; String toString(byte[] ba, int off, int length) { try { @@ -197,13 +196,13 @@ private CharsetEncoder encoder() { * The return values of this method are as follows: * * If the lookup name is exactly equal to the encoded string, return - * {@link Comparison#EXACT_MATCH}. + * {@link EXACT_MATCH}. * * If the parameter {@code matchDirectory} is {@code true} and the * two strings differ only by the encoded string having an extra - * trailing '/' character, then return {@link Comparison#DIRECTORY_MATCH}. + * trailing '/' character, then return {@link DIRECTORY_MATCH}. * - * Otherwise, return {@link Comparison#NO_MATCH} + * Otherwise, return {@link NO_MATCH} * * While a general implementation will need to decode bytes into a * String for comparison, this can be avoided if the String coder @@ -217,18 +216,18 @@ private CharsetEncoder encoder() { * a directory match will also be tested * */ - Comparison compare(String str, byte[] b, int off, int len, boolean matchDirectory) { + byte compare(String str, byte[] b, int off, int len, boolean matchDirectory) { String decoded = toString(b, off, len); if (decoded.startsWith(str)) { if (decoded.length() == str.length()) { - return Comparison.EXACT_MATCH; + return EXACT_MATCH; } else if (matchDirectory && decoded.length() == str.length() + 1 && decoded.endsWith("/") ) { - return Comparison.DIRECTORY_MATCH; + return DIRECTORY_MATCH; } } - return Comparison.NO_MATCH; + return NO_MATCH; } static final class UTF8ZipCoder extends ZipCoder { @@ -278,19 +277,19 @@ private boolean hasTrailingSlash(byte[] a, int end) { } @Override - Comparison compare(String str, byte[] b, int off, int len, boolean matchDirectory) { + byte compare(String str, byte[] b, int off, int len, boolean matchDirectory) { try { byte[] encoded = JLA.getBytesNoRepl(str, UTF_8.INSTANCE); int mismatch = Arrays.mismatch(encoded, 0, encoded.length, b, off, off+len); if (mismatch == -1) { - return Comparison.EXACT_MATCH; + return EXACT_MATCH; } else if (matchDirectory && len == mismatch + 1 && hasTrailingSlash(b, off + len)) { - return Comparison.DIRECTORY_MATCH; + return DIRECTORY_MATCH; } else { - return Comparison.NO_MATCH; + return NO_MATCH; } } catch (CharacterCodingException e) { - return Comparison.NO_MATCH; + return NO_MATCH; } } } diff --git a/src/java.base/share/classes/java/util/zip/ZipEntry.java b/src/java.base/share/classes/java/util/zip/ZipEntry.java index 8c8bfeb322960..b7ecd1bea8fbb 100644 --- a/src/java.base/share/classes/java/util/zip/ZipEntry.java +++ b/src/java.base/share/classes/java/util/zip/ZipEntry.java @@ -78,7 +78,7 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Approximately 128 years, in milliseconds (ignoring leap years etc). * - * This establish an approximate high-bound value for DOS times in + * This establishes an approximate high-bound value for DOS times in * milliseconds since epoch, used to enable an efficient but * sufficient bounds check to avoid generating extended last modified * time entries. @@ -564,20 +564,20 @@ void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) { // be the magic value and it "accidentally" has some // bytes in extra match the id. if (sz >= 16) { - size = get64(extra, off); - csize = get64(extra, off + 8); + size = get64S(extra, off); + csize = get64S(extra, off + 8); } } else { // CEN extra zip64 if (size == ZIP64_MAGICVAL) { if (off + 8 > len) // invalid zip64 extra break; // fields, just skip - size = get64(extra, off); + size = get64S(extra, off); } if (csize == ZIP64_MAGICVAL) { if (off + 16 > len) // invalid zip64 extra break; // fields, just skip - csize = get64(extra, off + 8); + csize = get64S(extra, off + 8); } } } @@ -588,15 +588,15 @@ void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) { int pos = off + 4; // reserved 4 bytes if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24) break; - long wtime = get64(extra, pos + 4); + long wtime = get64S(extra, pos + 4); if (wtime != WINDOWS_TIME_NOT_AVAILABLE) { mtime = winTimeToFileTime(wtime); } - wtime = get64(extra, pos + 12); + wtime = get64S(extra, pos + 12); if (wtime != WINDOWS_TIME_NOT_AVAILABLE) { atime = winTimeToFileTime(wtime); } - wtime = get64(extra, pos + 20); + wtime = get64S(extra, pos + 20); if (wtime != WINDOWS_TIME_NOT_AVAILABLE) { ctime = winTimeToFileTime(wtime); } diff --git a/src/java.base/share/classes/java/util/zip/ZipError.java b/src/java.base/share/classes/java/util/zip/ZipError.java index 2aa37bef010c2..933cc4470916e 100644 --- a/src/java.base/share/classes/java/util/zip/ZipError.java +++ b/src/java.base/share/classes/java/util/zip/ZipError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2019, 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 @@ -28,9 +28,12 @@ /** * Signals that an unrecoverable error has occurred. * + * @deprecated ZipError is no longer used and is obsolete. + * {@link ZipException} should be used instead. * @author Dave Bristor * @since 1.6 */ +@Deprecated(since="24", forRemoval = true) public class ZipError extends InternalError { @java.io.Serial private static final long serialVersionUID = 853973422266861979L; diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 333e3d0184976..21b9593c0172a 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -63,6 +63,7 @@ import jdk.internal.access.JavaUtilZipFileAccess; import jdk.internal.access.JavaUtilJarAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.util.ArraysSupport; import jdk.internal.util.OperatingSystem; import jdk.internal.perf.PerfCounter; import jdk.internal.ref.CleanerFactory; @@ -410,13 +411,10 @@ public InputStream getInputStream(ZipEntry entry) throws IOException { case DEFLATED: // Inflater likes a bit of slack // MORE: Compute good size for inflater stream: - long size = CENLEN(zsrc.cen, pos) + 2; + long size = CENSIZ(zsrc.cen, pos); if (size > 65536) { size = 8192; } - if (size <= 0) { - size = 4096; - } InputStream is = new ZipFileInflaterInputStream(in, res, (int) size); synchronized (istreams) { istreams.add(is); @@ -905,21 +903,21 @@ private void checkZIP64(byte[] cen, int cenpos) { if (size == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > end) break; - size = get64(cen, off); + size = get64S(cen, off); sz -= 8; off += 8; } if (rem == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > end) break; - rem = get64(cen, off); + rem = get64S(cen, off); sz -= 8; off += 8; } if (pos == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > end) break; - pos = get64(cen, off); + pos = get64S(cen, off); sz -= 8; off += 8; } @@ -1178,6 +1176,8 @@ private static class Source { // "META-INF/".length() private static final int META_INF_LEN = 9; private static final int[] EMPTY_META_VERSIONS = new int[0]; + // CEN size is limited to the maximum array size in the JDK + private static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; private final Key key; // the key in files private final @Stable ZipCoder zc; // ZIP coder used to decode/encode @@ -1185,7 +1185,7 @@ private static class Source { private int refs = 1; private RandomAccessFile zfile; // zfile of the underlying ZIP file - private byte[] cen; // CEN & ENDHDR + private byte[] cen; // CEN private long locpos; // position of first LOC header (usually 0) private byte[] comment; // ZIP file comment // list of meta entries in META-INF dir @@ -1236,12 +1236,12 @@ private int checkAndAddEntry(int pos, int index) int nlen = CENNAM(cen, pos); int elen = CENEXT(cen, pos); int clen = CENCOM(cen, pos); - long headerSize = (long)CENHDR + nlen + clen + elen; + int headerSize = CENHDR + nlen + clen + elen; // CEN header size + name length + comment length + extra length // should not exceed 65,535 bytes per the PKWare APP.NOTE // 4.4.10, 4.4.11, & 4.4.12. Also check that current CEN header will // not exceed the length of the CEN array - if (headerSize > 0xFFFF || pos + headerSize > cen.length - ENDHDR) { + if (headerSize > 0xFFFF || pos > cen.length - headerSize) { zerror("invalid CEN header (bad header size)"); } @@ -1294,7 +1294,7 @@ private void checkExtraFields(int cenPos, int startingOffset, } // CEN Offset where this Extra field ends int extraEndOffset = startingOffset + extraFieldLen; - if (extraEndOffset > cen.length - ENDHDR) { + if (extraEndOffset > cen.length) { zerror("Invalid CEN header (extra data field size too long)"); } int currentOffset = startingOffset; @@ -1373,7 +1373,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, // Check the uncompressed size is not negative if (size == ZIP64_MAGICVAL) { if ( blockSize >= Long.BYTES) { - if (get64(cen, off) < 0) { + if (get64S(cen, off) < 0) { zerror("Invalid zip64 extra block size value"); } off += Long.BYTES; @@ -1385,7 +1385,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, // Check the compressed size is not negative if (csize == ZIP64_MAGICVAL) { if (blockSize >= Long.BYTES) { - if (get64(cen, off) < 0) { + if (get64S(cen, off) < 0) { zerror("Invalid zip64 extra block compressed size value"); } off += Long.BYTES; @@ -1397,7 +1397,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, // Check the LOC offset is not negative if (locoff == ZIP64_MAGICVAL) { if (blockSize >= Long.BYTES) { - if (get64(cen, off) < 0) { + if (get64S(cen, off) < 0) { zerror("Invalid zip64 extra block LOC OFFSET value"); } // Note: We do not need to adjust the following fields as @@ -1605,7 +1605,7 @@ private final int readAt(byte[] buf, int off, int len, long pos) private static class End { - int centot; // 4 bytes + long centot; // 4 bytes long cenlen; // 4 bytes long cenoff; // 4 bytes long endpos; // 4 bytes @@ -1638,10 +1638,7 @@ private End findEND() throws IOException { } // Now scan the block backwards for END header signature for (int i = buf.length - ENDHDR; i >= 0; i--) { - if (buf[i+0] == (byte)'P' && - buf[i+1] == (byte)'K' && - buf[i+2] == (byte)'\005' && - buf[i+3] == (byte)'\006') { + if (get32(buf, i) == ENDSIG) { // Found ENDSIG header byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR); end.centot = ENDTOT(endbuf); @@ -1661,9 +1658,9 @@ private End findEND() throws IOException { if (cenpos < 0 || locpos < 0 || readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 || - GETSIG(sbuf) != CENSIG || + get32(sbuf, 0) != CENSIG || readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 || - GETSIG(sbuf) != LOCSIG) { + get32(sbuf, 0) != LOCSIG) { continue; } } @@ -1678,13 +1675,13 @@ private End findEND() throws IOException { byte[] loc64 = new byte[ZIP64_LOCHDR]; if (end.endpos < ZIP64_LOCHDR || readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) - != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) { + != loc64.length || get32(loc64, 0) != ZIP64_LOCSIG) { return end; } long end64pos = ZIP64_LOCOFF(loc64); byte[] end64buf = new byte[ZIP64_ENDHDR]; if (readFullyAt(end64buf, 0, end64buf.length, end64pos) - != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) { + != end64buf.length || get32(end64buf, 0) != ZIP64_ENDSIG) { return end; } // end64 candidate found, @@ -1700,7 +1697,7 @@ private End findEND() throws IOException { // to use the end64 values end.cenlen = cenlen64; end.cenoff = cenoff64; - end.centot = (int)centot64; // assume total < 2g + end.centot = centot64; end.endpos = end64pos; } catch (IOException x) {} // no ZIP64 loc/end return end; @@ -1732,15 +1729,18 @@ private void initCEN(int knownTotal) throws IOException { if (locpos < 0) { zerror("invalid END header (bad central directory offset)"); } - // read in the CEN and END - if (end.cenlen + ENDHDR >= Integer.MAX_VALUE) { + // read in the CEN + if (end.cenlen > MAX_CEN_SIZE) { zerror("invalid END header (central directory size too large)"); } - cen = this.cen = new byte[(int)(end.cenlen + ENDHDR)]; - if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { + if (end.centot < 0 || end.centot > end.cenlen / CENHDR) { + zerror("invalid END header (total entries count too large)"); + } + cen = this.cen = new byte[(int)end.cenlen]; + if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen) { zerror("read CEN tables failed"); } - this.total = end.centot; + this.total = Math.toIntExact(end.centot); } else { cen = this.cen; this.total = knownTotal; @@ -1765,18 +1765,18 @@ private void initCEN(int knownTotal) throws IOException { // Iterate through the entries in the central directory int idx = 0; // Index into the entries array int pos = 0; - int entryPos = CENHDR; - int limit = cen.length - ENDHDR; manifestNum = 0; - while (entryPos <= limit) { + int limit = cen.length - CENHDR; + while (pos <= limit) { if (idx >= entriesLength) { // This will only happen if the ZIP file has an incorrect // ENDTOT field, which usually means it contains more than // 65535 entries. - initCEN(countCENHeaders(cen, limit)); + initCEN(countCENHeaders(cen)); return; } + int entryPos = pos + CENHDR; // Checks the entry and adds values to entries[idx ... idx+2] int nlen = checkAndAddEntry(pos, idx); idx += 3; @@ -1807,7 +1807,6 @@ private void initCEN(int knownTotal) throws IOException { } // skip to the start of the next entry pos = nextEntryPos(pos, entryPos, nlen); - entryPos = pos + CENHDR; } // Adjust the total entries @@ -1829,7 +1828,7 @@ private void initCEN(int knownTotal) throws IOException { } else { metaVersions = EMPTY_META_VERSIONS; } - if (pos + ENDHDR != cen.length) { + if (pos != cen.length) { zerror("invalid CEN header (bad header size)"); } } @@ -1869,15 +1868,15 @@ private EntryPos getEntryPos(String name, boolean addSlash) { // Compare the lookup name with the name encoded in the CEN switch (zc.compare(name, cen, noff, nlen, addSlash)) { - case EXACT_MATCH: + case ZipCoder.EXACT_MATCH: // We found an exact match for "name" return new EntryPos(name, pos); - case DIRECTORY_MATCH: + case ZipCoder.DIRECTORY_MATCH: // We found the directory "name/" // Track its position, then continue the search for "name" dirPos = pos; break; - case NO_MATCH: + case ZipCoder.NO_MATCH: // Hash collision, continue searching } } @@ -2031,17 +2030,20 @@ private int getMetaVersion(int off, int len) { /** * Returns the number of CEN headers in a central directory. - * Will not throw, even if the ZIP file is corrupt. * * @param cen copy of the bytes in a ZIP file's central directory - * @param size number of bytes in central directory + * @throws ZipException if a CEN header exceeds the length of the CEN array */ - private static int countCENHeaders(byte[] cen, int size) { + private static int countCENHeaders(byte[] cen) throws ZipException { int count = 0; - for (int p = 0; - p + CENHDR <= size; - p += CENHDR + CENNAM(cen, p) + CENEXT(cen, p) + CENCOM(cen, p)) + for (int p = 0; p <= cen.length - CENHDR;) { + int headerSize = CENHDR + CENNAM(cen, p) + CENEXT(cen, p) + CENCOM(cen, p); + if (p > cen.length - headerSize) { + zerror("invalid CEN header (bad header size)"); + } + p += headerSize; count++; + } return count; } } diff --git a/src/java.base/share/classes/java/util/zip/ZipInputStream.java b/src/java.base/share/classes/java/util/zip/ZipInputStream.java index 5302cf7516077..3a433cf5c6d8f 100644 --- a/src/java.base/share/classes/java/util/zip/ZipInputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipInputStream.java @@ -603,14 +603,14 @@ private void readEnd(ZipEntry e) throws IOException { long sig = get32(tmpbuf, 0); if (sig != EXTSIG) { // no EXTSIG present e.crc = sig; - e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC); - e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC); + e.csize = get64S(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC); + e.size = get64S(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC); ((PushbackInputStream)in).unread( tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC, ZIP64_EXTCRC); } else { e.crc = get32(tmpbuf, ZIP64_EXTCRC); - e.csize = get64(tmpbuf, ZIP64_EXTSIZ); - e.size = get64(tmpbuf, ZIP64_EXTLEN); + e.csize = get64S(tmpbuf, ZIP64_EXTSIZ); + e.size = get64S(tmpbuf, ZIP64_EXTLEN); } } else { readFully(tmpbuf, 0, EXTHDR); diff --git a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java index f68c27b265ee1..f9712731a0894 100644 --- a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java @@ -378,6 +378,10 @@ public synchronized void write(byte[] b, int off, int len) * Finishes writing the contents of the ZIP output stream without closing * the underlying stream. Use this method when applying multiple filters * in succession to the same output stream. + *

      + * A ZipException will be thrown if the combined length, after encoding, + * of the entry name, the extra field data, the entry comment and + * {@linkplain #CENHDR CEN Header size}, exceeds 65,535 bytes. * @throws ZipException if a ZIP file error has occurred * @throws IOException if an I/O exception has occurred */ @@ -398,7 +402,9 @@ public void finish() throws IOException { } /** - * Closes the ZIP output stream as well as the stream being filtered. + * Closes the underlying stream and the stream being filtered after + * the contents of the ZIP output stream are fully written. + * * @throws ZipException if a ZIP file error has occurred * @throws IOException if an I/O error has occurred */ @@ -589,7 +595,8 @@ private void writeCEN(XEntry xentry) throws IOException { writeInt(csize); // compressed size writeInt(size); // uncompressed size byte[] nameBytes = zc.getBytes(e.name); - writeShort(nameBytes.length); + int nlen = nameBytes.length; + writeShort(nlen); int elen = getExtraLen(e.extra); if (hasZip64) { @@ -626,20 +633,19 @@ private void writeCEN(XEntry xentry) throws IOException { } } writeShort(elen); - byte[] commentBytes; + byte[] commentBytes = null; + int clen = 0; if (e.comment != null) { commentBytes = zc.getBytes(e.comment); - writeShort(Math.min(commentBytes.length, 0xffff)); - } else { - commentBytes = null; - writeShort(0); + clen = Math.min(commentBytes.length, 0xffff); } + writeShort(clen); // file comment length writeShort(0); // starting disk number writeShort(0); // internal file attributes (unused) // extra file attributes, used for storing posix permissions etc. writeInt(e.externalFileAttributes > 0 ? e.externalFileAttributes << 16 : 0); writeInt(offset); // relative offset of local header - writeBytes(nameBytes, 0, nameBytes.length); + writeBytes(nameBytes, 0, nlen); // take care of EXTID_ZIP64 and EXTID_EXTT if (hasZip64) { @@ -679,9 +685,17 @@ private void writeCEN(XEntry xentry) throws IOException { } } } + + // CEN header size + name length + comment length + extra length + // should not exceed 65,535 bytes per the PKWare APP.NOTE + // 4.4.10, 4.4.11, & 4.4.12. + long headerSize = (long)CENHDR + nlen + clen + elen; + if (headerSize > 0xFFFF ) { + throw new ZipException("invalid CEN header (bad header size)"); + } writeExtra(e.extra); if (commentBytes != null) { - writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff)); + writeBytes(commentBytes, 0, clen); } } diff --git a/src/java.base/share/classes/java/util/zip/ZipUtils.java b/src/java.base/share/classes/java/util/zip/ZipUtils.java index ab37fc03a5699..5b1d896f4208e 100644 --- a/src/java.base/share/classes/java/util/zip/ZipUtils.java +++ b/src/java.base/share/classes/java/util/zip/ZipUtils.java @@ -39,6 +39,7 @@ import jdk.internal.access.JavaNioAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; +import jdk.internal.util.Preconditions; class ZipUtils { @@ -170,7 +171,10 @@ static LocalDateTime javaEpochToLocalDateTime(long time) { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final int get16(byte[] b, int off) { - return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 1, b.length, Preconditions.AIOOBE_FORMATTER); + return Short.toUnsignedInt( + UNSAFE.getShortUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false)); } /** @@ -178,15 +182,20 @@ public static final int get16(byte[] b, int off) { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final long get32(byte[] b, int off) { - return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 3, b.length, Preconditions.AIOOBE_FORMATTER); + return Integer.toUnsignedLong( + UNSAFE.getIntUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false)); } /** * Fetches signed 64-bit value from byte array at specified offset. * The bytes are assumed to be in Intel (little-endian) byte order. */ - public static final long get64(byte[] b, int off) { - return get32(b, off) | (get32(b, off+4) << 32); + public static final long get64S(byte[] b, int off) { + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 7, b.length, Preconditions.AIOOBE_FORMATTER); + return UNSAFE.getLongUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false); } /** @@ -195,28 +204,9 @@ public static final long get64(byte[] b, int off) { * */ public static final int get32S(byte[] b, int off) { - return (get16(b, off) | (get16(b, off+2) << 16)); - } - - // fields access methods - static final int CH(byte[] b, int n) { - return b[n] & 0xff ; - } - - static final int SH(byte[] b, int n) { - return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); - } - - static final long LG(byte[] b, int n) { - return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; - } - - static final long LL(byte[] b, int n) { - return (LG(b, n)) | (LG(b, n + 4) << 32); - } - - static final long GETSIG(byte[] b) { - return LG(b, 0); + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 3, b.length, Preconditions.AIOOBE_FORMATTER); + return UNSAFE.getIntUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false); } /* @@ -231,56 +221,56 @@ static final long GETSIG(byte[] b) { // local file (LOC) header fields - static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature - static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract - static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags - static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method - static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time - static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data - static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size - static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size - static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length - static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length + static final long LOCSIG(byte[] b) { return get32(b, 0); } // signature + static final int LOCVER(byte[] b) { return get16(b, 4); } // version needed to extract + static final int LOCFLG(byte[] b) { return get16(b, 6); } // general purpose bit flags + static final int LOCHOW(byte[] b) { return get16(b, 8); } // compression method + static final long LOCTIM(byte[] b) { return get32(b, 10);} // modification time + static final long LOCCRC(byte[] b) { return get32(b, 14);} // crc of uncompressed data + static final long LOCSIZ(byte[] b) { return get32(b, 18);} // compressed data size + static final long LOCLEN(byte[] b) { return get32(b, 22);} // uncompressed data size + static final int LOCNAM(byte[] b) { return get16(b, 26);} // filename length + static final int LOCEXT(byte[] b) { return get16(b, 28);} // extra field length // extra local (EXT) header fields - static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data - static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size - static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size + static final long EXTCRC(byte[] b) { return get32(b, 4);} // crc of uncompressed data + static final long EXTSIZ(byte[] b) { return get32(b, 8);} // compressed size + static final long EXTLEN(byte[] b) { return get32(b, 12);} // uncompressed size // end of central directory header (END) fields - static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk - static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries - static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size - static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset - static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of ZIP file comment - static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} - - // zip64 end of central directory recoder fields - static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk - static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries - static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size - static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset - static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset + static final int ENDSUB(byte[] b) { return get16(b, 8); } // number of entries on this disk + static final int ENDTOT(byte[] b) { return get16(b, 10);} // total number of entries + static final long ENDSIZ(byte[] b) { return get32(b, 12);} // central directory size + static final long ENDOFF(byte[] b) { return get32(b, 16);} // central directory offset + static final int ENDCOM(byte[] b) { return get16(b, 20);} // size of ZIP file comment + static final int ENDCOM(byte[] b, int off) { return get16(b, off + 20);} + + // zip64 end of central directory record fields + static final long ZIP64_ENDTOD(byte[] b) { return get64S(b, 24);} // total number of entries on disk + static final long ZIP64_ENDTOT(byte[] b) { return get64S(b, 32);} // total number of entries + static final long ZIP64_ENDSIZ(byte[] b) { return get64S(b, 40);} // central directory size + static final long ZIP64_ENDOFF(byte[] b) { return get64S(b, 48);} // central directory offset + static final long ZIP64_LOCOFF(byte[] b) { return get64S(b, 8);} // zip64 end offset // central directory header (CEN) fields - static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } - static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } - static final int CENVEM_FA(byte[] b, int pos) { return CH(b, pos + 5); } // file attribute compatibility - static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } - static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } - static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} - static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} - static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} - static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} - static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} - static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} - static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} - static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} - static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} - static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} - static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} - static final int CENATX_PERMS(byte[] b, int pos) { return SH(b, pos + 40);} // posix permission data - static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} + static final long CENSIG(byte[] b, int pos) { return get32(b, pos + 0); } + static final int CENVEM(byte[] b, int pos) { return get16(b, pos + 4); } + static final int CENVEM_FA(byte[] b, int pos) { return Byte.toUnsignedInt(b[pos + 5]); } // file attribute compatibility + static final int CENVER(byte[] b, int pos) { return get16(b, pos + 6); } + static final int CENFLG(byte[] b, int pos) { return get16(b, pos + 8); } + static final int CENHOW(byte[] b, int pos) { return get16(b, pos + 10);} + static final long CENTIM(byte[] b, int pos) { return get32(b, pos + 12);} + static final long CENCRC(byte[] b, int pos) { return get32(b, pos + 16);} + static final long CENSIZ(byte[] b, int pos) { return get32(b, pos + 20);} + static final long CENLEN(byte[] b, int pos) { return get32(b, pos + 24);} + static final int CENNAM(byte[] b, int pos) { return get16(b, pos + 28);} + static final int CENEXT(byte[] b, int pos) { return get16(b, pos + 30);} + static final int CENCOM(byte[] b, int pos) { return get16(b, pos + 32);} + static final int CENDSK(byte[] b, int pos) { return get16(b, pos + 34);} + static final int CENATT(byte[] b, int pos) { return get16(b, pos + 36);} + static final long CENATX(byte[] b, int pos) { return get32(b, pos + 38);} + static final int CENATX_PERMS(byte[] b, int pos) { return get16(b, pos + 40);} // posix permission data + static final long CENOFF(byte[] b, int pos) { return get32(b, pos + 42);} // The END header is followed by a variable length comment of size < 64k. static final long END_MAXLEN = 0xFFFF + ENDHDR; @@ -293,16 +283,16 @@ static void loadLibrary() { jdk.internal.loader.BootLoader.loadLibrary("zip"); } - private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private static final long byteBufferArrayOffset = unsafe.objectFieldOffset(ByteBuffer.class, "hb"); - private static final long byteBufferOffsetOffset = unsafe.objectFieldOffset(ByteBuffer.class, "offset"); + private static final long byteBufferArrayOffset = UNSAFE.objectFieldOffset(ByteBuffer.class, "hb"); + private static final long byteBufferOffsetOffset = UNSAFE.objectFieldOffset(ByteBuffer.class, "offset"); static byte[] getBufferArray(ByteBuffer byteBuffer) { - return (byte[]) unsafe.getReference(byteBuffer, byteBufferArrayOffset); + return (byte[]) UNSAFE.getReference(byteBuffer, byteBufferArrayOffset); } static int getBufferOffset(ByteBuffer byteBuffer) { - return unsafe.getInt(byteBuffer, byteBufferOffsetOffset); + return UNSAFE.getInt(byteBuffer, byteBufferOffsetOffset); } } diff --git a/src/java.base/share/classes/javax/crypto/CryptoPermission.java b/src/java.base/share/classes/javax/crypto/CryptoPermission.java index f13eec7a1d92a..46b5b5fdef6b6 100644 --- a/src/java.base/share/classes/javax/crypto/CryptoPermission.java +++ b/src/java.base/share/classes/javax/crypto/CryptoPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ * The {@code CryptoPermission} class extends the * {@code java.security.Permission} class. A * {@code CryptoPermission} object is used to represent - * the ability of an application/applet to use certain + * the ability of an application to use certain * algorithms with certain key sizes and other * restrictions in certain environments. * diff --git a/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java b/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java index c08187d88a646..d360b3577a01c 100644 --- a/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java +++ b/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, 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 @@ -43,7 +43,7 @@ * of which are key recovery, key weakening, and * key escrow. * - *

      Applications or applets that use an exemption mechanism may be granted + *

      Applications that use an exemption mechanism may be granted * stronger encryption capabilities than those which don't. * * @since 1.4 diff --git a/src/java.base/share/classes/javax/crypto/JceSecurityManager.java b/src/java.base/share/classes/javax/crypto/JceSecurityManager.java index e9c408a2a56c0..b178c8bfb0253 100644 --- a/src/java.base/share/classes/javax/crypto/JceSecurityManager.java +++ b/src/java.base/share/classes/javax/crypto/JceSecurityManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,9 +36,9 @@ * The JCE security manager. * *

      The JCE security manager is responsible for determining the maximum - * allowable cryptographic strength for a given applet/application, for a given + * allowable cryptographic strength for a given application, for a given * algorithm, by consulting the configured jurisdiction policy files and - * the cryptographic permissions bundled with the applet/application. + * the cryptographic permissions bundled with the application. * * @author Jan Luehe * @@ -85,7 +85,7 @@ private JceSecurityManager() { /** * Returns the maximum allowable crypto strength for the given - * applet/application, for the given algorithm. + * application, for the given algorithm. */ CryptoPermission getCryptoPermission(String theAlg) { diff --git a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java index e76a51e5d68c7..2ad9a7748f2e6 100644 --- a/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/SecretKeySpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,11 +100,9 @@ public class SecretKeySpec implements KeySpec, SecretKey { * is null or key is null or empty. */ public SecretKeySpec(byte[] key, String algorithm) { - if (key == null || algorithm == null) { - throw new IllegalArgumentException("Missing argument"); - } - if (key.length == 0) { - throw new IllegalArgumentException("Empty key"); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.key = key.clone(); this.algorithm = algorithm; @@ -266,14 +264,22 @@ void clear() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - - if (key == null || algorithm == null) { - throw new InvalidObjectException("Missing argument"); + String errMsg = doSanityCheck(key, algorithm); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); } + byte[] temp = key; + this.key = temp.clone(); + Arrays.fill(temp, (byte) 0); + } - this.key = key.clone(); - if (key.length == 0) { - throw new InvalidObjectException("Invalid key length"); + private static String doSanityCheck(byte[] key, String algorithm) { + String errMsg = null; + if (key == null || algorithm == null) { + errMsg = "Missing argument"; + } else if (key.length == 0) { + errMsg = "Empty key"; } + return errMsg; } } diff --git a/src/java.base/share/classes/javax/net/SocketFactory.java b/src/java.base/share/classes/javax/net/SocketFactory.java index dd351e9275881..9ef82caf3f9f0 100644 --- a/src/java.base/share/classes/javax/net/SocketFactory.java +++ b/src/java.base/share/classes/javax/net/SocketFactory.java @@ -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 @@ -60,7 +60,7 @@ * *

      Factory classes are specified by environment-specific configuration * mechanisms. For example, the getDefault method could return - * a factory that was appropriate for a particular user or applet, and a + * a factory that was appropriate for a particular application, and a * framework could use a factory customized to its own purposes. * * @since 1.4 diff --git a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java index c005b4ea02b0b..1c35491e4e21f 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ChoiceCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,20 +102,18 @@ public class ChoiceCallback implements Callback, java.io.Serializable { public ChoiceCallback(String prompt, String[] choices, int defaultChoice, boolean multipleSelectionsAllowed) { - if (prompt == null || prompt.isEmpty() || - choices == null || choices.length == 0 || - defaultChoice < 0 || defaultChoice >= choices.length) - throw new IllegalArgumentException(); - + choices = (choices == null || choices.length == 0 ? choices : + choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.defaultChoice = defaultChoice; this.multipleSelectionsAllowed = multipleSelectionsAllowed; - this.choices = choices.clone(); - for (int i = 0; i < choices.length; i++) { - if (choices[i] == null || choices[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.choices = choices; } /** @@ -183,9 +181,11 @@ public void setSelectedIndex(int selection) { * @see #getSelectedIndexes */ public void setSelectedIndexes(int[] selections) { - if (!multipleSelectionsAllowed) + if (!multipleSelectionsAllowed) { throw new UnsupportedOperationException(); - this.selections = selections == null ? null : selections.clone(); + } + this.selections = ((selections == null || selections.length == 0) ? + selections : selections.clone()); } /** @@ -211,26 +211,35 @@ public int[] getSelectedIndexes() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + choices = (choices == null || choices.length == 0 ? + choices : choices.clone()); + String errMsg = doSanityCheck(prompt, choices, defaultChoice, + multipleSelectionsAllowed); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } + selections = (selections == null || selections.length == 0 ? + selections : selections.clone()); + if (selections != null && selections.length > 1 && + !multipleSelectionsAllowed) { + throw new InvalidObjectException("Multiple selections not allowed"); + } + } + + private static String doSanityCheck(String prompt, String[] choices, + int defaultChoice, boolean allowMultiple) { if ((prompt == null) || prompt.isEmpty() || (choices == null) || (choices.length == 0) || (defaultChoice < 0) || (defaultChoice >= choices.length)) { - throw new InvalidObjectException( - "Missing/invalid prompt/choices"); + return "Missing/invalid prompt/choices"; } - choices = choices.clone(); for (int i = 0; i < choices.length; i++) { - if ((choices[i] == null) || choices[i].isEmpty()) - throw new InvalidObjectException("Null/empty choices"); - } - - if (selections != null) { - selections = selections.clone(); - if (!multipleSelectionsAllowed && (selections.length != 1)) { - throw new InvalidObjectException( - "Multiple selections not allowed"); + if ((choices[i] == null) || choices[i].isEmpty()) { + return "Null/empty choices value"; } } + return null; } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java index 437ce7041a7d7..a00fc7013ecc1 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/ConfirmationCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package javax.security.auth.callback; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; /** @@ -189,25 +190,10 @@ public class ConfirmationCallback implements Callback, java.io.Serializable { */ public ConfirmationCallback(int messageType, int optionType, int defaultOption) { - - if (messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } this.prompt = null; @@ -250,21 +236,20 @@ public ConfirmationCallback(int messageType, public ConfirmationCallback(int messageType, String[] options, int defaultOption) { - if (messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, null, false); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = null; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -304,27 +289,11 @@ public ConfirmationCallback(int messageType, public ConfirmationCallback(String prompt, int messageType, int optionType, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) - throw new IllegalArgumentException(); - - switch (optionType) { - case YES_NO_OPTION: - if (defaultOption != YES && defaultOption != NO) - throw new IllegalArgumentException(); - break; - case YES_NO_CANCEL_OPTION: - if (defaultOption != YES && defaultOption != NO && - defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; - case OK_CANCEL_OPTION: - if (defaultOption != OK && defaultOption != CANCEL) - throw new IllegalArgumentException(); - break; + String errMsg = doSanityCheck(messageType, optionType, false, null, + defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); } - this.prompt = prompt; this.messageType = messageType; this.optionType = optionType; @@ -369,22 +338,20 @@ public ConfirmationCallback(String prompt, int messageType, public ConfirmationCallback(String prompt, int messageType, String[] options, int defaultOption) { - if (prompt == null || prompt.isEmpty() || - messageType < INFORMATION || messageType > ERROR || - options == null || options.length == 0 || - defaultOption < 0 || defaultOption >= options.length) - throw new IllegalArgumentException(); + if (options != null) { + options = options.clone(); + } + String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true, + options, defaultOption, prompt, true); + if (errMsg != null) { + throw new IllegalArgumentException(errMsg); + } this.prompt = prompt; this.messageType = messageType; this.optionType = UNSPECIFIED_OPTION; this.defaultOption = defaultOption; - - this.options = options.clone(); - for (int i = 0; i < options.length; i++) { - if (options[i] == null || options[i].isEmpty()) - throw new IllegalArgumentException(); - } + this.options = options; } /** @@ -491,6 +458,49 @@ public int getSelectedIndex() { return selection; } + private static String doSanityCheck(int msgType, int optionType, + boolean isUnspecifiedOption, String[] options, int defOption, + String prompt, boolean checkPrompt) { + // validate msgType + if (msgType < INFORMATION || msgType > ERROR) { + return "Invalid msgType"; + } + // validate prompt if checkPrompt == true + if (checkPrompt && (prompt == null || prompt.isEmpty())) { + return "Invalid prompt"; + } + // validate optionType + if (isUnspecifiedOption) { + if (optionType != UNSPECIFIED_OPTION) { + return "Invalid optionType"; + } + // check options + if (options == null || options.length == 0 || + defOption < 0 || defOption >= options.length) { + return "Invalid options and/or default option"; + } + for (String ov : options) { + if (ov == null || ov.isEmpty()) { + return "Invalid option value"; + } + } + } else { + if (optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) { + return "Invalid optionType"; + } + // validate defOption based on optionType + if ((optionType == YES_NO_OPTION && (defOption != YES && + defOption != NO)) || + (optionType == YES_NO_CANCEL_OPTION && (defOption != YES && + defOption != NO && defOption != CANCEL)) || + (optionType == OK_CANCEL_OPTION && (defOption != OK && + defOption != CANCEL))) { + return "Invalid default option"; + } + } + return null; + } + /** * Restores the state of this object from the stream. * @@ -502,8 +512,15 @@ public int getSelectedIndex() { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); + if (options != null) { options = options.clone(); } + String errMsg = doSanityCheck(messageType, optionType, + (optionType == UNSPECIFIED_OPTION), options, defaultOption, + prompt, false); + if (errMsg != null) { + throw new InvalidObjectException(errMsg); + } } } diff --git a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java index bbe7ab882a6a1..2bee38ceaaaf0 100644 --- a/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java +++ b/src/java.base/share/classes/javax/security/auth/callback/PasswordCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -178,7 +178,9 @@ private void readObject(ObjectInputStream stream) } if (inputPassword != null) { - inputPassword = inputPassword.clone(); + char[] temp = inputPassword; + inputPassword = temp.clone(); + Arrays.fill(temp, '0'); cleanable = CleanerFactory.cleaner().register( this, cleanerFor(inputPassword)); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java index 50c0bdc2a9fc0..b29b9f6f9551e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java @@ -140,14 +140,13 @@ public CharacterRangeTableAttribute readAttribute(AttributedElement e, ClassRead } @Override - protected void writeBody(BufWriter buf, CharacterRangeTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, CharacterRangeTableAttribute attr) { List ranges = attr.characterRangeTable(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(ranges.size()); for (CharacterRangeInfo info : ranges) { - buf.writeU2(info.startPc()); - buf.writeU2(info.endPc()); - buf.writeInt(info.characterRangeStart()); - buf.writeInt(info.characterRangeEnd()); + buf.writeU2U2(info.startPc(), info.endPc()); + buf.writeIntInt(info.characterRangeStart(), info.characterRangeEnd()); buf.writeU2(info.flags()); } } @@ -238,9 +237,10 @@ public EnclosingMethodAttribute readAttribute(AttributedElement e, ClassReader c } @Override - protected void writeBody(BufWriter buf, EnclosingMethodAttribute attr) { - buf.writeIndex(attr.enclosingClass()); - buf.writeIndexOrZero(attr.enclosingMethod().orElse(null)); + protected void writeBody(BufWriter bufWriter, EnclosingMethodAttribute attr) { + BufWriterImpl buf = (BufWriterImpl) bufWriter; + buf.writeU2U2(buf.cpIndex(attr.enclosingClass()), + buf.cpIndexOrZero(attr.enclosingMethod().orElse(null))); } } @@ -275,13 +275,14 @@ public InnerClassesAttribute readAttribute(AttributedElement e, ClassReader cf, } @Override - protected void writeBody(BufWriter buf, InnerClassesAttribute attr) { + protected void writeBody(BufWriter bufWriter, InnerClassesAttribute attr) { List classes = attr.classes(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(classes.size()); for (InnerClassInfo ic : classes) { - buf.writeIndex(ic.innerClass()); - buf.writeIndexOrZero(ic.outerClass().orElse(null)); - buf.writeIndexOrZero(ic.innerName().orElse(null)); + buf.writeU2U2U2(buf.cpIndex(ic.innerClass()), + buf.cpIndexOrZero(ic.outerClass().orElse(null)), + buf.cpIndexOrZero(ic.innerName().orElse(null))); buf.writeU2(ic.flagsMask()); } } @@ -300,12 +301,12 @@ public LineNumberTableAttribute readAttribute(AttributedElement e, ClassReader c } @Override - protected void writeBody(BufWriter buf, LineNumberTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, LineNumberTableAttribute attr) { List lines = attr.lineNumbers(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(lines.size()); for (LineNumberInfo line : lines) { - buf.writeU2(line.startPc()); - buf.writeU2(line.lineNumber()); + buf.writeU2U2(line.startPc(), line.lineNumber()); } } } @@ -323,15 +324,13 @@ public LocalVariableTableAttribute readAttribute(AttributedElement e, ClassReade } @Override - protected void writeBody(BufWriter buf, LocalVariableTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, LocalVariableTableAttribute attr) { List infos = attr.localVariables(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(infos.size()); for (LocalVariableInfo info : infos) { - buf.writeU2(info.startPc()); - buf.writeU2(info.length()); - buf.writeIndex(info.name()); - buf.writeIndex(info.type()); - buf.writeU2(info.slot()); + buf.writeU2U2(info.startPc(), info.length()); + buf.writeU2U2U2(buf.cpIndex(info.name()), buf.cpIndex(info.type()), info.slot()); } } } @@ -349,15 +348,13 @@ public LocalVariableTypeTableAttribute readAttribute(AttributedElement e, ClassR } @Override - protected void writeBody(BufWriter buf, LocalVariableTypeTableAttribute attr) { + protected void writeBody(BufWriter bufWriter, LocalVariableTypeTableAttribute attr) { List infos = attr.localVariableTypes(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(infos.size()); for (LocalVariableTypeInfo info : infos) { - buf.writeU2(info.startPc()); - buf.writeU2(info.length()); - buf.writeIndex(info.name()); - buf.writeIndex(info.signature()); - buf.writeU2(info.slot()); + buf.writeU2U2(info.startPc(), info.length()); + buf.writeU2U2U2(buf.cpIndex(info.name()), buf.cpIndex(info.signature()), info.slot()); } } } @@ -375,12 +372,13 @@ public MethodParametersAttribute readAttribute(AttributedElement e, ClassReader } @Override - protected void writeBody(BufWriter buf, MethodParametersAttribute attr) { + protected void writeBody(BufWriter bufWriter, MethodParametersAttribute attr) { List parameters = attr.parameters(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU1(parameters.size()); for (MethodParameterInfo info : parameters) { - buf.writeIndexOrZero(info.name().orElse(null)); - buf.writeU2(info.flagsMask()); + buf.writeU2U2(buf.cpIndexOrZero(info.name().orElse(null)), + info.flagsMask()); } } } @@ -398,26 +396,27 @@ public ModuleAttribute readAttribute(AttributedElement e, ClassReader cf, int p) } @Override - protected void writeBody(BufWriter buf, ModuleAttribute attr) { - buf.writeIndex(attr.moduleName()); - buf.writeU2(attr.moduleFlagsMask()); - buf.writeIndexOrZero(attr.moduleVersion().orElse(null)); + protected void writeBody(BufWriter bufWriter, ModuleAttribute attr) { + BufWriterImpl buf = (BufWriterImpl) bufWriter; + buf.writeU2U2U2(buf.cpIndex(attr.moduleName()), + attr.moduleFlagsMask(), + buf.cpIndexOrZero(attr.moduleVersion().orElse(null))); buf.writeU2(attr.requires().size()); for (ModuleRequireInfo require : attr.requires()) { - buf.writeIndex(require.requires()); - buf.writeU2(require.requiresFlagsMask()); - buf.writeIndexOrZero(require.requiresVersion().orElse(null)); + buf.writeU2U2U2(buf.cpIndex(require.requires()), + require.requiresFlagsMask(), + buf.cpIndexOrZero(require.requiresVersion().orElse(null))); } buf.writeU2(attr.exports().size()); for (ModuleExportInfo export : attr.exports()) { - buf.writeIndex(export.exportedPackage()); - buf.writeU2(export.exportsFlagsMask()); + buf.writeU2U2(buf.cpIndex(export.exportedPackage()), + export.exportsFlagsMask()); Util.writeListIndices(buf, export.exportsTo()); } buf.writeU2(attr.opens().size()); for (ModuleOpenInfo open : attr.opens()) { - buf.writeIndex(open.openedPackage()); - buf.writeU2(open.opensFlagsMask()); + buf.writeU2U2(buf.cpIndex(open.openedPackage()), + open.opensFlagsMask()); Util.writeListIndices(buf, open.opensTo()); } Util.writeListIndices(buf, attr.uses()); @@ -442,13 +441,13 @@ public ModuleHashesAttribute readAttribute(AttributedElement e, ClassReader cf, } @Override - protected void writeBody(BufWriter buf, ModuleHashesAttribute attr) { - buf.writeIndex(attr.algorithm()); + protected void writeBody(BufWriter bufWriter, ModuleHashesAttribute attr) { List hashes = attr.hashes(); - buf.writeU2(hashes.size()); + BufWriterImpl buf = (BufWriterImpl) bufWriter; + buf.writeU2U2(buf.cpIndex(attr.algorithm()), hashes.size()); for (ModuleHashInfo hash : hashes) { - buf.writeIndex(hash.moduleName()); - buf.writeU2(hash.hash().length); + buf.writeU2U2(buf.cpIndex(hash.moduleName()), + hash.hash().length); buf.writeBytes(hash.hash()); } } @@ -593,13 +592,14 @@ public RecordAttribute readAttribute(AttributedElement e, ClassReader cf, int p) } @Override - protected void writeBody(BufWriter buf, RecordAttribute attr) { + protected void writeBody(BufWriter bufWriter, RecordAttribute attr) { List components = attr.components(); + BufWriterImpl buf = (BufWriterImpl) bufWriter; buf.writeU2(components.size()); for (RecordComponentInfo info : components) { - buf.writeIndex(info.name()); - buf.writeIndex(info.descriptor()); - Util.writeAttributes((BufWriterImpl) buf, info.attributes()); + buf.writeU2U2(buf.cpIndex(info.name()), + buf.cpIndex(info.descriptor())); + Util.writeAttributes(buf, info.attributes()); } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java index dcbd8c8fee83d..71b08c1915f63 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractBoundLocalVariable.java @@ -88,15 +88,12 @@ public boolean writeLocalTo(BufWriterImpl b) { return false; } int length = endBci - startBci; - b.writeU2(startBci); - b.writeU2(length); + b.writeU2U2(startBci, length); if (b.canWriteDirect(code.constantPool())) { - b.writeU2(nameIndex()); - b.writeU2(secondaryIndex()); + b.writeU2U2(nameIndex(), secondaryIndex()); } else { - b.writeIndex(name()); - b.writeIndex(secondaryEntry()); + b.writeU2U2(b.cpIndex(name()), b.cpIndex(secondaryEntry())); } b.writeU2(slot()); return true; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 48c0cb7685702..23dad36b1b163 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -24,11 +24,11 @@ */ package jdk.internal.classfile.impl; +import java.lang.classfile.constantpool.PoolEntry; import java.lang.constant.ConstantDesc; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.lang.classfile.ClassFile; import java.lang.classfile.Instruction; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.instruction.SwitchCase; @@ -66,6 +66,8 @@ import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; +import static java.util.Objects.requireNonNull; + public abstract sealed class AbstractInstruction extends AbstractElement implements Instruction { @@ -247,6 +249,9 @@ public String toString() { public record SwitchCaseImpl(int caseValue, Label target) implements SwitchCase { + public SwitchCaseImpl { + requireNonNull(target); + } } public static final class BoundLookupSwitchInstruction @@ -421,7 +426,7 @@ public MemberRefEntry method() { @Override public boolean isInterface() { - return method().tag() == ClassFile.TAG_INTERFACEMETHODREF; + return method().tag() == PoolEntry.TAG_INTERFACE_METHODREF; } @Override @@ -801,7 +806,12 @@ public TypeKind typeKind() { @Override public void writeTo(DirectCodeBuilder writer) { - writer.writeLocalVar(op, slot); + var op = this.op; + if (op.sizeIfFixed() == 1) { + writer.writeBytecode(op); + } else { + writer.writeLocalVar(op, slot); + } } @Override @@ -832,7 +842,12 @@ public TypeKind typeKind() { @Override public void writeTo(DirectCodeBuilder writer) { - writer.writeLocalVar(op, slot); + var op = this.op; + if (op.sizeIfFixed() == 1) { + writer.writeBytecode(op); + } else { + writer.writeLocalVar(op, slot); + } } @Override @@ -848,9 +863,9 @@ public static final class UnboundIncrementInstruction final int constant; public UnboundIncrementInstruction(int slot, int constant) { - super(slot <= 255 && constant < 128 && constant > -127 - ? Opcode.IINC - : Opcode.IINC_W); + super(BytecodeHelpers.validateAndIsWideIinc(slot, constant) + ? Opcode.IINC_W + : Opcode.IINC); this.slot = slot; this.constant = constant; } @@ -867,7 +882,7 @@ public int constant() { @Override public void writeTo(DirectCodeBuilder writer) { - writer.writeIncrement(slot, constant); + writer.writeIncrement(op == Opcode.IINC_W, slot, constant); } @Override @@ -882,7 +897,7 @@ public static final class UnboundBranchInstruction public UnboundBranchInstruction(Opcode op, Label target) { super(op); - this.target = target; + this.target = requireNonNull(target); } @Override @@ -909,7 +924,7 @@ public static final class UnboundLookupSwitchInstruction public UnboundLookupSwitchInstruction(Label defaultTarget, List cases) { super(Opcode.LOOKUPSWITCH); - this.defaultTarget = defaultTarget; + this.defaultTarget = requireNonNull(defaultTarget); this.cases = List.copyOf(cases); } @@ -945,7 +960,7 @@ public UnboundTableSwitchInstruction(int lowValue, int highValue, Label defaultT super(Opcode.TABLESWITCH); this.lowValue = lowValue; this.highValue = highValue; - this.defaultTarget = defaultTarget; + this.defaultTarget = requireNonNull(defaultTarget); this.cases = List.copyOf(cases); } @@ -1020,7 +1035,7 @@ public static final class UnboundFieldInstruction public UnboundFieldInstruction(Opcode op, FieldRefEntry fieldEntry) { super(op); - this.fieldEntry = fieldEntry; + this.fieldEntry = requireNonNull(fieldEntry); } @Override @@ -1045,7 +1060,7 @@ public static final class UnboundInvokeInstruction public UnboundInvokeInstruction(Opcode op, MemberRefEntry methodEntry) { super(op); - this.methodEntry = methodEntry; + this.methodEntry = requireNonNull(methodEntry); } @Override @@ -1061,7 +1076,7 @@ public boolean isInterface() { @Override public int count() { return op == Opcode.INVOKEINTERFACE - ? Util.parameterSlots(Util.methodTypeSymbol(methodEntry.nameAndType())) + 1 + ? Util.parameterSlots(Util.methodTypeSymbol(methodEntry.type())) + 1 : 0; } @@ -1085,7 +1100,7 @@ public static final class UnboundInvokeDynamicInstruction public UnboundInvokeDynamicInstruction(InvokeDynamicEntry indyEntry) { super(Opcode.INVOKEDYNAMIC); - this.indyEntry = indyEntry; + this.indyEntry = requireNonNull(indyEntry); } @Override @@ -1110,7 +1125,7 @@ public static final class UnboundNewObjectInstruction public UnboundNewObjectInstruction(ClassEntry classEntry) { super(Opcode.NEW); - this.classEntry = classEntry; + this.classEntry = requireNonNull(classEntry); } @Override @@ -1135,7 +1150,7 @@ public static final class UnboundNewPrimitiveArrayInstruction public UnboundNewPrimitiveArrayInstruction(TypeKind typeKind) { super(Opcode.NEWARRAY); - this.typeKind = typeKind; + this.typeKind = requireNonNull(typeKind); } @Override @@ -1160,7 +1175,7 @@ public static final class UnboundNewReferenceArrayInstruction public UnboundNewReferenceArrayInstruction(ClassEntry componentTypeEntry) { super(Opcode.ANEWARRAY); - this.componentTypeEntry = componentTypeEntry; + this.componentTypeEntry = requireNonNull(componentTypeEntry); } @Override @@ -1187,7 +1202,7 @@ public static final class UnboundNewMultidimensionalArrayInstruction public UnboundNewMultidimensionalArrayInstruction(ClassEntry arrayTypeEntry, int dimensions) { super(Opcode.MULTIANEWARRAY); - this.arrayTypeEntry = arrayTypeEntry; + this.arrayTypeEntry = requireNonNull(arrayTypeEntry); this.dimensions = dimensions; } @@ -1245,7 +1260,7 @@ public static final class UnboundTypeCheckInstruction public UnboundTypeCheckInstruction(Opcode op, ClassEntry typeEntry) { super(op); - this.typeEntry = typeEntry; + this.typeEntry = requireNonNull(typeEntry); } @Override @@ -1347,7 +1362,7 @@ public static final class UnboundLoadConstantInstruction public UnboundLoadConstantInstruction(Opcode op, LoadableConstantEntry constant) { super(op); - this.constant = constant; + this.constant = requireNonNull(constant); } @Override @@ -1395,7 +1410,7 @@ public static final class UnboundJsrInstruction public UnboundJsrInstruction(Opcode op, Label target) { super(op); - this.target = target; + this.target = requireNonNull(target); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 7f14560fd1854..15b5262176441 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -24,12 +24,12 @@ */ package jdk.internal.classfile.impl; +import java.lang.classfile.constantpool.ConstantPoolException; import java.lang.constant.*; import java.lang.invoke.TypeDescriptor; import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.lang.classfile.ClassFile; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantDynamicEntry; import java.lang.classfile.constantpool.ConstantPool; @@ -56,6 +56,8 @@ import jdk.internal.util.ArraysSupport; import jdk.internal.vm.annotation.Stable; +import static java.util.Objects.requireNonNull; + public abstract sealed class AbstractPoolEntry { /* Invariant: a {CP,BSM} entry for pool P refer only to {CP,BSM} entries @@ -83,24 +85,32 @@ public static int hashString(int stringHash) { return stringHash | NON_ZERO; } - public static Utf8Entry rawUtf8EntryFromStandardAttributeName(String name) { - //assuming standard attribute names are all US_ASCII - var raw = name.getBytes(StandardCharsets.US_ASCII); - return new Utf8EntryImpl(null, 0, raw, 0, raw.length); + static int hashClassFromUtf8(boolean isArray, Utf8EntryImpl content) { + int hash = content.contentHash(); + return hashClassFromDescriptor(isArray ? hash : Util.descriptorStringHash(content.length(), hash)); + } + + static int hashClassFromDescriptor(int descriptorHash) { + return hash1(PoolEntry.TAG_CLASS, descriptorHash); + } + + static boolean isArrayDescriptor(Utf8EntryImpl cs) { + // Do not throw out-of-bounds for empty strings + return !cs.isEmpty() && cs.charAt(0) == '['; } @SuppressWarnings("unchecked") public static T maybeClone(ConstantPoolBuilder cp, T entry) { + if (cp.canWriteDirect(entry.constantPool())) + return entry; return (T)((AbstractPoolEntry)entry).clone(cp); } final ConstantPool constantPool; - public final byte tag; private final int index; private final int hash; - private AbstractPoolEntry(ConstantPool constantPool, int tag, int index, int hash) { - this.tag = (byte) tag; + private AbstractPoolEntry(ConstantPool constantPool, int index, int hash) { this.index = index; this.hash = hash; this.constantPool = constantPool; @@ -115,12 +125,10 @@ public int hashCode() { return hash; } - public byte tag() { - return tag; - } + public abstract byte tag(); public int width() { - return (tag == ClassFile.TAG_LONG || tag == ClassFile.TAG_DOUBLE) ? 2 : 1; + return 1; } abstract void writeTo(BufWriterImpl buf); @@ -147,7 +155,7 @@ enum State { RAW, BYTE, CHAR, STRING } private final int offset; private final int rawLen; // Set in any state other than RAW - private @Stable int hash; + private @Stable int contentHash; private @Stable int charLen; // Set in CHAR state private @Stable char[] chars; @@ -158,7 +166,7 @@ enum State { RAW, BYTE, CHAR, STRING } Utf8EntryImpl(ConstantPool cpm, int index, byte[] rawBytes, int offset, int rawLen) { - super(cpm, ClassFile.TAG_UTF8, index, 0); + super(cpm, index, 0); this.rawBytes = rawBytes; this.offset = offset; this.rawLen = rawLen; @@ -166,33 +174,38 @@ enum State { RAW, BYTE, CHAR, STRING } } Utf8EntryImpl(ConstantPool cpm, int index, String s) { - this(cpm, index, s, hashString(s.hashCode())); + this(cpm, index, s, s.hashCode()); } - Utf8EntryImpl(ConstantPool cpm, int index, String s, int hash) { - super(cpm, ClassFile.TAG_UTF8, index, 0); + Utf8EntryImpl(ConstantPool cpm, int index, String s, int contentHash) { + super(cpm, index, 0); this.rawBytes = null; this.offset = 0; this.rawLen = 0; this.state = State.STRING; this.stringValue = s; this.charLen = s.length(); - this.hash = hash; + this.contentHash = contentHash; } Utf8EntryImpl(ConstantPool cpm, int index, Utf8EntryImpl u) { - super(cpm, ClassFile.TAG_UTF8, index, 0); + super(cpm, index, 0); this.rawBytes = u.rawBytes; this.offset = u.offset; this.rawLen = u.rawLen; this.state = u.state; - this.hash = u.hash; + this.contentHash = u.contentHash; this.charLen = u.charLen; this.chars = u.chars; this.stringValue = u.stringValue; this.typeSym = u.typeSym; } + @Override + public byte tag() { + return TAG_UTF8; + } + /** * {@jvms 4.4.7} String content is encoded in modified UTF-8. * @@ -232,76 +245,80 @@ private void inflate() { int singleBytes = JLA.countPositives(rawBytes, offset, rawLen); int hash = ArraysSupport.hashCodeOfUnsigned(rawBytes, offset, singleBytes, 0); if (singleBytes == rawLen) { - this.hash = hashString(hash); + this.contentHash = hash; charLen = rawLen; state = State.BYTE; + } else { + inflateNonAscii(singleBytes, hash); } - else { - char[] chararr = new char[rawLen]; - int chararr_count = singleBytes; - // Inflate prefix of bytes to characters - JLA.inflateBytesToChars(rawBytes, offset, chararr, 0, singleBytes); - - int px = offset + singleBytes; - int utfend = offset + rawLen; - while (px < utfend) { - int c = (int) rawBytes[px] & 0xff; - switch (c >> 4) { - case 0, 1, 2, 3, 4, 5, 6, 7: { - // 0xxx xxxx - px++; - chararr[chararr_count++] = (char) c; - hash = 31 * hash + c; - break; + } + + private void inflateNonAscii(int singleBytes, int hash) { + char[] chararr = new char[rawLen]; + int chararr_count = singleBytes; + // Inflate prefix of bytes to characters + JLA.inflateBytesToChars(rawBytes, offset, chararr, 0, singleBytes); + + int px = offset + singleBytes; + int utfend = offset + rawLen; + while (px < utfend) { + int c = (int) rawBytes[px] & 0xff; + switch (c >> 4) { + case 0, 1, 2, 3, 4, 5, 6, 7: { + // 0xxx xxxx + px++; + chararr[chararr_count++] = (char) c; + hash = 31 * hash + c; + break; + } + case 12, 13: { + // 110x xxxx 10xx xxxx + px += 2; + if (px > utfend) { + throw malformedInput(utfend); + } + int char2 = rawBytes[px - 1]; + if ((char2 & 0xC0) != 0x80) { + throw malformedInput(px); } - case 12, 13: { - // 110x xxxx 10xx xxxx - px += 2; - if (px > utfend) { - throw new CpException("malformed input: partial character at end"); - } - int char2 = rawBytes[px - 1]; - if ((char2 & 0xC0) != 0x80) { - throw new CpException("malformed input around byte " + px); - } - char v = (char) (((c & 0x1F) << 6) | (char2 & 0x3F)); - chararr[chararr_count++] = v; - hash = 31 * hash + v; - break; + char v = (char) (((c & 0x1F) << 6) | (char2 & 0x3F)); + chararr[chararr_count++] = v; + hash = 31 * hash + v; + break; + } + case 14: { + // 1110 xxxx 10xx xxxx 10xx xxxx + px += 3; + if (px > utfend) { + throw malformedInput(utfend); } - case 14: { - // 1110 xxxx 10xx xxxx 10xx xxxx - px += 3; - if (px > utfend) { - throw new CpException("malformed input: partial character at end"); - } - int char2 = rawBytes[px - 2]; - int char3 = rawBytes[px - 1]; - if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { - throw new CpException("malformed input around byte " + (px - 1)); - } - char v = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F)); - chararr[chararr_count++] = v; - hash = 31 * hash + v; - break; + int char2 = rawBytes[px - 2]; + int char3 = rawBytes[px - 1]; + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { + throw malformedInput(px - 1); } - default: - // 10xx xxxx, 1111 xxxx - throw new CpException("malformed input around byte " + px); + char v = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F)); + chararr[chararr_count++] = v; + hash = 31 * hash + v; + break; } + default: + // 10xx xxxx, 1111 xxxx + throw malformedInput(px); } - this.hash = hashString(hash); - charLen = chararr_count; - this.chars = chararr; - state = State.CHAR; } + this.contentHash = hash; + charLen = chararr_count; + this.chars = chararr; + state = State.CHAR; + } + private ConstantPoolException malformedInput(int px) { + return new ConstantPoolException("#%d: malformed modified UTF8 around byte %d".formatted(index(), px)); } @Override public Utf8EntryImpl clone(ConstantPoolBuilder cp) { - if (cp.canWriteDirect(constantPool)) - return this; return (state == State.STRING && rawBytes == null) ? (Utf8EntryImpl) cp.utf8Entry(stringValue) : ((SplitConstantPool) cp).maybeCloneUtf8Entry(this); @@ -309,9 +326,13 @@ public Utf8EntryImpl clone(ConstantPoolBuilder cp) { @Override public int hashCode() { + return hashString(contentHash()); + } + + int contentHash() { if (state == State.RAW) inflate(); - return hash; + return contentHash; } @Override @@ -382,15 +403,47 @@ else if ((state == State.STRING && u.state == State.STRING)) return stringValue().equals(u.stringValue()); } + /** + * Returns if this utf8 entry's content equals a substring + * of {@code s} obtained as {@code s.substring(start, end - start)}. + * This check avoids a substring allocation. + */ + public boolean equalsRegion(String s, int start, int end) { + // start and end values trusted + if (state == State.RAW) + inflate(); + int len = charLen; + if (len != end - start) + return false; + + var sv = stringValue; + if (sv != null) { + return sv.regionMatches(0, s, start, len); + } + + var chars = this.chars; + if (chars != null) { + for (int i = 0; i < len; i++) + if (chars[i] != s.charAt(start + i)) + return false; + } else { + var bytes = this.rawBytes; + for (int i = 0; i < len; i++) + if (bytes[offset + i] != s.charAt(start + i)) + return false; + } + return true; + } + @Override public boolean equalsString(String s) { if (state == State.RAW) inflate(); switch (state) { case STRING: - return stringValue.equals(s); + return stringValue.equals(requireNonNull(s)); case CHAR: - if (charLen != s.length() || hash != hashString(s.hashCode())) + if (charLen != s.length() || contentHash != s.hashCode()) return false; for (int i=0; i extends Abstr protected final T ref1; public AbstractRefEntry(ConstantPool constantPool, int tag, int index, T ref1) { - super(constantPool, tag, index, hash1(tag, ref1.index())); + super(constantPool, index, hash1(tag, ref1.index())); this.ref1 = ref1; } @@ -454,8 +506,7 @@ public T ref1() { } void writeTo(BufWriterImpl pool) { - pool.writeU1(tag); - pool.writeU2(ref1.index()); + pool.writeU1U2(tag(), ref1.index()); } @Override @@ -470,7 +521,7 @@ abstract static sealed class AbstractRefsEntry catchTypeEntry) { - this.catchTypeEntry = catchTypeEntry.orElse(null); - this.handler = handler; - this.tryStart = tryStart; - this.tryEnd = tryEnd; + this(handler, tryStart, tryEnd, catchTypeEntry.orElse(null)); } @Override @@ -115,8 +114,8 @@ public static final class UnboundCharacterRange public UnboundCharacterRange(Label startScope, Label endScope, int characterRangeStart, int characterRangeEnd, int flags) { - this.startScope = startScope; - this.endScope = endScope; + this.startScope = requireNonNull(startScope); + this.endScope = requireNonNull(endScope); this.characterRangeStart = characterRangeStart; this.characterRangeEnd = characterRangeEnd; this.flags = flags; @@ -163,11 +162,12 @@ private abstract static sealed class AbstractLocalPseudo extends AbstractPseudoI protected final Label endScope; public AbstractLocalPseudo(int slot, Utf8Entry name, Utf8Entry descriptor, Label startScope, Label endScope) { + BytecodeHelpers.validateSlot(slot); this.slot = slot; - this.name = name; - this.descriptor = descriptor; - this.startScope = startScope; - this.endScope = endScope; + this.name = requireNonNull(name); + this.descriptor = requireNonNull(descriptor); + this.startScope = requireNonNull(startScope); + this.endScope = requireNonNull(endScope); } public int slot() { @@ -199,11 +199,8 @@ public boolean writeLocalTo(BufWriterImpl b) { return false; } int length = endBci - startBci; - b.writeU2(startBci); - b.writeU2(length); - b.writeIndex(name); - b.writeIndex(descriptor); - b.writeU2(slot()); + b.writeU2U2(startBci, length); + b.writeU2U2U2(b.cpIndex(name), b.cpIndex(descriptor), slot()); return true; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java index b2c67795fc4ba..f1c7e6aaf658b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationImpl.java @@ -29,11 +29,12 @@ import java.util.List; -import static java.lang.classfile.ClassFile.*; +import static java.util.Objects.requireNonNull; public record AnnotationImpl(Utf8Entry className, List elements) implements Annotation { public AnnotationImpl { + requireNonNull(className); elements = List.copyOf(elements); } @@ -52,6 +53,11 @@ public String toString() { public record AnnotationElementImpl(Utf8Entry name, AnnotationValue value) implements AnnotationElement { + public AnnotationElementImpl { + requireNonNull(name); + requireNonNull(value); + } + @Override public String toString() { return name + "=" + value; @@ -62,7 +68,7 @@ public record OfStringImpl(Utf8Entry constant) implements AnnotationValue.OfString { @Override public char tag() { - return AEV_STRING; + return TAG_STRING; } @Override @@ -75,7 +81,7 @@ public record OfDoubleImpl(DoubleEntry constant) implements AnnotationValue.OfDouble { @Override public char tag() { - return AEV_DOUBLE; + return TAG_DOUBLE; } @Override @@ -88,7 +94,7 @@ public record OfFloatImpl(FloatEntry constant) implements AnnotationValue.OfFloat { @Override public char tag() { - return AEV_FLOAT; + return TAG_FLOAT; } @Override @@ -101,7 +107,7 @@ public record OfLongImpl(LongEntry constant) implements AnnotationValue.OfLong { @Override public char tag() { - return AEV_LONG; + return TAG_LONG; } @Override @@ -114,7 +120,7 @@ public record OfIntImpl(IntegerEntry constant) implements AnnotationValue.OfInt { @Override public char tag() { - return AEV_INT; + return TAG_INT; } @Override @@ -127,7 +133,7 @@ public record OfShortImpl(IntegerEntry constant) implements AnnotationValue.OfShort { @Override public char tag() { - return AEV_SHORT; + return TAG_SHORT; } @Override @@ -140,7 +146,7 @@ public record OfCharImpl(IntegerEntry constant) implements AnnotationValue.OfChar { @Override public char tag() { - return AEV_CHAR; + return TAG_CHAR; } @Override @@ -153,7 +159,7 @@ public record OfByteImpl(IntegerEntry constant) implements AnnotationValue.OfByte { @Override public char tag() { - return AEV_BYTE; + return TAG_BYTE; } @Override @@ -166,7 +172,7 @@ public record OfBooleanImpl(IntegerEntry constant) implements AnnotationValue.OfBoolean { @Override public char tag() { - return AEV_BOOLEAN; + return TAG_BOOLEAN; } @Override @@ -183,7 +189,7 @@ public record OfArrayImpl(List values) @Override public char tag() { - return AEV_ARRAY; + return TAG_ARRAY; } } @@ -191,7 +197,7 @@ public record OfEnumImpl(Utf8Entry className, Utf8Entry constantName) implements AnnotationValue.OfEnum { @Override public char tag() { - return AEV_ENUM; + return TAG_ENUM; } } @@ -199,7 +205,7 @@ public record OfAnnotationImpl(Annotation annotation) implements AnnotationValue.OfAnnotation { @Override public char tag() { - return AEV_ANNOTATION; + return TAG_ANNOTATION; } } @@ -207,7 +213,7 @@ public record OfClassImpl(Utf8Entry className) implements AnnotationValue.OfClass { @Override public char tag() { - return AEV_CLASS; + return TAG_CLASS; } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java index 69511131b3700..9724cff35ad6d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java @@ -32,7 +32,8 @@ import java.lang.classfile.ClassReader; import java.lang.classfile.constantpool.*; import java.lang.classfile.TypeAnnotation; -import static java.lang.classfile.ClassFile.*; + +import static java.lang.classfile.AnnotationValue.*; import static java.lang.classfile.TypeAnnotation.TargetInfo.*; import java.util.List; @@ -59,20 +60,20 @@ public static AnnotationValue readElementValue(ClassReader classReader, int p) { char tag = (char) classReader.readU1(p); ++p; return switch (tag) { - case AEV_BYTE -> new AnnotationImpl.OfByteImpl(classReader.readEntry(p, IntegerEntry.class)); - case AEV_CHAR -> new AnnotationImpl.OfCharImpl(classReader.readEntry(p, IntegerEntry.class)); - case AEV_DOUBLE -> new AnnotationImpl.OfDoubleImpl(classReader.readEntry(p, DoubleEntry.class)); - case AEV_FLOAT -> new AnnotationImpl.OfFloatImpl(classReader.readEntry(p, FloatEntry.class)); - case AEV_INT -> new AnnotationImpl.OfIntImpl(classReader.readEntry(p, IntegerEntry.class)); - case AEV_LONG -> new AnnotationImpl.OfLongImpl(classReader.readEntry(p, LongEntry.class)); - case AEV_SHORT -> new AnnotationImpl.OfShortImpl(classReader.readEntry(p, IntegerEntry.class)); - case AEV_BOOLEAN -> new AnnotationImpl.OfBooleanImpl(classReader.readEntry(p, IntegerEntry.class)); - case AEV_STRING -> new AnnotationImpl.OfStringImpl(classReader.readEntry(p, Utf8Entry.class)); - case AEV_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readEntry(p, Utf8Entry.class), + case TAG_BYTE -> new AnnotationImpl.OfByteImpl(classReader.readEntry(p, IntegerEntry.class)); + case TAG_CHAR -> new AnnotationImpl.OfCharImpl(classReader.readEntry(p, IntegerEntry.class)); + case TAG_DOUBLE -> new AnnotationImpl.OfDoubleImpl(classReader.readEntry(p, DoubleEntry.class)); + case TAG_FLOAT -> new AnnotationImpl.OfFloatImpl(classReader.readEntry(p, FloatEntry.class)); + case TAG_INT -> new AnnotationImpl.OfIntImpl(classReader.readEntry(p, IntegerEntry.class)); + case TAG_LONG -> new AnnotationImpl.OfLongImpl(classReader.readEntry(p, LongEntry.class)); + case TAG_SHORT -> new AnnotationImpl.OfShortImpl(classReader.readEntry(p, IntegerEntry.class)); + case TAG_BOOLEAN -> new AnnotationImpl.OfBooleanImpl(classReader.readEntry(p, IntegerEntry.class)); + case TAG_STRING -> new AnnotationImpl.OfStringImpl(classReader.readEntry(p, Utf8Entry.class)); + case TAG_ENUM -> new AnnotationImpl.OfEnumImpl(classReader.readEntry(p, Utf8Entry.class), classReader.readEntry(p + 2, Utf8Entry.class)); - case AEV_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readEntry(p, Utf8Entry.class)); - case AEV_ANNOTATION -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p)); - case AEV_ARRAY -> { + case TAG_CLASS -> new AnnotationImpl.OfClassImpl(classReader.readEntry(p, Utf8Entry.class)); + case TAG_ANNOTATION -> new AnnotationImpl.OfAnnotationImpl(readAnnotation(classReader, p)); + case TAG_ARRAY -> { int numValues = classReader.readU2(p); p += 2; var values = new Object[numValues]; @@ -179,49 +180,49 @@ private static Label getLabel(LabelContext lc, int bciOffset, int targetType, in private static TypeAnnotation readTypeAnnotation(ClassReader classReader, int p, LabelContext lc) { int targetType = classReader.readU1(p++); var targetInfo = switch (targetType) { - case TAT_CLASS_TYPE_PARAMETER -> + case TARGET_CLASS_TYPE_PARAMETER -> ofClassTypeParameter(classReader.readU1(p)); - case TAT_METHOD_TYPE_PARAMETER -> + case TARGET_METHOD_TYPE_PARAMETER -> ofMethodTypeParameter(classReader.readU1(p)); - case TAT_CLASS_EXTENDS -> + case TARGET_CLASS_EXTENDS -> ofClassExtends(classReader.readU2(p)); - case TAT_CLASS_TYPE_PARAMETER_BOUND -> + case TARGET_CLASS_TYPE_PARAMETER_BOUND -> ofClassTypeParameterBound(classReader.readU1(p), classReader.readU1(p + 1)); - case TAT_METHOD_TYPE_PARAMETER_BOUND -> + case TARGET_METHOD_TYPE_PARAMETER_BOUND -> ofMethodTypeParameterBound(classReader.readU1(p), classReader.readU1(p + 1)); - case TAT_FIELD -> + case TARGET_FIELD -> ofField(); - case TAT_METHOD_RETURN -> + case TARGET_METHOD_RETURN -> ofMethodReturn(); - case TAT_METHOD_RECEIVER -> + case TARGET_METHOD_RECEIVER -> ofMethodReceiver(); - case TAT_METHOD_FORMAL_PARAMETER -> + case TARGET_METHOD_FORMAL_PARAMETER -> ofMethodFormalParameter(classReader.readU1(p)); - case TAT_THROWS -> + case TARGET_THROWS -> ofThrows(classReader.readU2(p)); - case TAT_LOCAL_VARIABLE -> + case TARGET_LOCAL_VARIABLE -> ofLocalVariable(readLocalVarEntries(classReader, p, lc, targetType)); - case TAT_RESOURCE_VARIABLE -> + case TARGET_RESOURCE_VARIABLE -> ofResourceVariable(readLocalVarEntries(classReader, p, lc, targetType)); - case TAT_EXCEPTION_PARAMETER -> + case TARGET_EXCEPTION_PARAMETER -> ofExceptionParameter(classReader.readU2(p)); - case TAT_INSTANCEOF -> + case TARGET_INSTANCEOF -> ofInstanceofExpr(getLabel(lc, classReader.readU2(p), targetType, p)); - case TAT_NEW -> + case TARGET_NEW -> ofNewExpr(getLabel(lc, classReader.readU2(p), targetType, p)); - case TAT_CONSTRUCTOR_REFERENCE -> + case TARGET_CONSTRUCTOR_REFERENCE -> ofConstructorReference(getLabel(lc, classReader.readU2(p), targetType, p)); - case TAT_METHOD_REFERENCE -> + case TARGET_METHOD_REFERENCE -> ofMethodReference(getLabel(lc, classReader.readU2(p), targetType, p)); - case TAT_CAST -> + case TARGET_CAST -> ofCastExpr(getLabel(lc, classReader.readU2(p), targetType, p), classReader.readU1(p + 2)); - case TAT_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT -> + case TARGET_CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT -> ofConstructorInvocationTypeArgument(getLabel(lc, classReader.readU2(p), targetType, p), classReader.readU1(p + 2)); - case TAT_METHOD_INVOCATION_TYPE_ARGUMENT -> + case TARGET_METHOD_INVOCATION_TYPE_ARGUMENT -> ofMethodInvocationTypeArgument(getLabel(lc, classReader.readU2(p), targetType, p), classReader.readU1(p + 2)); - case TAT_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT -> + case TARGET_CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT -> ofConstructorReferenceTypeArgument(getLabel(lc, classReader.readU2(p), targetType, p), classReader.readU1(p + 2)); - case TAT_METHOD_REFERENCE_TYPE_ARGUMENT -> + case TARGET_METHOD_REFERENCE_TYPE_ARGUMENT -> ofMethodReferenceTypeArgument(getLabel(lc, classReader.readU2(p), targetType, p), classReader.readU1(p + 2)); default -> throw new IllegalArgumentException("Unexpected targetType '%d' in TypeAnnotation, pos = %d".formatted(targetType, p - 1)); @@ -281,9 +282,8 @@ private static int skipTypeAnnotation(ClassReader classReader, int p) { } public static void writeAnnotation(BufWriterImpl buf, Annotation annotation) { - buf.writeIndex(annotation.className()); var elements = annotation.elements(); - buf.writeU2(elements.size()); + buf.writeU2U2(buf.cpIndex(annotation.className()), elements.size()); for (var e : elements) { buf.writeIndex(e.name()); AnnotationReader.writeAnnotationValue(buf, e.value()); @@ -314,8 +314,7 @@ public static void writeTypeAnnotation(BufWriterImpl buf, TypeAnnotation ta) { case TypeAnnotation.TypeParameterTarget tpt -> buf.writeU1(tpt.typeParameterIndex()); case TypeAnnotation.SupertypeTarget st -> buf.writeU2(st.supertypeIndex()); case TypeAnnotation.TypeParameterBoundTarget tpbt -> { - buf.writeU1(tpbt.typeParameterIndex()); - buf.writeU1(tpbt.boundIndex()); + buf.writeU1U1(tpbt.typeParameterIndex(), tpbt.boundIndex()); } case TypeAnnotation.EmptyTarget _ -> { // nothing to write @@ -326,24 +325,21 @@ public static void writeTypeAnnotation(BufWriterImpl buf, TypeAnnotation ta) { buf.writeU2(lvt.table().size()); for (var e : lvt.table()) { int startPc = labelToBci(lr, e.startLabel(), ta); - buf.writeU2(startPc); - buf.writeU2(labelToBci(lr, e.endLabel(), ta) - startPc); - buf.writeU2(e.index()); + buf.writeU2U2U2(startPc, labelToBci(lr, e.endLabel(), ta) - startPc, e.index()); } } case TypeAnnotation.CatchTarget ct -> buf.writeU2(ct.exceptionTableIndex()); case TypeAnnotation.OffsetTarget ot -> buf.writeU2(labelToBci(lr, ot.target(), ta)); case TypeAnnotation.TypeArgumentTarget tat -> { - buf.writeU2(labelToBci(lr, tat.target(), ta)); - buf.writeU1(tat.typeArgumentIndex()); + buf.writeU2U1(labelToBci(lr, tat.target(), ta), + tat.typeArgumentIndex()); } } // target_path buf.writeU1(ta.targetPath().size()); for (TypeAnnotation.TypePathComponent component : ta.targetPath()) { - buf.writeU1(component.typePathKind().tag()); - buf.writeU1(component.typeArgumentIndex()); + buf.writeU1U1(component.typePathKind().tag(), component.typeArgumentIndex()); } // annotation data @@ -362,16 +358,16 @@ public static void writeAnnotationValue(BufWriterImpl buf, AnnotationValue value var tag = value.tag(); buf.writeU1(tag); switch (value.tag()) { - case AEV_BOOLEAN, AEV_BYTE, AEV_CHAR, AEV_DOUBLE, AEV_FLOAT, AEV_INT, AEV_LONG, AEV_SHORT, AEV_STRING -> + case TAG_BOOLEAN, TAG_BYTE, TAG_CHAR, TAG_DOUBLE, TAG_FLOAT, TAG_INT, TAG_LONG, TAG_SHORT, TAG_STRING -> buf.writeIndex(((AnnotationValue.OfConstant) value).constant()); - case AEV_CLASS -> buf.writeIndex(((AnnotationValue.OfClass) value).className()); - case AEV_ENUM -> { + case TAG_CLASS -> buf.writeIndex(((AnnotationValue.OfClass) value).className()); + case TAG_ENUM -> { var enumValue = (AnnotationValue.OfEnum) value; buf.writeIndex(enumValue.className()); buf.writeIndex(enumValue.constantName()); } - case AEV_ANNOTATION -> writeAnnotation(buf, ((AnnotationValue.OfAnnotation) value).annotation()); - case AEV_ARRAY -> { + case TAG_ANNOTATION -> writeAnnotation(buf, ((AnnotationValue.OfAnnotation) value).annotation()); + case TAG_ARRAY -> { var array = ((AnnotationValue.OfArray) value).values(); buf.writeU2(array.size()); for (var e : array) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java index fb9ecc98902d7..6060170a8d1c9 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java @@ -24,14 +24,15 @@ */ package jdk.internal.classfile.impl; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; import java.lang.classfile.Attribute; import java.lang.classfile.AttributeMapper; public class AttributeHolder { - private final List> attributes = new ArrayList<>(); + private static final Attribute[] EMPTY_ATTRIBUTE_ARRAY = {}; + private int attributesCount = 0; + private Attribute[] attributes = EMPTY_ATTRIBUTE_ARRAY; public > void withAttribute(Attribute a) { if (a == null) @@ -39,36 +40,54 @@ public > void withAttribute(Attribute a) { @SuppressWarnings("unchecked") AttributeMapper am = (AttributeMapper) a.attributeMapper(); - if (!am.allowMultiple() && isPresent(am)) { - remove(am); + int attributesCount = this.attributesCount; + var attributes = this.attributes; + if (!am.allowMultiple()) { + // remove if + for (int i = attributesCount - 1; i >= 0; i--) { + if (attributes[i].attributeMapper() == am) { + attributesCount--; + System.arraycopy(attributes, i + 1, attributes, i, attributesCount - i); + } + } } - attributes.add(a); + + // add attribute + if (attributesCount >= attributes.length) { + int newCapacity = attributesCount + 4; + this.attributes = attributes = Arrays.copyOf(attributes, newCapacity); + } + attributes[attributesCount] = a; + this.attributesCount = attributesCount + 1; } public int size() { - return attributes.size(); + return attributesCount; } public void writeTo(BufWriterImpl buf) { - Util.writeAttributes(buf, attributes); + int attributesCount = this.attributesCount; + buf.writeU2(attributesCount); + for (int i = 0; i < attributesCount; i++) { + Util.writeAttribute(buf, attributes[i]); + } } @SuppressWarnings("unchecked") > A get(AttributeMapper am) { - for (Attribute a : attributes) + for (int i = 0; i < attributesCount; i++) { + Attribute a = attributes[i]; if (a.attributeMapper() == am) - return (A)a; + return (A) a; + } return null; } boolean isPresent(AttributeMapper am) { - for (Attribute a : attributes) - if (a.attributeMapper() == am) + for (int i = 0; i < attributesCount; i++) { + if (attributes[i].attributeMapper() == am) return true; + } return false; } - - private void remove(AttributeMapper am) { - attributes.removeIf(a -> a.attributeMapper() == am); - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java index b28bcc0b4b56f..2fdc9f3642624 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java @@ -33,6 +33,8 @@ import java.util.Objects; import java.lang.classfile.Instruction; +import static java.util.Objects.requireNonNull; + public final class BlockCodeBuilderImpl extends NonterminalCodeBuilder implements CodeBuilder.BlockCodeBuilder { @@ -80,7 +82,7 @@ private int topLocal(CodeBuilder parent) { @Override public CodeBuilder with(CodeElement element) { - parent.with(element); + parent.with(requireNonNull(element)); hasInstructions |= element instanceof Instruction; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index 15d3c8f5b347c..91b3b32190b81 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -36,6 +36,11 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.vm.annotation.ForceInline; + +import static java.lang.classfile.constantpool.PoolEntry.TAG_UTF8; +import static jdk.internal.util.ModifiedUtf.putChar; +import static jdk.internal.util.ModifiedUtf.utfLen; public final class BufWriterImpl implements BufWriter { private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); @@ -99,6 +104,7 @@ public void writeU1(int x) { elems[offset++] = (byte) x; } + @ForceInline @Override public void writeU2(int x) { reserveSpace(2); @@ -109,6 +115,93 @@ public void writeU2(int x) { this.offset = offset + 2; } + @ForceInline + public void writeU1U1(int x1, int x2) { + reserveSpace(2); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) x2; + this.offset = offset + 2; + } + + public void writeU1U2(int u1, int u2) { + reserveSpace(3); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) u1; + elems[offset + 1] = (byte) (u2 >> 8); + elems[offset + 2] = (byte) u2; + this.offset = offset + 3; + } + + public void writeU1U1U1(int x1, int x2, int x3) { + reserveSpace(3); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) x2; + elems[offset + 2] = (byte) x3; + this.offset = offset + 3; + } + + public void writeU1U1U2(int x1, int x2, int x3) { + reserveSpace(4); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) x2; + elems[offset + 2] = (byte) (x3 >> 8); + elems[offset + 3] = (byte) x3; + this.offset = offset + 4; + } + + public void writeU1U2U2(int x1, int x2, int x3) { + reserveSpace(5); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) (x2 >> 8); + elems[offset + 2] = (byte) x2; + elems[offset + 3] = (byte) (x3 >> 8); + elems[offset + 4] = (byte) x3; + this.offset = offset + 5; + } + + public void writeU2U1(int x1, int x2) { + reserveSpace(3); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 8); + elems[offset + 1] = (byte) x1; + elems[offset + 2] = (byte) x2; + this.offset = offset + 3; + } + + public void writeU2U2(int x1, int x2) { + reserveSpace(4); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 8); + elems[offset + 1] = (byte) x1; + elems[offset + 2] = (byte) (x2 >> 8); + elems[offset + 3] = (byte) x2; + this.offset = offset + 4; + } + + public void writeU2U2U2(int x1, int x2, int x3) { + reserveSpace(6); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 8); + elems[offset + 1] = (byte) x1; + elems[offset + 2] = (byte) (x2 >> 8); + elems[offset + 3] = (byte) x2; + elems[offset + 4] = (byte) (x3 >> 8); + elems[offset + 5] = (byte) x3; + this.offset = offset + 6; + } + @Override public void writeInt(int x) { reserveSpace(4); @@ -121,6 +214,21 @@ public void writeInt(int x) { this.offset = offset + 4; } + public void writeIntInt(int x1, int x2) { + reserveSpace(8); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 24); + elems[offset + 1] = (byte) (x1 >> 16); + elems[offset + 2] = (byte) (x1 >> 8); + elems[offset + 3] = (byte) x1; + elems[offset + 4] = (byte) (x2 >> 24); + elems[offset + 5] = (byte) (x2 >> 16); + elems[offset + 6] = (byte) (x2 >> 8); + elems[offset + 7] = (byte) x2; + this.offset = offset + 8; + } + @Override public void writeFloat(float x) { writeInt(Float.floatToIntBits(x)); @@ -157,46 +265,28 @@ public void writeBytes(BufWriterImpl other) { } @SuppressWarnings("deprecation") - void writeUTF(String str) { + void writeUtfEntry(String str) { int strlen = str.length(); int countNonZeroAscii = JLA.countNonZeroAscii(str); - int utflen = strlen; - if (countNonZeroAscii != strlen) { - for (int i = countNonZeroAscii; i < strlen; i++) { - int c = str.charAt(i); - if (c >= 0x80 || c == 0) - utflen += (c >= 0x800) ? 2 : 1; - } - } + int utflen = utfLen(str, countNonZeroAscii); if (utflen > 65535) { throw new IllegalArgumentException("string too long"); } - reserveSpace(utflen + 2); + reserveSpace(utflen + 3); int offset = this.offset; byte[] elems = this.elems; - elems[offset ] = (byte) (utflen >> 8); - elems[offset + 1] = (byte) utflen; - offset += 2; + elems[offset ] = (byte) TAG_UTF8; + elems[offset + 1] = (byte) (utflen >> 8); + elems[offset + 2] = (byte) utflen; + offset += 3; str.getBytes(0, countNonZeroAscii, elems, offset); offset += countNonZeroAscii; - for (int i = countNonZeroAscii; i < strlen; ++i) { - char c = str.charAt(i); - if (c >= '\001' && c <= '\177') { - elems[offset++] = (byte) c; - } else if (c > '\u07FF') { - elems[offset ] = (byte) (0xE0 | c >> 12 & 0xF); - elems[offset + 1] = (byte) (0x80 | c >> 6 & 0x3F); - elems[offset + 2] = (byte) (0x80 | c & 0x3F); - offset += 3; - } else { - elems[offset ] = (byte) (0xC0 | c >> 6 & 0x1F); - elems[offset + 1] = (byte) (0x80 | c & 0x3F); - offset += 2; - } + for (int i = countNonZeroAscii; i < strlen; i++) { + offset = putChar(elems, offset, str.charAt(i)); } this.offset = offset; @@ -283,19 +373,45 @@ public void copyTo(byte[] array, int bufferOffset) { // writeIndex methods ensure that any CP info written // is relative to the correct constant pool - @Override - public void writeIndex(PoolEntry entry) { + public int cpIndex(PoolEntry entry) { int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index(); if (idx < 1 || idx > Character.MAX_VALUE) - throw new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry); - writeU2(idx); + throw invalidIndex(idx, entry); + return idx; + } + + public int cpIndexOrZero(PoolEntry entry) { + if (entry == null || entry.index() == 0) + return 0; + return cpIndex(entry); + } + + @ForceInline + @Override + public void writeIndex(PoolEntry entry) { + writeU2(cpIndex(entry)); + } + + public void writeIndex(int bytecode, PoolEntry entry) { + writeU1U2(bytecode, cpIndex(entry)); + } + + static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) { + return new IllegalArgumentException(idx + " is not a valid index. Entry: " + entry); } @Override public void writeIndexOrZero(PoolEntry entry) { - if (entry == null || entry.index() == 0) - writeU2(0); - else - writeIndex(entry); + writeU2(cpIndexOrZero(entry)); + } + + /** + * Join head and tail into an exact-size buffer + */ + static byte[] join(BufWriterImpl head, BufWriterImpl tail) { + byte[] result = new byte[head.size() + tail.size()]; + head.copyTo(result, 0); + tail.copyTo(result, head.size()); + return result; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java index c506d265f6895..4ed458c398309 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java @@ -38,6 +38,8 @@ import java.util.Optional; import java.util.function.Consumer; +import static java.util.Objects.requireNonNull; + public final class BufferedCodeBuilder implements TerminalCodeBuilder { private final SplitConstantPool constantPool; @@ -121,7 +123,7 @@ public ConstantPoolBuilder constantPool() { public CodeBuilder with(CodeElement element) { if (finished) throw new IllegalStateException("Can't add elements after traversal"); - elements.add(element); + elements.add(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java index 8cf274d746c1c..0578cf85c4cc6 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java @@ -34,6 +34,8 @@ import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class BufferedFieldBuilder implements TerminalFieldBuilder { private final SplitConstantPool constantPool; @@ -49,8 +51,8 @@ public BufferedFieldBuilder(SplitConstantPool constantPool, Utf8Entry type) { this.constantPool = constantPool; this.context = context; - this.name = name; - this.desc = type; + this.name = requireNonNull(name); + this.desc = requireNonNull(type); this.flags = new AccessFlagsImpl(AccessFlag.Location.FIELD); } @@ -61,7 +63,7 @@ public ConstantPoolBuilder constantPool() { @Override public FieldBuilder with(FieldElement element) { - elements.add(element); + elements.add(requireNonNull(element)); if (element instanceof AccessFlags f) this.flags = f; return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java index bc6ab555ae597..8f511218d1e30 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java @@ -43,6 +43,8 @@ import java.lang.classfile.MethodModel; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class BufferedMethodBuilder implements TerminalMethodBuilder { private final List elements; @@ -63,15 +65,15 @@ public BufferedMethodBuilder(SplitConstantPool constantPool, this.elements = new ArrayList<>(); this.constantPool = constantPool; this.context = context; - this.name = nameInfo; - this.desc = typeInfo; + this.name = requireNonNull(nameInfo); + this.desc = requireNonNull(typeInfo); this.flags = new AccessFlagsImpl(AccessFlag.Location.METHOD, flags); this.original = original; } @Override public MethodBuilder with(MethodElement element) { - elements.add(element); + elements.add(requireNonNull(element)); if (element instanceof AccessFlags f) this.flags = checkFlags(f); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java index 75991531c319f..fde8905abc183 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java @@ -45,8 +45,10 @@ import java.lang.classfile.constantpool.MemberRefEntry; import java.lang.classfile.constantpool.MethodHandleEntry; import java.lang.classfile.constantpool.NameAndTypeEntry; +import java.util.Objects; -import static java.lang.classfile.ClassFile.*; +import static java.util.Objects.requireNonNull; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; /** * Note: This class switches on opcode.bytecode for code size @@ -60,6 +62,14 @@ public static IllegalArgumentException cannotConvertException(TypeKind from, Typ return new IllegalArgumentException(String.format("convert %s -> %s", from, to)); } + public static IllegalArgumentException slotOutOfBounds(int slot) { + return new IllegalArgumentException("Invalid slot index :".concat(Integer.toString(slot))); + } + + public static IllegalArgumentException slotOutOfBounds(Opcode opcode, int slot) { + return new IllegalArgumentException("Invalid slot index %d for %s".formatted(slot, opcode)); + } + public static Opcode loadOpcode(TypeKind tk, int slot) { return switch (tk) { case INT, SHORT, BYTE, CHAR, BOOLEAN @@ -78,7 +88,13 @@ public static Opcode aload(int slot) { case 1 -> Opcode.ALOAD_1; case 2 -> Opcode.ALOAD_2; case 3 -> Opcode.ALOAD_3; - default -> (slot < 256) ? Opcode.ALOAD : Opcode.ALOAD_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.ALOAD; + if ((slot & ~0xFFFF) == 0) + yield Opcode.ALOAD_W; + throw slotOutOfBounds(slot); + } }; } @@ -88,7 +104,13 @@ public static Opcode fload(int slot) { case 1 -> Opcode.FLOAD_1; case 2 -> Opcode.FLOAD_2; case 3 -> Opcode.FLOAD_3; - default -> (slot < 256) ? Opcode.FLOAD : Opcode.FLOAD_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.FLOAD; + if ((slot & ~0xFFFF) == 0) + yield Opcode.FLOAD_W; + throw slotOutOfBounds(slot); + } }; } @@ -98,7 +120,13 @@ public static Opcode dload(int slot) { case 1 -> Opcode.DLOAD_1; case 2 -> Opcode.DLOAD_2; case 3 -> Opcode.DLOAD_3; - default -> (slot < 256) ? Opcode.DLOAD : Opcode.DLOAD_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.DLOAD; + if ((slot & ~0xFFFF) == 0) + yield Opcode.DLOAD_W; + throw slotOutOfBounds(slot); + } }; } @@ -108,7 +136,13 @@ public static Opcode lload(int slot) { case 1 -> Opcode.LLOAD_1; case 2 -> Opcode.LLOAD_2; case 3 -> Opcode.LLOAD_3; - default -> (slot < 256) ? Opcode.LLOAD : Opcode.LLOAD_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.LLOAD; + if ((slot & ~0xFFFF) == 0) + yield Opcode.LLOAD_W; + throw slotOutOfBounds(slot); + } }; } @@ -118,7 +152,13 @@ public static Opcode iload(int slot) { case 1 -> Opcode.ILOAD_1; case 2 -> Opcode.ILOAD_2; case 3 -> Opcode.ILOAD_3; - default -> (slot < 256) ? Opcode.ILOAD : Opcode.ILOAD_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.ILOAD; + if ((slot & ~0xFFFF) == 0) + yield Opcode.ILOAD_W; + throw slotOutOfBounds(slot); + } }; } @@ -140,7 +180,13 @@ public static Opcode astore(int slot) { case 1 -> Opcode.ASTORE_1; case 2 -> Opcode.ASTORE_2; case 3 -> Opcode.ASTORE_3; - default -> (slot < 256) ? Opcode.ASTORE : Opcode.ASTORE_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.ASTORE; + if ((slot & ~0xFFFF) == 0) + yield Opcode.ASTORE_W; + throw slotOutOfBounds(slot); + } }; } @@ -150,7 +196,13 @@ public static Opcode fstore(int slot) { case 1 -> Opcode.FSTORE_1; case 2 -> Opcode.FSTORE_2; case 3 -> Opcode.FSTORE_3; - default -> (slot < 256) ? Opcode.FSTORE : Opcode.FSTORE_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.FSTORE; + if ((slot & ~0xFFFF) == 0) + yield Opcode.FSTORE_W; + throw slotOutOfBounds(slot); + } }; } @@ -160,7 +212,13 @@ public static Opcode dstore(int slot) { case 1 -> Opcode.DSTORE_1; case 2 -> Opcode.DSTORE_2; case 3 -> Opcode.DSTORE_3; - default -> (slot < 256) ? Opcode.DSTORE : Opcode.DSTORE_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.DSTORE; + if ((slot & ~0xFFFF) == 0) + yield Opcode.DSTORE_W; + throw slotOutOfBounds(slot); + } }; } @@ -170,7 +228,13 @@ public static Opcode lstore(int slot) { case 1 -> Opcode.LSTORE_1; case 2 -> Opcode.LSTORE_2; case 3 -> Opcode.LSTORE_3; - default -> (slot < 256) ? Opcode.LSTORE : Opcode.LSTORE_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.LSTORE; + if ((slot & ~0xFFFF) == 0) + yield Opcode.LSTORE_W; + throw slotOutOfBounds(slot); + } }; } @@ -180,7 +244,13 @@ public static Opcode istore(int slot) { case 1 -> Opcode.ISTORE_1; case 2 -> Opcode.ISTORE_2; case 3 -> Opcode.ISTORE_3; - default -> (slot < 256) ? Opcode.ISTORE : Opcode.ISTORE_W; + default -> { + if ((slot & ~0xFF) == 0) + yield Opcode.ISTORE; + if ((slot & ~0xFFFF) == 0) + yield Opcode.ISTORE_W; + throw slotOutOfBounds(slot); + } }; } @@ -195,6 +265,11 @@ public static Opcode returnOpcode(TypeKind tk) { }; } + public static int returnBytecode(TypeKind tk) { + int kind = Math.max(0, tk.ordinal() - 4); // BYTE, SHORT, CHAR, BOOLEAN becomes INT + return IRETURN + kind; + } + public static Opcode arrayLoadOpcode(TypeKind tk) { return switch (tk) { case BYTE, BOOLEAN -> Opcode.BALOAD; @@ -209,6 +284,20 @@ public static Opcode arrayLoadOpcode(TypeKind tk) { }; } + public static int arrayLoadBytecode(TypeKind tk) { + return switch (tk) { + case BYTE, BOOLEAN -> BALOAD; + case SHORT -> SALOAD; + case INT -> IALOAD; + case FLOAT -> FALOAD; + case LONG -> LALOAD; + case DOUBLE -> DALOAD; + case REFERENCE -> AALOAD; + case CHAR -> CALOAD; + case VOID -> throw new IllegalArgumentException("void not an allowable array type"); + }; + } + public static Opcode arrayStoreOpcode(TypeKind tk) { return switch (tk) { case BYTE, BOOLEAN -> Opcode.BASTORE; @@ -223,6 +312,20 @@ public static Opcode arrayStoreOpcode(TypeKind tk) { }; } + public static int arrayStoreBytecode(TypeKind tk) { + return switch (tk) { + case BYTE, BOOLEAN -> BASTORE; + case SHORT -> SASTORE; + case INT -> IASTORE; + case FLOAT -> FASTORE; + case LONG -> LASTORE; + case DOUBLE -> DASTORE; + case REFERENCE -> AASTORE; + case CHAR -> CASTORE; + case VOID -> throw new IllegalArgumentException("void not an allowable array type"); + }; + } + public static Opcode reverseBranchOpcode(Opcode op) { return switch (op) { case IFEQ -> Opcode.IFNE; @@ -245,6 +348,29 @@ public static Opcode reverseBranchOpcode(Opcode op) { }; } + public static int reverseBranchOpcode(int bytecode) { + return switch (bytecode) { + case IFEQ -> IFNE; + case IFNE -> IFEQ; + case IFLT -> IFGE; + case IFGE -> IFLT; + case IFGT -> IFLE; + case IFLE -> IFGT; + case IF_ICMPEQ -> IF_ICMPNE; + case IF_ICMPNE -> IF_ICMPEQ; + case IF_ICMPLT -> IF_ICMPGE; + case IF_ICMPGE -> IF_ICMPLT; + case IF_ICMPGT -> IF_ICMPLE; + case IF_ICMPLE -> IF_ICMPGT; + case IF_ACMPEQ -> IF_ACMPNE; + case IF_ACMPNE -> IF_ACMPEQ; + case IFNULL -> IFNONNULL; + case IFNONNULL -> IFNULL; + default -> throw new IllegalArgumentException( + String.format("Wrong opcode kind specified; found %d, expected %s", bytecode, Opcode.Kind.BRANCH)); + }; + } + public static Opcode convertOpcode(TypeKind from, TypeKind to) { return switch (from) { case INT -> @@ -305,6 +431,48 @@ public static TypeKind convertToType(Opcode opcode) { }; } + public static void validateSlot(Opcode opcode, int slot, boolean load) { + int size = opcode.sizeIfFixed(); + if (size == 1 && slot == (load ? intrinsicLoadSlot(opcode) : intrinsicStoreSlot(opcode)) || + size == 2 && (slot & ~0xFF) == 0 || + size == 4 && (slot & ~0xFFFF) == 0) + return; + throw slotOutOfBounds(opcode, slot); + } + + public static void validateSlot(int slot) { + if ((slot & ~0xFFFF) != 0) + throw slotOutOfBounds(slot); + } + + public static boolean validateAndIsWideIinc(int slot, int val) { + var ret = false; + if ((slot & ~0xFF) != 0) { + validateSlot(slot); + ret = true; + } + if ((byte) val != val) { + if ((short) val != val) { + throw new IllegalArgumentException("cannot encode as S2: ".concat(String.valueOf(val))); + } + ret = true; + } + return ret; + } + + public static void validateRet(Opcode opcode, int slot) { + if (opcode == Opcode.RET && (slot & ~0xFF) == 0 || + opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0) + return; + requireNonNull(opcode); + throw slotOutOfBounds(opcode, slot); + } + + public static void validateMultiArrayDimensions(int value) { + if (value < 1 || value > 0xFF) + throw new IllegalArgumentException("Not a valid array dimension: ".concat(String.valueOf(value))); + } + public static void validateSipush(int value) { if (value != (short) value) throw new IllegalArgumentException( @@ -384,7 +552,7 @@ public static LoadableConstantEntry constantEntry(ConstantPoolBuilder constantPo } if (constantValue instanceof DynamicConstantDesc value) { return handleConstantDescToHandleInfo(constantPool, value); } - throw new UnsupportedOperationException("not yet: " + constantValue); + throw new UnsupportedOperationException("not yet: " + requireNonNull(constantValue)); } public static ConstantDesc intrinsicConstantValue(Opcode opcode) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java index 8f05f20d739be..ebf803f5f2725 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java @@ -31,6 +31,8 @@ import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class ChainedClassBuilder implements ClassBuilder, Consumer { private final DirectClassBuilder terminal; @@ -47,7 +49,7 @@ public ChainedClassBuilder(ClassBuilder downstream, @Override public ClassBuilder with(ClassElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java index 8c6c80b3013f3..fa02a346fabd8 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedCodeBuilder.java @@ -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,8 @@ import java.util.function.Consumer; +import static java.util.Objects.requireNonNull; + public final class ChainedCodeBuilder extends NonterminalCodeBuilder implements CodeBuilder { @@ -59,7 +61,7 @@ public int allocateLocal(TypeKind typeKind) { @Override public CodeBuilder with(CodeElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java index b3a30b5351ba7..f9c2b50f414ac 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java @@ -30,6 +30,8 @@ import java.lang.classfile.FieldElement; import java.lang.classfile.constantpool.ConstantPoolBuilder; +import static java.util.Objects.requireNonNull; + public final class ChainedFieldBuilder implements FieldBuilder { private final TerminalFieldBuilder terminal; private final Consumer consumer; @@ -50,7 +52,7 @@ public ConstantPoolBuilder constantPool() { @Override public FieldBuilder with(FieldElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java index a7084116b9bd3..5bab6806b71b4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java @@ -33,6 +33,8 @@ import java.lang.classfile.MethodElement; import java.lang.classfile.constantpool.ConstantPoolBuilder; +import static java.util.Objects.requireNonNull; + public final class ChainedMethodBuilder implements MethodBuilder { final TerminalMethodBuilder terminal; final Consumer consumer; @@ -48,7 +50,7 @@ public ChainedMethodBuilder(MethodBuilder downstream, @Override public MethodBuilder with(MethodElement element) { - consumer.accept(element); + consumer.accept(requireNonNull(element)); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java index c6d9e55d8dbcc..ed81bcea009b8 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassFileImpl.java @@ -40,6 +40,8 @@ import java.lang.classfile.constantpool.Utf8Entry; import jdk.internal.classfile.impl.verifier.VerifierImpl; +import static java.util.Objects.requireNonNull; + public final class ClassFileImpl implements ClassFile { private Option stackMapsOption; @@ -123,7 +125,7 @@ public ClassFileImpl withOptions(Option... options) { } else if (o instanceof AttributeMapperOption oo) { amo = oo; } else { // null or unknown Option type - throw new IllegalArgumentException("Invalid option: " + o); + throw new IllegalArgumentException("Invalid option: " + requireNonNull(o)); } } return new ClassFileImpl(smo, deo, lno, apo, cpso, sjo, dco, dlo, chro, amo); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java index 6765bdee3bdac..9c2dd89553865 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java @@ -40,6 +40,7 @@ import static java.lang.constant.ConstantDescs.CD_Object; import static java.lang.classfile.ClassFile.*; +import static java.lang.classfile.constantpool.PoolEntry.*; import static java.util.Objects.requireNonNull; import static jdk.internal.constant.ConstantUtils.referenceClassDesc; @@ -174,10 +175,10 @@ public ClassHierarchyResolver.ClassHierarchyInfo getClassInfo(ClassDesc classDes switch (tag = in.readUnsignedByte()) { case TAG_UTF8 -> cpStrings[i] = in.readUTF(); case TAG_CLASS -> cpClasses[i] = in.readUnsignedShort(); - case TAG_STRING, TAG_METHODTYPE, TAG_MODULE, TAG_PACKAGE -> in.skipBytes(2); - case TAG_METHODHANDLE -> in.skipBytes(3); - case TAG_INTEGER, TAG_FLOAT, TAG_FIELDREF, TAG_METHODREF, TAG_INTERFACEMETHODREF, - TAG_NAMEANDTYPE, TAG_CONSTANTDYNAMIC, TAG_INVOKEDYNAMIC -> in.skipBytes(4); + case TAG_STRING, TAG_METHOD_TYPE, TAG_MODULE, TAG_PACKAGE -> in.skipBytes(2); + case TAG_METHOD_HANDLE -> in.skipBytes(3); + case TAG_INTEGER, TAG_FLOAT, TAG_FIELDREF, TAG_METHODREF, TAG_INTERFACE_METHODREF, + TAG_NAME_AND_TYPE, TAG_DYNAMIC, TAG_INVOKE_DYNAMIC -> in.skipBytes(4); case TAG_LONG, TAG_DOUBLE -> { in.skipBytes(8); i++; @@ -204,9 +205,9 @@ public StaticClassHierarchyResolver(Collection interfaceNames, Map convertVTIs(CodeAttribute lr, List { switch (s) { - case ITEM_DOUBLE -> { + case DOUBLE -> { ret.accept("double"); ret.accept("double2"); } - case ITEM_FLOAT -> + case FLOAT -> ret.accept("float"); - case ITEM_INTEGER -> + case INTEGER -> ret.accept("int"); - case ITEM_LONG -> { + case LONG -> { ret.accept("long"); ret.accept("long2"); } - case ITEM_NULL -> ret.accept("null"); - case ITEM_TOP -> ret.accept("?"); - case ITEM_UNINITIALIZED_THIS -> ret.accept("THIS"); + case NULL -> ret.accept("null"); + case TOP -> ret.accept("?"); + case UNINITIALIZED_THIS -> ret.accept("THIS"); } } case ObjectVerificationTypeInfo o -> @@ -564,6 +565,7 @@ private static Stream convertVTIs(CodeAttribute lr, List model, Verbosity verbosity) { + requireNonNull(verbosity); // we are using == checks in implementations return switch(model) { case ClassModel cm -> classToTree(cm, verbosity); case FieldModel fm -> fieldToTree(fm, verbosity); @@ -603,12 +605,12 @@ private static Node[] constantPoolToTree(ConstantPool cp, Verbosity verbosity) { case TAG_STRING -> "String"; case TAG_FIELDREF -> "Fieldref"; case TAG_METHODREF -> "Methodref"; - case TAG_INTERFACEMETHODREF -> "InterfaceMethodref"; - case TAG_NAMEANDTYPE -> "NameAndType"; - case TAG_METHODHANDLE -> "MethodHandle"; - case TAG_METHODTYPE -> "MethodType"; - case TAG_CONSTANTDYNAMIC -> "Dynamic"; - case TAG_INVOKEDYNAMIC -> "InvokeDynamic"; + case TAG_INTERFACE_METHODREF -> "InterfaceMethodref"; + case TAG_NAME_AND_TYPE -> "NameAndType"; + case TAG_METHOD_HANDLE -> "MethodHandle"; + case TAG_METHOD_TYPE -> "MethodType"; + case TAG_DYNAMIC -> "Dynamic"; + case TAG_INVOKE_DYNAMIC -> "InvokeDynamic"; case TAG_MODULE -> "Module"; case TAG_PACKAGE -> "Package"; default -> throw new AssertionError("Unknown CP tag: " + e.tag()); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java index 6a4b0cd486f07..0f183ad427f3d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java @@ -36,23 +36,7 @@ import java.lang.classfile.attribute.BootstrapMethodsAttribute; import java.lang.classfile.constantpool.*; -import static java.lang.classfile.ClassFile.TAG_CLASS; -import static java.lang.classfile.ClassFile.TAG_CONSTANTDYNAMIC; -import static java.lang.classfile.ClassFile.TAG_DOUBLE; -import static java.lang.classfile.ClassFile.TAG_FIELDREF; -import static java.lang.classfile.ClassFile.TAG_FLOAT; -import static java.lang.classfile.ClassFile.TAG_INTEGER; -import static java.lang.classfile.ClassFile.TAG_INTERFACEMETHODREF; -import static java.lang.classfile.ClassFile.TAG_INVOKEDYNAMIC; -import static java.lang.classfile.ClassFile.TAG_LONG; -import static java.lang.classfile.ClassFile.TAG_METHODHANDLE; -import static java.lang.classfile.ClassFile.TAG_METHODREF; -import static java.lang.classfile.ClassFile.TAG_METHODTYPE; -import static java.lang.classfile.ClassFile.TAG_MODULE; -import static java.lang.classfile.ClassFile.TAG_NAMEANDTYPE; -import static java.lang.classfile.ClassFile.TAG_PACKAGE; -import static java.lang.classfile.ClassFile.TAG_STRING; -import static java.lang.classfile.ClassFile.TAG_UTF8; +import static java.lang.classfile.constantpool.PoolEntry.*; public final class ClassReaderImpl implements ClassReader { @@ -98,15 +82,15 @@ public final class ClassReaderImpl ++p; switch (tag) { // 2 - case TAG_CLASS, TAG_METHODTYPE, TAG_MODULE, TAG_STRING, TAG_PACKAGE -> p += 2; + case TAG_CLASS, TAG_METHOD_TYPE, TAG_MODULE, TAG_STRING, TAG_PACKAGE -> p += 2; // 3 - case TAG_METHODHANDLE -> p += 3; + case TAG_METHOD_HANDLE -> p += 3; // 4 - case TAG_CONSTANTDYNAMIC, TAG_FIELDREF, TAG_FLOAT, TAG_INTEGER, - TAG_INTERFACEMETHODREF, TAG_INVOKEDYNAMIC, TAG_METHODREF, - TAG_NAMEANDTYPE -> p += 4; + case TAG_DYNAMIC, TAG_FIELDREF, TAG_FLOAT, TAG_INTEGER, + TAG_INTERFACE_METHODREF, TAG_INVOKE_DYNAMIC, TAG_METHODREF, + TAG_NAME_AND_TYPE -> p += 4; // 8 case TAG_DOUBLE, TAG_LONG -> { @@ -354,12 +338,12 @@ private static boolean checkTag(int tag, Class cls) { case TAG_STRING -> AbstractPoolEntry.StringEntryImpl.class; case TAG_FIELDREF -> AbstractPoolEntry.FieldRefEntryImpl.class; case TAG_METHODREF -> AbstractPoolEntry.MethodRefEntryImpl.class; - case TAG_INTERFACEMETHODREF -> AbstractPoolEntry.InterfaceMethodRefEntryImpl.class; - case TAG_NAMEANDTYPE -> AbstractPoolEntry.NameAndTypeEntryImpl.class; - case TAG_METHODHANDLE -> AbstractPoolEntry.MethodHandleEntryImpl.class; - case TAG_METHODTYPE -> AbstractPoolEntry.MethodTypeEntryImpl.class; - case TAG_CONSTANTDYNAMIC -> AbstractPoolEntry.ConstantDynamicEntryImpl.class; - case TAG_INVOKEDYNAMIC -> AbstractPoolEntry.InvokeDynamicEntryImpl.class; + case TAG_INTERFACE_METHODREF -> AbstractPoolEntry.InterfaceMethodRefEntryImpl.class; + case TAG_NAME_AND_TYPE -> AbstractPoolEntry.NameAndTypeEntryImpl.class; + case TAG_METHOD_HANDLE -> AbstractPoolEntry.MethodHandleEntryImpl.class; + case TAG_METHOD_TYPE -> AbstractPoolEntry.MethodTypeEntryImpl.class; + case TAG_DYNAMIC -> AbstractPoolEntry.ConstantDynamicEntryImpl.class; + case TAG_INVOKE_DYNAMIC -> AbstractPoolEntry.InvokeDynamicEntryImpl.class; case TAG_MODULE -> AbstractPoolEntry.ModuleEntryImpl.class; case TAG_PACKAGE -> AbstractPoolEntry.PackageEntryImpl.class; default -> null; @@ -369,7 +353,11 @@ private static boolean checkTag(int tag, Class cls) { static T checkType(PoolEntry e, int index, Class cls) { if (cls.isInstance(e)) return cls.cast(e); - throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index); + throw checkTypeError(index, cls); + } + + private static ConstantPoolException checkTypeError(int index, Class cls) { + return new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index); } @Override @@ -402,15 +390,15 @@ public T entryByIndex(int index, Class cls) { readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class)); case TAG_METHODREF -> new AbstractPoolEntry.MethodRefEntryImpl(this, index, readEntry(q, AbstractPoolEntry.ClassEntryImpl.class), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class)); - case TAG_INTERFACEMETHODREF -> new AbstractPoolEntry.InterfaceMethodRefEntryImpl(this, index, readEntry(q, AbstractPoolEntry.ClassEntryImpl.class), + case TAG_INTERFACE_METHODREF -> new AbstractPoolEntry.InterfaceMethodRefEntryImpl(this, index, readEntry(q, AbstractPoolEntry.ClassEntryImpl.class), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class)); - case TAG_NAMEANDTYPE -> new AbstractPoolEntry.NameAndTypeEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class), + case TAG_NAME_AND_TYPE -> new AbstractPoolEntry.NameAndTypeEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class), readEntry(q + 2, AbstractPoolEntry.Utf8EntryImpl.class)); - case TAG_METHODHANDLE -> new AbstractPoolEntry.MethodHandleEntryImpl(this, index, readU1(q), + case TAG_METHOD_HANDLE -> new AbstractPoolEntry.MethodHandleEntryImpl(this, index, readU1(q), readEntry(q + 1, AbstractPoolEntry.AbstractMemberRefEntry.class)); - case TAG_METHODTYPE -> new AbstractPoolEntry.MethodTypeEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class)); - case TAG_CONSTANTDYNAMIC -> new AbstractPoolEntry.ConstantDynamicEntryImpl(this, index, readU2(q), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class)); - case TAG_INVOKEDYNAMIC -> new AbstractPoolEntry.InvokeDynamicEntryImpl(this, index, readU2(q), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class)); + case TAG_METHOD_TYPE -> new AbstractPoolEntry.MethodTypeEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class)); + case TAG_DYNAMIC -> new AbstractPoolEntry.ConstantDynamicEntryImpl(this, index, readU2(q), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class)); + case TAG_INVOKE_DYNAMIC -> new AbstractPoolEntry.InvokeDynamicEntryImpl(this, index, readU2(q), readEntry(q + 2, AbstractPoolEntry.NameAndTypeEntryImpl.class)); case TAG_MODULE -> new AbstractPoolEntry.ModuleEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class)); case TAG_PACKAGE -> new AbstractPoolEntry.PackageEntryImpl(this, index, readEntry(q, AbstractPoolEntry.Utf8EntryImpl.class)); default -> throw new ConstantPoolException( diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index e08d2b76479be..aa9603b150855 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -39,7 +39,7 @@ import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.instruction.*; -import static java.lang.classfile.ClassFile.*; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; public final class CodeImpl extends BoundAttribute.BoundCodeAttribute @@ -55,13 +55,13 @@ public final class CodeImpl case ARRAY_STORE -> ArrayStoreInstruction.of(o); case CONSTANT -> ConstantInstruction.ofIntrinsic(o); case CONVERT -> ConvertInstruction.of(o); - case LOAD -> LoadInstruction.of(o, BytecodeHelpers.intrinsicLoadSlot(o)); + case LOAD -> new AbstractInstruction.UnboundLoadInstruction(o, BytecodeHelpers.intrinsicLoadSlot(o)); case MONITOR -> MonitorInstruction.of(o); case NOP -> NopInstruction.of(); case OPERATOR -> OperatorInstruction.of(o); case RETURN -> ReturnInstruction.of(o); case STACK -> StackInstruction.of(o); - case STORE -> StoreInstruction.of(o, BytecodeHelpers.intrinsicStoreSlot(o)); + case STORE -> new AbstractInstruction.UnboundStoreInstruction(o, BytecodeHelpers.intrinsicStoreSlot(o)); case THROW_EXCEPTION -> ThrowInstruction.of(); default -> throw new AssertionError("invalid opcode: " + o); }; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 6901ae7e24cf9..d1131d94db769 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +26,8 @@ package jdk.internal.classfile.impl; -import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; -import java.lang.constant.MethodTypeDesc; -import java.lang.reflect.AccessFlag; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -48,13 +46,21 @@ import java.lang.classfile.MethodTransform; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class DirectClassBuilder extends AbstractDirectBuilder implements ClassBuilder { + /** The value of default class access flags */ + static final int DEFAULT_CLASS_FLAGS = ClassFile.ACC_PUBLIC; + static final Util.Writable[] EMPTY_WRITABLE_ARRAY = {}; + static final ClassEntry[] EMPTY_CLASS_ENTRY_ARRAY = {}; final ClassEntry thisClassEntry; - private final List fields = new ArrayList<>(); - private final List methods = new ArrayList<>(); + private Util.Writable[] fields = EMPTY_WRITABLE_ARRAY; + private Util.Writable[] methods = EMPTY_WRITABLE_ARRAY; + private int fieldsCount = 0; + private int methodsCount = 0; private ClassEntry superclassEntry; private List interfaceEntries; private int majorVersion; @@ -67,7 +73,7 @@ public DirectClassBuilder(SplitConstantPool constantPool, ClassEntry thisClass) { super(constantPool, context); this.thisClassEntry = AbstractPoolEntry.maybeClone(constantPool, thisClass); - this.flags = ClassFile.DEFAULT_CLASS_FLAGS; + this.flags = DEFAULT_CLASS_FLAGS; this.superclassEntry = null; this.interfaceEntries = Collections.emptyList(); this.majorVersion = ClassFile.latestMajorVersion(); @@ -79,7 +85,7 @@ public ClassBuilder with(ClassElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } @@ -135,12 +141,20 @@ public ClassBuilder transformMethod(MethodModel method, MethodTransform transfor // internal / for use by elements ClassBuilder withField(Util.Writable field) { - fields.add(field); + if (fieldsCount >= fields.length) { + int newCapacity = fieldsCount + 8; + this.fields = Arrays.copyOf(fields, newCapacity); + } + fields[fieldsCount++] = field; return this; } ClassBuilder withMethod(Util.Writable method) { - methods.add(method); + if (methodsCount >= methods.length) { + int newCapacity = methodsCount + 8; + this.methods = Arrays.copyOf(methods, newCapacity); + } + methods[methodsCount++] = method; return this; } @@ -175,14 +189,14 @@ public byte[] build() { // BSM writers until everything else is written. // Do this early because it might trigger CP activity + var constantPool = this.constantPool; ClassEntry superclass = superclassEntry; if (superclass != null) superclass = AbstractPoolEntry.maybeClone(constantPool, superclass); else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisClassEntry.asInternalName())) superclass = constantPool.classEntry(ConstantDescs.CD_Object); - List ies = new ArrayList<>(interfaceEntries.size()); - for (ClassEntry ce : interfaceEntries) - ies.add(AbstractPoolEntry.maybeClone(constantPool, ce)); + int interfaceEntriesSize = interfaceEntries.size(); + ClassEntry[] ies = interfaceEntriesSize == 0 ? EMPTY_CLASS_ENTRY_ARRAY : buildInterfaceEnties(interfaceEntriesSize); // We maintain two writers, and then we join them at the end int size = sizeHint == 0 ? 256 : sizeHint; @@ -191,32 +205,35 @@ else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisC // The tail consists of fields and methods, and attributes // This should trigger all the CP/BSM mutation - Util.writeList(tail, fields); - Util.writeList(tail, methods); + Util.writeList(tail, fields, fieldsCount); + Util.writeList(tail, methods, methodsCount); int attributesOffset = tail.size(); attributes.writeTo(tail); // Now we have to append the BSM, if there is one - boolean written = constantPool.writeBootstrapMethods(tail); - if (written) { + if (constantPool.writeBootstrapMethods(tail)) { // Update attributes count tail.patchU2(attributesOffset, attributes.size() + 1); } // Now we can make the head head.writeInt(ClassFile.MAGIC_NUMBER); - head.writeU2(minorVersion); - head.writeU2(majorVersion); + head.writeU2U2(minorVersion, majorVersion); constantPool.writeTo(head); - head.writeU2(flags); - head.writeIndex(thisClassEntry); - head.writeIndexOrZero(superclass); - Util.writeListIndices(head, ies); + head.writeU2U2U2(flags, head.cpIndex(thisClassEntry), head.cpIndexOrZero(superclass)); + head.writeU2(interfaceEntriesSize); + for (int i = 0; i < interfaceEntriesSize; i++) { + head.writeIndex(ies[i]); + } // Join head and tail into an exact-size buffer - byte[] result = new byte[head.size() + tail.size()]; - head.copyTo(result, 0); - tail.copyTo(result, head.size()); - return result; + return BufWriterImpl.join(head, tail); + } + + private ClassEntry[] buildInterfaceEnties(int interfaceEntriesSize) { + var ies = new ClassEntry[interfaceEntriesSize]; + for (int i = 0; i < interfaceEntriesSize; i++) + ies[i] = AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i)); + return ies; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index a9be069591162..c00025e2a7e1e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -25,6 +25,8 @@ */ package jdk.internal.classfile.impl; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.classfile.Attribute; import java.lang.classfile.Attributes; import java.lang.classfile.ClassFile; @@ -52,8 +54,8 @@ import java.lang.classfile.instruction.LocalVariable; import java.lang.classfile.instruction.LocalVariableType; import java.lang.classfile.instruction.SwitchCase; -import java.lang.constant.ConstantDesc; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; @@ -62,18 +64,28 @@ import java.util.function.Consumer; import java.util.function.Function; -import static java.lang.classfile.Opcode.*; - +import static java.util.Objects.requireNonNull; import static jdk.internal.classfile.impl.BytecodeHelpers.*; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; public final class DirectCodeBuilder extends AbstractDirectBuilder implements TerminalCodeBuilder { - private final List characterRanges = new ArrayList<>(); + private static final CharacterRange[] EMPTY_CHARACTER_RANGE = {}; + private static final DeferredLabel[] EMPTY_LABEL_ARRAY = {}; + private static final LocalVariable[] EMPTY_LOCAL_VARIABLE_ARRAY = {}; + private static final LocalVariableType[] EMPTY_LOCAL_VARIABLE_TYPE_ARRAY = {}; + private static final AbstractPseudoInstruction.ExceptionCatchImpl[] EMPTY_HANDLER_ARRAY = {}; + private static final DeferredLabel[] EMPTY_DEFERRED_LABEL_ARRAY = {}; + final List handlers = new ArrayList<>(); - private final List localVariables = new ArrayList<>(); - private final List localVariableTypes = new ArrayList<>(); - private final boolean transformFwdJumps, transformBackJumps; + private CharacterRange[] characterRanges = EMPTY_CHARACTER_RANGE; + private LocalVariable[] localVariables = EMPTY_LOCAL_VARIABLE_ARRAY; + private LocalVariableType[] localVariableTypes = EMPTY_LOCAL_VARIABLE_TYPE_ARRAY; + private int characterRangesCount = 0; + private int localVariablesCount = 0; + private int localVariableTypesCount = 0; + private final boolean transformDeferredJumps, transformKnownJumps; private final Label startLabel, endLabel; final MethodInfo methodInfo; final BufWriterImpl bytecodesBufWriter; @@ -83,7 +95,8 @@ public final class DirectCodeBuilder private DedupLineNumberTableAttribute lineNumberWriter; private int topLocal; - List deferredLabels; + private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY; + private int deferredLabelsCount = 0; /* Locals management lazily computed maxLocal = -1 @@ -117,12 +130,12 @@ private DirectCodeBuilder(MethodInfo methodInfo, SplitConstantPool constantPool, ClassFileImpl context, CodeModel original, - boolean transformFwdJumps) { + boolean transformDeferredJumps) { super(constantPool, context); setOriginal(original); this.methodInfo = methodInfo; - this.transformFwdJumps = transformFwdJumps; - this.transformBackJumps = context.fixShortJumps(); + this.transformDeferredJumps = transformDeferredJumps; + this.transformKnownJumps = context.fixShortJumps(); bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength()) : new BufWriterImpl(constantPool, context); this.startLabel = new LabelImpl(this, 0); @@ -135,7 +148,7 @@ public CodeBuilder with(CodeElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } @@ -190,6 +203,13 @@ private void writeExceptionHandlers(BufWriterImpl buf) { int pos = buf.size(); int handlersSize = handlers.size(); buf.writeU2(handlersSize); + if (handlersSize > 0) { + writeExceptionHandlers(buf, pos); + } + } + + private void writeExceptionHandlers(BufWriterImpl buf, int pos) { + int handlersSize = handlers.size(); for (AbstractPseudoInstruction.ExceptionCatchImpl h : handlers) { int startPc = labelToBci(h.tryStart()); int endPc = labelToBci(h.tryEnd()); @@ -201,9 +221,7 @@ private void writeExceptionHandlers(BufWriterImpl buf) { throw new IllegalArgumentException("Unbound label in exception handler"); } } else { - buf.writeU2(startPc); - buf.writeU2(endPc); - buf.writeU2(handlerPc); + buf.writeU2U2U2(startPc, endPc, handlerPc); buf.writeIndexOrZero(h.catchTypeEntry()); handlersSize++; } @@ -220,15 +238,16 @@ private void buildContent() { processDeferredLabels(); if (context.passDebugElements()) { - if (!characterRanges.isEmpty()) { + if (characterRangesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int crSize = characterRanges.size(); + int crSize = characterRangesCount; b.writeU2(crSize); - for (CharacterRange cr : characterRanges) { + for (int i = 0; i < characterRangesCount; i++) { + CharacterRange cr = characterRanges[i]; var start = labelToBci(cr.startScope()); var end = labelToBci(cr.endScope()); if (start == -1 || end == -1) { @@ -238,28 +257,27 @@ public void writeBody(BufWriterImpl b) { throw new IllegalArgumentException("Unbound label in character range"); } } else { - b.writeU2(start); - b.writeU2(end - 1); - b.writeInt(cr.characterRangeStart()); - b.writeInt(cr.characterRangeEnd()); + b.writeU2U2(start, end - 1); + b.writeIntInt(cr.characterRangeStart(), cr.characterRangeEnd()); b.writeU2(cr.flags()); } } - if (crSize < characterRanges.size()) + if (crSize < characterRangesCount) b.patchU2(pos, crSize); } }; attributes.withAttribute(a); } - if (!localVariables.isEmpty()) { + if (localVariablesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int lvSize = localVariables.size(); + int lvSize = localVariablesCount; b.writeU2(lvSize); - for (LocalVariable l : localVariables) { + for (int i = 0; i < localVariablesCount; i++) { + LocalVariable l = localVariables[i]; if (!Util.writeLocalVariable(b, l)) { if (context.dropDeadLabels()) { lvSize--; @@ -268,21 +286,22 @@ public void writeBody(BufWriterImpl b) { } } } - if (lvSize < localVariables.size()) + if (lvSize < localVariablesCount) b.patchU2(pos, lvSize); } }; attributes.withAttribute(a); } - if (!localVariableTypes.isEmpty()) { + if (localVariableTypesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTypeTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int lvtSize = localVariableTypes.size(); - b.writeU2(localVariableTypes.size()); - for (LocalVariableType l : localVariableTypes) { + int lvtSize = localVariableTypesCount; + b.writeU2(lvtSize); + for (int i = 0; i < localVariableTypesCount; i++) { + LocalVariableType l = localVariableTypes[i]; if (!Util.writeLocalVariable(b, l)) { if (context.dropDeadLabels()) { lvtSize--; @@ -291,7 +310,7 @@ public void writeBody(BufWriterImpl b) { } } } - if (lvtSize < localVariableTypes.size()) + if (lvtSize < localVariableTypesCount) b.patchU2(pos, lvtSize); } }; @@ -308,22 +327,20 @@ public void writeBody(BufWriterImpl b) { private void writeCounters(boolean codeMatch, BufWriterImpl buf) { if (codeMatch) { var originalAttribute = (CodeImpl) original; - buf.writeU2(originalAttribute.maxStack()); - buf.writeU2(originalAttribute.maxLocals()); + buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals()); } else { StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); - buf.writeU2(cntr.maxStack()); - buf.writeU2(cntr.maxLocals()); + buf.writeU2U2(cntr.maxStack(), cntr.maxLocals()); } } private void generateStackMaps(BufWriterImpl buf) throws IllegalArgumentException { //new instance of generator immediately calculates maxStack, maxLocals, all frames, // patches dead bytecode blocks and removes them from exception table - StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf); - attributes.withAttribute(gen.stackMapTableAttribute()); - buf.writeU2(gen.maxStack()); - buf.writeU2(gen.maxLocals()); + var dcb = DirectCodeBuilder.this; + StackMapGenerator gen = StackMapGenerator.of(dcb, buf); + dcb.attributes.withAttribute(gen.stackMapTableAttribute()); + buf.writeU2U2(gen.maxStack(), gen.maxLocals()); } private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) { @@ -345,40 +362,37 @@ private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) { @Override public void writeBody(BufWriterImpl buf) { - buf.setLabelContext(DirectCodeBuilder.this); + DirectCodeBuilder dcb = DirectCodeBuilder.this; + buf.setLabelContext(dcb); int codeLength = curPc(); if (codeLength == 0 || codeLength >= 65536) { throw new IllegalArgumentException(String.format( "Code length %d is outside the allowed range in %s%s", codeLength, - methodInfo.methodName().stringValue(), - methodInfo.methodTypeSymbol().displayDescriptor())); + dcb.methodInfo.methodName().stringValue(), + dcb.methodInfo.methodTypeSymbol().displayDescriptor())); } - if (codeAndExceptionsMatch(codeLength)) { - if (context.stackMapsWhenRequired()) { - attributes.withAttribute(original.findAttribute(Attributes.stackMapTable()).orElse(null)); - writeCounters(true, buf); - } else if (context.generateStackMaps()) { - generateStackMaps(buf); - } else if (context.dropStackMaps()) { + boolean codeMatch = dcb.original != null && codeAndExceptionsMatch(codeLength); + var context = dcb.context; + if (context.stackMapsWhenRequired()) { + if (codeMatch) { + dcb.attributes.withAttribute(dcb.original.findAttribute(Attributes.stackMapTable()).orElse(null)); writeCounters(true, buf); - } - } else { - if (context.stackMapsWhenRequired()) { + } else { tryGenerateStackMaps(false, buf); - } else if (context.generateStackMaps()) { - generateStackMaps(buf); - } else if (context.dropStackMaps()) { - writeCounters(false, buf); } + } else if (context.generateStackMaps()) { + generateStackMaps(buf); + } else if (context.dropStackMaps()) { + writeCounters(codeMatch, buf); } buf.writeInt(codeLength); - buf.writeBytes(bytecodesBufWriter); - writeExceptionHandlers(buf); - attributes.writeTo(buf); + buf.writeBytes(dcb.bytecodesBufWriter); + dcb.writeExceptionHandlers(buf); + dcb.attributes.writeTo(buf); buf.setLabelContext(null); } }; @@ -398,8 +412,7 @@ public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool, ClassFile private void push() { //subsequent identical line numbers are skipped if (lastPc >= 0 && lastLine != writtenLine) { - buf.writeU2(lastPc); - buf.writeU2(lastLine); + buf.writeU2U2(lastPc, lastLine); writtenLine = lastLine; } } @@ -449,32 +462,16 @@ private boolean codeAndExceptionsMatch(int codeLength) { private record DeferredLabel(int labelPc, int size, int instructionPc, Label label) { } - private void writeLabelOffset(int nBytes, int instructionPc, Label label) { - int targetBci = labelToBci(label); - if (targetBci == -1) { - int pc = bytecodesBufWriter.skip(nBytes); - if (deferredLabels == null) - deferredLabels = new ArrayList<>(); - deferredLabels.add(new DeferredLabel(pc, nBytes, instructionPc, label)); - } - else { - int branchOffset = targetBci - instructionPc; - if (nBytes == 2 && (short)branchOffset != branchOffset) throw new LabelOverflowException(); - bytecodesBufWriter.writeIntBytes(nBytes, branchOffset); - } - } - private void processDeferredLabels() { - if (deferredLabels != null) { - for (DeferredLabel dl : deferredLabels) { - int branchOffset = labelToBci(dl.label) - dl.instructionPc; - if (dl.size == 2) { - if ((short)branchOffset != branchOffset) throw new LabelOverflowException(); - bytecodesBufWriter.patchU2(dl.labelPc, branchOffset); - } else { - assert dl.size == 4; - bytecodesBufWriter.patchInt(dl.labelPc, branchOffset); - } + for (int i = 0; i < deferredLabelsCount; i++) { + DeferredLabel dl = deferredLabels[i]; + int branchOffset = labelToBci(dl.label) - dl.instructionPc; + if (dl.size == 2) { + if ((short) branchOffset != branchOffset) throw new LabelOverflowException(); + bytecodesBufWriter.patchU2(dl.labelPc, branchOffset); + } else { + assert dl.size == 4; + bytecodesBufWriter.patchInt(dl.labelPc, branchOffset); } } } @@ -482,70 +479,140 @@ private void processDeferredLabels() { // Instruction writing public void writeBytecode(Opcode opcode) { - if (opcode.isWide()) - bytecodesBufWriter.writeU1(ClassFile.WIDE); - bytecodesBufWriter.writeU1(opcode.bytecode() & 0xFF); - } - - public void writeLocalVar(Opcode opcode, int localVar) { - writeBytecode(opcode); - switch (opcode.sizeIfFixed()) { - case 1 -> { } - case 2 -> bytecodesBufWriter.writeU1(localVar); - case 4 -> bytecodesBufWriter.writeU2(localVar); - default -> throw new IllegalArgumentException("Unexpected instruction size: " + opcode); - } + assert !opcode.isWide(); + bytecodesBufWriter.writeU1(opcode.bytecode()); } - public void writeIncrement(int slot, int val) { - Opcode opcode = (slot < 256 && val < 128 && val > -127) - ? IINC - : IINC_W; - writeBytecode(opcode); + // Instruction version, refer to opcode + public void writeLocalVar(Opcode opcode, int slot) { if (opcode.isWide()) { - bytecodesBufWriter.writeU2(slot); - bytecodesBufWriter.writeU2(val); + bytecodesBufWriter.writeU2U2(opcode.bytecode(), slot); + } else { + bytecodesBufWriter.writeU1U1(opcode.bytecode(), slot); + } + } + + // Shortcut version, refer to and validate slot + private void writeLocalVar(int bytecode, int slot) { + // TODO validation like (slot & 0xFFFF) == slot + if (slot < 256) { + bytecodesBufWriter.writeU1U1(bytecode, slot); + } else { + bytecodesBufWriter.writeU1U1U2(WIDE, bytecode, slot); + } + } + + public void writeIncrement(boolean wide, int slot, int val) { + if (wide) { + bytecodesBufWriter.writeU2U2U2((WIDE << 8) | IINC, slot, val); } else { - bytecodesBufWriter.writeU1(slot); - bytecodesBufWriter.writeU1(val); + bytecodesBufWriter.writeU1U1U1(IINC, slot, val); } } public void writeBranch(Opcode op, Label target) { + if (op.sizeIfFixed() == 3) { + writeShortJump(op.bytecode(), target); + } else { + writeLongJump(op.bytecode(), target); + } + } + + private void writeLongLabelOffset(int instructionPc, Label label) { + int targetBci = labelToBci(label); + + // algebraic union of jump | (instructionPc, target), distinguished by null == target. + int jumpOrInstructionPc; + Label nullOrTarget; + if (targetBci == -1) { + jumpOrInstructionPc = instructionPc; + nullOrTarget = label; + } else { + jumpOrInstructionPc = targetBci - instructionPc; + nullOrTarget = null; + } + + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); + } + + private void writeShortJump(int bytecode, Label target) { int instructionPc = curPc(); int targetBci = labelToBci(target); + + // algebraic union of jump | (instructionPc, target), distinguished by null == target. + int jumpOrInstructionPc; + Label nullOrTarget; + if (targetBci == -1) { + jumpOrInstructionPc = instructionPc; + nullOrTarget = target; + } else { + jumpOrInstructionPc = targetBci - instructionPc; + nullOrTarget = null; + } + //transform short-opcode forward jumps if enforced, and backward jumps if enabled and overflowing - if (op.sizeIfFixed() == 3 && (targetBci == -1 - ? transformFwdJumps - : (transformBackJumps - && targetBci - instructionPc < Short.MIN_VALUE))) { - if (op == GOTO) { - writeBytecode(GOTO_W); - writeLabelOffset(4, instructionPc, target); - } else if (op == JSR) { - writeBytecode(JSR_W); - writeLabelOffset(4, instructionPc, target); + if (transformDeferredJumps || transformKnownJumps && nullOrTarget == null && jumpOrInstructionPc < Short.MIN_VALUE) { + fixShortJump(bytecode, jumpOrInstructionPc, nullOrTarget); + } else { + bytecodesBufWriter.writeU1(bytecode); + writeParsedShortLabel(jumpOrInstructionPc, nullOrTarget); + } + } + + private void writeLongJump(int bytecode, Label target) { + int instructionPc = curPc(); + bytecodesBufWriter.writeU1(bytecode); + writeLongLabelOffset(instructionPc, target); + } + + private void fixShortJump(int bytecode, int jumpOrInstructionPc, Label nullOrTarget) { + if (bytecode == GOTO) { + bytecodesBufWriter.writeU1(GOTO_W); + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); + } else if (bytecode == JSR) { + bytecodesBufWriter.writeU1(JSR_W); + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); + } else { + bytecodesBufWriter.writeU1U2( + BytecodeHelpers.reverseBranchOpcode(bytecode), // u1 + 8); // u1 + s2 + u1 + s4 // s2 + bytecodesBufWriter.writeU1(GOTO_W); // u1 + if (nullOrTarget == null) { + jumpOrInstructionPc -= 3; // jump -= 3; } else { - writeBytecode(BytecodeHelpers.reverseBranchOpcode(op)); - Label bypassJump = newLabel(); - writeLabelOffset(2, instructionPc, bypassJump); - writeBytecode(GOTO_W); - writeLabelOffset(4, instructionPc + 3, target); - labelBinding(bypassJump); + jumpOrInstructionPc += 3; // instructionPc += 3; } + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); // s4 + } + } + + private void writeParsedShortLabel(int jumpOrInstructionPc, Label nullOrTarget) { + if (nullOrTarget == null) { + if ((short) jumpOrInstructionPc != jumpOrInstructionPc) + throw new LabelOverflowException(); + bytecodesBufWriter.writeU2(jumpOrInstructionPc); + } else { + int pc = bytecodesBufWriter.skip(2); + addLabel(new DeferredLabel(pc, 2, jumpOrInstructionPc, nullOrTarget)); + } + } + + private void writeParsedLongLabel(int jumpOrInstructionPc, Label nullOrTarget) { + if (nullOrTarget == null) { + bytecodesBufWriter.writeInt(jumpOrInstructionPc); } else { - writeBytecode(op); - writeLabelOffset(op.sizeIfFixed() == 3 ? 2 : 4, instructionPc, target); + int pc = bytecodesBufWriter.skip(4); + addLabel(new DeferredLabel(pc, 4, jumpOrInstructionPc, nullOrTarget)); } } public void writeLookupSwitch(Label defaultTarget, List cases) { int instructionPc = curPc(); - writeBytecode(LOOKUPSWITCH); + bytecodesBufWriter.writeU1(LOOKUPSWITCH); int pad = 4 - (curPc() % 4); if (pad != 4) bytecodesBufWriter.skip(pad); // padding content can be anything - writeLabelOffset(4, instructionPc, defaultTarget); + writeLongLabelOffset(instructionPc, defaultTarget); bytecodesBufWriter.writeInt(cases.size()); cases = new ArrayList<>(cases); cases.sort(new Comparator<>() { @@ -556,103 +623,91 @@ public int compare(SwitchCase c1, SwitchCase c2) { }); for (var c : cases) { bytecodesBufWriter.writeInt(c.caseValue()); - writeLabelOffset(4, instructionPc, c.target()); + var target = c.target(); + writeLongLabelOffset(instructionPc, target); } } public void writeTableSwitch(int low, int high, Label defaultTarget, List cases) { int instructionPc = curPc(); - writeBytecode(TABLESWITCH); + bytecodesBufWriter.writeU1(TABLESWITCH); int pad = 4 - (curPc() % 4); if (pad != 4) bytecodesBufWriter.skip(pad); // padding content can be anything - writeLabelOffset(4, instructionPc, defaultTarget); - bytecodesBufWriter.writeInt(low); - bytecodesBufWriter.writeInt(high); + writeLongLabelOffset(instructionPc, defaultTarget); + bytecodesBufWriter.writeIntInt(low, high); var caseMap = new HashMap(cases.size()); for (var c : cases) { caseMap.put(c.caseValue(), c.target()); } for (long l = low; l<=high; l++) { - writeLabelOffset(4, instructionPc, caseMap.getOrDefault((int)l, defaultTarget)); + var target = caseMap.getOrDefault((int)l, defaultTarget); + writeLongLabelOffset(instructionPc, target); } } public void writeFieldAccess(Opcode opcode, FieldRefEntry ref) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(ref); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); } public void writeInvokeNormal(Opcode opcode, MemberRefEntry ref) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(ref); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); } public void writeInvokeInterface(Opcode opcode, InterfaceMethodRefEntry ref, int count) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(ref); - bytecodesBufWriter.writeU1(count); - bytecodesBufWriter.writeU1(0); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); + bytecodesBufWriter.writeU1U1(count, 0); } public void writeInvokeDynamic(InvokeDynamicEntry ref) { - writeBytecode(INVOKEDYNAMIC); - bytecodesBufWriter.writeIndex(ref); - bytecodesBufWriter.writeU2(0); + bytecodesBufWriter.writeU1U2U2(INVOKEDYNAMIC, bytecodesBufWriter.cpIndex(ref), 0); } public void writeNewObject(ClassEntry type) { - writeBytecode(NEW); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(NEW, type); } public void writeNewPrimitiveArray(int newArrayCode) { - writeBytecode(NEWARRAY); - bytecodesBufWriter.writeU1(newArrayCode); + bytecodesBufWriter.writeU1U1(NEWARRAY, newArrayCode); } public void writeNewReferenceArray(ClassEntry type) { - writeBytecode(ANEWARRAY); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(ANEWARRAY, type); } public void writeNewMultidimensionalArray(int dimensions, ClassEntry type) { - writeBytecode(MULTIANEWARRAY); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(MULTIANEWARRAY, type); bytecodesBufWriter.writeU1(dimensions); } public void writeTypeCheck(Opcode opcode, ClassEntry type) { - writeBytecode(opcode); - bytecodesBufWriter.writeIndex(type); + bytecodesBufWriter.writeIndex(opcode.bytecode(), type); } public void writeArgumentConstant(Opcode opcode, int value) { - writeBytecode(opcode); if (opcode.sizeIfFixed() == 3) { - bytecodesBufWriter.writeU2(value); + bytecodesBufWriter.writeU1U2(opcode.bytecode(), value); } else { - bytecodesBufWriter.writeU1(value); + bytecodesBufWriter.writeU1U1(opcode.bytecode(), value); } } public void writeLoadConstant(Opcode opcode, LoadableConstantEntry value) { // Make sure Long and Double have LDC2_W and - // rewrite to _W if index is > 256 + // rewrite to _W if index is >= 256 int index = AbstractPoolEntry.maybeClone(constantPool, value).index(); - Opcode op = opcode; if (value instanceof LongEntry || value instanceof DoubleEntry) { - op = LDC2_W; + opcode = Opcode.LDC2_W; } else if (index >= 256) - op = LDC_W; + opcode = Opcode.LDC_W; - writeBytecode(op); - if (op.sizeIfFixed() == 3) { - bytecodesBufWriter.writeU2(index); + assert !opcode.isWide(); + if (opcode.sizeIfFixed() == 3) { + bytecodesBufWriter.writeU1U2(opcode.bytecode(), index); } else { - bytecodesBufWriter.writeU1(index); + bytecodesBufWriter.writeU1U1(opcode.bytecode(), index); } } @@ -668,7 +723,11 @@ public int labelToBci(Label label) { if (context == this) { return lab.getBCI(); } - else if (context == mruParent) { + return labelToBci(context, lab); + } + + private int labelToBci(LabelContext context, LabelImpl lab) { + if (context == mruParent) { return mruParentTable[lab.getBCI()] - 1; } else if (context instanceof CodeAttribute parent) { @@ -708,14 +767,18 @@ public void setLabelTarget(Label label) { @Override public void setLabelTarget(Label label, int bci) { LabelImpl lab = (LabelImpl) label; - LabelContext context = lab.labelContext(); - - if (context == this) { + if (lab.labelContext() == this) { if (lab.getBCI() != -1) throw new IllegalArgumentException("Setting label target for already-set label"); lab.setBCI(bci); + } else { + setLabelTarget(lab, bci); } - else if (context == mruParent) { + } + + private void setLabelTarget(LabelImpl lab, int bci) { + LabelContext context = lab.labelContext(); + if (context == mruParent) { mruParentTable[lab.getBCI()] = bci + 1; } else if (context instanceof CodeAttribute parent) { @@ -730,7 +793,7 @@ public int[] apply(CodeAttribute x) { mruParent = parent; mruParentTable = table; - mruParentTable[lab.getBCI()] = bci + 1; + table[lab.getBCI()] = bci + 1; } else if (context instanceof BufferedCodeBuilder) { // Hijack the label @@ -742,7 +805,19 @@ else if (context instanceof BufferedCodeBuilder) { } public void addCharacterRange(CharacterRange element) { - characterRanges.add(element); + if (characterRangesCount >= characterRanges.length) { + int newCapacity = characterRangesCount + 8; + this.characterRanges = Arrays.copyOf(characterRanges, newCapacity); + } + characterRanges[characterRangesCount++] = element; + } + + public void addLabel(DeferredLabel label) { + if (deferredLabelsCount >= deferredLabels.length) { + int newCapacity = deferredLabelsCount + 8; + this.deferredLabels = Arrays.copyOf(deferredLabels, newCapacity); + } + deferredLabels[deferredLabelsCount++] = label; } public void addHandler(ExceptionCatch element) { @@ -754,11 +829,19 @@ public void addHandler(ExceptionCatch element) { } public void addLocalVariable(LocalVariable element) { - localVariables.add(element); + if (localVariablesCount >= localVariables.length) { + int newCapacity = localVariablesCount + 8; + this.localVariables = Arrays.copyOf(localVariables, newCapacity); + } + localVariables[localVariablesCount++] = element; } public void addLocalVariableType(LocalVariableType element) { - localVariableTypes.add(element); + if (localVariableTypesCount >= localVariableTypes.length) { + int newCapacity = localVariableTypesCount + 8; + this.localVariableTypes = Arrays.copyOf(localVariableTypes, newCapacity); + } + localVariableTypes[localVariableTypesCount++] = element; } @Override @@ -779,28 +862,54 @@ public LabelOverflowException() { // Fast overrides to avoid intermediate instructions // These are helpful for direct class building + @Override + public CodeBuilder return_() { + bytecodesBufWriter.writeU1(RETURN); + return this; + } + @Override public CodeBuilder return_(TypeKind tk) { - writeBytecode(BytecodeHelpers.returnOpcode(tk)); + bytecodesBufWriter.writeU1(returnBytecode(tk)); return this; } @Override public CodeBuilder storeLocal(TypeKind tk, int slot) { - writeLocalVar(BytecodeHelpers.storeOpcode(tk, slot), slot); + return switch (tk) { + case INT, SHORT, BYTE, CHAR, BOOLEAN + -> istore(slot); + case LONG -> lstore(slot); + case DOUBLE -> dstore(slot); + case FLOAT -> fstore(slot); + case REFERENCE -> astore(slot); + case VOID -> throw new IllegalArgumentException("void"); + }; + } + + @Override + public CodeBuilder labelBinding(Label label) { + setLabelTarget(label, curPc()); return this; } @Override public CodeBuilder loadLocal(TypeKind tk, int slot) { - writeLocalVar(BytecodeHelpers.loadOpcode(tk, slot), slot); - return this; + return switch (tk) { + case INT, SHORT, BYTE, CHAR, BOOLEAN + -> iload(slot); + case LONG -> lload(slot); + case DOUBLE -> dload(slot); + case FLOAT -> fload(slot); + case REFERENCE -> aload(slot); + case VOID -> throw new IllegalArgumentException("void"); + }; } @Override public CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) { - if (opcode == INVOKEINTERFACE) { - int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.nameAndType())) + 1; + if (opcode == Opcode.INVOKEINTERFACE) { + int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.type())) + 1; writeInvokeInterface(opcode, (InterfaceMethodRefEntry) ref, slots); } else { writeInvokeNormal(opcode, ref); @@ -808,21 +917,45 @@ public CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) { return this; } + @Override + public CodeBuilder invokespecial(ClassDesc owner, String name, MethodTypeDesc type) { + bytecodesBufWriter.writeIndex(INVOKESPECIAL, constantPool().methodRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder invokestatic(ClassDesc owner, String name, MethodTypeDesc type) { + bytecodesBufWriter.writeIndex(INVOKESTATIC, constantPool().methodRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder invokevirtual(ClassDesc owner, String name, MethodTypeDesc type) { + bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, constantPool().methodRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder getfield(ClassDesc owner, String name, ClassDesc type) { + bytecodesBufWriter.writeIndex(GETFIELD, constantPool().fieldRefEntry(owner, name, type)); + return this; + } + @Override public CodeBuilder fieldAccess(Opcode opcode, FieldRefEntry ref) { - writeFieldAccess(opcode, ref); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); return this; } @Override public CodeBuilder arrayLoad(TypeKind tk) { - writeBytecode(BytecodeHelpers.arrayLoadOpcode(tk)); + bytecodesBufWriter.writeU1(BytecodeHelpers.arrayLoadBytecode(tk)); return this; } @Override public CodeBuilder arrayStore(TypeKind tk) { - writeBytecode(BytecodeHelpers.arrayStoreOpcode(tk)); + bytecodesBufWriter.writeU1(BytecodeHelpers.arrayStoreBytecode(tk)); return this; } @@ -834,19 +967,23 @@ public CodeBuilder branch(Opcode op, Label target) { @Override public CodeBuilder nop() { - writeBytecode(NOP); + bytecodesBufWriter.writeU1(NOP); return this; } @Override public CodeBuilder aconst_null() { - writeBytecode(ACONST_NULL); + bytecodesBufWriter.writeU1(ACONST_NULL); return this; } @Override public CodeBuilder aload(int slot) { - writeLocalVar(BytecodeHelpers.aload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ALOAD_0 + slot); + } else { + writeLocalVar(ALOAD, slot); + } return this; } @@ -858,380 +995,530 @@ public CodeBuilder anewarray(ClassEntry entry) { @Override public CodeBuilder arraylength() { - writeBytecode(ARRAYLENGTH); + bytecodesBufWriter.writeU1(ARRAYLENGTH); + return this; + } + + @Override + public CodeBuilder areturn() { + bytecodesBufWriter.writeU1(ARETURN); return this; } @Override public CodeBuilder astore(int slot) { - writeLocalVar(BytecodeHelpers.astore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ASTORE_0 + slot); + } else { + writeLocalVar(ASTORE, slot); + } return this; } @Override public CodeBuilder athrow() { - writeBytecode(ATHROW); + bytecodesBufWriter.writeU1(ATHROW); return this; } @Override public CodeBuilder bipush(int b) { BytecodeHelpers.validateBipush(b); - writeArgumentConstant(BIPUSH, b); + bytecodesBufWriter.writeU1U1(BIPUSH, b); return this; } @Override public CodeBuilder checkcast(ClassEntry type) { - writeTypeCheck(CHECKCAST, type); + bytecodesBufWriter.writeIndex(CHECKCAST, type); return this; } @Override public CodeBuilder d2f() { - writeBytecode(D2F); + bytecodesBufWriter.writeU1(D2F); return this; } @Override public CodeBuilder d2i() { - writeBytecode(D2I); + bytecodesBufWriter.writeU1(D2I); return this; } @Override public CodeBuilder d2l() { - writeBytecode(D2L); + bytecodesBufWriter.writeU1(D2L); return this; } @Override public CodeBuilder dadd() { - writeBytecode(DADD); + bytecodesBufWriter.writeU1(DADD); return this; } @Override public CodeBuilder dcmpg() { - writeBytecode(DCMPG); + bytecodesBufWriter.writeU1(DCMPG); return this; } @Override public CodeBuilder dcmpl() { - writeBytecode(DCMPL); + bytecodesBufWriter.writeU1(DCMPL); return this; } @Override public CodeBuilder dconst_0() { - writeBytecode(DCONST_0); + bytecodesBufWriter.writeU1(DCONST_0); return this; } @Override public CodeBuilder dconst_1() { - writeBytecode(DCONST_1); + bytecodesBufWriter.writeU1(DCONST_1); return this; } @Override public CodeBuilder ddiv() { - writeBytecode(DDIV); + bytecodesBufWriter.writeU1(DDIV); return this; } @Override public CodeBuilder dload(int slot) { - writeLocalVar(BytecodeHelpers.dload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(DLOAD_0 + slot); + } else { + writeLocalVar(DLOAD, slot); + } return this; } @Override public CodeBuilder dmul() { - writeBytecode(DMUL); + bytecodesBufWriter.writeU1(DMUL); return this; } @Override public CodeBuilder dneg() { - writeBytecode(DNEG); + bytecodesBufWriter.writeU1(DNEG); return this; } @Override public CodeBuilder drem() { - writeBytecode(DREM); + bytecodesBufWriter.writeU1(DREM); + return this; + } + + @Override + public CodeBuilder dreturn() { + bytecodesBufWriter.writeU1(DRETURN); return this; } @Override public CodeBuilder dstore(int slot) { - writeLocalVar(BytecodeHelpers.dstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(DSTORE_0 + slot); + } else { + writeLocalVar(DSTORE, slot); + } return this; } @Override public CodeBuilder dsub() { - writeBytecode(DSUB); + bytecodesBufWriter.writeU1(DSUB); return this; } @Override public CodeBuilder dup() { - writeBytecode(DUP); + bytecodesBufWriter.writeU1(DUP); return this; } @Override public CodeBuilder dup2() { - writeBytecode(DUP2); + bytecodesBufWriter.writeU1(DUP2); return this; } @Override public CodeBuilder dup2_x1() { - writeBytecode(DUP2_X1); + bytecodesBufWriter.writeU1(DUP2_X1); return this; } @Override public CodeBuilder dup2_x2() { - writeBytecode(DUP2_X2); + bytecodesBufWriter.writeU1(DUP2_X2); return this; } @Override public CodeBuilder dup_x1() { - writeBytecode(DUP_X1); + bytecodesBufWriter.writeU1(DUP_X1); return this; } @Override public CodeBuilder dup_x2() { - writeBytecode(DUP_X2); + bytecodesBufWriter.writeU1(DUP_X2); return this; } @Override public CodeBuilder f2d() { - writeBytecode(F2D); + bytecodesBufWriter.writeU1(F2D); return this; } @Override public CodeBuilder f2i() { - writeBytecode(F2I); + bytecodesBufWriter.writeU1(F2I); return this; } @Override public CodeBuilder f2l() { - writeBytecode(F2L); + bytecodesBufWriter.writeU1(F2L); return this; } @Override public CodeBuilder fadd() { - writeBytecode(FADD); + bytecodesBufWriter.writeU1(FADD); return this; } @Override public CodeBuilder fcmpg() { - writeBytecode(FCMPG); + bytecodesBufWriter.writeU1(FCMPG); return this; } @Override public CodeBuilder fcmpl() { - writeBytecode(FCMPL); + bytecodesBufWriter.writeU1(FCMPL); return this; } @Override public CodeBuilder fconst_0() { - writeBytecode(FCONST_0); + bytecodesBufWriter.writeU1(FCONST_0); return this; } @Override public CodeBuilder fconst_1() { - writeBytecode(FCONST_1); + bytecodesBufWriter.writeU1(FCONST_1); return this; } @Override public CodeBuilder fconst_2() { - writeBytecode(FCONST_2); + bytecodesBufWriter.writeU1(FCONST_2); return this; } @Override public CodeBuilder fdiv() { - writeBytecode(FDIV); + bytecodesBufWriter.writeU1(FDIV); return this; } @Override public CodeBuilder fload(int slot) { - writeLocalVar(BytecodeHelpers.fload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(FLOAD_0 + slot); + } else { + writeLocalVar(FLOAD, slot); + } return this; } @Override public CodeBuilder fmul() { - writeBytecode(FMUL); + bytecodesBufWriter.writeU1(FMUL); return this; } @Override public CodeBuilder fneg() { - writeBytecode(FNEG); + bytecodesBufWriter.writeU1(FNEG); return this; } @Override public CodeBuilder frem() { - writeBytecode(FREM); + bytecodesBufWriter.writeU1(FREM); + return this; + } + + @Override + public CodeBuilder freturn() { + bytecodesBufWriter.writeU1(FRETURN); return this; } @Override public CodeBuilder fstore(int slot) { - writeLocalVar(BytecodeHelpers.fstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(FSTORE_0 + slot); + } else { + writeLocalVar(FSTORE, slot); + } return this; } @Override public CodeBuilder fsub() { - writeBytecode(FSUB); + bytecodesBufWriter.writeU1(FSUB); + return this; + } + + @Override + public CodeBuilder getstatic(ClassDesc owner, String name, ClassDesc type) { + bytecodesBufWriter.writeIndex(GETSTATIC, constantPool().fieldRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder goto_(Label target) { + writeShortJump(GOTO, target); return this; } @Override public CodeBuilder i2b() { - writeBytecode(I2B); + bytecodesBufWriter.writeU1(I2B); return this; } @Override public CodeBuilder i2c() { - writeBytecode(I2C); + bytecodesBufWriter.writeU1(I2C); return this; } @Override public CodeBuilder i2d() { - writeBytecode(I2D); + bytecodesBufWriter.writeU1(I2D); return this; } @Override public CodeBuilder i2f() { - writeBytecode(I2F); + bytecodesBufWriter.writeU1(I2F); return this; } @Override public CodeBuilder i2l() { - writeBytecode(I2L); + bytecodesBufWriter.writeU1(I2L); return this; } @Override public CodeBuilder i2s() { - writeBytecode(I2S); + bytecodesBufWriter.writeU1(I2S); return this; } @Override public CodeBuilder iadd() { - writeBytecode(IADD); + bytecodesBufWriter.writeU1(IADD); return this; } @Override public CodeBuilder iand() { - writeBytecode(IAND); + bytecodesBufWriter.writeU1(IAND); return this; } @Override public CodeBuilder iconst_0() { - writeBytecode(ICONST_0); + bytecodesBufWriter.writeU1(ICONST_0); return this; } @Override public CodeBuilder iconst_1() { - writeBytecode(ICONST_1); + bytecodesBufWriter.writeU1(ICONST_1); return this; } @Override public CodeBuilder iconst_2() { - writeBytecode(ICONST_2); + bytecodesBufWriter.writeU1(ICONST_2); return this; } @Override public CodeBuilder iconst_3() { - writeBytecode(ICONST_3); + bytecodesBufWriter.writeU1(ICONST_3); return this; } @Override public CodeBuilder iconst_4() { - writeBytecode(ICONST_4); + bytecodesBufWriter.writeU1(ICONST_4); return this; } @Override public CodeBuilder iconst_5() { - writeBytecode(ICONST_5); + bytecodesBufWriter.writeU1(ICONST_5); return this; } @Override public CodeBuilder iconst_m1() { - writeBytecode(ICONST_M1); + bytecodesBufWriter.writeU1(ICONST_M1); return this; } @Override public CodeBuilder idiv() { - writeBytecode(IDIV); + bytecodesBufWriter.writeU1(IDIV); + return this; + } + + @Override + public CodeBuilder if_acmpeq(Label target) { + writeShortJump(IF_ACMPEQ, target); + return this; + } + + @Override + public CodeBuilder if_acmpne(Label target) { + writeShortJump(IF_ACMPNE, target); + return this; + } + + @Override + public CodeBuilder if_icmpeq(Label target) { + writeShortJump(IF_ICMPEQ, target); + return this; + } + + @Override + public CodeBuilder if_icmpge(Label target) { + writeShortJump(IF_ICMPGE, target); + return this; + } + + @Override + public CodeBuilder if_icmpgt(Label target) { + writeShortJump(IF_ICMPGT, target); + return this; + } + + @Override + public CodeBuilder if_icmple(Label target) { + writeShortJump(IF_ICMPLE, target); + return this; + } + + @Override + public CodeBuilder if_icmplt(Label target) { + writeShortJump(IF_ICMPLT, target); + return this; + } + + @Override + public CodeBuilder if_icmpne(Label target) { + writeShortJump(IF_ICMPNE, target); + return this; + } + + @Override + public CodeBuilder ifnonnull(Label target) { + writeShortJump(IFNONNULL, target); + return this; + } + + @Override + public CodeBuilder ifnull(Label target) { + writeShortJump(IFNULL, target); + return this; + } + + @Override + public CodeBuilder ifeq(Label target) { + writeShortJump(IFEQ, target); + return this; + } + + @Override + public CodeBuilder ifge(Label target) { + writeShortJump(IFGE, target); + return this; + } + + @Override + public CodeBuilder ifgt(Label target) { + writeShortJump(IFGT, target); + return this; + } + + @Override + public CodeBuilder ifle(Label target) { + writeShortJump(IFLE, target); + return this; + } + + @Override + public CodeBuilder iflt(Label target) { + writeShortJump(IFLT, target); + return this; + } + + @Override + public CodeBuilder ifne(Label target) { + writeShortJump(IFNE, target); return this; } @Override public CodeBuilder iinc(int slot, int val) { - writeIncrement(slot, val); + writeIncrement(validateAndIsWideIinc(slot, val), slot, val); return this; } @Override public CodeBuilder iload(int slot) { - writeLocalVar(BytecodeHelpers.iload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ILOAD_0 + slot); + } else { + writeLocalVar(ILOAD, slot); + } return this; } @Override public CodeBuilder imul() { - writeBytecode(IMUL); + bytecodesBufWriter.writeU1(IMUL); return this; } @Override public CodeBuilder ineg() { - writeBytecode(INEG); + bytecodesBufWriter.writeU1(INEG); return this; } @Override public CodeBuilder instanceOf(ClassEntry target) { - writeTypeCheck(INSTANCEOF, target); + bytecodesBufWriter.writeIndex(INSTANCEOF, target); return this; } @@ -1243,85 +1530,95 @@ public CodeBuilder invokedynamic(InvokeDynamicEntry ref) { @Override public CodeBuilder invokeinterface(InterfaceMethodRefEntry ref) { - writeInvokeInterface(INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1); + writeInvokeInterface(Opcode.INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1); return this; } @Override public CodeBuilder invokespecial(InterfaceMethodRefEntry ref) { - writeInvokeNormal(INVOKESPECIAL, ref); + bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref); return this; } @Override public CodeBuilder invokespecial(MethodRefEntry ref) { - writeInvokeNormal(INVOKESPECIAL, ref); + bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref); return this; } @Override public CodeBuilder invokestatic(InterfaceMethodRefEntry ref) { - writeInvokeNormal(INVOKESTATIC, ref); + bytecodesBufWriter.writeIndex(INVOKESTATIC, ref); return this; } @Override public CodeBuilder invokestatic(MethodRefEntry ref) { - writeInvokeNormal(INVOKESTATIC, ref); + bytecodesBufWriter.writeIndex(INVOKESTATIC, ref); return this; } @Override public CodeBuilder invokevirtual(MethodRefEntry ref) { - writeInvokeNormal(INVOKEVIRTUAL, ref); + bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, ref); return this; } @Override public CodeBuilder ior() { - writeBytecode(IOR); + bytecodesBufWriter.writeU1(IOR); return this; } @Override public CodeBuilder irem() { - writeBytecode(IREM); + bytecodesBufWriter.writeU1(IREM); + return this; + } + + @Override + public CodeBuilder ireturn() { + bytecodesBufWriter.writeU1(IRETURN); return this; } @Override public CodeBuilder ishl() { - writeBytecode(ISHL); + bytecodesBufWriter.writeU1(ISHL); return this; } @Override public CodeBuilder ishr() { - writeBytecode(ISHR); + bytecodesBufWriter.writeU1(ISHR); return this; } @Override public CodeBuilder istore(int slot) { - writeLocalVar(BytecodeHelpers.istore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ISTORE_0 + slot); + } else { + writeLocalVar(ISTORE, slot); + } return this; } @Override public CodeBuilder isub() { - writeBytecode(ISUB); + bytecodesBufWriter.writeU1(ISUB); return this; } @Override public CodeBuilder iushr() { - writeBytecode(IUSHR); + bytecodesBufWriter.writeU1(IUSHR); return this; } @Override public CodeBuilder ixor() { - writeBytecode(IXOR); + bytecodesBufWriter.writeU1(IXOR); return this; } @@ -1333,49 +1630,49 @@ public CodeBuilder lookupswitch(Label defaultTarget, List cases) { @Override public CodeBuilder l2d() { - writeBytecode(L2D); + bytecodesBufWriter.writeU1(L2D); return this; } @Override public CodeBuilder l2f() { - writeBytecode(L2F); + bytecodesBufWriter.writeU1(L2F); return this; } @Override public CodeBuilder l2i() { - writeBytecode(L2I); + bytecodesBufWriter.writeU1(L2I); return this; } @Override public CodeBuilder ladd() { - writeBytecode(LADD); + bytecodesBufWriter.writeU1(LADD); return this; } @Override public CodeBuilder land() { - writeBytecode(LAND); + bytecodesBufWriter.writeU1(LAND); return this; } @Override public CodeBuilder lcmp() { - writeBytecode(LCMP); + bytecodesBufWriter.writeU1(LCMP); return this; } @Override public CodeBuilder lconst_0() { - writeBytecode(LCONST_0); + bytecodesBufWriter.writeU1(LCONST_0); return this; } @Override public CodeBuilder lconst_1() { - writeBytecode(LCONST_1); + bytecodesBufWriter.writeU1(LCONST_1); return this; } @@ -1387,85 +1684,99 @@ public CodeBuilder ldc(LoadableConstantEntry entry) { @Override public CodeBuilder ldiv() { - writeBytecode(LDIV); + bytecodesBufWriter.writeU1(LDIV); return this; } @Override public CodeBuilder lload(int slot) { - writeLocalVar(BytecodeHelpers.lload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(LLOAD_0 + slot); + } else { + writeLocalVar(LLOAD, slot); + } return this; } @Override public CodeBuilder lmul() { - writeBytecode(LMUL); + bytecodesBufWriter.writeU1(LMUL); return this; } @Override public CodeBuilder lneg() { - writeBytecode(LNEG); + bytecodesBufWriter.writeU1(LNEG); return this; } @Override public CodeBuilder lor() { - writeBytecode(LOR); + bytecodesBufWriter.writeU1(LOR); return this; } @Override public CodeBuilder lrem() { - writeBytecode(LREM); + bytecodesBufWriter.writeU1(LREM); + return this; + } + + @Override + public CodeBuilder lreturn() { + bytecodesBufWriter.writeU1(LRETURN); return this; } @Override public CodeBuilder lshl() { - writeBytecode(LSHL); + bytecodesBufWriter.writeU1(LSHL); return this; } @Override public CodeBuilder lshr() { - writeBytecode(LSHR); + bytecodesBufWriter.writeU1(LSHR); return this; } @Override public CodeBuilder lstore(int slot) { - writeLocalVar(BytecodeHelpers.lstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(LSTORE_0 + slot); + } else { + writeLocalVar(LSTORE, slot); + } return this; } @Override public CodeBuilder lsub() { - writeBytecode(LSUB); + bytecodesBufWriter.writeU1(LSUB); return this; } @Override public CodeBuilder lushr() { - writeBytecode(LUSHR); + bytecodesBufWriter.writeU1(LUSHR); return this; } @Override public CodeBuilder lxor() { - writeBytecode(LXOR); + bytecodesBufWriter.writeU1(LXOR); return this; } @Override public CodeBuilder monitorenter() { - writeBytecode(MONITORENTER); + bytecodesBufWriter.writeU1(MONITORENTER); return this; } @Override public CodeBuilder monitorexit() { - writeBytecode(MONITOREXIT); + bytecodesBufWriter.writeU1(MONITOREXIT); return this; } @@ -1492,26 +1803,26 @@ public CodeBuilder newarray(TypeKind typeKind) { @Override public CodeBuilder pop() { - writeBytecode(POP); + bytecodesBufWriter.writeU1(POP); return this; } @Override public CodeBuilder pop2() { - writeBytecode(POP2); + bytecodesBufWriter.writeU1(POP2); return this; } @Override public CodeBuilder sipush(int s) { BytecodeHelpers.validateSipush(s); - writeArgumentConstant(SIPUSH, s); + bytecodesBufWriter.writeU1U2(SIPUSH, s); return this; } @Override public CodeBuilder swap() { - writeBytecode(SWAP); + bytecodesBufWriter.writeU1(SWAP); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java index 15547924cf37f..222ed6b9792d2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectFieldBuilder.java @@ -33,6 +33,8 @@ import java.lang.classfile.FieldModel; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class DirectFieldBuilder extends AbstractDirectBuilder implements TerminalFieldBuilder, Util.Writable { @@ -48,8 +50,8 @@ public DirectFieldBuilder(SplitConstantPool constantPool, FieldModel original) { super(constantPool, context); setOriginal(original); - this.name = name; - this.desc = type; + this.name = requireNonNull(name); + this.desc = requireNonNull(type); this.flags = flags; } @@ -58,7 +60,7 @@ public FieldBuilder with(FieldElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } @@ -80,9 +82,7 @@ void setFlags(int flags) { @Override public void writeTo(BufWriterImpl buf) { - buf.writeU2(flags); - buf.writeIndex(name); - buf.writeIndex(desc); + buf.writeU2U2U2(flags, buf.cpIndex(name), buf.cpIndex(desc)); attributes.writeTo(buf); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index ec4e148baf30d..cdd5f8155e1c7 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -38,6 +38,8 @@ import java.lang.classfile.MethodModel; import java.lang.classfile.constantpool.Utf8Entry; +import static java.util.Objects.requireNonNull; + public final class DirectMethodBuilder extends AbstractDirectBuilder implements TerminalMethodBuilder, Util.Writable { @@ -55,8 +57,8 @@ public DirectMethodBuilder(SplitConstantPool constantPool, MethodModel original) { super(constantPool, context); setOriginal(original); - this.name = nameInfo; - this.desc = typeInfo; + this.name = requireNonNull(nameInfo); + this.desc = requireNonNull(typeInfo); this.flags = flags; } @@ -114,7 +116,7 @@ public MethodBuilder with(MethodElement element) { if (element instanceof AbstractElement ae) { ae.writeTo(this); } else { - writeAttribute((CustomAttribute) element); + writeAttribute((CustomAttribute) requireNonNull(element)); } return this; } @@ -148,9 +150,7 @@ public DirectMethodBuilder run(Consumer handler) { @Override public void writeTo(BufWriterImpl buf) { - buf.writeU2(flags); - buf.writeIndex(name); - buf.writeIndex(desc); + buf.writeU2U2U2(flags, buf.cpIndex(name), buf.cpIndex(desc)); attributes.writeTo(buf); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java b/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java index d5ebbbebe7ada..be350cb581444 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java @@ -113,6 +113,7 @@ public void put(int hash, int index) { throw new IllegalArgumentException("hash must be nonzero"); int ptr = (hash & mask1) << 1; + var data = this.data; int k = data[ptr]; if (k == 0) { data[ptr] = hash; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java index 30bb8136e45ab..205e36588b421 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java @@ -84,9 +84,9 @@ public void writeTo(BufWriterImpl buf) { reader.copyBytesTo(buf, startPos, endPos - startPos); } else { - buf.writeU2(flags().flagsMask()); - buf.writeIndex(fieldName()); - buf.writeIndex(fieldType()); + buf.writeU2U2U2(flags().flagsMask(), + buf.cpIndex(fieldName()), + buf.cpIndex(fieldType())); Util.writeAttributes(buf, attributes()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java index 2aaf5f033c0b6..aac0fafaef146 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java @@ -24,8 +24,6 @@ */ package jdk.internal.classfile.impl; -import java.util.Objects; - import java.lang.classfile.Label; import java.lang.classfile.instruction.LabelTarget; @@ -52,7 +50,7 @@ public final class LabelImpl private int bci; public LabelImpl(LabelContext labelContext, int bci) { - this.labelContext = Objects.requireNonNull(labelContext); + this.labelContext = labelContext; this.bci = bci; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java index 8467152504ee5..4f7799d1fa7d5 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java @@ -103,9 +103,9 @@ public void writeTo(BufWriterImpl buf) { reader.copyBytesTo(buf, startPos, endPos - startPos); } else { - buf.writeU2(flags().flagsMask()); - buf.writeIndex(methodName()); - buf.writeIndex(methodType()); + buf.writeU2U2U2(flags().flagsMask(), + buf.cpIndex(methodName()), + buf.cpIndex(methodType())); Util.writeAttributes(buf, attributes()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java b/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java index 860b7e74b4dde..659f41f9699ae 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java @@ -32,8 +32,6 @@ import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.Stable; -import static java.lang.classfile.ClassFile.*; - public final class RawBytecodeHelper { public static final BiFunction, IllegalArgumentException> @@ -43,6 +41,210 @@ public IllegalArgumentException apply(String s) { return new IllegalArgumentException(s); } }); + public static final int + ILLEGAL = -1, + NOP = 0, + ACONST_NULL = 1, + ICONST_M1 = 2, + ICONST_0 = 3, + ICONST_1 = 4, + ICONST_2 = 5, + ICONST_3 = 6, + ICONST_4 = 7, + ICONST_5 = 8, + LCONST_0 = 9, + LCONST_1 = 10, + FCONST_0 = 11, + FCONST_1 = 12, + FCONST_2 = 13, + DCONST_0 = 14, + DCONST_1 = 15, + BIPUSH = 16, + SIPUSH = 17, + LDC = 18, + LDC_W = 19, + LDC2_W = 20, + ILOAD = 21, + LLOAD = 22, + FLOAD = 23, + DLOAD = 24, + ALOAD = 25, + ILOAD_0 = 26, + ILOAD_1 = 27, + ILOAD_2 = 28, + ILOAD_3 = 29, + LLOAD_0 = 30, + LLOAD_1 = 31, + LLOAD_2 = 32, + LLOAD_3 = 33, + FLOAD_0 = 34, + FLOAD_1 = 35, + FLOAD_2 = 36, + FLOAD_3 = 37, + DLOAD_0 = 38, + DLOAD_1 = 39, + DLOAD_2 = 40, + DLOAD_3 = 41, + ALOAD_0 = 42, + ALOAD_1 = 43, + ALOAD_2 = 44, + ALOAD_3 = 45, + IALOAD = 46, + LALOAD = 47, + FALOAD = 48, + DALOAD = 49, + AALOAD = 50, + BALOAD = 51, + CALOAD = 52, + SALOAD = 53, + ISTORE = 54, + LSTORE = 55, + FSTORE = 56, + DSTORE = 57, + ASTORE = 58, + ISTORE_0 = 59, + ISTORE_1 = 60, + ISTORE_2 = 61, + ISTORE_3 = 62, + LSTORE_0 = 63, + LSTORE_1 = 64, + LSTORE_2 = 65, + LSTORE_3 = 66, + FSTORE_0 = 67, + FSTORE_1 = 68, + FSTORE_2 = 69, + FSTORE_3 = 70, + DSTORE_0 = 71, + DSTORE_1 = 72, + DSTORE_2 = 73, + DSTORE_3 = 74, + ASTORE_0 = 75, + ASTORE_1 = 76, + ASTORE_2 = 77, + ASTORE_3 = 78, + IASTORE = 79, + LASTORE = 80, + FASTORE = 81, + DASTORE = 82, + AASTORE = 83, + BASTORE = 84, + CASTORE = 85, + SASTORE = 86, + POP = 87, + POP2 = 88, + DUP = 89, + DUP_X1 = 90, + DUP_X2 = 91, + DUP2 = 92, + DUP2_X1 = 93, + DUP2_X2 = 94, + SWAP = 95, + IADD = 96, + LADD = 97, + FADD = 98, + DADD = 99, + ISUB = 100, + LSUB = 101, + FSUB = 102, + DSUB = 103, + IMUL = 104, + LMUL = 105, + FMUL = 106, + DMUL = 107, + IDIV = 108, + LDIV = 109, + FDIV = 110, + DDIV = 111, + IREM = 112, + LREM = 113, + FREM = 114, + DREM = 115, + INEG = 116, + LNEG = 117, + FNEG = 118, + DNEG = 119, + ISHL = 120, + LSHL = 121, + ISHR = 122, + LSHR = 123, + IUSHR = 124, + LUSHR = 125, + IAND = 126, + LAND = 127, + IOR = 128, + LOR = 129, + IXOR = 130, + LXOR = 131, + IINC = 132, + I2L = 133, + I2F = 134, + I2D = 135, + L2I = 136, + L2F = 137, + L2D = 138, + F2I = 139, + F2L = 140, + F2D = 141, + D2I = 142, + D2L = 143, + D2F = 144, + I2B = 145, + I2C = 146, + I2S = 147, + LCMP = 148, + FCMPL = 149, + FCMPG = 150, + DCMPL = 151, + DCMPG = 152, + IFEQ = 153, + IFNE = 154, + IFLT = 155, + IFGE = 156, + IFGT = 157, + IFLE = 158, + IF_ICMPEQ = 159, + IF_ICMPNE = 160, + IF_ICMPLT = 161, + IF_ICMPGE = 162, + IF_ICMPGT = 163, + IF_ICMPLE = 164, + IF_ACMPEQ = 165, + IF_ACMPNE = 166, + GOTO = 167, + JSR = 168, + RET = 169, + TABLESWITCH = 170, + LOOKUPSWITCH = 171, + IRETURN = 172, + LRETURN = 173, + FRETURN = 174, + DRETURN = 175, + ARETURN = 176, + RETURN = 177, + GETSTATIC = 178, + PUTSTATIC = 179, + GETFIELD = 180, + PUTFIELD = 181, + INVOKEVIRTUAL = 182, + INVOKESPECIAL = 183, + INVOKESTATIC = 184, + INVOKEINTERFACE = 185, + INVOKEDYNAMIC = 186, + NEW = 187, + NEWARRAY = 188, + ANEWARRAY = 189, + ARRAYLENGTH = 190, + ATHROW = 191, + CHECKCAST = 192, + INSTANCEOF = 193, + MONITORENTER = 194, + MONITOREXIT = 195, + WIDE = 196, + MULTIANEWARRAY = 197, + IFNULL = 198, + IFNONNULL = 199, + GOTO_W = 200, + JSR_W = 201; public record CodeRange(byte[] array, int length) { public RawBytecodeHelper start() { @@ -50,8 +252,6 @@ public RawBytecodeHelper start() { } } - public static final int ILLEGAL = -1; - /** * The length of opcodes, or -1 for no fixed length. * This is generated as if: @@ -99,7 +299,6 @@ public static int align(int n) { private int nextBci; private int bci; private int opcode; - private boolean isWide; public static CodeRange of(byte[] array) { return new CodeRange(array, array.length); @@ -141,14 +340,14 @@ public void reset(int nextBci) { * be valid and can be accessed unchecked. */ public int opcode() { - return opcode; + return opcode & 0xFF; } /** * Returns whether the current functional opcode is in wide. */ public boolean isWide() { - return isWide; + return (opcode & (WIDE << 8)) != 0; } /** @@ -211,7 +410,7 @@ public int destW() { // *load, *store, iinc public int getIndex() { - return isWide ? getU2Unchecked(bci + 2) : getIndexU1(); + return isWide() ? getU2Unchecked(bci + 2) : getIndexU1(); } // ldc @@ -243,12 +442,11 @@ public boolean next() { int len = LENGTHS[code & 0xFF]; // & 0xFF eliminates bound check this.bci = bci; opcode = code; - isWide = false; if (len <= 0) { len = checkSpecialInstruction(bci, end, code); // sets opcode } - if (len <= 0 || (nextBci += len) > end) { + if ((nextBci += len) > end) { opcode = ILLEGAL; } @@ -257,37 +455,34 @@ public boolean next() { // Put rarely used code in another method to reduce code size private int checkSpecialInstruction(int bci, int end, int code) { + int len = -1; if (code == WIDE) { - if (bci + 1 >= end) { - return -1; + if (bci + 1 < end) { + opcode = (WIDE << 8) | (code = getIndexU1()); + // Validated in UtilTest.testOpcodeLengthTable + len = LENGTHS[code] * 2; } - opcode = code = getIndexU1(); - isWide = true; - // Validated in UtilTest.testOpcodeLengthTable - return LENGTHS[code] * 2; - } - if (code == TABLESWITCH) { + } else if (code == TABLESWITCH) { int alignedBci = align(bci + 1); - if (alignedBci + 3 * 4 >= end) { - return -1; + if (alignedBci + 3 * 4 < end) { + int lo = getIntUnchecked(alignedBci + 1 * 4); + int hi = getIntUnchecked(alignedBci + 2 * 4); + long l = alignedBci - bci + (3L + (long) hi - lo + 1L) * 4L; + len = l > 0 && ((int) l == l) ? (int) l : -1; } - int lo = getIntUnchecked(alignedBci + 1 * 4); - int hi = getIntUnchecked(alignedBci + 2 * 4); - long l = alignedBci - bci + (3L + (long) hi - lo + 1L) * 4L; - return l > 0 && ((int) l == l) ? (int) l : -1; - } - if (code == LOOKUPSWITCH) { + } else if (code == LOOKUPSWITCH) { int alignedBci = align(bci + 1); - if (alignedBci + 2 * 4 >= end) { - return -1; + if (alignedBci + 2 * 4 < end) { + int npairs = getIntUnchecked(alignedBci + 4); + if (npairs >= 0) { + long l = alignedBci - bci + (2L + 2L * npairs) * 4L; + len = l > 0 && ((int) l == l) ? (int) l : -1; + } } - int npairs = getIntUnchecked(alignedBci + 4); - if (npairs < 0) { - return -1; - } - long l = alignedBci - bci + (2L + 2L * npairs) * 4L; - return l > 0 && ((int) l == l) ? (int) l : -1; } - return -1; + if (len <= 0) { + opcode = ILLEGAL; + } + return len; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 0b5d64023b962..4c76033885735 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -31,28 +31,14 @@ import java.lang.classfile.Attributes; import java.lang.classfile.ClassReader; -import java.lang.classfile.ClassFile; import java.lang.classfile.BootstrapMethodEntry; import java.lang.classfile.attribute.BootstrapMethodsAttribute; import java.lang.classfile.constantpool.*; -import java.util.Objects; - -import static java.lang.classfile.ClassFile.TAG_CLASS; -import static java.lang.classfile.ClassFile.TAG_CONSTANTDYNAMIC; -import static java.lang.classfile.ClassFile.TAG_DOUBLE; -import static java.lang.classfile.ClassFile.TAG_FIELDREF; -import static java.lang.classfile.ClassFile.TAG_FLOAT; -import static java.lang.classfile.ClassFile.TAG_INTEGER; -import static java.lang.classfile.ClassFile.TAG_INTERFACEMETHODREF; -import static java.lang.classfile.ClassFile.TAG_INVOKEDYNAMIC; -import static java.lang.classfile.ClassFile.TAG_LONG; -import static java.lang.classfile.ClassFile.TAG_METHODHANDLE; -import static java.lang.classfile.ClassFile.TAG_METHODREF; -import static java.lang.classfile.ClassFile.TAG_METHODTYPE; -import static java.lang.classfile.ClassFile.TAG_MODULE; -import static java.lang.classfile.ClassFile.TAG_NAMEANDTYPE; -import static java.lang.classfile.ClassFile.TAG_PACKAGE; -import static java.lang.classfile.ClassFile.TAG_STRING; + +import jdk.internal.constant.ConstantUtils; + +import static java.lang.classfile.constantpool.PoolEntry.*; +import static java.util.Objects.requireNonNull; public final class SplitConstantPool implements ConstantPoolBuilder { @@ -101,20 +87,27 @@ public int bootstrapMethodCount() { @Override public PoolEntry entryByIndex(int index) { if (index <= 0 || index >= size()) { - throw new ConstantPoolException("Bad CP index: " + index); + throw badCP(index); } PoolEntry pe = (index < parentSize) ? parent.entryByIndex(index) : myEntries[index - parentSize]; if (pe == null) { - throw new ConstantPoolException("Unusable CP index: " + index); + throw unusableCP(index); } return pe; } + private static ConstantPoolException badCP(int index) { + return new ConstantPoolException("Bad CP index: " + index); + } + + private static ConstantPoolException unusableCP(int index) { + return new ConstantPoolException("Unusable CP index: " + index); + } + @Override public T entryByIndex(int index, Class cls) { - Objects.requireNonNull(cls); return ClassReaderImpl.checkType(entryByIndex(index), index, cls); } @@ -130,6 +123,7 @@ public BootstrapMethodEntryImpl bootstrapMethodEntry(int index) { @Override public boolean canWriteDirect(ConstantPool other) { + requireNonNull(other); return this == other || parent == other; } @@ -179,8 +173,10 @@ void writeTo(BufWriterImpl buf) { } private EntryMap map() { + int parentSize = this.parentSize; + var map = this.map; if (map == null) { - map = new EntryMap(Math.max(size, 1024), .75f); + this.map = map = new EntryMap(Math.max(size, 1024), .75f); // Doing a full scan here yields fall-off-the-cliff performance results, // especially if we only need a few entries that are already @@ -217,8 +213,10 @@ private void fullScan() { } private EntryMap bsmMap() { + int bsmSize = this.bsmSize; + var bsmMap = this.bsmMap; if (bsmMap == null) { - bsmMap = new EntryMap(Math.max(bsmSize, 16), .75f); + this.bsmMap = bsmMap = new EntryMap(Math.max(bsmSize, 16), .75f); for (int i=0; i fieldRefEntry(reference.owner(), reference.nameAndType()); - case TAG_METHODREF -> methodRefEntry(reference.owner(), reference.nameAndType()); - case TAG_INTERFACEMETHODREF -> interfaceMethodRefEntry(reference.owner(), reference.nameAndType()); - default -> throw new IllegalArgumentException(String.format("Bad tag %d", reference.tag())); - }; - } - - int hash = AbstractPoolEntry.hash2(TAG_METHODHANDLE, refKind, reference.index()); + reference = AbstractPoolEntry.maybeClone(this, reference); + int hash = AbstractPoolEntry.hash2(TAG_METHOD_HANDLE, refKind, reference.index()); EntryMap map1 = map(); for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) { PoolEntry e = entryByIndex(map1.getIndexByToken(token)); - if (e.tag() == TAG_METHODHANDLE + if (e.tag() == TAG_METHOD_HANDLE && e instanceof AbstractPoolEntry.MethodHandleEntryImpl ce && ce.kind() == refKind && ce.reference() == reference) return ce; @@ -538,14 +629,13 @@ public InvokeDynamicEntry invokeDynamicEntry(BootstrapMethodEntry bootstrapMetho if (!canWriteDirect(bootstrapMethodEntry.constantPool())) bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(), bootstrapMethodEntry.arguments()); - if (!canWriteDirect(nameAndType.constantPool())) - nameAndType = nameAndTypeEntry(nameAndType.name(), nameAndType.type()); - int hash = AbstractPoolEntry.hash2(TAG_INVOKEDYNAMIC, + nameAndType = AbstractPoolEntry.maybeClone(this, nameAndType); + int hash = AbstractPoolEntry.hash2(TAG_INVOKE_DYNAMIC, bootstrapMethodEntry.bsmIndex(), nameAndType.index()); EntryMap map1 = map(); for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) { PoolEntry e = entryByIndex(map1.getIndexByToken(token)); - if (e.tag() == TAG_INVOKEDYNAMIC + if (e.tag() == TAG_INVOKE_DYNAMIC && e instanceof AbstractPoolEntry.InvokeDynamicEntryImpl ce && ce.bootstrap() == bootstrapMethodEntry && ce.nameAndType() == nameAndType) return ce; @@ -569,14 +659,13 @@ public ConstantDynamicEntry constantDynamicEntry(BootstrapMethodEntry bootstrapM if (!canWriteDirect(bootstrapMethodEntry.constantPool())) bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(), bootstrapMethodEntry.arguments()); - if (!canWriteDirect(nameAndType.constantPool())) - nameAndType = nameAndTypeEntry(nameAndType.name(), nameAndType.type()); - int hash = AbstractPoolEntry.hash2(TAG_CONSTANTDYNAMIC, + nameAndType = AbstractPoolEntry.maybeClone(this, nameAndType); + int hash = AbstractPoolEntry.hash2(TAG_DYNAMIC, bootstrapMethodEntry.bsmIndex(), nameAndType.index()); EntryMap map1 = map(); for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) { PoolEntry e = entryByIndex(map1.getIndexByToken(token)); - if (e.tag() == TAG_CONSTANTDYNAMIC + if (e.tag() == TAG_DYNAMIC && e instanceof AbstractPoolEntry.ConstantDynamicEntryImpl ce && ce.bootstrap() == bootstrapMethodEntry && ce.nameAndType() == nameAndType) return ce; @@ -628,8 +717,7 @@ public StringEntry stringEntry(Utf8Entry utf8) { @Override public BootstrapMethodEntry bsmEntry(MethodHandleEntry methodReference, List arguments) { - if (!canWriteDirect(methodReference.constantPool())) - methodReference = methodHandleEntry(methodReference.kind(), methodReference.reference()); + methodReference = AbstractPoolEntry.maybeClone(this, methodReference); for (LoadableConstantEntry a : arguments) { if (!canWriteDirect(a.constantPool())) { // copy args list diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java index 783ff4bf106c2..843528df84bb0 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java @@ -41,6 +41,8 @@ import java.util.stream.Collectors; import static java.lang.classfile.ClassFile.*; +import static java.lang.classfile.constantpool.PoolEntry.*; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; public final class StackCounter { @@ -120,8 +122,8 @@ public StackCounter(LabelContext labelContext, for (var smfi : smta.entries()) { int frameStack = smfi.stack().size(); for (var vti : smfi.stack()) { - if (vti == StackMapFrameInfo.SimpleVerificationTypeInfo.ITEM_LONG - || vti == StackMapFrameInfo.SimpleVerificationTypeInfo.ITEM_DOUBLE) frameStack++; + if (vti == StackMapFrameInfo.SimpleVerificationTypeInfo.LONG + || vti == StackMapFrameInfo.SimpleVerificationTypeInfo.DOUBLE) frameStack++; } if (maxStack < frameStack) maxStack = frameStack; targets.add(new Target(labelContext.labelToBci(smfi.target()), frameStack)); @@ -324,7 +326,7 @@ public StackCounter(LabelContext labelContext, case INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC -> { var cpe = cp.entryByIndex(bcs.getIndexU2()); var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType(); - var mtd = Util.methodTypeSymbol(nameAndType); + var mtd = Util.methodTypeSymbol(nameAndType.type()); var delta = Util.slotSize(mtd.returnType()) - Util.parameterSlots(mtd); if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) { delta--; @@ -376,11 +378,11 @@ public int maxStack() { private void processLdc(int index) { switch (cp.entryByIndex(index).tag()) { - case TAG_UTF8, TAG_STRING, TAG_CLASS, TAG_INTEGER, TAG_FLOAT, TAG_METHODHANDLE, TAG_METHODTYPE -> + case TAG_UTF8, TAG_STRING, TAG_CLASS, TAG_INTEGER, TAG_FLOAT, TAG_METHOD_HANDLE, TAG_METHOD_TYPE -> addStackSlot(+1); case TAG_DOUBLE, TAG_LONG -> addStackSlot(+2); - case TAG_CONSTANTDYNAMIC -> + case TAG_DYNAMIC -> addStackSlot(cp.entryByIndex(index, ConstantDynamicEntry.class).typeKind().slotSize()); default -> throw error("CP entry #%d %s is not loadable constant".formatted(index, cp.entryByIndex(index).tag())); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index bdee788c9da77..28b877cd83c70 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -41,13 +41,15 @@ import java.util.Objects; import static java.lang.classfile.ClassFile.*; +import static java.lang.classfile.attribute.StackMapFrameInfo.VerificationTypeInfo.*; +import static java.util.Objects.requireNonNull; public class StackMapDecoder { private static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251; - private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = new StackMapFrameInfo[0]; + private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {}; private final ClassReader classReader; private final int pos; @@ -75,7 +77,7 @@ public static List initFrameLocals(ClassEntry thisClass, S if (!isStatic) { vtis = new VerificationTypeInfo[methodType.parameterCount() + 1]; if ("".equals(methodName) && !ConstantDescs.CD_Object.equals(thisClass.asSymbol())) { - vtis[i++] = SimpleVerificationTypeInfo.ITEM_UNINITIALIZED_THIS; + vtis[i++] = SimpleVerificationTypeInfo.UNINITIALIZED_THIS; } else { vtis[i++] = new StackMapDecoder.ObjectVerificationTypeInfoImpl(thisClass); } @@ -85,10 +87,10 @@ public static List initFrameLocals(ClassEntry thisClass, S for (int pi = 0; pi < methodType.parameterCount(); pi++) { var arg = methodType.parameterType(pi); vtis[i++] = switch (arg.descriptorString().charAt(0)) { - case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.ITEM_INTEGER; - case 'J' -> SimpleVerificationTypeInfo.ITEM_LONG; - case 'F' -> SimpleVerificationTypeInfo.ITEM_FLOAT; - case 'D' -> SimpleVerificationTypeInfo.ITEM_DOUBLE; + case 'I', 'S', 'C' ,'B', 'Z' -> SimpleVerificationTypeInfo.INTEGER; + case 'J' -> SimpleVerificationTypeInfo.LONG; + case 'F' -> SimpleVerificationTypeInfo.FLOAT; + case 'D' -> SimpleVerificationTypeInfo.DOUBLE; case 'V' -> throw new IllegalArgumentException("Illegal method argument type: " + arg); default -> new StackMapDecoder.ObjectVerificationTypeInfoImpl(TemporaryConstantPool.INSTANCE.classEntry(arg)); }; @@ -134,8 +136,7 @@ private static void writeFrame(BufWriterImpl out, int offsetDelta, List l1, List - {} - case VT_OBJECT -> - bw.writeIndex(((ObjectVerificationTypeInfo)vti).className()); - case VT_UNINITIALIZED -> - bw.writeU2(bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget())); + int tag = vti.tag(); + switch (tag) { + case ITEM_TOP, ITEM_INTEGER, ITEM_FLOAT, ITEM_DOUBLE, ITEM_LONG, ITEM_NULL, + ITEM_UNINITIALIZED_THIS -> + bw.writeU1(tag); + case ITEM_OBJECT -> + bw.writeU1U2(tag, bw.cpIndex(((ObjectVerificationTypeInfo)vti).className())); + case ITEM_UNINITIALIZED -> + bw.writeU1U2(tag, bw.labelContext().labelToBci(((UninitializedVerificationTypeInfo)vti).newTarget())); default -> throw new IllegalArgumentException("Invalid verification type tag: " + vti.tag()); } } @@ -232,24 +231,27 @@ List entries() { private VerificationTypeInfo readVerificationTypeInfo() { int tag = classReader.readU1(p++); return switch (tag) { - case VT_TOP -> SimpleVerificationTypeInfo.ITEM_TOP; - case VT_INTEGER -> SimpleVerificationTypeInfo.ITEM_INTEGER; - case VT_FLOAT -> SimpleVerificationTypeInfo.ITEM_FLOAT; - case VT_DOUBLE -> SimpleVerificationTypeInfo.ITEM_DOUBLE; - case VT_LONG -> SimpleVerificationTypeInfo.ITEM_LONG; - case VT_NULL -> SimpleVerificationTypeInfo.ITEM_NULL; - case VT_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.ITEM_UNINITIALIZED_THIS; - case VT_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class)); - case VT_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2())); + case ITEM_TOP -> SimpleVerificationTypeInfo.TOP; + case ITEM_INTEGER -> SimpleVerificationTypeInfo.INTEGER; + case ITEM_FLOAT -> SimpleVerificationTypeInfo.FLOAT; + case ITEM_DOUBLE -> SimpleVerificationTypeInfo.DOUBLE; + case ITEM_LONG -> SimpleVerificationTypeInfo.LONG; + case ITEM_NULL -> SimpleVerificationTypeInfo.NULL; + case ITEM_UNINITIALIZED_THIS -> SimpleVerificationTypeInfo.UNINITIALIZED_THIS; + case ITEM_OBJECT -> new ObjectVerificationTypeInfoImpl(classReader.entryByIndex(u2(), ClassEntry.class)); + case ITEM_UNINITIALIZED -> new UninitializedVerificationTypeInfoImpl(ctx.getLabel(u2())); default -> throw new IllegalArgumentException("Invalid verification type tag: " + tag); }; } public static record ObjectVerificationTypeInfoImpl( ClassEntry className) implements ObjectVerificationTypeInfo { + public ObjectVerificationTypeInfoImpl { + requireNonNull(className); + } @Override - public int tag() { return VT_OBJECT; } + public int tag() { return ITEM_OBJECT; } @Override public boolean equals(Object o) { @@ -272,9 +274,12 @@ public String toString() { } public static record UninitializedVerificationTypeInfoImpl(Label newTarget) implements UninitializedVerificationTypeInfo { + public UninitializedVerificationTypeInfoImpl { + requireNonNull(newTarget); + } @Override - public int tag() { return VT_UNINITIALIZED; } + public int tag() { return ITEM_UNINITIALIZED; } @Override public String toString() { @@ -294,6 +299,7 @@ public static record StackMapFrameImpl(int frameType, List stack) implements StackMapFrameInfo { public StackMapFrameImpl { + requireNonNull(target); locals = List.copyOf(locals); stack = List.copyOf(stack); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index c6564c289d6f5..1af13267c8094 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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 +29,6 @@ import java.lang.classfile.Attribute; import java.lang.classfile.Attributes; import java.lang.classfile.BufWriter; -import java.lang.classfile.ClassFile; import java.lang.classfile.Label; import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.constantpool.ClassEntry; @@ -40,15 +40,17 @@ import java.lang.constant.MethodTypeDesc; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import jdk.internal.constant.ReferenceClassDescImpl; +import jdk.internal.constant.PrimitiveClassDescImpl; import jdk.internal.util.Preconditions; import static java.lang.classfile.ClassFile.*; +import static java.lang.classfile.constantpool.PoolEntry.*; import static java.lang.constant.ConstantDescs.*; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; /** * StackMapGenerator is responsible for stack map frames generation. @@ -57,8 +59,8 @@ *

      * The {@linkplain #generate() frames computation} consists of following steps: *

        - *
      1. {@linkplain #detectFrameOffsets() Detection} of mandatory stack map frames offsets:
          - *
        • Mandatory stack map frame offsets include all jump and switch instructions targets, + *
        • {@linkplain #detectFrames() Detection} of mandatory stack map frames: * @moduleGraph * @since 11 diff --git a/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m b/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m index eb2338ca94491..bc3d9aaf68242 100644 --- a/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m +++ b/src/java.prefs/macosx/native/libprefs/MacOSXPreferencesFile.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, 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 @@ -83,7 +83,7 @@ static void throwOutOfMemoryError(JNIEnv *env, const char *msg) c = exceptionClass; } else { c = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); - if ((*env)->ExceptionOccurred(env)) return; + if ((*env)->ExceptionCheck(env)) return; exceptionClass = (*env)->NewGlobalRef(env, c); } @@ -211,7 +211,7 @@ static jarray createJavaStringArray(JNIEnv *env, CFIndex count) c = stringClass; } else { c = (*env)->FindClass(env, "java/lang/String"); - if ((*env)->ExceptionOccurred(env)) return NULL; + if ((*env)->ExceptionCheck(env)) return NULL; stringClass = (*env)->NewGlobalRef(env, c); } @@ -892,7 +892,7 @@ static void createTreeForPath(CFStringRef path, CFStringRef name, result = NULL; } else { CFStringRef cfString = copyToCFString(env, value); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { // memory error in copyToCFString result = NULL; } else if (cfString == NULL) { @@ -940,10 +940,10 @@ static void BuildJavaArrayFn(const void *key, const void *value, void *context) CFStringRef cfString = NULL; JNIEnv *env = args->env; - if ((*env)->ExceptionOccurred(env)) return; // already failed + if ((*env)->ExceptionCheck(env)) return; // already failed cfString = copyToCFString(env, propkey); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { // memory error in copyToCFString } else if (!cfString) { // bogus value type in prefs file - no Java errors available @@ -960,9 +960,9 @@ static void BuildJavaArrayFn(const void *key, const void *value, void *context) } if (CFStringGetLength(cfString) <= 0) goto bad; // ignore empty javaString = toJavaString(env, cfString); - if ((*env)->ExceptionOccurred(env)) goto bad; + if ((*env)->ExceptionCheck(env)) goto bad; (*env)->SetObjectArrayElement(env, args->result,args->used,javaString); - if ((*env)->ExceptionOccurred(env)) goto bad; + if ((*env)->ExceptionCheck(env)) goto bad; args->used++; } @@ -1003,7 +1003,7 @@ static jarray getStringsForNode(JNIEnv *env, jobject klass, jobject jpath, args.used = 0; args.allowSlash = allowSlash; CFDictionaryApplyFunction(node, BuildJavaArrayFn, &args); - if (!(*env)->ExceptionOccurred(env)) { + if (!(*env)->ExceptionCheck(env)) { // array construction succeeded if (args.used < count) { // finished array is smaller than expected. diff --git a/src/java.prefs/unix/classes/java/util/prefs/FileSystemPreferences.java b/src/java.prefs/unix/classes/java/util/prefs/FileSystemPreferences.java index ed76ce57f9472..5f0531c0ff7de 100644 --- a/src/java.prefs/unix/classes/java/util/prefs/FileSystemPreferences.java +++ b/src/java.prefs/unix/classes/java/util/prefs/FileSystemPreferences.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 @@ -957,6 +957,8 @@ private boolean lockFile(boolean shared) throws SecurityException{ try { Thread.sleep(sleepTime); } catch(InterruptedException e) { + // Don't lose the interrupt. + Thread.currentThread().interrupt(); checkLockFile0ErrorCode(errorCode); return false; } diff --git a/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m b/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m index 11645d152cdfa..43cab418e1700 100644 --- a/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m +++ b/src/java.security.jgss/macosx/native/libosxkrb5/SCDynamicStoreConfig.m @@ -51,7 +51,7 @@ void _SCDynamicStoreCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, vo jmethodID jm_Config_refresh = (*env)->GetStaticMethodID(env, jc_Config, "refresh", "()V"); CHECK_NULL(jm_Config_refresh); (*env)->CallStaticVoidMethod(env, jc_Config, jm_Config_refresh); - if ((*env)->ExceptionOccurred(env) != NULL) { + if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionClear(env); } if (createdFromAttach) { 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 5a6f74e130ca0..6140168ebbb9e 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, 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 @@ -170,7 +170,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 c6f3f083a6630..c331833e9f05b 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, 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 @@ -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 55c1be3c0d819..71aaddda9deff 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, 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 @@ -273,9 +273,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 46168cf83775c..b18f7d8eae1f1 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, 2022, 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; @@ -225,15 +226,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 fa596fc6a1e5c..92b694efb86ca 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java @@ -33,8 +33,10 @@ import sun.security.jgss.TokenTracker; import sun.security.krb5.*; import java.io.InputStream; -import java.io.OutputStream; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.OutputStream; import java.security.*; import javax.security.auth.Subject; import javax.security.auth.kerberos.ServicePermission; @@ -899,15 +901,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 != null) { - DEBUG.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 { @@ -1050,12 +1048,6 @@ public final byte[] unwrap(byte[] inBuf, int offset, int len, setSequencingAndReplayProps(token, msgProp); } - if (DEBUG != null) { - DEBUG.println("Krb5Context.unwrap: data=[" - + getHexBytes(data, 0, data.length) - + "]"); - } - return data; } @@ -1405,8 +1397,22 @@ public byte[] getEncoded() { @Override public String toString() { - return "Kerberos session key: etype: " + key.getEType() + "\n" + - new HexDumpEncoder().encodeBuffer(key.getBytes()); + return "Kerberos session key: etype=" + key.getEType() + + ", " + Krb5Util.keyInfo(key.getBytes()); + } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException + ("KerberosSessionKey not directly deserializable"); } } @@ -1477,5 +1483,4 @@ public void setAuthTime(String authTime) { public void setAuthzData(AuthorizationData authzData) { this.authzData = authzData; } - } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index 1b109cc881f15..4cc306282e687 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, 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 @@ -33,7 +33,9 @@ import javax.security.auth.kerberos.KerberosPrincipal; import java.io.Serial; import java.net.InetAddress; +import java.io.InvalidObjectException; import java.io.IOException; +import java.io.ObjectInputStream; import java.util.Date; import java.security.AccessController; import java.security.PrivilegedExceptionAction; @@ -400,4 +402,17 @@ public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { throw ge; } } + + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("Krb5InitCredential not deserializable"); + } } diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java index 10dea6749e59e..e784b7b33cad6 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 @@ -187,4 +187,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 f975ba15a673e..b5453fae916a0 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 "EncryptionKey: keyType=" + keyType - + " kvno=" + kvno - + " keyValue (hex dump)=" - + (keyValue == null || keyValue.length == 0 ? - " Empty Key" : '\n' - + Krb5.hexDumper.encodeBuffer(keyValue) - + '\n'); + + ", kvno=" + kvno + ", " + Krb5Util.keyInfo(keyValue); } /** diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java index b93ced00c65c1..db6192ce9ee2c 100644 --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java @@ -83,28 +83,36 @@ * * http://www.ietf.org/rfc/rfc4120.txt. */ -// The instance fields not statically typed as Serializable are ASN.1 -// encoded and written by the writeObject method. -@SuppressWarnings("serial") + public class KRBError implements java.io.Serializable { static final long serialVersionUID = 3643809337475284503L; - private int pvno; - private int msgType; - private KerberosTime cTime; //optional - private Integer cuSec; //optional - private KerberosTime sTime; - private Integer suSec; - private int errorCode; - private Realm crealm; //optional - private PrincipalName cname; //optional - private PrincipalName sname; - private String eText; //optional - private byte[] eData; //optional - private Checksum eCksum; //optional - - private PAData[] pa; // PA-DATA in eData + private transient int pvno; + private transient int msgType; + private transient KerberosTime cTime; //optional + private transient Integer cuSec; //optional + private transient KerberosTime sTime; + private transient Integer suSec; + private transient int errorCode; + private transient Realm crealm; //optional + private transient PrincipalName cname; //optional + private transient PrincipalName sname; + private transient String eText; //optional + private transient byte[] eData; //optional + private transient Checksum eCksum; //optional + + private transient PAData[] pa; // PA-DATA in eData + + + /** + * Restores the state of this object from the stream. + * + * @param is the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java index c3cac113c4029..0850abb53c83a 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 @@ -320,9 +320,6 @@ public static String getErrorMessage(int i) { public static final Debug DEBUG = Debug.of("krb5", GetPropertyAction .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 9d24de5c2e61c..6b9f5de0c3d8e 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 @@ -195,10 +195,6 @@ private void acquire() System.out.print("Password for " + princName + ":"); System.out.flush(); psswd = Password.readPassword(System.in); - if (DEBUG != null) { - DEBUG.println(">>> Kinit console input " + - new String(psswd)); - } } builder = new KrbAsReqBuilder(principal, psswd); } else { diff --git a/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c b/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c index 221aaccbf2bab..fe71f334617e1 100644 --- a/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c +++ b/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c @@ -877,13 +877,13 @@ jobject BuildTicket(JNIEnv *env, PUCHAR encodedTicket, ULONG encodedTicketSize) (*env)->SetByteArrayRegion(env, ary, (jsize) 0, encodedTicketSize, (jbyte *)encodedTicket); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); return (jobject) NULL; } ticket = (*env)->NewObject(env, ticketClass, ticketConstructor, ary); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); return (jobject) NULL; } @@ -993,7 +993,7 @@ jobject BuildEncryptionKey(JNIEnv *env, PKERB_CRYPTO_KEY cryptoKey) { } (*env)->SetByteArrayRegion(env, ary, (jsize) 0, cryptoKey->Length, (jbyte *)cryptoKey->Value); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); } else { encryptionKey = (*env)->NewObject(env, encryptionKeyClass, @@ -1018,7 +1018,7 @@ jobject BuildTicketFlags(JNIEnv *env, PULONG flags) { } (*env)->SetByteArrayRegion(env, ary, (jsize) 0, sizeof(*flags), (jbyte *)&nlflags); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { (*env)->DeleteLocalRef(env, ary); } else { ticketFlags = (*env)->NewObject(env, ticketFlagsClass, diff --git a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java index 4eb29482e29ec..35ce808c18756 100644 --- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java +++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java @@ -28,12 +28,13 @@ import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.spi.AttachProvider; -import java.io.InputStream; -import java.io.IOException; import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.Files; +import java.util.Optional; import static java.nio.charset.StandardCharsets.UTF_8; @@ -47,13 +48,35 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // location is the same for all processes, otherwise the tools // will not be able to find all Hotspot processes. // Any changes to this needs to be synchronized with HotSpot. - private static final String tmpdir = "/tmp"; + private static final Path TMPDIR = Path.of("/tmp"); + + private static final Path PROC = Path.of("/proc"); + private static final Path NS_MNT = Path.of("ns/mnt"); + private static final Path NS_PID = Path.of("ns/pid"); + private static final Path SELF = PROC.resolve("self"); + private static final Path STATUS = Path.of("status"); + private static final Path ROOT_TMP = Path.of("root/tmp"); + + private static final Optional SELF_MNT_NS; + + static { + Path nsPath = null; + + try { + nsPath = Files.readSymbolicLink(SELF.resolve(NS_MNT)); + } catch (IOException _) { + // do nothing + } finally { + SELF_MNT_NS = Optional.ofNullable(nsPath); + } + } + String socket_path; + /** * Attaches to the target VM */ - VirtualMachineImpl(AttachProvider provider, String vmid) - throws AttachNotSupportedException, IOException + VirtualMachineImpl(AttachProvider provider, String vmid) throws AttachNotSupportedException, IOException { super(provider, vmid); @@ -64,12 +87,12 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { } // Try to resolve to the "inner most" pid namespace - int ns_pid = getNamespacePid(pid); + final long ns_pid = getNamespacePid(pid); // Find the socket file. If not found then we attempt to start the // attach mechanism in the target VM by sending it a QUIT signal. // Then we attempt to find the socket file again. - File socket_file = findSocketFile(pid, ns_pid); + final File socket_file = findSocketFile(pid, ns_pid); socket_path = socket_file.getPath(); if (!socket_file.exists()) { // Keep canonical version of File, to delete, in case target process ends and /proc link has gone: @@ -211,49 +234,102 @@ protected void close(long fd) throws IOException { } // Return the socket file for the given process. - private File findSocketFile(int pid, int ns_pid) throws IOException { - String root = findTargetProcessTmpDirectory(pid, ns_pid); - return new File(root, ".java_pid" + ns_pid); + private File findSocketFile(long pid, long ns_pid) throws AttachNotSupportedException, IOException { + return new File(findTargetProcessTmpDirectory(pid, ns_pid), ".java_pid" + ns_pid); } // On Linux a simple handshake is used to start the attach mechanism // if not already started. The client creates a .attach_pid file in the // target VM's working directory (or temp directory), and the SIGQUIT handler // checks for the file. - private File createAttachFile(int pid, int ns_pid) throws IOException { - String fn = ".attach_pid" + ns_pid; - String path = "/proc/" + pid + "/cwd/" + fn; - File f = new File(path); + private File createAttachFile(long pid, long ns_pid) throws AttachNotSupportedException, IOException { + Path fn = Path.of(".attach_pid" + ns_pid); + Path path = PROC.resolve(Path.of(Long.toString(pid), "cwd")).resolve(fn); + File f = new File(path.toString()); try { // Do not canonicalize the file path, or we will fail to attach to a VM in a container. f.createNewFile(); - } catch (IOException x) { - String root = findTargetProcessTmpDirectory(pid, ns_pid); - f = new File(root, fn); + } catch (IOException _) { + f = new File(findTargetProcessTmpDirectory(pid, ns_pid), fn.toString()); f.createNewFile(); } return f; } - private String findTargetProcessTmpDirectory(int pid, int ns_pid) throws IOException { - String root; - if (pid != ns_pid) { - // A process may not exist in the same mount namespace as the caller, e.g. - // if we are trying to attach to a JVM process inside a container. - // Instead, attach relative to the target root filesystem as exposed by - // procfs regardless of namespaces. - String procRootDirectory = "/proc/" + pid + "/root"; - if (!Files.isReadable(Path.of(procRootDirectory))) { - throw new IOException( - String.format("Unable to access root directory %s " + - "of target process %d", procRootDirectory, pid)); + private String findTargetProcessTmpDirectory(long pid, long ns_pid) throws AttachNotSupportedException, IOException { + // We need to handle at least 4 different cases: + // 1. Caller and target processes share PID namespace and root filesystem (host to host or container to + // container with both /tmp mounted between containers). + // 2. Caller and target processes share PID namespace and root filesystem but the target process has elevated + // privileges (host to host). + // 3. Caller and target processes share PID namespace but NOT root filesystem (container to container). + // 4. Caller and target processes share neither PID namespace nor root filesystem (host to container). + + Optional target = ProcessHandle.of(pid); + Optional ph = target; + long nsPid = ns_pid; + Optional prevPidNS = Optional.empty(); + + while (ph.isPresent()) { + final var curPid = ph.get().pid(); + final var procPidPath = PROC.resolve(Long.toString(curPid)); + Optional targetMountNS = Optional.empty(); + + try { + // attempt to read the target's mnt ns id + targetMountNS = Optional.ofNullable(Files.readSymbolicLink(procPidPath.resolve(NS_MNT))); + } catch (IOException _) { + // if we fail to read the target's mnt ns id then we either don't have access or it no longer exists! + if (!Files.exists(procPidPath)) { + throw new IOException(String.format("unable to attach, %s non-existent! process: %d terminated", procPidPath, pid)); + } + // the process still exists, but we don't have privileges to read its procfs } - root = procRootDirectory + "/" + tmpdir; + final var sameMountNS = SELF_MNT_NS.isPresent() && SELF_MNT_NS.equals(targetMountNS); + + if (sameMountNS) { + return TMPDIR.toString(); // we share TMPDIR in common! + } else { + // we could not read the target's mnt ns + final var procPidRootTmp = procPidPath.resolve(ROOT_TMP); + if (Files.isReadable(procPidRootTmp)) { + return procPidRootTmp.toString(); // not in the same mnt ns but tmp is accessible via /proc + } + } + + // let's attempt to obtain the pid ns, best efforts to avoid crossing pid ns boundaries (as with a container) + Optional curPidNS = Optional.empty(); + + try { + // attempt to read the target's pid ns id + curPidNS = Optional.ofNullable(Files.readSymbolicLink(procPidPath.resolve(NS_PID))); + } catch (IOException _) { + // if we fail to read the target's pid ns id then we either don't have access or it no longer exists! + if (!Files.exists(procPidPath)) { + throw new IOException(String.format("unable to attach, %s non-existent! process: %d terminated", procPidPath, pid)); + } + // the process still exists, but we don't have privileges to read its procfs + } + + // recurse "up" the process hierarchy if appropriate. PID 1 cannot have a parent in the same namespace + final var havePidNSes = prevPidNS.isPresent() && curPidNS.isPresent(); + final var ppid = ph.get().parent(); + + if (ppid.isPresent() && (havePidNSes && curPidNS.equals(prevPidNS)) || (!havePidNSes && nsPid > 1)) { + ph = ppid; + nsPid = getNamespacePid(ph.get().pid()); // get the ns pid of the parent + prevPidNS = curPidNS; + } else { + ph = Optional.empty(); + } + } + + if (target.orElseThrow(AttachNotSupportedException::new).isAlive()) { + return TMPDIR.toString(); // fallback... } else { - root = tmpdir; + throw new IOException(String.format("unable to attach, process: %d terminated", pid)); } - return root; } /* @@ -270,13 +346,12 @@ private void writeString(int fd, String s) throws IOException { write(fd, b, 0, 1); } - // Return the inner most namespaced PID if there is one, // otherwise return the original PID. - private int getNamespacePid(int pid) throws AttachNotSupportedException, IOException { + private long getNamespacePid(long pid) throws AttachNotSupportedException, IOException { // Assuming a real procfs sits beneath, reading this doesn't block // nor will it consume a lot of memory. - String statusFile = "/proc/" + pid + "/status"; + final var statusFile = PROC.resolve(Long.toString(pid)).resolve(STATUS).toString(); File f = new File(statusFile); if (!f.exists()) { return pid; // Likely a bad pid, but this is properly handled later. @@ -292,8 +367,7 @@ private int getNamespacePid(int pid) throws AttachNotSupportedException, IOExcep // The last entry represents the PID the JVM "thinks" it is. // Even in non-namespaced pids these entries should be // valid. You could refer to it as the inner most pid. - int ns_pid = Integer.parseInt(parts[parts.length - 1]); - return ns_pid; + return Long.parseLong(parts[parts.length - 1]); } } // Old kernels may not have NSpid field (i.e. 3.10). diff --git a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c index a8b17c22d83f2..74d0e7bf7d5fa 100644 --- a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c @@ -432,7 +432,7 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue return; } } - if ((*env)->ExceptionOccurred(env)) return; + if ((*env)->ExceptionCheck(env)) return; } } for (i = argsLen; i < MAX_ARGS; i++) { @@ -463,7 +463,7 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue stubLen = (DWORD)(*env)->GetArrayLength(env, stub); stubCode = (*env)->GetByteArrayElements(env, stub, &isCopy); - if ((*env)->ExceptionOccurred(env)) return; + if ((*env)->ExceptionCheck(env)) return; pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if (pCode == NULL) { @@ -636,7 +636,7 @@ static jboolean jstring_to_cstring(JNIEnv* env, jstring jstr, char* cstr, size_t cstr[0] = '\0'; } else { str = JNU_GetStringPlatformChars(env, jstr, &isCopy); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return result; } if (strlen(str) >= cstr_buf_size) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index 4e62828c42658..cee6a16a10fc6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -420,6 +420,23 @@ protected Symtab(Context context) throws CompletionFailure { missingInfoHandler, target.runtimeUseNestAccess()); + noModule = new ModuleSymbol(names.empty, null) { + @Override public boolean isNoModule() { + return true; + } + }; + addRootPackageFor(noModule); + + Source source = Source.instance(context); + if (Feature.MODULES.allowedInSource(source)) { + java_base = enterModule(names.java_base); + //avoid completing java.base during the Symtab initialization + java_base.completer = Completer.NULL_COMPLETER; + java_base.visiblePackages = Collections.emptyMap(); + } else { + java_base = noModule; + } + // create the basic builtin symbols unnamedModule = new ModuleSymbol(names.empty, null) { { @@ -427,7 +444,6 @@ protected Symtab(Context context) throws CompletionFailure { exports = List.nil(); provides = List.nil(); uses = List.nil(); - ModuleSymbol java_base = enterModule(names.java_base); com.sun.tools.javac.code.Directive.RequiresDirective d = new com.sun.tools.javac.code.Directive.RequiresDirective(java_base, EnumSet.of(com.sun.tools.javac.code.Directive.RequiresFlag.MANDATED)); @@ -447,7 +463,6 @@ public String toString() { exports = List.nil(); provides = List.nil(); uses = List.nil(); - ModuleSymbol java_base = enterModule(names.java_base); com.sun.tools.javac.code.Directive.RequiresDirective d = new com.sun.tools.javac.code.Directive.RequiresDirective(java_base, EnumSet.of(com.sun.tools.javac.code.Directive.RequiresFlag.MANDATED)); @@ -456,13 +471,6 @@ public String toString() { }; addRootPackageFor(errModule); - noModule = new ModuleSymbol(names.empty, null) { - @Override public boolean isNoModule() { - return true; - } - }; - addRootPackageFor(noModule); - noSymbol = new TypeSymbol(NIL, 0, names.empty, Type.noType, rootPackage) { @Override @DefinedBy(Api.LANGUAGE_MODEL) public R accept(ElementVisitor v, P p) { @@ -526,16 +534,6 @@ public R accept(ElementVisitor v, P p) { // Enter symbol for the errSymbol scope.enter(errSymbol); - Source source = Source.instance(context); - if (Feature.MODULES.allowedInSource(source)) { - java_base = enterModule(names.java_base); - //avoid completing java.base during the Symtab initialization - java_base.completer = Completer.NULL_COMPLETER; - java_base.visiblePackages = Collections.emptyMap(); - } else { - java_base = noModule; - } - // Get the initial completer for ModuleSymbols from Modules moduleCompleter = Modules.instance(context).getCompleter(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index 5bf1bc0ead149..55293535ff840 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -1853,6 +1853,10 @@ Symbol findMethod(Env env, bestSoFar, allowBoxing, useVarargs); + if (bestSoFar.kind == AMBIGUOUS) { + AmbiguityError a_err = (AmbiguityError)bestSoFar.baseSymbol(); + bestSoFar = a_err.mergeAbstracts(site); + } return bestSoFar; } // where @@ -2757,7 +2761,7 @@ Symbol resolveMethod(DiagnosticPosition pos, return lookupMethod(env, pos, env.enclClass.sym, resolveMethodCheck, new BasicLookupHelper(name, env.enclClass.sym.type, argtypes, typeargtypes) { @Override - Symbol doLookup(Env env, MethodResolutionPhase phase) { + Symbol lookup(Env env, MethodResolutionPhase phase) { return findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); @@ -2789,7 +2793,7 @@ private Symbol resolveQualifiedMethod(MethodResolutionContext resolveContext, List typeargtypes) { return lookupMethod(env, pos, location, resolveContext, new BasicLookupHelper(name, site, argtypes, typeargtypes) { @Override - Symbol doLookup(Env env, MethodResolutionPhase phase) { + Symbol lookup(Env env, MethodResolutionPhase phase) { return findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); @@ -2913,7 +2917,7 @@ private Symbol resolveConstructor(MethodResolutionContext resolveContext, List typeargtypes) { return lookupMethod(env, pos, site.tsym, resolveContext, new BasicLookupHelper(names.init, site, argtypes, typeargtypes) { @Override - Symbol doLookup(Env env, MethodResolutionPhase phase) { + Symbol lookup(Env env, MethodResolutionPhase phase) { return findConstructor(pos, env, site, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); @@ -2972,7 +2976,7 @@ Symbol resolveDiamond(DiagnosticPosition pos, return lookupMethod(env, pos, site.tsym, resolveMethodCheck, new BasicLookupHelper(names.init, site, argtypes, typeargtypes) { @Override - Symbol doLookup(Env env, MethodResolutionPhase phase) { + Symbol lookup(Env env, MethodResolutionPhase phase) { return findDiamond(pos, env, site, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); @@ -3503,18 +3507,6 @@ abstract class BasicLookupHelper extends LookupHelper { super(name, site, argtypes, typeargtypes, maxPhase); } - @Override - final Symbol lookup(Env env, MethodResolutionPhase phase) { - Symbol sym = doLookup(env, phase); - if (sym.kind == AMBIGUOUS) { - AmbiguityError a_err = (AmbiguityError)sym.baseSymbol(); - sym = a_err.mergeAbstracts(site); - } - return sym; - } - - abstract Symbol doLookup(Env env, MethodResolutionPhase phase); - @Override Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { if (sym.kind.isResolutionError()) { @@ -3561,10 +3553,6 @@ ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) { abstract JCMemberReference.ReferenceKind referenceKind(Symbol sym); Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { - if (sym.kind == AMBIGUOUS) { - AmbiguityError a_err = (AmbiguityError)sym.baseSymbol(); - sym = a_err.mergeAbstracts(site); - } //skip error reporting return sym; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 2373f869b0a34..6abf9f057b043 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -2352,6 +2352,12 @@ private TypeAnnotationSymbolVisitor(List attributes) { this.attributes = attributes; } + /** + * A supertype_index value of 65535 specifies that the annotation appears on the superclass + * in an extends clause of a class declaration, see JVMS 4.7.20.1 + */ + public static final int SUPERCLASS_INDEX = 65535; + @Override public Void visitClassSymbol(Symbol.ClassSymbol s, Void unused) { ClassType t = (ClassType) s.type; @@ -2361,7 +2367,7 @@ public Void visitClassSymbol(Symbol.ClassSymbol s, Void unused) { interfaces.add(addTypeAnnotations(itf, classExtends(i++))); } t.interfaces_field = interfaces.toList(); - t.supertype_field = addTypeAnnotations(t.supertype_field, classExtends(65535)); + t.supertype_field = addTypeAnnotations(t.supertype_field, classExtends(SUPERCLASS_INDEX)); if (t.typarams_field != null) { t.typarams_field = rewriteTypeParameters( diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java index 1bc5de7f73a80..71e39a6a4080f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java @@ -193,10 +193,14 @@ public NoType getNoType(TypeKind kind) { public ArrayType getArrayType(TypeMirror componentType) { switch (componentType.getKind()) { case VOID: + case NONE: + case NULL: case EXECUTABLE: case WILDCARD: // heh! case PACKAGE: case MODULE: + case UNION: + case INTERSECTION: throw new IllegalArgumentException(componentType.toString()); } return new Type.ArrayType((Type) componentType, syms.arrayClass); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java index acd79c69bd45f..be311bb3ab84c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/DocCommentParser.java @@ -286,16 +286,17 @@ protected List content(Phase phase) { int depth = 1; // only used when phase is INLINE int pos = bp; // only used when phase is INLINE + if (textKind == DocTree.Kind.MARKDOWN) { + initMarkdownLine(); + } + loop: while (bp < buflen) { switch (ch) { case '\n', '\r' -> { nextChar(); if (textKind == DocTree.Kind.MARKDOWN) { - markdown.update(); - if (markdown.isIndentedCodeBlock()) { - markdown.skipLine(); - } + initMarkdownLine(); } } @@ -488,6 +489,17 @@ void defaultContentCharacter() { nextChar(); } + void initMarkdownLine() { + if (textStart == -1) { + textStart = bp; + } + markdown.update(); + if (markdown.isIndentedCodeBlock()) { + markdown.skipLine(); + lastNonWhite = bp - 1; // do not include newline or EOF + } + } + private IllegalStateException unknownTextKind(DocTree.Kind textKind) { return new IllegalStateException(textKind.toString()); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java index 77f6eefc073eb..9e0767b6e2161 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DocTreeMaker.java @@ -592,27 +592,33 @@ Pair, List> splitBody(List list) { case TEXT, MARKDOWN -> { var peekedNext = iter.hasNext() ? alist.get(iter.nextIndex()) : null; var content = getContent(dt); - int breakOffset = getSentenceBreak(dt.getKind(), content, peekedNext); - if (breakOffset > 0) { - // the end of sentence is within the current node; - // split it, skipping whitespace in between the two parts - var fsPart = newNode(dt.getKind(), dt.pos, content.substring(0, breakOffset).stripTrailing()); - fs.add(fsPart); - int wsOffset = skipWhiteSpace(content, breakOffset); - if (wsOffset > 0) { - var bodyPart = newNode(dt.getKind(), dt.pos + wsOffset, content.substring(wsOffset)); - body.add(bodyPart); - } - foundFirstSentence = true; - } else if (peekedNext != null && isSentenceBreak(peekedNext, false)) { - // the next node is a sentence break, so this is the end of the first sentence; - // remove trailing spaces - var fsPart = newNode(dt.getKind(), dt.pos, content.stripTrailing()); - fs.add(fsPart); + if (isFirst && dt.getKind() == Kind.MARKDOWN && isIndented(content)) { + // begins with an indented code block (unusual), so no first sentence + body.add(dt); foundFirstSentence = true; } else { - // no sentence break found; keep scanning - fs.add(dt); + int breakOffset = getSentenceBreak(dt.getKind(), content, peekedNext); + if (breakOffset > 0) { + // the end of sentence is within the current node; + // split it, skipping whitespace in between the two parts + var fsPart = newNode(dt.getKind(), dt.pos, content.substring(0, breakOffset).stripTrailing()); + fs.add(fsPart); + int wsOffset = skipWhiteSpace(content, breakOffset); + if (wsOffset > 0) { + var bodyPart = newNode(dt.getKind(), dt.pos + wsOffset, content.substring(wsOffset)); + body.add(bodyPart); + } + foundFirstSentence = true; + } else if (peekedNext != null && isSentenceBreak(peekedNext, false)) { + // the next node is a sentence break, so this is the end of the first sentence; + // remove trailing spaces + var fsPart = newNode(dt.getKind(), dt.pos, content.stripTrailing()); + fs.add(fsPart); + foundFirstSentence = true; + } else { + // no sentence break found; keep scanning + fs.add(dt); + } } } @@ -651,6 +657,11 @@ private String getContent(DCTree dt) { }; } + private static final Pattern INDENT = Pattern.compile(" {4}| {0,3}\t"); + private boolean isIndented(String s) { + return INDENT.matcher(s).lookingAt(); + } + private DCTree newNode(DocTree.Kind kind, int pos, String text) { return switch (kind) { case TEXT -> m.at(pos).newTextTree(text); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java index e97d07b1d2bfa..919b2325ef669 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/Pretty.java @@ -1467,7 +1467,7 @@ public void visitLiteral(JCLiteral tree) { break; case CHAR: print('\''); - print(Convert.quote(String.valueOf((char)((Number)tree.value).intValue()))); + print(Convert.quote((char)((Number)tree.value).intValue(), true)); print('\''); break; case BOOLEAN: diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index d528f1b848584..49718e254b3e1 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -240,6 +240,19 @@ protected Object writeReplace() throws ObjectStreamException { return new KeyRep(type, getAlgorithm(), format, getEncodedInternal()); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("P11Key not directly deserializable"); + } + public String toString() { token.ensureValid(); String s1 = token.provider.getName() + " " + algorithm + " " + type diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java index 70effc141bc11..7ef8510ddee48 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecureRandom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 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 @@ -187,9 +187,23 @@ private void implNextBytes(byte[] bytes) { } } + /** + * Restores the state of this object from the stream. + * + * @param in the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + if (token == null) { + throw new InvalidObjectException("token is null"); + } + if (mixBuffer != null) { + mixBuffer = mixBuffer.clone(); + } // assign default values to non-null transient fields iBuffer = new byte[IBUFFER_SIZE]; ibuffered = 0; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index 44a2c4efb0694..e77edc98399eb 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -1947,6 +1947,19 @@ private Object writeReplace() throws ObjectStreamException { return new SunPKCS11Rep(this); } + /** + * Restores the state of this object from the stream. + * + * @param stream the {@code ObjectInputStream} from which data is read + * @throws IOException if an I/O error occurs + * @throws ClassNotFoundException if a serialized class cannot be loaded + */ + @java.io.Serial + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new InvalidObjectException("SunPKCS11 not directly deserializable"); + } + /** * Serialized representation of the SunPKCS11 provider. */ diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java index a25fa1c39e5b7..d6c291ebc570c 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java @@ -127,11 +127,6 @@ public String toString() { sb.append(pPassword.length); sb.append(Constants.NEWLINE); - sb.append(Constants.INDENT); - sb.append("pPassword: "); - sb.append(pPassword); - sb.append(Constants.NEWLINE); - sb.append(Constants.INDENT); sb.append("ulSaltLen: "); sb.append(pSalt.length); diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp index 962b1ae51722b..0ad95d0eac7cf 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp @@ -420,7 +420,6 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLo jboolean isCopy; jlongArray array; jlong *regs; - int i; struct ps_prochandle* ph = get_proc_handle(env, this_obj); if (get_lwp_regs(ph, lwp_id, &gregs) != true) { diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c index d3b4d3d1af927..4cb791111bcf6 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c @@ -356,7 +356,6 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t if (shdr->sh_type == sym_section) { ELF_SYM *syms; - int rslt; size_t size, n, j, htab_sz; // FIXME: there could be multiple data buffers associated with the @@ -390,8 +389,9 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t goto bad; } - rslt = hcreate_r(htab_sz, symtab->hash_table); - // guarantee(rslt, "unexpected failure: hcreate_r"); + if (hcreate_r(htab_sz, symtab->hash_table) == 0) { + goto bad; + } // shdr->sh_link points to the section that contains the actual strings // for symbol names. the st_name field in ELF_SYM is just the diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java index d59971b33ecf0..8d06a257e626d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/code/StubQueue.java @@ -52,8 +52,8 @@ public class StubQueue extends VMObject { private static CIntegerField queueEndField; private static CIntegerField numberOfStubsField; - // The type of the contained stubs (i.e., InterpreterCodelet, - // ICStub). Must be a subclass of type Stub. + // The type of the contained stubs (i.e., InterpreterCodelet). + // Must be a subclass of type Stub. private Class stubType; static { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CompressedOops.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CompressedOops.java index 056d75199579c..01c9974153392 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CompressedOops.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CompressedOops.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 @@ -64,8 +64,8 @@ private static boolean typeExists(TypeDataBase db, String type) { private static synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("CompressedOops"); - baseField = type.getAddressField("_narrow_oop._base"); - shiftField = type.getCIntegerField("_narrow_oop._shift"); + baseField = type.getAddressField("_base"); + shiftField = type.getCIntegerField("_shift"); } public CompressedOops() { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java index 224206ee6fe59..a47a632c286ef 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java @@ -345,8 +345,9 @@ private Frame senderForUpcallStub(PPC64RegisterMap map, UpcallStub stub) { //------------------------------------------------------------------------------ // frame::adjust_unextended_sp private void adjustUnextendedSP() { - raw_unextendedSP = getFP(); + // Nothing to do. senderForInterpreterFrame finds the correct unextendedSP. } + private Frame senderForInterpreterFrame(PPC64RegisterMap map) { if (DEBUG) { System.out.println("senderForInterpreterFrame"); diff --git a/src/jdk.httpserver/share/classes/module-info.java b/src/jdk.httpserver/share/classes/module-info.java index 46dbb6ea64f68..23e04405ee863 100644 --- a/src/jdk.httpserver/share/classes/module-info.java +++ b/src/jdk.httpserver/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, 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 @@ -70,6 +70,16 @@ * while the headers are being read, then the connection is terminated and the request ignored. * If the value is less than or equal to zero, then the default value is used. *
        • + *
        • {@systemProperty sun.net.httpserver.maxReqHeaderSize} (default: 393216 or 384kB)
          + * The maximum header field section size that the server is prepared to accept. + * This is computed as the sum of the size of the header name, plus + * the size of the header value, plus an overhead of 32 bytes for + * each field section line. The request line counts as a first field section line, + * where the name is empty and the value is the whole line. + * If this limit is exceeded while the headers are being read, then the connection + * is terminated and the request ignored. + * If the value is less than or equal to zero, there is no limit. + *

        • *
        • {@systemProperty sun.net.httpserver.maxReqTime} (default: -1)
          * The maximum time in milliseconds allowed to receive a request headers and body. * In practice, the actual time is a function of request size, network speed, and handler 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 99dac0547cead..6c0c2c271ed4a 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, 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 @@ -44,8 +44,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 { @@ -75,6 +77,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) { @@ -87,20 +90,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; @@ -138,13 +148,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*/ @@ -178,6 +197,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; } @@ -205,6 +229,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 21f6165b05eae..9186dd4c168c1 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; @@ -107,6 +111,14 @@ public Void run () { 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); @@ -215,6 +227,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.incubator.vector/linux/native/libsleef/lib/vector_math_rvv.c b/src/jdk.incubator.vector/linux/native/libsleef/lib/vector_math_rvv.c new file mode 100644 index 0000000000000..438ca0c92ba0f --- /dev/null +++ b/src/jdk.incubator.vector/linux/native/libsleef/lib/vector_math_rvv.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, Rivos 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 + * 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. + */ + +// On riscv, sleef vector apis depend on native vector intrinsic, which is supported on +// some compiler, e.g. gcc 14+. +// __riscv_v_intrinsic is used to tell if the compiler supports vector intrinsic. +// +// At compile-time, if the current compiler does support vector intrinsics, bridge +// functions will be built in the library. In case the current compiler doesn't support +// vector intrinsics (gcc < 14), then the bridge functions won't be compiled. +// At run-time, if the library is found and the bridge functions are available in the +// library, then the java vector API will call into the bridge functions and sleef. + +#if __GNUC__ >= 14 || (defined(__clang_major__) && __clang_major__ >= 17) + +#ifdef __riscv_v_intrinsic + +#include + +#include + +#include "../generated/misc.h" +#include "../generated/sleefinline_rvvm1.h" + +#include + +// We maintain an invariant in java world that default dynamic rounding mode is RNE, +// please check JDK-8330094, JDK-8330266 for more details. +// Currently, sleef source on riscv does not change rounding mode to others except +// of RNE. But we still think it's safer to make sure that after calling into sleef +// the dynamic rounding mode is always RNE. + +#ifdef DEBUG +#define CHECK_FRM __asm__ __volatile__ ( \ + " frrm t0 \n\t" \ + " beqz t0, 2f \n\t" \ + " csrrw x0, cycle, x0 \n\t" \ + "2: \n\t" \ + : : : "memory" ); +#else +#define CHECK_FRM +#endif + +#define DEFINE_VECTOR_MATH_UNARY_RVV(op, type) \ +JNIEXPORT \ +type op##rvv(type input) { \ + type res = Sleef_##op##rvvm1(input); \ + CHECK_FRM \ + return res; \ +} + +#define DEFINE_VECTOR_MATH_BINARY_RVV(op, type) \ +JNIEXPORT \ +type op##rvv(type input1, type input2) { \ + type res = Sleef_##op##rvvm1(input1, input2); \ + CHECK_FRM \ + return res; \ +} + +DEFINE_VECTOR_MATH_UNARY_RVV(tanfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(sinfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(sinhfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(cosfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(coshfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(asinfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(acosfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(atanfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(cbrtfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(logfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(log10fx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(log1pfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(expfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(expm1fx_u10, vfloat_rvvm1_sleef) + +DEFINE_VECTOR_MATH_UNARY_RVV(tandx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(sindx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(sinhdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(cosdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(coshdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(asindx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(acosdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(atandx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(cbrtdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(logdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(log10dx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(log1pdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(expdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_UNARY_RVV(expm1dx_u10, vdouble_rvvm1_sleef) + +DEFINE_VECTOR_MATH_BINARY_RVV(atan2fx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_BINARY_RVV(powfx_u10, vfloat_rvvm1_sleef) +DEFINE_VECTOR_MATH_BINARY_RVV(hypotfx_u05, vfloat_rvvm1_sleef) + +DEFINE_VECTOR_MATH_BINARY_RVV(atan2dx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_BINARY_RVV(powdx_u10, vdouble_rvvm1_sleef) +DEFINE_VECTOR_MATH_BINARY_RVV(hypotdx_u05, vdouble_rvvm1_sleef) + +#undef DEFINE_VECTOR_MATH_UNARY_RVV + +#undef DEFINE_VECTOR_MATH_BINARY_RVV + +#endif /* __riscv_v_intrinsic */ + +#endif /* check gcc and clang version */ diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java index d648fd86484e0..fc39cb6adaccb 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java @@ -133,7 +133,7 @@ public final VectorShuffle checkIndexes() { } @ForceInline - public final VectorShuffle wrapIndexes() { + public final VectorShuffle wrapIndexesTemplate() { Vector shufvec = this.toVector(); VectorMask vecmask = shufvec.compare(VectorOperators.LT, vspecies().zero()); if (vecmask.anyTrue()) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java index 02a389d08b2e9..0bc25958a7617 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java @@ -503,7 +503,7 @@ public Byte128Vector selectFrom(Vector v, VectorMask m) { return (Byte128Vector) super.selectFromTemplate((Byte128Vector) v, - (Byte128Mask) m); // specialize + Byte128Mask.class, (Byte128Mask) m); // specialize } @@ -860,6 +860,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Byte128Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Byte128Shuffle.class, this, VLENGTH, + (s) -> ((Byte128Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Byte128Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java index 4e035b13b5e04..639646aa77ad5 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java @@ -503,7 +503,7 @@ public Byte256Vector selectFrom(Vector v, VectorMask m) { return (Byte256Vector) super.selectFromTemplate((Byte256Vector) v, - (Byte256Mask) m); // specialize + Byte256Mask.class, (Byte256Mask) m); // specialize } @@ -892,6 +892,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Byte256Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Byte256Shuffle.class, this, VLENGTH, + (s) -> ((Byte256Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Byte256Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java index 15dd06cdccc00..2d8151f6800f3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java @@ -503,7 +503,7 @@ public Byte512Vector selectFrom(Vector v, VectorMask m) { return (Byte512Vector) super.selectFromTemplate((Byte512Vector) v, - (Byte512Mask) m); // specialize + Byte512Mask.class, (Byte512Mask) m); // specialize } @@ -956,6 +956,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Byte512Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Byte512Shuffle.class, this, VLENGTH, + (s) -> ((Byte512Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Byte512Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java index 6ac9d7a8918b1..bc8ed7d704d96 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java @@ -503,7 +503,7 @@ public Byte64Vector selectFrom(Vector v, VectorMask m) { return (Byte64Vector) super.selectFromTemplate((Byte64Vector) v, - (Byte64Mask) m); // specialize + Byte64Mask.class, (Byte64Mask) m); // specialize } @@ -844,6 +844,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Byte64Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Byte64Shuffle.class, this, VLENGTH, + (s) -> ((Byte64Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Byte64Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java index ed6e15ca293b2..597afd8d165b3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java @@ -503,7 +503,7 @@ public ByteMaxVector selectFrom(Vector v, VectorMask m) { return (ByteMaxVector) super.selectFromTemplate((ByteMaxVector) v, - (ByteMaxMask) m); // specialize + ByteMaxMask.class, (ByteMaxMask) m); // specialize } @@ -830,6 +830,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public ByteMaxShuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, ByteMaxShuffle.class, this, VLENGTH, + (s) -> ((ByteMaxShuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public ByteMaxShuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index 8fae8d71b042a..a23bbc7f70957 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -2393,17 +2393,18 @@ ByteVector sliceTemplate(int origin) { */ @Override public abstract - ByteVector rearrange(VectorShuffle m); + ByteVector rearrange(VectorShuffle shuffle); /*package-private*/ @ForceInline final > ByteVector rearrangeTemplate(Class shuffletype, S shuffle) { - shuffle.checkIndexes(); + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, null, byte.class, length(), - this, shuffle, null, + this, ws, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); return v1.lane(ei); @@ -2428,17 +2429,14 @@ ByteVector rearrangeTemplate(Class shuffletype, M m) { m.check(masktype, this); - VectorMask valid = shuffle.laneIsValid(); - if (m.andNot(valid).anyTrue()) { - shuffle.checkIndexes(); - throw new AssertionError(); - } + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, byte.class, length(), - this, shuffle, m, + this, ws, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); - return ei < 0 || !m_.laneIsSet(i) ? 0 : v1.lane(ei); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); } @@ -2551,7 +2549,10 @@ byte.class, length(), this, m, /*package-private*/ @ForceInline final ByteVector selectFromTemplate(ByteVector v) { - return v.rearrange(this.toShuffle()); + return (ByteVector)VectorSupport.selectFromOp(getClass(), null, byte.class, + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); } /** @@ -2563,9 +2564,15 @@ final ByteVector selectFromTemplate(ByteVector v) { /*package-private*/ @ForceInline - final ByteVector selectFromTemplate(ByteVector v, - AbstractMask m) { - return v.rearrange(this.toShuffle(), m); + final + > + ByteVector selectFromTemplate(ByteVector v, + Class masktype, M m) { + m.check(masktype, this); + return (ByteVector)VectorSupport.selectFromOp(getClass(), masktype, byte.class, + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); } /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java index f2a077604f7fc..00840026fff9f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java @@ -490,7 +490,7 @@ public Double128Vector selectFrom(Vector v, VectorMask m) { return (Double128Vector) super.selectFromTemplate((Double128Vector) v, - (Double128Mask) m); // specialize + Double128Mask.class, (Double128Mask) m); // specialize } @@ -821,6 +821,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Double128Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Double128Shuffle.class, this, VLENGTH, + (s) -> ((Double128Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Double128Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java index da9dd421b17c2..4b42deba73841 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java @@ -490,7 +490,7 @@ public Double256Vector selectFrom(Vector v, VectorMask m) { return (Double256Vector) super.selectFromTemplate((Double256Vector) v, - (Double256Mask) m); // specialize + Double256Mask.class, (Double256Mask) m); // specialize } @@ -825,6 +825,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Double256Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Double256Shuffle.class, this, VLENGTH, + (s) -> ((Double256Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Double256Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java index d23f09e774b4a..c188f990c335f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java @@ -490,7 +490,7 @@ public Double512Vector selectFrom(Vector v, VectorMask m) { return (Double512Vector) super.selectFromTemplate((Double512Vector) v, - (Double512Mask) m); // specialize + Double512Mask.class, (Double512Mask) m); // specialize } @@ -833,6 +833,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Double512Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Double512Shuffle.class, this, VLENGTH, + (s) -> ((Double512Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Double512Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java index 19bd640f97844..032fa1ac277d9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java @@ -490,7 +490,7 @@ public Double64Vector selectFrom(Vector v, VectorMask m) { return (Double64Vector) super.selectFromTemplate((Double64Vector) v, - (Double64Mask) m); // specialize + Double64Mask.class, (Double64Mask) m); // specialize } @@ -819,6 +819,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Double64Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Double64Shuffle.class, this, VLENGTH, + (s) -> ((Double64Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Double64Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java index 73f6f2ece5d12..7251ec82aa63c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java @@ -490,7 +490,7 @@ public DoubleMaxVector selectFrom(Vector v, VectorMask m) { return (DoubleMaxVector) super.selectFromTemplate((DoubleMaxVector) v, - (DoubleMaxMask) m); // specialize + DoubleMaxMask.class, (DoubleMaxMask) m); // specialize } @@ -818,6 +818,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public DoubleMaxShuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, DoubleMaxShuffle.class, this, VLENGTH, + (s) -> ((DoubleMaxShuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public DoubleMaxShuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 59e6719573209..6cc12048d4632 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -2235,17 +2235,18 @@ DoubleVector sliceTemplate(int origin) { */ @Override public abstract - DoubleVector rearrange(VectorShuffle m); + DoubleVector rearrange(VectorShuffle shuffle); /*package-private*/ @ForceInline final > DoubleVector rearrangeTemplate(Class shuffletype, S shuffle) { - shuffle.checkIndexes(); + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, null, double.class, length(), - this, shuffle, null, + this, ws, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); return v1.lane(ei); @@ -2270,17 +2271,14 @@ DoubleVector rearrangeTemplate(Class shuffletype, M m) { m.check(masktype, this); - VectorMask valid = shuffle.laneIsValid(); - if (m.andNot(valid).anyTrue()) { - shuffle.checkIndexes(); - throw new AssertionError(); - } + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, double.class, length(), - this, shuffle, m, + this, ws, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); - return ei < 0 || !m_.laneIsSet(i) ? 0 : v1.lane(ei); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); } @@ -2393,7 +2391,10 @@ DoubleVector expandTemplate(Class masktype, M m) { /*package-private*/ @ForceInline final DoubleVector selectFromTemplate(DoubleVector v) { - return v.rearrange(this.toShuffle()); + return (DoubleVector)VectorSupport.selectFromOp(getClass(), null, double.class, + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); } /** @@ -2405,9 +2406,15 @@ final DoubleVector selectFromTemplate(DoubleVector v) { /*package-private*/ @ForceInline - final DoubleVector selectFromTemplate(DoubleVector v, - AbstractMask m) { - return v.rearrange(this.toShuffle(), m); + final + > + DoubleVector selectFromTemplate(DoubleVector v, + Class masktype, M m) { + m.check(masktype, this); + return (DoubleVector)VectorSupport.selectFromOp(getClass(), masktype, double.class, + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); } /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java index ae47beca5de53..2e016725f812b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java @@ -490,7 +490,7 @@ public Float128Vector selectFrom(Vector v, VectorMask m) { return (Float128Vector) super.selectFromTemplate((Float128Vector) v, - (Float128Mask) m); // specialize + Float128Mask.class, (Float128Mask) m); // specialize } @@ -825,6 +825,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Float128Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Float128Shuffle.class, this, VLENGTH, + (s) -> ((Float128Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Float128Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java index d5c0506a54257..00e6083588339 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java @@ -490,7 +490,7 @@ public Float256Vector selectFrom(Vector v, VectorMask m) { return (Float256Vector) super.selectFromTemplate((Float256Vector) v, - (Float256Mask) m); // specialize + Float256Mask.class, (Float256Mask) m); // specialize } @@ -833,6 +833,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Float256Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Float256Shuffle.class, this, VLENGTH, + (s) -> ((Float256Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Float256Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java index 536f7db69465e..1f2a792c52c2e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java @@ -490,7 +490,7 @@ public Float512Vector selectFrom(Vector v, VectorMask m) { return (Float512Vector) super.selectFromTemplate((Float512Vector) v, - (Float512Mask) m); // specialize + Float512Mask.class, (Float512Mask) m); // specialize } @@ -849,6 +849,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Float512Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Float512Shuffle.class, this, VLENGTH, + (s) -> ((Float512Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Float512Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java index 849062c6cb84b..6c913ce84a9ba 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java @@ -490,7 +490,7 @@ public Float64Vector selectFrom(Vector v, VectorMask m) { return (Float64Vector) super.selectFromTemplate((Float64Vector) v, - (Float64Mask) m); // specialize + Float64Mask.class, (Float64Mask) m); // specialize } @@ -821,6 +821,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Float64Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Float64Shuffle.class, this, VLENGTH, + (s) -> ((Float64Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Float64Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java index b14797f6788d4..b9a0a93f91253 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java @@ -490,7 +490,7 @@ public FloatMaxVector selectFrom(Vector v, VectorMask m) { return (FloatMaxVector) super.selectFromTemplate((FloatMaxVector) v, - (FloatMaxMask) m); // specialize + FloatMaxMask.class, (FloatMaxMask) m); // specialize } @@ -818,6 +818,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public FloatMaxShuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, FloatMaxShuffle.class, this, VLENGTH, + (s) -> ((FloatMaxShuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public FloatMaxShuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 45427817e3dac..b962dc55ce351 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -2247,17 +2247,18 @@ FloatVector sliceTemplate(int origin) { */ @Override public abstract - FloatVector rearrange(VectorShuffle m); + FloatVector rearrange(VectorShuffle shuffle); /*package-private*/ @ForceInline final > FloatVector rearrangeTemplate(Class shuffletype, S shuffle) { - shuffle.checkIndexes(); + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, null, float.class, length(), - this, shuffle, null, + this, ws, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); return v1.lane(ei); @@ -2282,17 +2283,14 @@ FloatVector rearrangeTemplate(Class shuffletype, M m) { m.check(masktype, this); - VectorMask valid = shuffle.laneIsValid(); - if (m.andNot(valid).anyTrue()) { - shuffle.checkIndexes(); - throw new AssertionError(); - } + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, float.class, length(), - this, shuffle, m, + this, ws, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); - return ei < 0 || !m_.laneIsSet(i) ? 0 : v1.lane(ei); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); } @@ -2405,7 +2403,10 @@ float.class, length(), this, m, /*package-private*/ @ForceInline final FloatVector selectFromTemplate(FloatVector v) { - return v.rearrange(this.toShuffle()); + return (FloatVector)VectorSupport.selectFromOp(getClass(), null, float.class, + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); } /** @@ -2417,9 +2418,15 @@ final FloatVector selectFromTemplate(FloatVector v) { /*package-private*/ @ForceInline - final FloatVector selectFromTemplate(FloatVector v, - AbstractMask m) { - return v.rearrange(this.toShuffle(), m); + final + > + FloatVector selectFromTemplate(FloatVector v, + Class masktype, M m) { + m.check(masktype, this); + return (FloatVector)VectorSupport.selectFromOp(getClass(), masktype, float.class, + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); } /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java index cd652941fb366..f7135e19cb6e2 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java @@ -503,7 +503,7 @@ public Int128Vector selectFrom(Vector v, VectorMask m) { return (Int128Vector) super.selectFromTemplate((Int128Vector) v, - (Int128Mask) m); // specialize + Int128Mask.class, (Int128Mask) m); // specialize } @@ -836,6 +836,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Int128Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Int128Shuffle.class, this, VLENGTH, + (s) -> ((Int128Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Int128Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java index b76a1035561f6..474ff974b3169 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java @@ -503,7 +503,7 @@ public Int256Vector selectFrom(Vector v, VectorMask m) { return (Int256Vector) super.selectFromTemplate((Int256Vector) v, - (Int256Mask) m); // specialize + Int256Mask.class, (Int256Mask) m); // specialize } @@ -844,6 +844,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Int256Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Int256Shuffle.class, this, VLENGTH, + (s) -> ((Int256Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Int256Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java index 3a42c6611445b..9fec8c0c99f13 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java @@ -503,7 +503,7 @@ public Int512Vector selectFrom(Vector v, VectorMask m) { return (Int512Vector) super.selectFromTemplate((Int512Vector) v, - (Int512Mask) m); // specialize + Int512Mask.class, (Int512Mask) m); // specialize } @@ -860,6 +860,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Int512Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Int512Shuffle.class, this, VLENGTH, + (s) -> ((Int512Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Int512Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java index 4181e6b4ea3e7..3b3c0723ee1a3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java @@ -503,7 +503,7 @@ public Int64Vector selectFrom(Vector v, VectorMask m) { return (Int64Vector) super.selectFromTemplate((Int64Vector) v, - (Int64Mask) m); // specialize + Int64Mask.class, (Int64Mask) m); // specialize } @@ -832,6 +832,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Int64Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Int64Shuffle.class, this, VLENGTH, + (s) -> ((Int64Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Int64Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java index 785022fcd65f0..5738cb7a4bc94 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java @@ -503,7 +503,7 @@ public IntMaxVector selectFrom(Vector v, VectorMask m) { return (IntMaxVector) super.selectFromTemplate((IntMaxVector) v, - (IntMaxMask) m); // specialize + IntMaxMask.class, (IntMaxMask) m); // specialize } @@ -841,6 +841,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public IntMaxShuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, IntMaxShuffle.class, this, VLENGTH, + (s) -> ((IntMaxShuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public IntMaxShuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 3317e25e73e73..16b5ceecba35e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -2378,17 +2378,18 @@ IntVector sliceTemplate(int origin) { */ @Override public abstract - IntVector rearrange(VectorShuffle m); + IntVector rearrange(VectorShuffle shuffle); /*package-private*/ @ForceInline final > IntVector rearrangeTemplate(Class shuffletype, S shuffle) { - shuffle.checkIndexes(); + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, null, int.class, length(), - this, shuffle, null, + this, ws, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); return v1.lane(ei); @@ -2413,17 +2414,14 @@ IntVector rearrangeTemplate(Class shuffletype, M m) { m.check(masktype, this); - VectorMask valid = shuffle.laneIsValid(); - if (m.andNot(valid).anyTrue()) { - shuffle.checkIndexes(); - throw new AssertionError(); - } + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, int.class, length(), - this, shuffle, m, + this, ws, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); - return ei < 0 || !m_.laneIsSet(i) ? 0 : v1.lane(ei); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); } @@ -2536,7 +2534,10 @@ int.class, length(), this, m, /*package-private*/ @ForceInline final IntVector selectFromTemplate(IntVector v) { - return v.rearrange(this.toShuffle()); + return (IntVector)VectorSupport.selectFromOp(getClass(), null, int.class, + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); } /** @@ -2548,9 +2549,15 @@ final IntVector selectFromTemplate(IntVector v) { /*package-private*/ @ForceInline - final IntVector selectFromTemplate(IntVector v, - AbstractMask m) { - return v.rearrange(this.toShuffle(), m); + final + > + IntVector selectFromTemplate(IntVector v, + Class masktype, M m) { + m.check(masktype, this); + return (IntVector)VectorSupport.selectFromOp(getClass(), masktype, int.class, + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); } /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java index 302c71bc13b10..567789627c6cb 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java @@ -493,7 +493,7 @@ public Long128Vector selectFrom(Vector v, VectorMask m) { return (Long128Vector) super.selectFromTemplate((Long128Vector) v, - (Long128Mask) m); // specialize + Long128Mask.class, (Long128Mask) m); // specialize } @@ -822,6 +822,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Long128Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Long128Shuffle.class, this, VLENGTH, + (s) -> ((Long128Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Long128Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java index 04c51c377e1de..5ef0f121464f6 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java @@ -493,7 +493,7 @@ public Long256Vector selectFrom(Vector v, VectorMask m) { return (Long256Vector) super.selectFromTemplate((Long256Vector) v, - (Long256Mask) m); // specialize + Long256Mask.class, (Long256Mask) m); // specialize } @@ -826,6 +826,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Long256Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Long256Shuffle.class, this, VLENGTH, + (s) -> ((Long256Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Long256Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java index fbcd57400d579..acdb471609f22 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java @@ -493,7 +493,7 @@ public Long512Vector selectFrom(Vector v, VectorMask m) { return (Long512Vector) super.selectFromTemplate((Long512Vector) v, - (Long512Mask) m); // specialize + Long512Mask.class, (Long512Mask) m); // specialize } @@ -834,6 +834,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Long512Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Long512Shuffle.class, this, VLENGTH, + (s) -> ((Long512Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Long512Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java index 3b2e77f1a54ac..627f7437367cb 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java @@ -493,7 +493,7 @@ public Long64Vector selectFrom(Vector v, VectorMask m) { return (Long64Vector) super.selectFromTemplate((Long64Vector) v, - (Long64Mask) m); // specialize + Long64Mask.class, (Long64Mask) m); // specialize } @@ -820,6 +820,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Long64Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Long64Shuffle.class, this, VLENGTH, + (s) -> ((Long64Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Long64Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java index cc3641be3d62f..aec3bb89fcd02 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java @@ -493,7 +493,7 @@ public LongMaxVector selectFrom(Vector v, VectorMask m) { return (LongMaxVector) super.selectFromTemplate((LongMaxVector) v, - (LongMaxMask) m); // specialize + LongMaxMask.class, (LongMaxMask) m); // specialize } @@ -820,6 +820,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public LongMaxShuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, LongMaxShuffle.class, this, VLENGTH, + (s) -> ((LongMaxShuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public LongMaxShuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 9dd3f2eb13626..15ac2bc7b7f6c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -2244,17 +2244,18 @@ LongVector sliceTemplate(int origin) { */ @Override public abstract - LongVector rearrange(VectorShuffle m); + LongVector rearrange(VectorShuffle shuffle); /*package-private*/ @ForceInline final > LongVector rearrangeTemplate(Class shuffletype, S shuffle) { - shuffle.checkIndexes(); + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, null, long.class, length(), - this, shuffle, null, + this, ws, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); return v1.lane(ei); @@ -2279,17 +2280,14 @@ LongVector rearrangeTemplate(Class shuffletype, M m) { m.check(masktype, this); - VectorMask valid = shuffle.laneIsValid(); - if (m.andNot(valid).anyTrue()) { - shuffle.checkIndexes(); - throw new AssertionError(); - } + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, long.class, length(), - this, shuffle, m, + this, ws, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); - return ei < 0 || !m_.laneIsSet(i) ? 0 : v1.lane(ei); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); } @@ -2402,7 +2400,10 @@ long.class, length(), this, m, /*package-private*/ @ForceInline final LongVector selectFromTemplate(LongVector v) { - return v.rearrange(this.toShuffle()); + return (LongVector)VectorSupport.selectFromOp(getClass(), null, long.class, + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); } /** @@ -2414,9 +2415,15 @@ final LongVector selectFromTemplate(LongVector v) { /*package-private*/ @ForceInline - final LongVector selectFromTemplate(LongVector v, - AbstractMask m) { - return v.rearrange(this.toShuffle(), m); + final + > + LongVector selectFromTemplate(LongVector v, + Class masktype, M m) { + m.check(masktype, this); + return (LongVector)VectorSupport.selectFromOp(getClass(), masktype, long.class, + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); } /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java index 7703df9a59a3d..fe34886512a13 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java @@ -503,7 +503,7 @@ public Short128Vector selectFrom(Vector v, VectorMask m) { return (Short128Vector) super.selectFromTemplate((Short128Vector) v, - (Short128Mask) m); // specialize + Short128Mask.class, (Short128Mask) m); // specialize } @@ -844,6 +844,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Short128Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Short128Shuffle.class, this, VLENGTH, + (s) -> ((Short128Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Short128Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java index cf84593019523..243e24ad26bef 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java @@ -503,7 +503,7 @@ public Short256Vector selectFrom(Vector v, VectorMask m) { return (Short256Vector) super.selectFromTemplate((Short256Vector) v, - (Short256Mask) m); // specialize + Short256Mask.class, (Short256Mask) m); // specialize } @@ -860,6 +860,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Short256Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Short256Shuffle.class, this, VLENGTH, + (s) -> ((Short256Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Short256Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java index 67a5073df0525..4114783608960 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java @@ -503,7 +503,7 @@ public Short512Vector selectFrom(Vector v, VectorMask m) { return (Short512Vector) super.selectFromTemplate((Short512Vector) v, - (Short512Mask) m); // specialize + Short512Mask.class, (Short512Mask) m); // specialize } @@ -892,6 +892,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Short512Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Short512Shuffle.class, this, VLENGTH, + (s) -> ((Short512Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Short512Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java index 263ee10d907ba..d80d4c4e2ec52 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java @@ -503,7 +503,7 @@ public Short64Vector selectFrom(Vector v, VectorMask m) { return (Short64Vector) super.selectFromTemplate((Short64Vector) v, - (Short64Mask) m); // specialize + Short64Mask.class, (Short64Mask) m); // specialize } @@ -836,6 +836,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public Short64Shuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, Short64Shuffle.class, this, VLENGTH, + (s) -> ((Short64Shuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public Short64Shuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java index 07a2caebef0ae..799483a667590 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java @@ -503,7 +503,7 @@ public ShortMaxVector selectFrom(Vector v, VectorMask m) { return (ShortMaxVector) super.selectFromTemplate((ShortMaxVector) v, - (ShortMaxMask) m); // specialize + ShortMaxMask.class, (ShortMaxMask) m); // specialize } @@ -830,6 +830,13 @@ public VectorShuffle cast(VectorSpecies s) { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public ShortMaxShuffle wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, ShortMaxShuffle.class, this, VLENGTH, + (s) -> ((ShortMaxShuffle)(((AbstractShuffle)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public ShortMaxShuffle rearrange(VectorShuffle shuffle) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index ba21e8a9e9563..fb0512fd5b9ea 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -2394,17 +2394,18 @@ ShortVector sliceTemplate(int origin) { */ @Override public abstract - ShortVector rearrange(VectorShuffle m); + ShortVector rearrange(VectorShuffle shuffle); /*package-private*/ @ForceInline final > ShortVector rearrangeTemplate(Class shuffletype, S shuffle) { - shuffle.checkIndexes(); + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, null, short.class, length(), - this, shuffle, null, + this, ws, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); return v1.lane(ei); @@ -2429,17 +2430,14 @@ ShortVector rearrangeTemplate(Class shuffletype, M m) { m.check(masktype, this); - VectorMask valid = shuffle.laneIsValid(); - if (m.andNot(valid).anyTrue()) { - shuffle.checkIndexes(); - throw new AssertionError(); - } + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, short.class, length(), - this, shuffle, m, + this, ws, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); - return ei < 0 || !m_.laneIsSet(i) ? 0 : v1.lane(ei); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); } @@ -2552,7 +2550,10 @@ short.class, length(), this, m, /*package-private*/ @ForceInline final ShortVector selectFromTemplate(ShortVector v) { - return v.rearrange(this.toShuffle()); + return (ShortVector)VectorSupport.selectFromOp(getClass(), null, short.class, + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); } /** @@ -2564,9 +2565,15 @@ final ShortVector selectFromTemplate(ShortVector v) { /*package-private*/ @ForceInline - final ShortVector selectFromTemplate(ShortVector v, - AbstractMask m) { - return v.rearrange(this.toShuffle(), m); + final + > + ShortVector selectFromTemplate(ShortVector v, + Class masktype, M m) { + m.check(masktype, this); + return (ShortVector)VectorSupport.selectFromOp(getClass(), masktype, short.class, + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); } /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java index d34ac79e7c32c..fda073f686389 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java @@ -2614,17 +2614,14 @@ public abstract VectorMask compare(VectorOperators.Comparison op, * elements of this vector. * * For each lane {@code N} of the shuffle, and for each lane - * source index {@code I=s.laneSource(N)} in the shuffle, + * source index {@code I=s.wrapIndex(s.laneSource(N))} in the shuffle, * the output lane {@code N} obtains the value from * the input vector at lane {@code I}. * * @param s the shuffle controlling lane index selection * @return the rearrangement of the lane elements of this vector - * @throws IndexOutOfBoundsException if there are any exceptional - * source indexes in the shuffle * @see #rearrange(VectorShuffle,VectorMask) * @see #rearrange(VectorShuffle,Vector) - * @see VectorShuffle#laneIsValid() */ public abstract Vector rearrange(VectorShuffle s); @@ -2636,27 +2633,22 @@ public abstract VectorMask compare(VectorOperators.Comparison op, * elements of this vector. * * For each lane {@code N} of the shuffle, and for each lane - * source index {@code I=s.laneSource(N)} in the shuffle, + * source index {@code I=s.wrapIndex(s.laneSource(N))} in the shuffle, * the output lane {@code N} obtains the value from * the input vector at lane {@code I} if the mask is set. * Otherwise the output lane {@code N} is set to zero. * *

          This method returns the value of this pseudocode: *

          {@code
          -     * Vector r = this.rearrange(s.wrapIndexes());
          -     * VectorMask valid = s.laneIsValid();
          -     * if (m.andNot(valid).anyTrue()) throw ...;
          +     * Vector r = this.rearrange(s);
                * return broadcast(0).blend(r, m);
                * }
          * * @param s the shuffle controlling lane index selection * @param m the mask controlling application of the shuffle * @return the rearrangement of the lane elements of this vector - * @throws IndexOutOfBoundsException if there are any exceptional - * source indexes in the shuffle where the mask is set * @see #rearrange(VectorShuffle) * @see #rearrange(VectorShuffle,Vector) - * @see VectorShuffle#laneIsValid() */ public abstract Vector rearrange(VectorShuffle s, VectorMask m); @@ -2747,7 +2739,7 @@ public abstract VectorMask compare(VectorOperators.Comparison op, * this vector. * * For each lane {@code N} of this vector, and for each lane - * value {@code I=this.lane(N)} in this vector, + * value {@code I=wrapIndex(this.lane(N))} in this vector, * the output lane {@code N} obtains the value from * the argument vector at lane {@code I}. * @@ -2760,8 +2752,6 @@ public abstract VectorMask compare(VectorOperators.Comparison op, * * @param v the vector supplying the result values * @return the rearrangement of the lane elements of {@code v} - * @throws IndexOutOfBoundsException if any invalid - * source indexes are found in {@code this} * @see #rearrange(VectorShuffle) */ public abstract Vector selectFrom(Vector v); @@ -2787,9 +2777,6 @@ public abstract VectorMask compare(VectorOperators.Comparison op, * @param v the vector supplying the result values * @param m the mask controlling selection from {@code v} * @return the rearrangement of the lane elements of {@code v} - * @throws IndexOutOfBoundsException if any invalid - * source indexes are found in {@code this}, - * in a lane which is set in the mask * @see #selectFrom(Vector) * @see #rearrange(VectorShuffle,VectorMask) */ diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index d7562bae4755a..fcc128ea8c7b0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -2770,17 +2770,18 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { */ @Override public abstract - $abstractvectortype$ rearrange(VectorShuffle<$Boxtype$> m); + $abstractvectortype$ rearrange(VectorShuffle<$Boxtype$> shuffle); /*package-private*/ @ForceInline final > $abstractvectortype$ rearrangeTemplate(Class shuffletype, S shuffle) { - shuffle.checkIndexes(); + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, null, $type$.class, length(), - this, shuffle, null, + this, ws, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); return v1.lane(ei); @@ -2805,17 +2806,14 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { M m) { m.check(masktype, this); - VectorMask<$Boxtype$> valid = shuffle.laneIsValid(); - if (m.andNot(valid).anyTrue()) { - shuffle.checkIndexes(); - throw new AssertionError(); - } + @SuppressWarnings("unchecked") + S ws = (S) shuffle.wrapIndexes(); return VectorSupport.rearrangeOp( getClass(), shuffletype, masktype, $type$.class, length(), - this, shuffle, m, + this, ws, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = s_.laneSource(i); - return ei < 0 || !m_.laneIsSet(i) ? 0 : v1.lane(ei); + return !m_.laneIsSet(i) ? 0 : v1.lane(ei); })); } @@ -2928,7 +2926,10 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { /*package-private*/ @ForceInline final $abstractvectortype$ selectFromTemplate($abstractvectortype$ v) { - return v.rearrange(this.toShuffle()); + return ($Type$Vector)VectorSupport.selectFromOp(getClass(), null, $type$.class, + length(), this, v, null, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle())); } /** @@ -2940,9 +2941,15 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { /*package-private*/ @ForceInline - final $abstractvectortype$ selectFromTemplate($abstractvectortype$ v, - AbstractMask<$Boxtype$> m) { - return v.rearrange(this.toShuffle(), m); + final + > + $abstractvectortype$ selectFromTemplate($abstractvectortype$ v, + Class masktype, M m) { + m.check(masktype, this); + return ($Type$Vector)VectorSupport.selectFromOp(getClass(), masktype, $type$.class, + length(), this, v, m, + (v1, v2, _m) -> + v2.rearrange(v1.toShuffle(), _m)); } /// Ternary operations diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template index 111d4bbefd44b..483962b4e0670 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template @@ -509,7 +509,7 @@ final class $vectortype$ extends $abstractvectortype$ { VectorMask<$Boxtype$> m) { return ($vectortype$) super.selectFromTemplate(($vectortype$) v, - ($masktype$) m); // specialize + $masktype$.class, ($masktype$) m); // specialize } @@ -1118,6 +1118,13 @@ final class $vectortype$ extends $abstractvectortype$ { return s.shuffleFromArray(shuffleArray, 0).check(s); } + @Override + @ForceInline + public $shuffletype$ wrapIndexes() { + return VectorSupport.wrapShuffleIndexes(ETYPE, $shuffletype$.class, this, VLENGTH, + (s) -> (($shuffletype$)(((AbstractShuffle<$Boxtype$>)(s)).wrapIndexesTemplate()))); + } + @ForceInline @Override public $shuffletype$ rearrange(VectorShuffle<$Boxtype$> shuffle) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java index 9a1fef061e3f4..d25a7c974a5cf 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCICompilerConfig.java @@ -104,12 +104,15 @@ static JVMCICompilerFactory getCompilerFactory(HotSpotJVMCIRuntime runtime) { } } if (factory == null) { + String reason; if (Services.IS_IN_NATIVE_IMAGE) { - throw runtime.exitHotSpotWithMessage(1, "JVMCI compiler '%s' not found in JVMCI native library.%n" + + reason = String.format("JVMCI compiler '%s' not found in JVMCI native library.%n" + "Use -XX:-UseJVMCINativeLibrary when specifying a JVMCI compiler available on a class path with %s.%n", compilerName, compPropertyName); + } else { + reason = String.format("JVMCI compiler '%s' specified by %s not found%n", compilerName, compPropertyName); } - throw runtime.exitHotSpotWithMessage(1, "JVMCI compiler '%s' specified by %s not found%n", compilerName, compPropertyName); + factory = new DummyCompilerFactory(reason, runtime); } } } else { diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index 78a03a4332d47..014b420e1a237 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -197,6 +197,7 @@ public ExitException(int errorCode) { private boolean hasExpiringCert = false; private boolean hasExpiringTsaCert = false; private boolean noTimestamp = true; + private boolean hasNonexistentEntries = false; // Expiration date. The value could be null if signed by a trusted cert. private Date expireDate = null; @@ -735,6 +736,7 @@ void verifyJar(String jarName) Map sigMap = new HashMap<>(); Map sigNameMap = new HashMap<>(); Map unparsableSignatures = new HashMap<>(); + Map> entriesInSF = new HashMap<>(); try { jf = new JarFile(jarName, true); @@ -782,6 +784,7 @@ void verifyJar(String jarName) break; } } + entriesInSF.put(alias, sf.getEntries().keySet()); if (!found) { unparsableSignatures.putIfAbsent(alias, String.format( @@ -881,6 +884,9 @@ void verifyJar(String jarName) sb.append('\n'); } } + for (var signed : entriesInSF.values()) { + signed.remove(name); + } } else if (showcerts && !verbose.equals("all")) { // Print no info for unsigned entries when -verbose:all, // to be consistent with old behavior. @@ -1076,6 +1082,13 @@ void verifyJar(String jarName) if (verbose != null) { System.out.println(history); } + var signed = entriesInSF.get(s); + if (!signed.isEmpty()) { + if (verbose != null) { + System.out.println(rb.getString("history.nonexistent.entries") + signed); + } + hasNonexistentEntries = true; + } } else { unparsableSignatures.putIfAbsent(s, String.format( rb.getString("history.nobk"), s)); @@ -1311,6 +1324,9 @@ private void displayMessagesAndResult(boolean isSigning) { } } + if (hasNonexistentEntries) { + warnings.add(rb.getString("nonexistent.entries.found")); + } if (externalFileAttributesDetected) { warnings.add(rb.getString("external.file.attributes.detected")); } diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java index 6a86b72ad1b10..810bd107bdebc 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java @@ -164,6 +164,7 @@ public class Resources extends java.util.ListResourceBundle { {"history.with.ts", "- Signed by \"%1$s\"\n Digest algorithm: %2$s\n Signature algorithm: %3$s, %4$s\n Timestamped by \"%6$s\" on %5$tc\n Timestamp digest algorithm: %7$s\n Timestamp signature algorithm: %8$s, %9$s"}, {"history.without.ts", "- Signed by \"%1$s\"\n Digest algorithm: %2$s\n Signature algorithm: %3$s, %4$s"}, + {"history.nonexistent.entries", " Warning: nonexistent signed entries: "}, {"history.unparsable", "- Unparsable signature-related file %s"}, {"history.nosf", "- Missing signature-related file META-INF/%s.SF"}, {"history.nobk", "- Missing block file for signature-related file META-INF/%s.SF"}, @@ -178,6 +179,7 @@ public class Resources extends java.util.ListResourceBundle { {"key.bit.disabled", "%d-bit key (disabled)"}, {"key.bit.eccurve.disabled", "%1$d-bit %2$s key (disabled)"}, {"unknown.size", "unknown size"}, + {"nonexistent.entries.found", "This jar contains signed entries for files that do not exist. See the -verbose output for more details."}, {"external.file.attributes.detected", "POSIX file permission and/or symlink attributes detected. These attributes are ignored when signing and are not protected by the signature."}, {"jarsigner.", "jarsigner: "}, 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 fdf05489168f7..9bb1ebaaf620b 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 @@ -2642,19 +2642,31 @@ public void addRestrictedInfo(ExecutableElement forWhat, Content target) { //in Java platform: var restrictedDiv = HtmlTree.DIV(HtmlStyles.restrictedBlock); restrictedDiv.setId(htmlIds.forRestrictedSection(forWhat)); - String name = forWhat.getSimpleName().toString(); + var name = forWhat.getSimpleName().toString(); var nameCode = HtmlTree.CODE(Text.of(name)); - String leadingNoteKey = "doclet.RestrictedLeadingNote"; - Content leadingNote = - contents.getContent(leadingNoteKey, nameCode); - restrictedDiv.add(HtmlTree.SPAN(HtmlStyles.restrictedLabel, - leadingNote)); - Content note1 = contents.getContent("doclet.RestrictedTrailingNote1", nameCode); + var restrictedMethodLink = getRestrictedMethodDocLink(); + var leadingNoteKey = "doclet.RestrictedLeadingNote"; + var leadingNote = contents.getContent(leadingNoteKey, nameCode, restrictedMethodLink); + restrictedDiv.add(HtmlTree.SPAN(HtmlStyles.restrictedLabel, leadingNote)); + var note1 = contents.getContent("doclet.RestrictedTrailingNote1", nameCode); restrictedDiv.add(HtmlTree.DIV(HtmlStyles.restrictedComment, note1)); - Content note2 = contents.getContent("doclet.RestrictedTrailingNote2", nameCode); + var note2 = contents.getContent("doclet.RestrictedTrailingNote2", nameCode); restrictedDiv.add(HtmlTree.DIV(HtmlStyles.restrictedComment, note2)); target.add(restrictedDiv); } } + private Content getRestrictedMethodDocLink() { + var restrictedMethodLabel = contents.getContent("doclet.RestrictedMethod"); + var javaLang = utils.elementUtils.getPackageElement("java.lang"); + if (utils.isIncluded(javaLang)) { + var restrictedDocPath = pathToRoot + .resolve(docPaths.forPackage(javaLang)) + .resolve(DocPaths.DOC_FILES) + .resolve(DocPaths.RESTRICTED_DOC); + return links.createLink(restrictedDocPath, restrictedMethodLabel); + } + return restrictedMethodLabel; + } + } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template index 633f453bc43b1..2185b1c951f8e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template @@ -300,6 +300,7 @@ document.addEventListener("DOMContentLoaded", function(e) { }); var expanded = false; var windowWidth; + var bodyHeight; function collapse(e) { if (expanded) { mainnav.removeAttribute("style"); @@ -365,6 +366,7 @@ document.addEventListener("DOMContentLoaded", function(e) { var scrollTimeoutNeeded; var prevHash; function initSectionData() { + bodyHeight = document.body.offsetHeight; sections = [{ id: "", top: 0 }].concat(Array.from(main.querySelectorAll("section[id], h2[id], h2 a[id], div[id]")) .filter((e) => { return sidebar.querySelector("a[href=\"#" + encodeURI(e.getAttribute("id")) + "\"]") !== null @@ -469,7 +471,7 @@ document.addEventListener("DOMContentLoaded", function(e) { expand(); } } - if (sections) { + if (sections && document.body.offsetHeight !== bodyHeight) { initSectionData(); prevHash = null; handleScroll(); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index 6141ce46fe8fc..bb001912229c2 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -424,7 +424,8 @@ doclet.ReflectivePreviewAPI={0} refers to one or more reflective preview APIs: doclet.UsesDeclaredUsingPreview={0} refers to one or more types which are declared using a preview feature of the Java language: {1}. doclet.PreviewTrailingNote1=Programs can only use {0} when preview features are enabled. doclet.PreviewTrailingNote2=Preview features may be removed in a future release, or upgraded to permanent features of the Java platform. -doclet.RestrictedLeadingNote={0} is a restricted method of the Java platform. +doclet.RestrictedMethod=restricted method +doclet.RestrictedLeadingNote={0} is a {1} of the Java platform. doclet.RestrictedTrailingNote1=Programs can only use {0} when access to restricted methods is enabled. doclet.RestrictedTrailingNote2=Restricted methods are unsafe, and, if used incorrectly, might crash \ the JVM or result in memory corruption. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index 1130f14dc35b4..641a6684444f9 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -28,8 +28,8 @@ /* Line height for continuous text blocks */ --block-line-height: 1.4em; /* Text colors for body and block elements */ - --body-text-color: #353833; - --block-text-color: #474747; + --body-text-color: #282828; + --block-text-color: #282828; /* Background colors for various structural elements */ --body-background-color: #ffffff; --section-background-color: #f8f8f8; @@ -49,8 +49,11 @@ /* Text color for page title */ --title-color: #2c4557; /* Text colors for links */ - --link-color: #4A6782; + --link-color: #437291; --link-color-active: #bb7a2a; + /* Table of contents */ + --toc-background-color: var(--section-background-color); + --toc-link-color: #4a698a; /* Snippet colors */ --snippet-background-color: #ebecee; --snippet-text-color: var(--block-text-color); @@ -99,6 +102,9 @@ a:link, a:visited { text-decoration:none; color:var(--link-color); } +nav a:link, nav a:visited { + color: var(--toc-link-color); +} a[href]:hover, a[href]:active { text-decoration:none; color:var(--link-color-active); @@ -360,8 +366,11 @@ main { padding:10px 20px; position:relative; } -section[id$=-description] :is(dl, ol, ul, p, div, blockquote, pre):last-child, -section[id$=-description] :is(dl, ol, ul):last-child > :is(li, dd):last-child { +/* Compensate for non-collapsing margins between element description and summary tables */ +div.horizontal-scroll > section[id$=-description] > :is(dl, ol, ul, p, div, blockquote, pre):last-child, +div.horizontal-scroll > section[id$=-description] > :last-child > :is(li, dd):last-child, +section.class-description > div.horizontal-scroll > :is(dl, ol, ul, p, div, blockquote, pre):last-child, +section.class-description > div.horizontal-scroll > :last-child > :is(li, dd):last-child { margin-bottom:4px; } dl.notes > dt { @@ -395,7 +404,7 @@ dl.name-value > dd { * Styles for table of contents. */ .main-grid nav.toc { - background-color: var(--section-background-color); + background-color: var(--toc-background-color); border-right: 1px solid var(--border-color); position: sticky; top: calc(var(--nav-height)); @@ -406,8 +415,6 @@ dl.name-value > dd { z-index: 1; } .main-grid nav.toc div.toc-header { - background-color: var(--section-background-color); - border-right: 1px solid var(--border-color); top: var(--nav-height); z-index: 1; padding: 15px 20px; @@ -470,7 +477,6 @@ nav.toc div.toc-header { display: inline-flex; align-items: center; color: var(--body-text-color); - background-color: var(--body-background-color); font-size: 0.856em; font-weight: bold; white-space: nowrap; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java index 5ae2d1590721c..47cf3e81ebed5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java @@ -163,6 +163,9 @@ public static DocPath indexN(int n) { /** The name of the file for restricted methods. */ public static final DocPath RESTRICTED_LIST = DocPath.create("restricted-list.html"); + /** The name of the doc-file for restricted methods. */ + public static final DocPath RESTRICTED_DOC = DocPath.create("RestrictedMethods.html"); + /** The name of the directory for the resource files. */ public static final DocPath RESOURCE_FILES = DocPath.create("resource-files"); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java index a531a987f0d40..0ec72e4ae1cf3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, 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 @@ -870,10 +870,12 @@ private List getFiles(ModulePackage modpkg, private ModuleSymbol findModuleOfPackageName(String packageName) { Name pack = names.fromString(packageName); - for (ModuleSymbol msym : modules.allModules()) { - PackageSymbol p = syms.getPackage(msym, pack); - if (p != null && !p.members().isEmpty()) { - return msym; + if (modules.modulesInitialized()) { + for (ModuleSymbol msym : modules.allModules()) { + PackageSymbol p = syms.getPackage(msym, pack); + if (p != null && !p.members().isEmpty()) { + return msym; + } } } return null; diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java index 3b9336c499d9c..cccbaf14f5399 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/AttributeWriter.java @@ -35,6 +35,7 @@ import java.lang.classfile.attribute.*; import static java.lang.classfile.ClassFile.*; import static java.lang.classfile.attribute.StackMapFrameInfo.*; +import static java.lang.classfile.instruction.CharacterRange.*; /* * A writer for writing Attributes as text. @@ -144,23 +145,23 @@ public void write(Attribute a, CodeAttribute lr) { (e.characterRangeStart() & 0x3ff), (e.characterRangeEnd() >> 10), (e.characterRangeEnd() & 0x3ff))); - if ((e.flags() & CRT_STATEMENT) != 0) + if ((e.flags() & FLAG_STATEMENT) != 0) print(", statement"); - if ((e.flags() & CRT_BLOCK) != 0) + if ((e.flags() & FLAG_BLOCK) != 0) print(", block"); - if ((e.flags() & CRT_ASSIGNMENT) != 0) + if ((e.flags() & FLAG_ASSIGNMENT) != 0) print(", assignment"); - if ((e.flags() & CRT_FLOW_CONTROLLER) != 0) + if ((e.flags() & FLAG_FLOW_CONTROLLER) != 0) print(", flow-controller"); - if ((e.flags() & CRT_FLOW_TARGET) != 0) + if ((e.flags() & FLAG_FLOW_TARGET) != 0) print(", flow-target"); - if ((e.flags() & CRT_INVOKE) != 0) + if ((e.flags() & FLAG_INVOKE) != 0) print(", invoke"); - if ((e.flags() & CRT_CREATE) != 0) + if ((e.flags() & FLAG_CREATE) != 0) print(", create"); - if ((e.flags() & CRT_BRANCH_TRUE) != 0) + if ((e.flags() & FLAG_BRANCH_TRUE) != 0) print(", branch-true"); - if ((e.flags() & CRT_BRANCH_FALSE) != 0) + if ((e.flags() & FLAG_BRANCH_FALSE) != 0) print(", branch-false"); println(); } @@ -705,13 +706,13 @@ void printMap(String name, List map, CodeAttribute lr) { String mapTypeName(SimpleVerificationTypeInfo type) { return switch (type) { - case ITEM_TOP -> "top"; - case ITEM_INTEGER -> "int"; - case ITEM_FLOAT -> "float"; - case ITEM_LONG -> "long"; - case ITEM_DOUBLE -> "double"; - case ITEM_NULL -> "null"; - case ITEM_UNINITIALIZED_THIS -> "this"; + case TOP -> "top"; + case INTEGER -> "int"; + case FLOAT -> "float"; + case LONG -> "long"; + case DOUBLE -> "double"; + case NULL -> "null"; + case UNINITIALIZED_THIS -> "this"; }; } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java index 764f93bd54aeb..77fbce8839e72 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java @@ -741,7 +741,7 @@ static String getJavaName(String name) { */ String getConstantValue(ClassDesc d, ConstantValueEntry cpInfo) { switch (cpInfo.tag()) { - case ClassFile.TAG_INTEGER: { + case PoolEntry.TAG_INTEGER: { var val = (Integer)cpInfo.constantValue(); switch (d.descriptorString()) { case "C": @@ -755,7 +755,7 @@ String getConstantValue(ClassDesc d, ConstantValueEntry cpInfo) { return String.valueOf(val); } } - case ClassFile.TAG_STRING: + case PoolEntry.TAG_STRING: return getConstantStringValue(cpInfo.constantValue().toString()); default: return constantWriter.stringValue(cpInfo); diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java index 519a5adcf6278..8f6b9b1d2ed03 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/CodeWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, 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 @@ -92,20 +92,22 @@ public void writeVerboseHeader(CodeAttribute attr) { public void writeInstrs(CodeAttribute attr) { List detailWriters = getDetailWriters(attr); - int pc = 0; + int[] pcState = {0}; try { - for (var coe: attr) { + attr.forEach(coe -> { if (coe instanceof Instruction instr) { - for (InstructionDetailWriter w: detailWriters) + int pc = pcState[0]; + for (InstructionDetailWriter w : detailWriters) w.writeDetails(pc, instr); writeInstr(pc, instr, attr); - pc += instr.sizeInBytes(); + pcState[0] = pc + instr.sizeInBytes(); } - } + }); } catch (IllegalArgumentException e) { - report("error at or after byte " + pc); + report("error at or after address " + pcState[0] + ": " + e.getMessage()); } + int pc = pcState[0]; for (InstructionDetailWriter w: detailWriters) w.flush(pc); } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java index c649ce982969e..b3c6b5b6c640d 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java @@ -26,7 +26,8 @@ package com.sun.tools.javap; import java.lang.classfile.constantpool.*; -import static java.lang.classfile.ClassFile.*; + +import static java.lang.classfile.constantpool.PoolEntry.*; /* * Write a constant pool entry. @@ -156,13 +157,13 @@ String cpTagName(int tag) { case TAG_CLASS -> "Class"; case TAG_STRING -> "String"; case TAG_FIELDREF -> "Fieldref"; - case TAG_METHODHANDLE -> "MethodHandle"; - case TAG_METHODTYPE -> "MethodType"; + case TAG_METHOD_HANDLE -> "MethodHandle"; + case TAG_METHOD_TYPE -> "MethodType"; case TAG_METHODREF -> "Methodref"; - case TAG_INTERFACEMETHODREF -> "InterfaceMethodref"; - case TAG_INVOKEDYNAMIC -> "InvokeDynamic"; - case TAG_CONSTANTDYNAMIC -> "Dynamic"; - case TAG_NAMEANDTYPE -> "NameAndType"; + case TAG_INTERFACE_METHODREF -> "InterfaceMethodref"; + case TAG_INVOKE_DYNAMIC -> "InvokeDynamic"; + case TAG_DYNAMIC -> "Dynamic"; + case TAG_NAME_AND_TYPE -> "NameAndType"; default -> "Unknown"; }; } @@ -177,13 +178,13 @@ String tagName(int tag) { case TAG_CLASS -> "class"; case TAG_STRING -> "String"; case TAG_FIELDREF -> "Field"; - case TAG_METHODHANDLE -> "MethodHandle"; - case TAG_METHODTYPE -> "MethodType"; + case TAG_METHOD_HANDLE -> "MethodHandle"; + case TAG_METHOD_TYPE -> "MethodType"; case TAG_METHODREF -> "Method"; - case TAG_INTERFACEMETHODREF -> "InterfaceMethod"; - case TAG_INVOKEDYNAMIC -> "InvokeDynamic"; - case TAG_CONSTANTDYNAMIC -> "Dynamic"; - case TAG_NAMEANDTYPE -> "NameAndType"; + case TAG_INTERFACE_METHODREF -> "InterfaceMethod"; + case TAG_INVOKE_DYNAMIC -> "InvokeDynamic"; + case TAG_DYNAMIC -> "Dynamic"; + case TAG_NAME_AND_TYPE -> "NameAndType"; default -> "(unknown tag " + tag + ")"; }; } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java index a1d939bcc4f23..d899569724e66 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/StackMapWriter.java @@ -122,25 +122,25 @@ void print(StackMapFrameInfo.VerificationTypeInfo entry, boolean firstThis) { switch (entry) { case StackMapFrameInfo.SimpleVerificationTypeInfo s -> { switch (s) { - case ITEM_TOP -> + case TOP -> print("top"); - case ITEM_INTEGER -> + case INTEGER -> print("int"); - case ITEM_FLOAT -> + case FLOAT -> print("float"); - case ITEM_LONG -> + case LONG -> print("long"); - case ITEM_DOUBLE -> + case DOUBLE -> print("double"); - case ITEM_NULL -> + case NULL -> print("null"); - case ITEM_UNINITIALIZED_THIS -> + case UNINITIALIZED_THIS -> print("uninit_this"); } } diff --git a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c index af78969e316ac..b6d246d043363 100644 --- a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c +++ b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryConnection.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2017, 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 @@ -115,7 +115,7 @@ packetToByteArray(JNIEnv *env, jdwpPacket *str) /* total packet length is header + data */ array = (*env)->NewByteArray(env, total_length); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return NULL; } @@ -144,7 +144,7 @@ packetToByteArray(JNIEnv *env, jdwpPacket *str) if (data_length > 0) { (*env)->SetByteArrayRegion(env, array, JDWP_HEADER_SIZE, data_length, str->type.cmd.data); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return NULL; } } @@ -174,7 +174,7 @@ byteArrayToPacket(JNIEnv *env, jbyteArray b, jdwpPacket *str) * Get the packet header */ (*env)->GetByteArrayRegion(env, b, 0, sizeof(pktHeader), pktHeader); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { /* b shorter than sizeof(pktHeader) */ return; } @@ -221,7 +221,7 @@ byteArrayToPacket(JNIEnv *env, jbyteArray b, jdwpPacket *str) } (*env)->GetByteArrayRegion(env, b, sizeof(pktHeader), /*sizeof(CmdPacket)+4*/ data_length, data); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { free(data); return; } @@ -326,7 +326,7 @@ JNIEXPORT void JNICALL Java_com_sun_tools_jdi_SharedMemoryConnection_sendPacket0 jint rc; byteArrayToPacket(env, b, &packet); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return; } diff --git a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c index 7258503f7e173..867ed1d8567fa 100644 --- a/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c +++ b/src/jdk.jdi/share/native/libdt_shmem/SharedMemoryTransport.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ void throwException(JNIEnv *env, char *exceptionClassName, char *message) { jclass excClass = (*env)->FindClass(env, exceptionClassName); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return; } (*env)->ThrowNew(env, excClass, message); @@ -109,7 +109,7 @@ JNIEXPORT jlong JNICALL Java_com_sun_tools_jdi_SharedMemoryTransportService_atta const char *addrChars; addrChars = (*env)->GetStringUTFChars(env, address, NULL); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return CONNECTION_TO_ID(connection); } else if (addrChars == NULL) { throwException(env, "java/lang/InternalError", "GetStringUTFChars failed"); @@ -143,7 +143,7 @@ JNIEXPORT jstring JNICALL Java_com_sun_tools_jdi_SharedMemoryTransportService_na throwShmemException(env, "shmemBase_name failed", rc); } else { nameString = (*env)->NewStringUTF(env, namePtr); - if ((nameString == NULL) && !(*env)->ExceptionOccurred(env)) { + if ((nameString == NULL) && !(*env)->ExceptionCheck(env)) { throwException(env, "java/lang/InternalError", "Unable to create string"); } } @@ -190,7 +190,7 @@ JNIEXPORT jlong JNICALL Java_com_sun_tools_jdi_SharedMemoryTransportService_star if (address != NULL) { addrChars = (*env)->GetStringUTFChars(env, address, NULL); - if ((*env)->ExceptionOccurred(env)) { + if ((*env)->ExceptionCheck(env)) { return TRANSPORT_TO_ID(transport); } else if (addrChars == NULL) { throwException(env, "java/lang/InternalError", "GetStringUTFChars failed"); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c index 7e198be9a4637..335acc62dcf4f 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c @@ -1652,6 +1652,185 @@ setAgentPropertyValue(JNIEnv *env, char *propertyName, char* propertyValue) } } +#ifdef DEBUG +// APIs that can be called when debugging the debug agent + +#define check_jvmti_status(err, msg) \ + if (err != JVMTI_ERROR_NONE) { \ + EXIT_ERROR(err, msg); \ + } + +char* +translateThreadState(jint flags) { + char str[15 * 20]; + str[0] = '\0'; + + if (flags & JVMTI_THREAD_STATE_ALIVE) { + strcat(str, " ALIVE"); + } + if (flags & JVMTI_THREAD_STATE_TERMINATED) { + strcat(str, " TERMINATED"); + } + if (flags & JVMTI_THREAD_STATE_RUNNABLE) { + strcat(str, " RUNNABLE"); + } + if (flags & JVMTI_THREAD_STATE_WAITING) { + strcat(str, " WAITING"); + } + if (flags & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) { + strcat(str, " WAITING_INDEFINITELY"); + } + if (flags & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) { + strcat(str, " WAITING_WITH_TIMEOUT"); + } + if (flags & JVMTI_THREAD_STATE_SLEEPING) { + strcat(str, " SLEEPING"); + } + if (flags & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) { + strcat(str, " IN_OBJECT_WAIT"); + } + if (flags & JVMTI_THREAD_STATE_PARKED) { + strcat(str, " PARKED"); + } + if (flags & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) { + strcat(str, " BLOCKED_ON_MONITOR_ENTER"); + } + if (flags & JVMTI_THREAD_STATE_SUSPENDED) { + strcat(str, " SUSPENDED"); + } + if (flags & JVMTI_THREAD_STATE_INTERRUPTED) { + strcat(str, " INTERRUPTED"); + } + if (flags & JVMTI_THREAD_STATE_IN_NATIVE) { + strcat(str, " IN_NATIVE"); + } + + if (strlen(str) == 0) { + strcpy(str, ""); + } + + char* tstate = (char*)jvmtiAllocate((int)strlen(str) + 1); + strcpy(tstate, str); + + return tstate; +} + +char* +getThreadName(jthread thread) { + jvmtiThreadInfo thr_info; + jvmtiError err; + + memset(&thr_info, 0, sizeof(thr_info)); + err = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo) + (gdata->jvmti, thread, &thr_info); + if (err == JVMTI_ERROR_WRONG_PHASE || err == JVMTI_ERROR_THREAD_NOT_ALIVE) { + return NULL; // VM or target thread completed its work + } + check_jvmti_status(err, "getThreadName: error in JVMTI GetThreadInfo call"); + + char* tname = thr_info.name; + if (tname == NULL) { + const char* UNNAMED_STR = ""; + size_t UNNAMED_LEN = strlen(UNNAMED_STR); + tname = (char*)jvmtiAllocate((int)UNNAMED_LEN + 1); + strcpy(tname, UNNAMED_STR); + } + return tname; +} + +char* +getMethodName(jmethodID method) { + char* mname = NULL; + jvmtiError err; + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodName) + (gdata->jvmti, method, &mname, NULL, NULL); + check_jvmti_status(err, "getMethodName: error in JVMTI GetMethodName call"); + + return mname; +} + +static char* +get_method_class_name(jmethodID method) { + jclass klass = NULL; + char* cname = NULL; + char* result = NULL; + jvmtiError err; + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodDeclaringClass) + (gdata->jvmti, method, &klass); + check_jvmti_status(err, "get_method_class_name: error in JVMTI GetMethodDeclaringClass"); + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetClassSignature) + (gdata->jvmti, klass, &cname, NULL); + check_jvmti_status(err, "get_method_class_name: error in JVMTI GetClassSignature"); + + size_t len = strlen(cname) - 2; // get rid of leading 'L' and trailing ';' + result = (char*)jvmtiAllocate((int)len + 1); + strncpy(result, cname + 1, len); // skip leading 'L' + result[len] = '\0'; + jvmtiDeallocate((void*)cname); + return result; +} + +static void +print_method(jmethodID method, jint depth) { + char* cname = NULL; + char* mname = NULL; + char* msign = NULL; + jvmtiError err; + + cname = get_method_class_name(method); + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodName) + (gdata->jvmti, method, &mname, &msign, NULL); + check_jvmti_status(err, "print_method: error in JVMTI GetMethodName"); + + tty_message("%2d: %s: %s%s", depth, cname, mname, msign); + jvmtiDeallocate((void*)cname); + jvmtiDeallocate((void*)mname); + jvmtiDeallocate((void*)msign); +} + +#define MAX_FRAME_COUNT_PRINT_STACK_TRACE 200 + +void +printStackTrace(jthread thread) { + jvmtiFrameInfo frames[MAX_FRAME_COUNT_PRINT_STACK_TRACE]; + char* tname = getThreadName(thread); + jint count = 0; + + jvmtiError err = JVMTI_FUNC_PTR(gdata->jvmti,GetStackTrace) + (gdata->jvmti, thread, 0, MAX_FRAME_COUNT_PRINT_STACK_TRACE, frames, &count); + check_jvmti_status(err, "printStackTrace: error in JVMTI GetStackTrace"); + + tty_message("JVMTI Stack Trace for thread %s: frame count: %d", tname, count); + for (int depth = 0; depth < count; depth++) { + print_method(frames[depth].method, depth); + } + jvmtiDeallocate((void*)tname); +} + +void +printThreadInfo(jthread thread) { + jvmtiThreadInfo thread_info; + jint thread_state; + jvmtiError err; + err = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo) + (gdata->jvmti, thread, &thread_info); + check_jvmti_status(err, "Error in GetThreadInfo"); + err = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadState) + (gdata->jvmti, thread, &thread_state); + check_jvmti_status(err, "Error in GetThreadState"); + const char* state = translateThreadState(thread_state); + tty_message("Thread: %p, name: %s, state(%x): %s, attrs: %s %s", + thread, thread_info.name, thread_state, state, + (isVThread(thread) ? "virtual": "platform"), + (thread_info.is_daemon ? "daemon": "")); +} + +#endif /* DEBUG*/ + /** * Return property value as JDWP allocated string in UTF8 encoding */ diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.h b/src/jdk.jdwp.agent/share/native/libjdwp/util.h index 75281813709e0..3d499d7d56985 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.h @@ -386,6 +386,15 @@ jvmtiError allNestedClasses(jclass clazz, jclass **ppnested, jint *pcount); void setAgentPropertyValue(JNIEnv *env, char *propertyName, char* propertyValue); +#ifdef DEBUG +// APIs that can be called when debugging the debug agent +char* translateThreadState(jint flags); +char* getThreadName(jthread thread); +char* getMethodName(jmethodID method); +void printStackTrace(jthread thread); +void printThreadInfo(jthread thread); +#endif + void *jvmtiAllocate(jint numBytes); void jvmtiDeallocate(void *buffer); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java index 523622084843c..1373c3ca4968f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java @@ -84,7 +84,7 @@ private void buildSetMethod(ClassBuilder builder) { int index = 0; for (ValueDescriptor v : fields) { codeBuilder.iload(1); - codeBuilder.ldc(index); + codeBuilder.loadConstant(index); Label notEqual = codeBuilder.newLabel(); codeBuilder.if_icmpne(notEqual); codeBuilder.aload(0); // this diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java index 71dc97d114e2c..d962a535829ef 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java @@ -499,7 +499,7 @@ private void makeInstrumented() { // if (!settingsMethod(eventConfiguration.settingX)) goto fail; codeBuilder.aload(0); getEventConfiguration(codeBuilder); - codeBuilder.ldc(index); + codeBuilder.loadConstant(index); invokevirtual(codeBuilder, TYPE_EVENT_CONFIGURATION, METHOD_EVENT_CONFIGURATION_GET_SETTING); MethodTypeDesc mdesc = MethodTypeDesc.ofDescriptor("(" + sd.paramType().descriptorString() + ")Z"); codeBuilder.checkcast(sd.paramType()); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java index d0b95df848474..516d094b5daa8 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java @@ -30,7 +30,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -288,41 +287,4 @@ protected final String exampleDirectory() { return "/directory/recordings"; } } - - static String expandFilename(String filename) { - if (filename == null || filename.indexOf('%') == -1) { - return filename; - } - - String pid = null; - String time = null; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < filename.length(); i++) { - char c = filename.charAt(i); - if (c == '%' && i < filename.length() - 1) { - char nc = filename.charAt(i + 1); - if (nc == '%') { // %% ==> % - sb.append('%'); - i++; - } else if (nc == 'p') { - if (pid == null) { - pid = JVM.getPid(); - } - sb.append(pid); - i++; - } else if (nc == 't') { - if (time == null) { - time = ValueFormatter.formatDateTime(LocalDateTime.now()); - } - sb.append(time); - i++; - } else { - sb.append('%'); - } - } else { - sb.append(c); - } - } - return sb.toString(); - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java index c14ce6025ce9b..151456745b0ac 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/ArgumentParser.java @@ -25,14 +25,18 @@ package jdk.jfr.internal.dcmd; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringJoiner; + +import jdk.jfr.internal.JVM; import jdk.jfr.internal.util.SpellChecker; import jdk.jfr.internal.util.TimespanUnit; +import jdk.jfr.internal.util.ValueFormatter; final class ArgumentParser { private final Map options = new HashMap<>(); @@ -221,15 +225,59 @@ private String readText(String abortChars) { private Object value(String name, String type, String text) { return switch (type) { - case "JULONG" -> parseLong(name, text); + case "INT" -> parseLong(name, text); case "STRING", "STRING SET" -> text == null ? "" : text; case "BOOLEAN" -> parseBoolean(name, text); case "NANOTIME" -> parseNanotime(name, text); case "MEMORY SIZE" -> parseMemorySize(name, text); + case "FILE" -> text == null ? "" : parseFilename(text); default -> throw new InternalError("Unknown type: " + type); }; } + /** + * Expands filename arguments replacing '%p' with the PID + * and '%t' with the time in 'yyyy_MM_dd_HH_mm_ss' format. + * @param filename a filename to be expanded + * @return filename with expanded arguments + */ + private String parseFilename(String filename) { + if (filename == null || filename.indexOf('%') == -1) { + return filename; + } + + String pid = null; + String time = null; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < filename.length(); i++) { + char c = filename.charAt(i); + if (c == '%' && i < filename.length() - 1) { + char nc = filename.charAt(i + 1); + if (nc == '%') { // %% ==> % + sb.append('%'); + i++; + } else if (nc == 'p') { + if (pid == null) { + pid = JVM.getPid(); + } + sb.append(pid); + i++; + } else if (nc == 't') { + if (time == null) { + time = ValueFormatter.formatDateTime(LocalDateTime.now()); + } + sb.append(time); + i++; + } else { + sb.append('%'); + } + } else { + sb.append(c); + } + } + return sb.toString(); + } + private Long parseLong(String name, String text) { if (text == null) { throw new IllegalArgumentException("Parsing error long value: syntax error, value is null"); @@ -361,4 +409,4 @@ void checkSpelling(Set excludeSet) { } } } -} \ No newline at end of file +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java index 605eb4bc745d7..7f68ba7ee7951 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java @@ -55,7 +55,7 @@ final class DCmdDump extends AbstractDCmd { public void execute(ArgumentParser parser) throws DCmdException { parser.checkUnknownArguments(); String name = parser.getOption("name"); - String filename = expandFilename(parser.getOption("filename")); + String filename = parser.getOption("filename"); Long maxAge = parser.getOption("maxage"); Long maxSize = parser.getOption("maxsize"); String begin = parser.getOption("begin"); @@ -230,14 +230,14 @@ public String[] getHelp() { dumped. If no filename is given, a filename is generated from the PID and the current date. The filename may also be a directory in which case, the filename is generated from the PID and the current date in - the specified directory. (STRING, no default value) + the specified directory. (FILE, no default value) Note: If a filename is given, '%%p' in the filename will be replaced by the PID, and '%%t' will be replaced by the time in 'yyyy_MM_dd_HH_mm_ss' format. maxage (Optional) Length of time for dumping the flight recording data to a - file. (INTEGER followed by 's' for seconds 'm' for minutes or 'h' for + file. (INT followed by 's' for seconds 'm' for minutes or 'h' for hours, no default value) maxsize (Optional) Maximum size for the amount of data to dump from a flight @@ -284,7 +284,7 @@ public Argument[] getArgumentInfos() { "STRING", false, true, null, false), new Argument("filename", "Copy recording data to file, e.g. \\\"" + exampleFilename() + "\\\"", - "STRING", false, true, null, false), + "FILE", false, true, null, false), new Argument("maxage", "Maximum duration to dump, in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, true, null, false), diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdQuery.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdQuery.java index 4c296d8a78fa7..b2e9aecda3889 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdQuery.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdQuery.java @@ -141,7 +141,7 @@ private String getOptions() { Options: - maxage (Optional) Length of time for the query to span. (INTEGER followed by + maxage (Optional) Length of time for the query to span. (INT followed by 's' for seconds 'm' for minutes or 'h' for hours, no default value) maxsize (Optional) Maximum size for the query to span in bytes if one of @@ -154,7 +154,7 @@ private String getOptions() { verbose (Optional) Display additional information about the query execution. (BOOLEAN, false) - width (Optional) Maximum number of horizontal characters. (BOOLEAN, false)"""; + width (Optional) Maximum number of horizontal characters. (INT, default value is 100)"""; } @Override @@ -170,7 +170,7 @@ public Argument[] getArgumentInfos() { null, false), new Argument("verbose", "Display additional information about the query execution", "BOOLEAN", false, true, "false", false), - new Argument("width", "Maximum number of horizontal characters", "JULONG", false, true, "100", + new Argument("width", "Maximum number of horizontal characters", "INT", false, true, "100", false), }; } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java index d115b9f5b62dc..26c095541b6aa 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java @@ -80,7 +80,7 @@ public void execute(ArgumentParser parser) throws DCmdException { Long delay = parser.getOption("delay"); Long duration = parser.getOption("duration"); Boolean disk = parser.getOption("disk"); - String path = expandFilename(parser.getOption("filename")); + String path = parser.getOption("filename"); Long maxAge = parser.getOption("maxage"); Long maxSize = parser.getOption("maxsize"); Long flush = parser.getOption("flush-interval"); @@ -353,7 +353,7 @@ private static String helpTemplate() { Options: delay (Optional) Length of time to wait before starting to record - (INTEGER followed by 's' for seconds 'm' for minutes or h' for + (INT followed by 's' for seconds 'm' for minutes or h' for hours, 0s) disk (Optional) Flag for also writing the data to disk while recording @@ -368,7 +368,7 @@ Virtual Machine (JVM) shuts down. If set to 'true' and no value id-1-2021_09_14_09_00.jfr) (BOOLEAN, false) duration (Optional) Length of time to record. Note that 0s means forever - (INTEGER followed by 's' for seconds 'm' for minutes or 'h' for + (INT followed by 's' for seconds 'm' for minutes or 'h' for hours, 0s) filename (Optional) Name of the file to which the flight recording data is @@ -377,7 +377,7 @@ Virtual Machine (JVM) shuts down. If set to 'true' and no value placed in the directory where the process was started. The filename may also be a directory in which case, the filename is generated from the PID and the current date in the specified - directory. (STRING, no default value) + directory. (FILE, no default value) Note: If a filename is given, '%p' in the filename will be replaced by the PID, and '%t' will be replaced by the time in @@ -385,7 +385,7 @@ Virtual Machine (JVM) shuts down. If set to 'true' and no value maxage (Optional) Maximum time to keep the recorded data on disk. This parameter is valid only when the disk parameter is set to true. - Note 0s means forever. (INTEGER followed by 's' for seconds 'm' + Note 0s means forever. (INT followed by 's' for seconds 'm' for minutes or 'h' for hours, 0s) maxsize (Optional) Maximum size of the data to keep on disk in bytes if @@ -501,7 +501,7 @@ public Argument[] getArgumentInfos() { "BOOLEAN", false, true, "true", false), new Argument("filename", "Resulting recording filename, e.g. \\\"" + exampleFilename() + "\\\"", - "STRING", false, true, "hotspot-pid-xxxxx-id-y-YYYY_MM_dd_HH_mm_ss.jfr", false), + "FILE", false, true, "hotspot-pid-xxxxx-id-y-YYYY_MM_dd_HH_mm_ss.jfr", false), new Argument("maxage", "Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, true, "0", false), diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java index 92d2d28b4721a..5cf983645c65c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java @@ -44,7 +44,7 @@ final class DCmdStop extends AbstractDCmd { protected void execute(ArgumentParser parser) throws DCmdException { parser.checkUnknownArguments(); String name = parser.getOption("name"); - String filename = expandFilename(parser.getOption("filename")); + String filename = parser.getOption("filename"); try { Recording recording = findRecording(name); WriteableUserPath path = PrivateAccess.getInstance().getPlatformRecording(recording).getDestination(); @@ -80,7 +80,7 @@ public String[] getHelp() { filename (Optional) Name of the file to which the recording is written when the recording is stopped. If no path is provided, the data from the recording - is discarded. (STRING, no default value) + is discarded. (FILE, no default value) Note: If a path is given, '%%p' in the path will be replaced by the PID, and '%%t' will be replaced by the time in 'yyyy_MM_dd_HH_mm_ss' format. @@ -107,7 +107,7 @@ public Argument[] getArgumentInfos() { "STRING", true, true, null, false), new Argument("filename", "Copy recording data to file, e.g. \\\"" + exampleFilename() + "\\\"", - "STRING", false, true, null, false) + "FILE", false, true, null, false) }; } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdView.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdView.java index 1d6f6e3dd2398..8d1c45b64c989 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdView.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdView.java @@ -108,9 +108,9 @@ public String getOptions() { return """ Options: - cell-height (Optional) Maximum number of rows in a table cell. (INTEGER, no default value) + cell-height (Optional) Maximum number of rows in a table cell. (INT, no default value) - maxage (Optional) Length of time for the view to span. (INTEGER followed by + maxage (Optional) Length of time for the view to span. (INT followed by 's' for seconds 'm' for minutes or 'h' for hours, default value is 10m) maxsize (Optional) Maximum size for the view to span in bytes if one of @@ -127,7 +127,7 @@ public String getOptions() { See list below for available views. (STRING, no default value) width (Optional) The width of the view in characters - (INTEGER, no default value)"""; + (INT, default value is 100)"""; } public String getExamples() { @@ -136,7 +136,7 @@ public String getExamples() { $ jcmd JFR.view gc - $ jcmd JFR.view width=160 hot-methods $ jcmd JFR.view verbose=true allocation-by-class @@ -154,7 +154,7 @@ public Argument[] getArgumentInfos() { return new Argument[] { new Argument("cell-height", "Maximum heigth of a table cell", - "JULONG", false, true, "1", false), + "INT", false, true, "1", false), new Argument("maxage", "Maximum duration of data to view, in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit", "NANOTIME", false, true, "10m", false), @@ -172,7 +172,7 @@ public Argument[] getArgumentInfos() { "STRING", true, false, null, false), new Argument("width", "Maximum number of horizontal characters", - "JULONG", false, true, "100", false) + "INT", false, true, "100", false) }; } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java index 4edc90a97b7be..9b9e7ad1ce893 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java @@ -33,6 +33,8 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; + +import static java.lang.classfile.constantpool.PoolEntry.*; import static java.util.ResourceBundle.Control; import java.util.Set; import java.util.function.IntUnaryOperator; @@ -41,7 +43,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.lang.classfile.ClassFile.*; + import jdk.tools.jlink.internal.ResourcePrevisitor; import jdk.tools.jlink.internal.StringTable; import jdk.tools.jlink.plugin.ResourcePoolModule; @@ -300,18 +302,18 @@ private boolean stripUnsupportedLocales(byte[] bytes) { } case TAG_CLASS, TAG_STRING, - TAG_METHODTYPE, + TAG_METHOD_TYPE, TAG_MODULE, TAG_PACKAGE -> offset += 3; - case TAG_METHODHANDLE -> offset += 4; + case TAG_METHOD_HANDLE -> offset += 4; case TAG_INTEGER, TAG_FLOAT, TAG_FIELDREF, TAG_METHODREF, - TAG_INTERFACEMETHODREF, - TAG_NAMEANDTYPE, - TAG_CONSTANTDYNAMIC, - TAG_INVOKEDYNAMIC -> offset += 5; + TAG_INTERFACE_METHODREF, + TAG_NAME_AND_TYPE, + TAG_DYNAMIC, + TAG_INVOKE_DYNAMIC -> offset += 5; case TAG_LONG, TAG_DOUBLE -> {offset += 9; cpSlot++;} //additional slot for double and long entries default -> throw new IllegalArgumentException("Unknown constant pool entry: 0x" diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java index 6f8bc9b7779bf..71c509e7deafd 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/StringSharingPlugin.java @@ -51,6 +51,8 @@ import jdk.tools.jlink.internal.ResourcePrevisitor; import jdk.tools.jlink.internal.StringTable; +import static java.lang.classfile.constantpool.PoolEntry.*; + /** * * A Plugin that stores the image classes constant pool UTF_8 entries into the @@ -214,7 +216,7 @@ private byte[] optimize(ResourcePoolEntry resource, ResourcePoolBuilder resource int tag = stream.readUnsignedByte(); byte[] arr; switch (tag) { - case ClassFile.TAG_UTF8: { + case TAG_UTF8: { String original = stream.readUTF(); // 2 cases, a Descriptor or a simple String if (descriptorIndexes.contains(i)) { @@ -238,8 +240,8 @@ private byte[] optimize(ResourcePoolEntry resource, ResourcePoolBuilder resource break; } - case ClassFile.TAG_LONG: - case ClassFile.TAG_DOUBLE: + case TAG_LONG: + case TAG_DOUBLE: i++; default: { out.write(tag); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/I18N.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/I18N.java index bed20ec1190ea..2e37a5c91af32 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/I18N.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/I18N.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -24,36 +24,64 @@ */ package jdk.jpackage.internal; +import java.util.ArrayList; +import java.util.List; +import java.util.ListResourceBundle; +import java.util.Map; import jdk.internal.util.OperatingSystem; import java.util.ResourceBundle; +import static java.util.stream.Collectors.toMap; +import java.util.stream.Stream; class I18N { static String getString(String key) { - if (PLATFORM.containsKey(key)) { - return PLATFORM.getString(key); - } - return SHARED.getString(key); + return BUNDLE.getString(key); } - private static final ResourceBundle SHARED = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MainResources"); + private static class MultiResourceBundle extends ListResourceBundle { + + MultiResourceBundle(ResourceBundle... bundles) { + contents = Stream.of(bundles).map(bundle -> { + return bundle.keySet().stream().map(key -> { + return Map.entry(key, bundle.getObject(key)); + }); + }).flatMap(x -> x).collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (o, n) -> { + // Override old value with the new one + return n; + })).entrySet().stream().map(e -> { + return new Object[]{e.getKey(), e.getValue()}; + }).toArray(Object[][]::new); + } + + @Override + protected Object[][] getContents() { + return contents; + } + + private final Object[][] contents; + } - private static final ResourceBundle PLATFORM; + private static final MultiResourceBundle BUNDLE; static { + List bundleNames = new ArrayList<>(); + + bundleNames.add("jdk.jpackage.internal.resources.MainResources"); + if (OperatingSystem.isLinux()) { - PLATFORM = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.LinuxResources"); + bundleNames.add("jdk.jpackage.internal.resources.LinuxResources"); } else if (OperatingSystem.isWindows()) { - PLATFORM = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.WinResources"); + bundleNames.add("jdk.jpackage.internal.resources.WinResources"); + bundleNames.add("jdk.jpackage.internal.resources.WinResourcesNoL10N"); } else if (OperatingSystem.isMacOS()) { - PLATFORM = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); + bundleNames.add("jdk.jpackage.internal.resources.MacResources"); } else { throw new IllegalStateException("Unknown platform"); } + + BUNDLE = new MultiResourceBundle(bundleNames.stream().map(ResourceBundle::getBundle) + .toArray(ResourceBundle[]::new)); } } diff --git a/src/jdk.jpackage/share/native/common/Log.cpp b/src/jdk.jpackage/share/native/common/Log.cpp index 66154ee8247e2..8227bb1cce4dd 100644 --- a/src/jdk.jpackage/share/native/common/Log.cpp +++ b/src/jdk.jpackage/share/native/common/Log.cpp @@ -40,10 +40,6 @@ namespace { // variables are initialized if any. This will result in AV. To avoid such // use cases keep logging module free from static variables that require // initialization with functions called by CRT. - // - - // by default log everything - const Logger::LogLevel defaultLogLevel = Logger::LOG_TRACE; char defaultLogAppenderMemory[sizeof(StreamLogAppender)] = {}; diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties index 5843a750adea9..9e7504364d35a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties @@ -35,7 +35,6 @@ resource.setup-icon=setup dialog icon resource.post-app-image-script=script to run after application image is populated resource.post-msi-script=script to run after msi file for exe installer is created resource.wxl-file=WiX localization file -resource.wxl-file-name=MsiInstallerStrings_en.wxl resource.main-wix-file=Main WiX project file resource.overrides-wix-file=Overrides WiX project file resource.shortcutpromptdlg-wix-file=Shortcut prompt dialog WiX project file diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N.properties new file mode 100644 index 0000000000000..9bb545b942fe2 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N.properties @@ -0,0 +1,28 @@ +# +# 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. +# +# + + +resource.wxl-file-name=MsiInstallerStrings_en.wxl diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_de.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_de.properties new file mode 100644 index 0000000000000..aa3aebf8cc482 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_de.properties @@ -0,0 +1,28 @@ +# +# 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. +# +# + + +resource.wxl-file-name=MsiInstallerStrings_de.wxl diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_ja.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_ja.properties new file mode 100644 index 0000000000000..3162e29f4ca9c --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_ja.properties @@ -0,0 +1,28 @@ +# +# 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. +# +# + + +resource.wxl-file-name=MsiInstallerStrings_ja.wxl diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_zh_CN.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_zh_CN.properties new file mode 100644 index 0000000000000..cc96dba78dbdb --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResourcesNoL10N_zh_CN.properties @@ -0,0 +1,28 @@ +# +# 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. +# +# + + +resource.wxl-file-name=MsiInstallerStrings_zh_CN.wxl diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties index e0c15bfb8fba2..a7212d9640a7a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties @@ -35,7 +35,6 @@ resource.setup-icon=Symbol für Dialogfeld "Setup" resource.post-app-image-script=Auszuführendes Skript nach dem Auffüllen des Anwendungsimages resource.post-msi-script=Auszuführendes Skript nach dem Erstellen der MSI-Datei für das EXE-Installationsprogramm resource.wxl-file=WiX-Lokalisierungsdatei -resource.wxl-file-name=MsiInstallerStrings_de.wxl resource.main-wix-file=Haupt-WiX-Projektdatei resource.overrides-wix-file=Überschreibt WiX-Projektdatei resource.shortcutpromptdlg-wix-file=Dialogfeld für Verknüpfungs-Prompt der WiX-Projektdatei diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties index e0bcd18c81103..352aab7a4934b 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties @@ -35,7 +35,6 @@ resource.setup-icon=設定ダイアログ・アイコン resource.post-app-image-script=アプリケーション・イメージを移入した後に実行するスクリプト resource.post-msi-script=exeインストーラのmsiファイルが作成された後に実行するスクリプト resource.wxl-file=WiXローカリゼーション・ファイル -resource.wxl-file-name=MsiInstallerStrings_ja.wxl resource.main-wix-file=メインWiXプロジェクト・ファイル resource.overrides-wix-file=WiXプロジェクト・ファイルのオーバーライド resource.shortcutpromptdlg-wix-file=ショートカット・プロンプト・ダイアログWiXプロジェクト・ファイル diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties index e2b8bd37135f4..a8d4a4471d6d0 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties @@ -35,7 +35,6 @@ resource.setup-icon=设置对话框图标 resource.post-app-image-script=要在填充应用程序映像之后运行的脚本 resource.post-msi-script=在为 exe 安装程序创建 msi 文件之后要运行的脚本 resource.wxl-file=WiX 本地化文件 -resource.wxl-file-name=MsiInstallerStrings_zh_CN.wxl resource.main-wix-file=主 WiX 项目文件 resource.overrides-wix-file=覆盖 WiX 项目文件 resource.shortcutpromptdlg-wix-file=快捷方式提示对话框 WiX 项目文件 diff --git a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java index be84beb03bb46..eee0ea051d4fe 100644 --- a/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.java +++ b/src/jdk.management/share/classes/com/sun/management/internal/DiagnosticCommandImpl.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 @@ -317,7 +317,7 @@ private Descriptor commandDescriptor(Wrapper w) throws IllegalArgumentException for (DiagnosticCommandArgumentInfo arginfo : w.info.getArgumentsInfo()) { HashMap argmap = new HashMap<>(); argmap.put("dcmd.arg.name", arginfo.getName()); - argmap.put("dcmd.arg.type", arginfo.getType()); + argmap.put("dcmd.arg.type", sanitiseType(arginfo.getType())); argmap.put("dcmd.arg.description", arginfo.getDescription()); argmap.put("dcmd.arg.isMandatory", arginfo.isMandatory()); argmap.put("dcmd.arg.isMultiple", arginfo.isMultiple()); @@ -335,6 +335,22 @@ private Descriptor commandDescriptor(Wrapper w) throws IllegalArgumentException return new ImmutableDescriptor(map); } + // Type names that will be published in dcmd.arg.type: + private static final String [] publicTypes = new String [] { "INT", "STRING", "BOOLEAN", "STRING SET", "MEMORY SIZE", "NANOTIME" }; + + private static final String sanitiseType(String typeName) { + // For any typeName not in the set to be made public, return "STRING". + if (typeName == null) { + return null; + } + for (String t : publicTypes) { + if (typeName.equals(t)) { + return t; + } + } + return "STRING"; + } + private static final String notifName = "javax.management.Notification"; diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java index 0d5ca39c6f74b..3d55186bac882 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java @@ -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 @@ -46,11 +46,15 @@ import javax.naming.OperationNotSupportedException; import javax.naming.ServiceUnavailableException; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.HashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.IntStream; import jdk.internal.ref.CleanerFactory; import sun.security.jca.JCAUtil; @@ -95,7 +99,7 @@ public class DnsClient { private static final int DEFAULT_PORT = 53; private static final int TRANSACTION_ID_BOUND = 0x10000; - private static final int MIN_TIMEOUT = 50; // msec after which there are no retries. + private static final int MIN_TIMEOUT = 0; // msec after which there are no retries. private static final SecureRandom random = JCAUtil.getSecureRandom(); private InetAddress[] servers; private int[] serverPorts; @@ -223,20 +227,28 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, Exception caughtException = null; boolean[] doNotRetry = new boolean[servers.length]; - + // Holder for unfulfilled timeouts left for each server + AtomicLong[] unfulfilledUdpTimeouts = IntStream.range(0, servers.length) + .mapToObj(_ -> new AtomicLong()) + .toArray(AtomicLong[]::new); try { // // The UDP retry strategy is to try the 1st server, and then // each server in order. If no answer, double the timeout // and try each server again. // - for (int retry = 0; retry < retries; retry++) { - + for (int retry = 0; retry <= retries; retry++) { + boolean isLastRetry = retry == retries; // Try each name server. for (int i = 0; i < servers.length; i++) { if (doNotRetry[i]) { continue; } + // unfulfilledServerTimeout is always >= 0 + AtomicLong unfulfilledServerTimeout = unfulfilledUdpTimeouts[i]; + if (isLastRetry && unfulfilledServerTimeout.get() == 0) { + continue; + } // send the request packet and wait for a response. try { @@ -245,7 +257,7 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, } byte[] msg = doUdpQuery(pkt, servers[i], serverPorts[i], - retry, xid); + retry, xid, unfulfilledServerTimeout, isLastRetry); assert msg != null; Header hdr = new Header(msg, msg.length); @@ -259,7 +271,12 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, // Try each server, starting with the one that just // provided the truncated message. - int retryTimeout = (timeout * (1 << retry)); + long retryTimeout = Math.clamp( + timeout * (1L << (isLastRetry + ? retry - 1 + : retry)), + 0L, Integer.MAX_VALUE); + ; for (int j = 0; j < servers.length; j++) { int ij = (i + j) % servers.length; if (doNotRetry[ij]) { @@ -301,15 +318,23 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, if (debug) { dprint("Caught Exception:" + ex); } - if (caughtException == null) { + if (caughtException == null || servers.length == 1) { + // If there are several servers we continue trying with other + // servers, otherwise this exception will be reported caughtException = ex; + } else { + // Best reporting effort + caughtException.addSuppressed(ex); } doNotRetry[i] = true; } catch (IOException e) { if (debug) { dprint("Caught IOException:" + e); } - if (caughtException == null) { + if (caughtException instanceof CommunicationException ce) { + e.addSuppressed(ce); + caughtException = e; + } else if (caughtException == null) { caughtException = e; } } catch (ClosedSelectorException e) { @@ -327,8 +352,13 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, caughtException = e; } } catch (NamingException e) { - if (caughtException == null) { + if (caughtException == null || servers.length == 1) { + // If there are several servers we continue trying with other + // servers, otherwise this exception will be reported caughtException = e; + } else { + // Best reporting effort + caughtException.addSuppressed(e); } doNotRetry[i] = true; } @@ -339,8 +369,8 @@ ResourceRecords query(DnsName fqdn, int qclass, int qtype, reqs.remove(xid); // cleanup } - if (caughtException instanceof NamingException) { - throw (NamingException) caughtException; + if (caughtException instanceof NamingException ne) { + throw ne; } // A network timeout or other error occurred. NamingException ne = new CommunicationException("DNS error"); @@ -424,10 +454,32 @@ ResourceRecords queryZone(DnsName zone, int qclass, boolean recursion) * is enqueued with the corresponding xid in 'resps'. */ private byte[] doUdpQuery(Packet pkt, InetAddress server, - int port, int retry, int xid) + int port, int retry, int xid, + AtomicLong unfulfilledTimeout, + boolean unfulfilledOnly) throws IOException, NamingException { udpChannelLock.lock(); + + + // use 1L below to ensure conversion to long and avoid potential + // integer overflow (timeout is an int). + // no point in supporting timeout > Integer.MAX_VALUE, clamp if needed + // timeout remaining after successive 'blockingReceive()'. + long thisIterationTimeout = unfulfilledOnly + ? 0L + : Math.clamp(timeout * (1L << retry), 0L, Integer.MAX_VALUE); + + // Compensate with server's positive unfulfilled timeout. + // Calling method never supplies zero 'unfulfilledTimeout' when + // 'unfulfilledOnly' is 'true', therefore 'thisIterationTimeout' + // will always be a positive number, ie infinite timeout + // is not possible. + thisIterationTimeout += unfulfilledTimeout.get(); + + // Track left timeout for the current retry + long timeoutLeft = thisIterationTimeout; + long start = 0; try { try (DatagramChannel udpChannel = getDatagramChannel()) { ByteBuffer opkt = ByteBuffer.wrap(pkt.getData(), 0, pkt.length()); @@ -436,13 +488,11 @@ private byte[] doUdpQuery(Packet pkt, InetAddress server, // Packets may only be sent to or received from this server address InetSocketAddress target = new InetSocketAddress(server, port); udpChannel.connect(target); - int pktTimeout = (timeout * (1 << retry)); udpChannel.write(opkt); - // timeout remaining after successive 'blockingReceive()' - int timeoutLeft = pktTimeout; int cnt = 0; boolean gotData = false; + start = System.nanoTime(); do { // prepare for retry if (gotData) { @@ -456,9 +506,7 @@ private byte[] doUdpQuery(Packet pkt, InetAddress server, ") for:" + xid + " sock-timeout:" + timeoutLeft + " ms."); } - long start = System.currentTimeMillis(); - gotData = blockingReceive(udpChannel, ipkt, timeoutLeft); - long end = System.currentTimeMillis(); + gotData = blockingReceive(udpChannel, target, ipkt, timeoutLeft); assert gotData || ipkt.position() == 0; if (gotData && isMatchResponse(data, xid)) { return data; @@ -471,17 +519,23 @@ private byte[] doUdpQuery(Packet pkt, InetAddress server, return cachedMsg; } } - timeoutLeft = pktTimeout - ((int) (end - start)); + long elapsedMillis = TimeUnit.NANOSECONDS + .toMillis(System.nanoTime() - start); + timeoutLeft = thisIterationTimeout - elapsedMillis; } while (timeoutLeft > MIN_TIMEOUT); // no matching packets received within the timeout throw new SocketTimeoutException(); } } finally { + long carryoverTimeout = thisIterationTimeout - + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + unfulfilledTimeout.set(Math.max(0, carryoverTimeout)); udpChannelLock.unlock(); } } - boolean blockingReceive(DatagramChannel dc, ByteBuffer buffer, long timeout) throws IOException { + boolean blockingReceive(DatagramChannel dc, InetSocketAddress target, + ByteBuffer buffer, long timeout) throws IOException { boolean dataReceived = false; // The provided datagram channel will be used by the caller only to receive data after // it is put to non-blocking mode @@ -491,10 +545,15 @@ boolean blockingReceive(DatagramChannel dc, ByteBuffer buffer, long timeout) thr udpChannelSelector.select(timeout); var keys = udpChannelSelector.selectedKeys(); if (keys.contains(selectionKey) && selectionKey.isReadable()) { - dc.receive(buffer); - dataReceived = true; + int before = buffer.position(); + var senderAddress = dc.receive(buffer); + // Empty packets are ignored + dataReceived = target.equals(senderAddress) && buffer.position() > before; + } + // Avoid contention with Selector.close() if called by a clean-up thread + synchronized (keys) { + keys.clear(); } - keys.clear(); } finally { selectionKey.cancel(); // Flush the canceled key out of the selected key set @@ -750,14 +809,19 @@ class Tcp { private final Socket sock; private final java.io.InputStream in; final java.io.OutputStream out; - private int timeoutLeft; + private long timeoutLeft; - Tcp(InetAddress server, int port, int timeout) throws IOException { + Tcp(InetAddress server, int port, long timeout) throws IOException { sock = new Socket(); try { - long start = System.currentTimeMillis(); - sock.connect(new InetSocketAddress(server, port), timeout); - timeoutLeft = (int) (timeout - (System.currentTimeMillis() - start)); + long start = System.nanoTime(); + // It is safe to cast to int since the value is + // clamped by the caller + int intTimeout = (int) timeout; + sock.connect(new InetSocketAddress(server, port), intTimeout); + timeoutLeft = Duration.ofMillis(timeout) + .minus(Duration.ofNanos((System.nanoTime() - start))) + .toMillis(); if (timeoutLeft <= 0) throw new SocketTimeoutException(); @@ -785,14 +849,16 @@ private interface SocketReadOp { private int readWithTimeout(SocketReadOp reader) throws IOException { if (timeoutLeft <= 0) throw new SocketTimeoutException(); - - sock.setSoTimeout(timeoutLeft); - long start = System.currentTimeMillis(); + // It is safe to cast to int since the value is clamped + int intTimeout = (int) timeoutLeft; + sock.setSoTimeout(intTimeout); + long start = System.nanoTime(); try { return reader.read(); } finally { - timeoutLeft -= (int) (System.currentTimeMillis() - start); + timeoutLeft -= TimeUnit.NANOSECONDS.toMillis( + System.nanoTime() - start); } } diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java index 028108494af87..fe35a4979cc3b 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsContext.java @@ -176,13 +176,13 @@ public Object addToEnvironment(String propName, Object propVal) } else if (propName.equals(INIT_TIMEOUT)) { int val = Integer.parseInt((String) propVal); if (timeout != val) { - timeout = val; + timeout = Math.max(val, 0); resolver = null; } } else if (propName.equals(RETRIES)) { int val = Integer.parseInt((String) propVal); if (retries != val) { - retries = val; + retries = Math.clamp(val, 1, 30); resolver = null; } } @@ -257,11 +257,11 @@ private void initFromEnvironment() val = (String) environment.get(INIT_TIMEOUT); timeout = (val == null) ? DEFAULT_INIT_TIMEOUT - : Integer.parseInt(val); + : Math.max(Integer.parseInt(val), 0); val = (String) environment.get(RETRIES); retries = (val == null) ? DEFAULT_RETRIES - : Integer.parseInt(val); + : Math.clamp(Integer.parseInt(val), 1, 30); } private CT getLookupCT(String attrId) 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 24f26d4364b94..18016a07260fb 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 @@ -43,7 +43,7 @@ import sun.security.jgss.krb5.Krb5Util; import sun.security.krb5.Credentials; import sun.security.util.Debug; -import sun.security.util.HexDumpEncoder; + import static sun.security.util.ResourcesMgr.getAuthResourceString; /** @@ -769,15 +769,11 @@ private void attemptAuthentication(boolean getPasswdFromSharedState) if (debug != null) { debug.println("principal is " + principal); - HexDumpEncoder hd = new HexDumpEncoder(); if (ktab != null) { debug.println("Will use keytab"); } else if (storeKey) { for (int i = 0; i < encKeys.length; i++) { - debug.println("EncryptionKey: keyType=" + - encKeys[i].getEType() + - " keyBytes (hex dump)=" + - hd.encodeBuffer(encKeys[i].getBytes())); + debug.println(encKeys[i].toString()); } } } @@ -868,7 +864,7 @@ private void promptForPass(boolean getPasswdFromSharedState) } if (debug != null) { debug.println - ("password is " + new String(password)); + ("Get password from shared state"); } return; } diff --git a/test/hotspot/gtest/gc/shared/test_oopStorage.cpp b/test/hotspot/gtest/gc/shared/test_oopStorage.cpp index a4ac2fee66ba4..d55d6dbfc2320 100644 --- a/test/hotspot/gtest/gc/shared/test_oopStorage.cpp +++ b/test/hotspot/gtest/gc/shared/test_oopStorage.cpp @@ -93,6 +93,18 @@ class OopStorage::TestAccess : public AllStatic { static void block_array_set_block_count(ActiveArray* blocks, size_t count) { blocks->_block_count = count; } + + static const oop* get_block_pointer(const Block& block, unsigned index) { + return block.get_pointer(index); + } + + static Block* new_block(const OopStorage& owner) { + return Block::new_block(&owner); + } + + static void delete_block(const Block& block) { + Block::delete_block(block); + } }; typedef OopStorage::TestAccess TestAccess; @@ -518,24 +530,35 @@ TEST_VM_F(OopStorageTest, bulk_allocation) { } } -#ifndef DISABLE_GARBAGE_ALLOCATION_STATUS_TESTS -TEST_VM_F(OopStorageTest, invalid_pointer) { - { - char* mem = NEW_C_HEAP_ARRAY(char, 1000, mtInternal); - oop* ptr = reinterpret_cast(align_down(mem + 250, sizeof(oop))); - // Predicate returns false for some malloc'ed block. - EXPECT_EQ(OopStorage::INVALID_ENTRY, storage().allocation_status(ptr)); - FREE_C_HEAP_ARRAY(char, mem); - } +TEST_VM_F(OopStorageTest, invalid_malloc_pointer) { + char* mem = NEW_C_HEAP_ARRAY(char, 1000, mtInternal); + oop* ptr = reinterpret_cast(align_down(mem + 250, sizeof(oop))); + // Predicate returns false for some malloc'ed block. + EXPECT_EQ(OopStorage::INVALID_ENTRY, storage().allocation_status(ptr)); + FREE_C_HEAP_ARRAY(char, mem); +} - { - oop obj; - oop* ptr = &obj; - // Predicate returns false for some "random" location. - EXPECT_EQ(OopStorage::INVALID_ENTRY, storage().allocation_status(ptr)); - } +TEST_VM_F(OopStorageTest, invalid_random_pointer) { + oop obj; + oop* ptr = &obj; + // Predicate returns false for some "random" location. + EXPECT_EQ(OopStorage::INVALID_ENTRY, storage().allocation_status(ptr)); +} + +TEST_VM_F(OopStorageTest, invalid_block_pointer) { + // Allocate a block for storage, but don't insert it into the storage. This + // also tests the false positive case of block_for_ptr where we have a + // reference to storage at just the "right" place. + const OopBlock* block = TestAccess::new_block(storage()); + ASSERT_NE(block, NULL_BLOCK); + const oop* ptr = TestAccess::get_block_pointer(*block, 0); + EXPECT_EQ(OopStorage::INVALID_ENTRY, storage().allocation_status(ptr)); + TestAccess::delete_block(*block); +} + +TEST_VM_F(OopStorageTest, invalid_null_pointer) { + EXPECT_EQ(OopStorage::INVALID_ENTRY, storage().allocation_status(nullptr)); } -#endif // DISABLE_GARBAGE_ALLOCATION_STATUS_TESTS class OopStorageTest::CountingIterateClosure { public: diff --git a/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp b/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp index c8ada2574c0e7..947ff120d15fe 100644 --- a/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp +++ b/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp @@ -23,15 +23,21 @@ */ #include "precompiled.hpp" -#include "gc/shared/oopStorage.hpp" +#include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageSet.hpp" #include "memory/allocation.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/vmOperations.hpp" +#include "runtime/vmThread.hpp" #include "utilities/debug.hpp" #include "utilities/enumIterator.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "unittest.hpp" +using ::testing::HasSubstr; +using ::testing::Not; + class OopStorageSetTest : public ::testing::Test { protected: // Returns index of s in storages, or size if not found. @@ -83,6 +89,8 @@ class OopStorageSetTest : public ::testing::Test { EnumRange(), &OopStorageSet::fill_all); } + + class VM_PrintAtSafepoint; }; TEST_VM_F(OopStorageSetTest, strong_iteration) { @@ -96,3 +104,77 @@ TEST_VM_F(OopStorageSetTest, weak_iteration) { TEST_VM_F(OopStorageSetTest, all_iteration) { test_all_iteration(); } + +class OopStorageSetTest::VM_PrintAtSafepoint : public VM_GTestExecuteAtSafepoint { +private: + class PrintContainingClosure : public Closure { + public: + void do_oop(oop* addr) { + // Direct slot hit. + { + stringStream ss; + bool printed = OopStorageSet::print_containing(addr, &ss); + ASSERT_TRUE(printed); + ASSERT_THAT(ss.freeze(), HasSubstr("is a pointer")); + ASSERT_THAT(ss.freeze(), HasSubstr("into block")); + ASSERT_THAT(ss.freeze(), HasSubstr("in oop storage")); + ASSERT_THAT(ss.freeze(), Not(HasSubstr("(unaligned)"))); + } + + // Unaligned pointer to adjacent slot, should still be in oop storage range. + { + char* unaligned_addr = (char*)addr + 1; + stringStream ss; + bool printed = OopStorageSet::print_containing(unaligned_addr, &ss); + ASSERT_TRUE(printed); + ASSERT_THAT(ss.freeze(), HasSubstr("is a pointer")); + ASSERT_THAT(ss.freeze(), HasSubstr("into block")); + ASSERT_THAT(ss.freeze(), HasSubstr("in oop storage")); + ASSERT_THAT(ss.freeze(), HasSubstr("(unaligned)")); + } + } + }; + +public: + void doit() { + PrintContainingClosure cl; + for (OopStorage* storage : OopStorageSet::Range()) { + storage->oops_do(&cl); + } + } +}; + +TEST_VM_F(OopStorageSetTest, print_containing) { + // nullptrs print nothing + { + stringStream ss; + bool printed = OopStorageSet::print_containing(nullptr, &ss); + ASSERT_FALSE(printed); + EXPECT_STREQ("", ss.freeze()); + } + + // Goofy values print nothing: unaligned out of storage pointer. + { + stringStream ss; + bool printed = OopStorageSet::print_containing((char*)0x1, &ss); + ASSERT_FALSE(printed); + EXPECT_STREQ("", ss.freeze()); + } + + // Goofy values print nothing: aligned out of storage pointer. + { + stringStream ss; + bool printed = OopStorageSet::print_containing((char*)alignof(oop), &ss); + ASSERT_FALSE(printed); + EXPECT_STREQ("", ss.freeze()); + } + + // All slot addresses should print well. + { + VM_PrintAtSafepoint op; + { + ThreadInVMfromNative invm(JavaThread::current()); + VMThread::execute(&op); + } + } +} diff --git a/test/hotspot/gtest/nmt/test_arrayWithFreeList.cpp b/test/hotspot/gtest/nmt/test_arrayWithFreeList.cpp index a2110e9e22e87..e1b008505826e 100644 --- a/test/hotspot/gtest/nmt/test_arrayWithFreeList.cpp +++ b/test/hotspot/gtest/nmt/test_arrayWithFreeList.cpp @@ -74,7 +74,7 @@ struct LL { // That's a very fancy word that means that a templated type like Foo can be passed around like only Foo at first // and then be 'applied' to some E. Think of it like passing around a lambda or function pointer, but on a template level, // where Foo is a function that can be called on some type with the return type being Foo. -template class Allocator> +template class Allocator> struct LL2 { struct Node; using NodeAllocator = Allocator; diff --git a/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp b/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp index f735022ea2b66..84ed28589520a 100644 --- a/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023 SAP SE. All rights reserved. - * 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 @@ -33,10 +33,10 @@ #include "unittest.hpp" // Check NMT header for integrity, as well as expected type and size. -static void check_expected_malloc_header(const void* payload, MEMFLAGS type, size_t size) { +static void check_expected_malloc_header(const void* payload, MemTag mem_tag, size_t size) { const MallocHeader* hdr = MallocHeader::resolve_checked(payload); EXPECT_EQ(hdr->size(), size); - EXPECT_EQ(hdr->flags(), type); + EXPECT_EQ(hdr->mem_tag(), mem_tag); } // ASAN complains about allocating very large sizes diff --git a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp index 7f6000b1212fb..c054ed0e67624 100644 --- a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp @@ -42,9 +42,9 @@ static bool compare_limits(const malloclimit* a, const malloclimit* b) { static bool compare_sets(const MallocLimitSet* a, const MallocLimitSet* b) { if (compare_limits(a->global_limit(), b->global_limit())) { - for (int i = 0; i < mt_number_of_types; i++) { - if (!compare_limits(a->category_limit(NMTUtil::index_to_flag(i)), - b->category_limit(NMTUtil::index_to_flag(i)))) { + for (int i = 0; i < mt_number_of_tags; i++) { + if (!compare_limits(a->category_limit(NMTUtil::index_to_tag(i)), + b->category_limit(NMTUtil::index_to_tag(i)))) { return false; } } @@ -96,11 +96,11 @@ TEST(NMT, MallocLimitPerCategory) { TEST(NMT, MallocLimitCategoryEnumNames) { MallocLimitSet expected; stringStream option; - for (int i = 0; i < mt_number_of_types; i++) { - MEMFLAGS f = NMTUtil::index_to_flag(i); - if (f != MEMFLAGS::mtNone) { - expected.set_category_limit(f, (i + 1) * M, MallocLimitMode::trigger_fatal); - option.print("%s%s:%dM", (i > 0 ? "," : ""), NMTUtil::flag_to_enum_name(f), i + 1); + for (int i = 0; i < mt_number_of_tags; i++) { + MemTag mem_tag = NMTUtil::index_to_tag(i); + if (mem_tag != MemTag::mtNone) { + expected.set_category_limit(mem_tag, (i + 1) * M, MallocLimitMode::trigger_fatal); + option.print("%s%s:%dM", (i > 0 ? "," : ""), NMTUtil::tag_to_enum_name(mem_tag), i + 1); } } test(option.base(), expected); @@ -109,11 +109,11 @@ TEST(NMT, MallocLimitCategoryEnumNames) { TEST(NMT, MallocLimitAllCategoriesHaveHumanReadableNames) { MallocLimitSet expected; stringStream option; - for (int i = 0; i < mt_number_of_types; i++) { - MEMFLAGS f = NMTUtil::index_to_flag(i); - if (f != MEMFLAGS::mtNone) { - expected.set_category_limit(f, (i + 1) * M, MallocLimitMode::trigger_fatal); - option.print("%s%s:%dM", (i > 0 ? "," : ""), NMTUtil::flag_to_name(f), i + 1); + for (int i = 0; i < mt_number_of_tags; i++) { + MemTag mem_tag = NMTUtil::index_to_tag(i); + if (mem_tag != MemTag::mtNone) { + expected.set_category_limit(mem_tag, (i + 1) * M, MallocLimitMode::trigger_fatal); + option.print("%s%s:%dM", (i > 0 ? "," : ""), NMTUtil::tag_to_name(mem_tag), i + 1); } } test(option.base(), expected); diff --git a/test/hotspot/gtest/nmt/test_nmt_reserved_region.cpp b/test/hotspot/gtest/nmt/test_nmt_reserved_region.cpp index 0708ce5f3000a..b5f8899034675 100644 --- a/test/hotspot/gtest/nmt/test_nmt_reserved_region.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_reserved_region.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2023 SAP SE. All rights reserved. - * Copyright (c) 2023, 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 @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "nmt/nmtCommon.hpp" #include "nmt/memTracker.hpp" #include "nmt/virtualMemoryTracker.hpp" #include "runtime/os.hpp" @@ -33,12 +34,12 @@ TEST_VM(NMT, ReservedRegionCopy) { address dummy1 = (address)0x10000000; NativeCallStack stack1(&dummy1, 1); ReservedMemoryRegion region1(dummy1, os::vm_page_size(), stack1, mtThreadStack); - VirtualMemorySummary::record_reserved_memory(os::vm_page_size(), region1.flag()); + VirtualMemorySummary::record_reserved_memory(os::vm_page_size(), region1.mem_tag()); region1.add_committed_region(dummy1, os::vm_page_size(), stack1); address dummy2 = (address)0x20000000; NativeCallStack stack2(&dummy2, 1); ReservedMemoryRegion region2(dummy2, os::vm_page_size(), stack2, mtCode); - VirtualMemorySummary::record_reserved_memory(os::vm_page_size(), region2.flag()); + VirtualMemorySummary::record_reserved_memory(os::vm_page_size(), region2.mem_tag()); region2.add_committed_region(dummy2, os::vm_page_size(), stack2); region2 = region1; @@ -46,7 +47,7 @@ TEST_VM(NMT, ReservedRegionCopy) { CommittedRegionIterator itr = region2.iterate_committed_regions(); const CommittedMemoryRegion* rgn = itr.next(); ASSERT_EQ(rgn->base(), dummy1); // Now we should see dummy1 - ASSERT_EQ(region2.flag(), mtThreadStack); // Should be correct flag + ASSERT_EQ(region2.mem_tag(), mtThreadStack); // Should be correct memory tag ASSERT_EQ(region2.call_stack()->get_frame(0), dummy1); // Check the stack rgn = itr.next(); ASSERT_EQ(rgn, (const CommittedMemoryRegion*)nullptr); // and nothing else diff --git a/test/hotspot/gtest/nmt/test_nmt_totals.cpp b/test/hotspot/gtest/nmt/test_nmt_totals.cpp index bf2c1397e7def..01ffc7c0ce3ac 100644 --- a/test/hotspot/gtest/nmt/test_nmt_totals.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_totals.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2022 SAP SE. All rights reserved. - * 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 @@ -89,8 +89,8 @@ TEST_VM(NMTNumbers, totals) { void* p[NUM_ALLOCS]; for (int i = 0; i < NUM_ALLOCS; i ++) { // spread over categories - int category = i % (mt_number_of_types - 1); - p[i] = NEW_C_HEAP_ARRAY(char, ALLOC_SIZE, (MEMFLAGS)category); + int category = i % (mt_number_of_tags - 1); + p[i] = NEW_C_HEAP_ARRAY(char, ALLOC_SIZE, (MemTag)category); } const totals_t t2 = get_totals(); diff --git a/test/hotspot/gtest/nmt/test_vmatree.cpp b/test/hotspot/gtest/nmt/test_vmatree.cpp index 1c1bc31b5b498..7a5a98b786305 100644 --- a/test/hotspot/gtest/nmt/test_vmatree.cpp +++ b/test/hotspot/gtest/nmt/test_vmatree.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "memory/allocation.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "nmt/nmtNativeCallStackStorage.hpp" #include "nmt/vmatree.hpp" #include "runtime/os.hpp" @@ -171,7 +171,6 @@ class NMTVMATreeTest : public testing::Test { }; - TEST_VM_F(NMTVMATreeTest, OverlappingReservationsResultInTwoNodes) { VMATree::RegionData rd{si[0], mtTest}; Tree tree; @@ -181,6 +180,23 @@ TEST_VM_F(NMTVMATreeTest, OverlappingReservationsResultInTwoNodes) { EXPECT_EQ(2, count_nodes(tree)); } +TEST_VM_F(NMTVMATreeTest, UseFlagInplace) { + Tree tree; + VMATree::RegionData rd1(si[0], mtTest); + VMATree::RegionData rd2(si[1], mtNone); + tree.reserve_mapping(0, 100, rd1); + tree.commit_mapping(20, 50, rd2, true); + tree.uncommit_mapping(30, 10, rd2); + tree.visit_in_order([&](Node* node) { + if (node->key() != 100) { + EXPECT_EQ(mtTest, node->val().out.mem_tag()) << "failed at: " << node->key(); + if (node->key() != 20 && node->key() != 40) { + EXPECT_EQ(VMATree::StateType::Reserved, node->val().out.type()); + } + } + }); +} + // Low-level tests inspecting the state of the tree. TEST_VM_F(NMTVMATreeTest, LowLevel) { adjacent_2_nodes(VMATree::empty_regiondata); @@ -214,7 +230,7 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { treap(tree).visit_in_order([&](Node* x) { EXPECT_TRUE(x->key() == 0 || x->key() == 100); if (x->key() == 0) { - EXPECT_EQ(x->val().out.regiondata().flag, mtTest); + EXPECT_EQ(x->val().out.regiondata().mem_tag, mtTest); } }); @@ -240,6 +256,7 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { EXPECT_EQ(nullptr, treap_root(tree)); } + { // A committed region inside of/replacing a reserved region // should replace the reserved region's metadata. Tree::RegionData rd{si[0], mtNMT}; @@ -249,10 +266,10 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { tree.commit_mapping(0, 100, rd2); treap(tree).visit_range_in_order(0, 99999, [&](Node* x) { if (x->key() == 0) { - EXPECT_EQ(mtTest, x->val().out.regiondata().flag); + EXPECT_EQ(mtTest, x->val().out.regiondata().mem_tag); } if (x->key() == 100) { - EXPECT_EQ(mtTest, x->val().in.regiondata().flag); + EXPECT_EQ(mtTest, x->val().in.regiondata().mem_tag); } }); } @@ -274,11 +291,11 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { Tree::RegionData rd2(NCS::StackIndex(), mtNMT); Tree tree; VMATree::SummaryDiff all_diff = tree.reserve_mapping(0, 100, rd); - VMATree::SingleDiff diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(100, diff.reserve); all_diff = tree.reserve_mapping(50, 25, rd2); - diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; - VMATree::SingleDiff diff2 = all_diff.flag[NMTUtil::flag_to_index(mtNMT)]; + diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; + VMATree::SingleDiff diff2 = all_diff.tag[NMTUtil::tag_to_index(mtNMT)]; EXPECT_EQ(-25, diff.reserve); EXPECT_EQ(25, diff2.reserve); } @@ -286,31 +303,31 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { Tree::RegionData rd(NCS::StackIndex(), mtTest); Tree tree; VMATree::SummaryDiff all_diff = tree.reserve_mapping(0, 100, rd); - VMATree::SingleDiff diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(100, diff.reserve); all_diff = tree.release_mapping(0, 100); - diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(-100, diff.reserve); } { // Convert some of a released mapping to a committed one Tree::RegionData rd(NCS::StackIndex(), mtTest); Tree tree; VMATree::SummaryDiff all_diff = tree.reserve_mapping(0, 100, rd); - VMATree::SingleDiff diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(diff.reserve, 100); all_diff = tree.commit_mapping(0, 100, rd); - diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(0, diff.reserve); EXPECT_EQ(100, diff.commit); } - { // Adjacent reserved mappings with same flag + { // Adjacent reserved mappings with same type Tree::RegionData rd(NCS::StackIndex(), mtTest); Tree tree; VMATree::SummaryDiff all_diff = tree.reserve_mapping(0, 100, rd); - VMATree::SingleDiff diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(diff.reserve, 100); all_diff = tree.reserve_mapping(100, 100, rd); - diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(100, diff.reserve); } { // Adjacent reserved mappings with different flags @@ -318,12 +335,12 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { Tree::RegionData rd2(NCS::StackIndex(), mtNMT); Tree tree; VMATree::SummaryDiff all_diff = tree.reserve_mapping(0, 100, rd); - VMATree::SingleDiff diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(diff.reserve, 100); all_diff = tree.reserve_mapping(100, 100, rd2); - diff = all_diff.flag[NMTUtil::flag_to_index(mtTest)]; + diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)]; EXPECT_EQ(0, diff.reserve); - diff = all_diff.flag[NMTUtil::flag_to_index(mtNMT)]; + diff = all_diff.tag[NMTUtil::tag_to_index(mtNMT)]; EXPECT_EQ(100, diff.reserve); } @@ -334,27 +351,27 @@ TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { tree.commit_mapping(128, 128, rd); tree.commit_mapping(512, 128, rd); VMATree::SummaryDiff diff = tree.commit_mapping(0, 1024, rd); - EXPECT_EQ(768, diff.flag[NMTUtil::flag_to_index(mtTest)].commit); - EXPECT_EQ(768, diff.flag[NMTUtil::flag_to_index(mtTest)].reserve); + EXPECT_EQ(768, diff.tag[NMTUtil::tag_to_index(mtTest)].commit); + EXPECT_EQ(768, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve); } } // Exceedingly simple tracker for page-granular allocations // Use it for testing consistency with VMATree. -struct SimpleVMATracker : public CHeapObj { + struct SimpleVMATracker : public CHeapObj { const size_t page_size = 4096; - enum Type { Reserved, Committed, Free }; + enum Kind { Reserved, Committed, Free }; struct Info { - Type type; - MEMFLAGS flag; + Kind kind; + MemTag mem_tag; NativeCallStack stack; - Info() : type(Free), flag(mtNone), stack() {} + Info() : kind(Free), mem_tag(mtNone), stack() {} - Info(Type type, NativeCallStack stack, MEMFLAGS flag) - : type(type), flag(flag), stack(stack) {} + Info(Kind kind, NativeCallStack stack, MemTag mem_tag) + : kind(kind), mem_tag(mem_tag), stack(stack) {} bool eq(Info other) { - return flag == other.flag && stack.equals(other.stack); + return kind == other.kind && stack.equals(other.stack); } }; // Page (4KiB) granular array @@ -368,7 +385,7 @@ struct SimpleVMATracker : public CHeapObj { } } - VMATree::SummaryDiff do_it(Type type, size_t start, size_t size, NativeCallStack stack, MEMFLAGS flag) { + VMATree::SummaryDiff do_it(Kind kind, size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) { assert(is_aligned(size, page_size) && is_aligned(start, page_size), "page alignment"); VMATree::SummaryDiff diff; @@ -377,23 +394,23 @@ struct SimpleVMATracker : public CHeapObj { const size_t end_idx = start_idx + page_count; assert(end_idx < SimpleVMATracker::num_pages, ""); - Info new_info(type, stack, flag); + Info new_info(kind, stack, mem_tag); for (size_t i = start_idx; i < end_idx; i++) { Info& old_info = pages[i]; // Register diff - if (old_info.type == Reserved) { - diff.flag[(int)old_info.flag].reserve -= page_size; - } else if (old_info.type == Committed) { - diff.flag[(int)old_info.flag].reserve -= page_size; - diff.flag[(int)old_info.flag].commit -= page_size; + if (old_info.kind == Reserved) { + diff.tag[(int)old_info.mem_tag].reserve -= page_size; + } else if (old_info.kind == Committed) { + diff.tag[(int)old_info.mem_tag].reserve -= page_size; + diff.tag[(int)old_info.mem_tag].commit -= page_size; } - if (type == Reserved) { - diff.flag[(int)new_info.flag].reserve += page_size; - } else if(type == Committed) { - diff.flag[(int)new_info.flag].reserve += page_size; - diff.flag[(int)new_info.flag].commit += page_size; + if (kind == Reserved) { + diff.tag[(int)new_info.mem_tag].reserve += page_size; + } else if (kind == Committed) { + diff.tag[(int)new_info.mem_tag].reserve += page_size; + diff.tag[(int)new_info.mem_tag].commit += page_size; } // Overwrite old one with new pages[i] = new_info; @@ -401,12 +418,12 @@ struct SimpleVMATracker : public CHeapObj { return diff; } - VMATree::SummaryDiff reserve(size_t start, size_t size, NativeCallStack stack, MEMFLAGS flag) { - return do_it(Reserved, start, size, stack, flag); + VMATree::SummaryDiff reserve(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) { + return do_it(Reserved, start, size, stack, mem_tag); } - VMATree::SummaryDiff commit(size_t start, size_t size, NativeCallStack stack, MEMFLAGS flag) { - return do_it(Committed, start, size, stack, flag); + VMATree::SummaryDiff commit(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) { + return do_it(Committed, start, size, stack, mem_tag); } VMATree::SummaryDiff release(size_t start, size_t size) { @@ -423,7 +440,7 @@ TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { const size_t page_size = tr->page_size; VMATree tree; NCS ncss(true); - constexpr const int candidates_len_flags = 4; + constexpr const int candidates_len_tags = 4; constexpr const int candidates_len_stacks = 2; NativeCallStack candidate_stacks[candidates_len_stacks] = { @@ -431,7 +448,7 @@ TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { make_stack(0xB), }; - const MEMFLAGS candidate_flags[candidates_len_flags] = { + const MemTag candidate_tags[candidates_len_tags] = { mtNMT, mtTest, }; @@ -455,30 +472,30 @@ TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { const size_t start = page_start * page_size; const size_t size = num_pages * page_size; - const MEMFLAGS flag = candidate_flags[os::random() % candidates_len_flags]; + const MemTag mem_tag = candidate_tags[os::random() % candidates_len_tags]; const NativeCallStack stack = candidate_stacks[os::random() % candidates_len_stacks]; const NCS::StackIndex si = ncss.push(stack); - VMATree::RegionData data(si, flag); + VMATree::RegionData data(si, mem_tag); - const SimpleVMATracker::Type type = (SimpleVMATracker::Type)(os::random() % 3); + const SimpleVMATracker::Kind kind = (SimpleVMATracker::Kind)(os::random() % 3); VMATree::SummaryDiff tree_diff; VMATree::SummaryDiff simple_diff; - if (type == SimpleVMATracker::Reserved) { - simple_diff = tr->reserve(start, size, stack, flag); + if (kind == SimpleVMATracker::Reserved) { + simple_diff = tr->reserve(start, size, stack, mem_tag); tree_diff = tree.reserve_mapping(start, size, data); - } else if (type == SimpleVMATracker::Committed) { - simple_diff = tr->commit(start, size, stack, flag); + } else if (kind == SimpleVMATracker::Committed) { + simple_diff = tr->commit(start, size, stack, mem_tag); tree_diff = tree.commit_mapping(start, size, data); } else { simple_diff = tr->release(start, size); tree_diff = tree.release_mapping(start, size); } - for (int j = 0; j < mt_number_of_types; j++) { - VMATree::SingleDiff td = tree_diff.flag[j]; - VMATree::SingleDiff sd = simple_diff.flag[j]; + for (int j = 0; j < mt_number_of_tags; j++) { + VMATree::SingleDiff td = tree_diff.tag[j]; + VMATree::SingleDiff sd = simple_diff.tag[j]; ASSERT_EQ(td.reserve, sd.reserve); ASSERT_EQ(td.commit, sd.commit); } @@ -489,7 +506,7 @@ TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { size_t j = 0; while (j < SimpleVMATracker::num_pages) { while (j < SimpleVMATracker::num_pages && - tr->pages[j].type == SimpleVMATracker::Free) { + tr->pages[j].kind == SimpleVMATracker::Free) { j++; } @@ -520,8 +537,8 @@ TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { ASSERT_TRUE(starti.stack.equals(start_stack)); ASSERT_TRUE(endi.stack.equals(end_stack)); - ASSERT_EQ(starti.flag, startn->val().out.flag()); - ASSERT_EQ(endi.flag, endn->val().in.flag()); + ASSERT_EQ(starti.mem_tag, startn->val().out.mem_tag()); + ASSERT_EQ(endi.mem_tag, endn->val().in.mem_tag()); } } } diff --git a/test/hotspot/gtest/runtime/test_os_linux.cpp b/test/hotspot/gtest/runtime/test_os_linux.cpp index 69c3d991b2a4f..77b83ac1bd74d 100644 --- a/test/hotspot/gtest/runtime/test_os_linux.cpp +++ b/test/hotspot/gtest/runtime/test_os_linux.cpp @@ -360,10 +360,10 @@ TEST_VM(os_linux, pretouch_thp_and_use_concurrent) { EXPECT_TRUE(os::commit_memory(heap, size, false)); { - auto pretouch = [heap, size](Thread*, int) { + auto pretouch = [&](Thread*, int) { os::pretouch_memory(heap, heap + size, os::vm_page_size()); }; - auto useMemory = [heap, size](Thread*, int) { + auto useMemory = [&](Thread*, int) { int* iptr = reinterpret_cast(heap); for (int i = 0; i < 1000; i++) *iptr++ = i; }; diff --git a/test/hotspot/gtest/runtime/test_os_windows.cpp b/test/hotspot/gtest/runtime/test_os_windows.cpp index d611b5287a9da..c7e99bcdbc5dd 100644 --- a/test/hotspot/gtest/runtime/test_os_windows.cpp +++ b/test/hotspot/gtest/runtime/test_os_windows.cpp @@ -722,6 +722,114 @@ TEST_VM(os_windows, processor_count) { } } +TEST_VM(os_windows, large_page_init_multiple_sizes) { + // Call request_lock_memory_privilege() and check the result + if (!os::win32::request_lock_memory_privilege()) { + GTEST_SKIP() << "Skipping test because lock memory privilege is not granted."; + } + // Set globals to make sure we hit the correct code path + AutoSaveRestore guardUseLargePages(UseLargePages); + AutoSaveRestore guardEnableAllLargePageSizesForWindows(EnableAllLargePageSizesForWindows); + AutoSaveRestore guardLargePageSizeInBytes(LargePageSizeInBytes); + FLAG_SET_CMDLINE(UseLargePages, true); + FLAG_SET_CMDLINE(EnableAllLargePageSizesForWindows, true); + + // Determine the minimum page size + const size_t min_size = GetLargePageMinimum(); + + // End the test if GetLargePageMinimum returns 0 + if (min_size == 0) { + GTEST_SKIP() << "Large pages are not supported on this system."; + return; + } + + // Set LargePageSizeInBytes to 4 times the minimum page size + FLAG_SET_CMDLINE(LargePageSizeInBytes, 4 * min_size); // Set a value for multiple page sizes + + // Initialize large page settings + os::large_page_init(); + + // Verify that large pages are enabled + EXPECT_TRUE(UseLargePages) << "UseLargePages should be true after initialization for LargePageSizeInBytes = 4 * min_size"; + + // Verify that decided_large_page_size is greater than the default page size + const size_t default_page_size = os::vm_page_size(); + size_t decided_large_page_size = os::win32::large_page_init_decide_size(); + EXPECT_GT(decided_large_page_size, default_page_size) << "Large page size should be greater than the default page size for LargePageSizeInBytes = 4 * min_size"; + +#if !defined(IA32) + size_t page_size_count = 0; + size_t page_size = os::page_sizes().largest(); + + do { + ++page_size_count; + page_size = os::page_sizes().next_smaller(page_size); + } while (page_size >= os::page_sizes().smallest()); + + EXPECT_GT(page_size_count, 1u) << "There should be multiple large page sizes available."; + + size_t large_page_size = decided_large_page_size; + + for (size_t page_size = os::page_sizes().largest(); page_size >= min_size; page_size = os::page_sizes().next_smaller(page_size)) { + EXPECT_TRUE(page_size % min_size == 0) << "Each page size should be a multiple of the minimum large page size."; + EXPECT_LE(page_size, large_page_size) << "Page size should not exceed the determined large page size."; + } +#endif +} + +TEST_VM(os_windows, large_page_init_decide_size) { + // Initial setup + // Call request_lock_memory_privilege() and check the result + if (!os::win32::request_lock_memory_privilege()) { + GTEST_SKIP() << "Skipping test because lock memory privilege is not granted."; + } + AutoSaveRestore guardUseLargePages(UseLargePages); + AutoSaveRestore guardLargePageSizeInBytes(LargePageSizeInBytes); + FLAG_SET_CMDLINE(UseLargePages, true); + FLAG_SET_CMDLINE(LargePageSizeInBytes, 0); // Reset to default + + // Test for large page support + size_t decided_size = os::win32::large_page_init_decide_size(); + size_t min_size = GetLargePageMinimum(); + if (min_size == 0) { + EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 when large page is not supported by the processor"; + return; + } + + // Scenario 1: Test with 2MB large page size + if (min_size == 2 * M) { + FLAG_SET_CMDLINE(LargePageSizeInBytes, 2 * M); // Set large page size to 2MB + decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size + EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page and OS reported size are both 2M"; + } + + // Scenario 2: Test with 1MB large page size + if (min_size == 2 * M) { + FLAG_SET_CMDLINE(LargePageSizeInBytes, 1 * M); // Set large page size to 1MB + decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size + EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page is 1M and OS reported size is 2M"; + } + +#if defined(IA32) || defined(AMD64) + FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * M); // Set large page size to 5MB + if (!EnableAllLargePageSizesForWindows) { + decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size + EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 for large pages bigger than 4mb on IA32 or AMD64"; + } +#endif + + // Additional check for non-multiple of minimum size + // Set an arbitrary large page size which is not a multiple of min_size + FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * min_size + 1); + + // Recalculate decided size + decided_size = os::win32::large_page_init_decide_size(); + + // Assert that the decided size defaults to minimum page size when LargePageSizeInBytes + // is not a multiple of the minimum size, assuming conditions are always met + EXPECT_EQ(decided_size, 0) << "Expected decided size to default to 0 when LargePageSizeInBytes is not a multiple of minimum size"; +} + class ReserveMemorySpecialRunnable : public TestRunnable { public: void runUnitTest() const { diff --git a/test/hotspot/gtest/utilities/test_growableArray.cpp b/test/hotspot/gtest/utilities/test_growableArray.cpp index 74eb354cb2ee2..cc8d49cd6b57f 100644 --- a/test/hotspot/gtest/utilities/test_growableArray.cpp +++ b/test/hotspot/gtest/utilities/test_growableArray.cpp @@ -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 @@ -35,8 +35,8 @@ struct WithEmbeddedArray { // Arena allocated data array WithEmbeddedArray(Arena* arena, int initial_max) : _a(arena, initial_max, 0, 0) {} // CHeap allocated data array - WithEmbeddedArray(int initial_max, MEMFLAGS memflags) : _a(initial_max, memflags) { - assert(memflags != mtNone, "test requirement"); + WithEmbeddedArray(int initial_max, MemTag mem_tag) : _a(initial_max, mem_tag) { + assert(mem_tag != mtNone, "test requirement"); } WithEmbeddedArray(const GrowableArray& other) : _a(other) {} }; diff --git a/test/hotspot/gtest/utilities/test_resourceHash.cpp b/test/hotspot/gtest/utilities/test_resourceHash.cpp index 9124f4b977ccf..e9834ef6e2e0e 100644 --- a/test/hotspot/gtest/utilities/test_resourceHash.cpp +++ b/test/hotspot/gtest/utilities/test_resourceHash.cpp @@ -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 @@ -23,6 +23,7 @@ #include "precompiled.hpp" #include "classfile/symbolTable.hpp" +#include "nmt/nmtCommon.hpp" #include "memory/allocation.hpp" #include "memory/resourceArea.hpp" #include "oops/symbolHandle.hpp" @@ -35,7 +36,7 @@ class CommonResourceHashtableTest : public ::testing::Test { protected: typedef void* K; typedef uintx V; - const static MEMFLAGS MEM_TYPE = mtInternal; + const static MemTag MEM_TAG = mtInternal; static unsigned identity_hash(const K& k) { return (unsigned) (uintptr_t) k; @@ -93,7 +94,7 @@ class SmallResourceHashtableTest : public CommonResourceHashtableTest { static void test(V step) { EqualityTestIter et; - ResourceHashtable rh; + ResourceHashtable rh; ASSERT_FALSE(rh.contains(as_K(step))); @@ -225,7 +226,7 @@ class GenericResourceHashtableTest : public CommonResourceHashtableTest { static void test(unsigned num_elements = SIZE) { EqualityTestIter et; - ResourceHashtable rh; + ResourceHashtable rh; for (uintptr_t i = 0; i < num_elements; ++i) { ASSERT_TRUE(rh.put(as_K(i), i)); diff --git a/test/hotspot/gtest/utilities/test_utf8.cpp b/test/hotspot/gtest/utilities/test_utf8.cpp index 80f6671207bbd..1636862f7675b 100644 --- a/test/hotspot/gtest/utilities/test_utf8.cpp +++ b/test/hotspot/gtest/utilities/test_utf8.cpp @@ -22,7 +22,7 @@ */ #include "precompiled.hpp" -#include "nmt/memflags.hpp" +#include "nmt/memTag.hpp" #include "runtime/os.hpp" #include "utilities/utf8.hpp" #include "unittest.hpp" diff --git a/test/hotspot/jtreg/ProblemList-Xcomp.txt b/test/hotspot/jtreg/ProblemList-Xcomp.txt index 384374b4f76ce..2e9b6da3828ec 100644 --- a/test/hotspot/jtreg/ProblemList-Xcomp.txt +++ b/test/hotspot/jtreg/ProblemList-Xcomp.txt @@ -39,9 +39,11 @@ serviceability/jvmti/vthread/SuspendWithInterruptLock/SuspendWithInterruptLock.j serviceability/sa/ClhsdbInspect.java 8283578 windows-x64 -vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_a/TestDescription.java 8308367 windows-x64 -vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manySame_a/TestDescription.java 8308367 windows-x64 -vmTestbase/vm/mlvm/indy/func/jvmti/redefineClassInTarget/TestDescription.java 8308367 windows-x64 +vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_a/TestDescription.java 8308367 generic-all +vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manySame_a/TestDescription.java 8308367 generic-all +vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2none_b/TestDescription.java 8308367 generic-all +vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_none2indy_b/TestDescription.java 8308367 generic-all +vmTestbase/vm/mlvm/indy/func/jvmti/redefineClassInTarget/TestDescription.java 8308367 generic-all vmTestbase/nsk/jdi/StepRequest/addClassFilter_rt/filter_rt001/TestDescription.java 8043571 generic-all vmTestbase/nsk/jdi/StepRequest/addClassFilter_rt/filter_rt003/TestDescription.java 8043571 generic-all @@ -50,6 +52,10 @@ vmTestbase/nsk/jvmti/scenarios/capability/CM03/cm03t001/TestDescription.java 829 vmTestbase/nsk/stress/thread/thread006.java 8321476 linux-all +compiler/cha/TypeProfileFinalMethod.java 8341039 generic-all + gc/arguments/TestNewSizeFlags.java 8299116 macosx-aarch64 +runtime/cds/appcds/DumpRuntimeClassesTest.java 8341452 generic-all + runtime/condy/escapeAnalysis/TestEscapeCondy.java 8339694 generic-all diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 2086bc2ed054b..3ff450dc3ad92 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -53,7 +53,6 @@ compiler/loopopts/TestUnreachableInnerLoop.java 8288981 linux-s390x compiler/c2/Test8004741.java 8235801 generic-all compiler/c2/irTests/TestDuplicateBackedge.java 8318904 generic-all -compiler/c2/irTests/TestIfMinMax.java 8339220 linux-s390x compiler/codecache/jmx/PoolsIndependenceTest.java 8264632 macosx-all compiler/codecache/CheckLargePages.java 8332654 linux-x64 @@ -115,7 +114,6 @@ runtime/os/TestTracePageSizes.java#Serial 8267460 linux-aarch64 runtime/ErrorHandling/CreateCoredumpOnCrash.java 8267433 macosx-x64 runtime/StackGuardPages/TestStackGuardPagesNative.java 8303612 linux-all runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le -runtime/Thread/TestAlwaysPreTouchStacks.java 8335167 macosx-aarch64 runtime/cds/appcds/customLoader/HelloCustom_JFR.java 8241075 linux-all,windows-x64 applications/jcstress/copy.java 8229852 linux-all @@ -123,6 +121,7 @@ applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJcmd.java 8278102 linux-all containers/docker/TestMemoryAwareness.java 8303470 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 +containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 ############################################################################# @@ -136,6 +135,8 @@ serviceability/jvmti/vthread/GetThreadStateMountedTest/GetThreadStateMountedTest serviceability/jvmti/vthread/GetSetLocalTest/GetSetLocalTest.java 8286836 generic-all serviceability/jvmti/vthread/CarrierThreadEventNotification/CarrierThreadEventNotification.java 8333681 generic-all serviceability/dcmd/gc/RunFinalizationTest.java 8227120 generic-all +serviceability/dcmd/vm/SystemDumpMapTest.java 8340401 windows-all +serviceability/dcmd/vm/SystemMapTest.java 8340401 windows-all serviceability/sa/ClhsdbCDSCore.java 8267433,8318754 macosx-x64,macosx-aarch64 serviceability/sa/ClhsdbFindPC.java#xcomp-core 8267433,8318754 macosx-x64,macosx-aarch64 @@ -172,6 +173,7 @@ vmTestbase/nsk/jvmti/scenarios/capability/CM03/cm03t001/TestDescription.java 807 vmTestbase/nsk/jvmti/InterruptThread/intrpthrd003/TestDescription.java 8288911 macosx-all vmTestbase/gc/lock/jni/jnilock002/TestDescription.java 8192647 generic-all +vmTestbase/gc/memory/Nio/Nio.java 8340728 generic-all vmTestbase/jit/escape/LockCoarsening/LockCoarsening001.java 8148743 generic-all vmTestbase/jit/escape/LockCoarsening/LockCoarsening002.java 8208259 generic-all diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 74540b6ad45dc..962fc36838c37 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -81,12 +81,12 @@ requires.properties= \ vm.jvmti \ vm.graal.enabled \ jdk.hasLibgraal \ - vm.libgraal.enabled \ + vm.libgraal.jit \ vm.compiler1.enabled \ vm.compiler2.enabled \ vm.musl \ vm.flagless \ - docker.support \ + container.support \ systemd.support \ jdk.containerized diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index 101cbe76afdda..f54dc79fcfc8f 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -435,8 +435,11 @@ hotspot_cds_only = \ hotspot_appcds_dynamic = \ runtime/cds/appcds/ \ -runtime/cds/appcds/cacheObject \ + -runtime/cds/appcds/complexURI \ -runtime/cds/appcds/customLoader \ -runtime/cds/appcds/dynamicArchive \ + -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java \ + -runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java \ -runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java \ -runtime/cds/appcds/javaldr/ArrayTest.java \ -runtime/cds/appcds/javaldr/ExceptionDuringDumpAtObjectsInitPhase.java \ @@ -453,6 +456,7 @@ hotspot_appcds_dynamic = \ -runtime/cds/appcds/BadBSM.java \ -runtime/cds/appcds/DumpClassList.java \ -runtime/cds/appcds/DumpClassListWithLF.java \ + -runtime/cds/appcds/DumpRuntimeClassesTest.java \ -runtime/cds/appcds/DumpingWithNoCoops.java \ -runtime/cds/appcds/ExtraSymbols.java \ -runtime/cds/appcds/LambdaContainsOldInf.java \ diff --git a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java index 544f84c9b2726..ccf4822e6769b 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java +++ b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java @@ -24,7 +24,7 @@ /* * @test * @key stress randomness - * @bug 8252219 8256535 8317349 + * @bug 8252219 8256535 8317349 8319879 8335334 * @requires vm.compiler2.enabled * @summary Tests that different combinations of stress options and * -XX:StressSeed=N are accepted. @@ -48,6 +48,14 @@ * compiler.arguments.TestStressOptions * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressMacroExpansion -XX:StressSeed=42 * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressIncrementalInlining + * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressIncrementalInlining -XX:StressSeed=42 + * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressUnstableIfTraps + * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressUnstableIfTraps -XX:StressSeed=42 + * compiler.arguments.TestStressOptions */ package compiler.arguments; diff --git a/test/hotspot/jtreg/compiler/blackhole/BlackholeExperimentalUnlockTest.java b/test/hotspot/jtreg/compiler/blackhole/BlackholeExperimentalUnlockTest.java index 6597b2186f266..0a403a784506f 100644 --- a/test/hotspot/jtreg/compiler/blackhole/BlackholeExperimentalUnlockTest.java +++ b/test/hotspot/jtreg/compiler/blackhole/BlackholeExperimentalUnlockTest.java @@ -25,6 +25,7 @@ * @test * @library /test/lib / * @requires vm.flagless + * @requires ! vm.opt.final.UnlockExperimentalVMOptions * @requires vm.compMode != "Xint" * @run driver compiler.blackhole.BlackholeExperimentalUnlockTest */ diff --git a/test/hotspot/jtreg/compiler/c2/TestCallDevirtualizationWithInfiniteLoop.java b/test/hotspot/jtreg/compiler/c2/TestCallDevirtualizationWithInfiniteLoop.java new file mode 100644 index 0000000000000..1afbe8d94653e --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestCallDevirtualizationWithInfiniteLoop.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. + * + * 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 8336726 + * @summary Test that post-parse call devirtualization works when call does not have an IO projection. + * @run main/othervm -XX:-TieredCompilation -Xcomp -XX:CompileCommand=compileonly,TestCallDevirtualizationWithInfiniteLoop::test + * TestCallDevirtualizationWithInfiniteLoop + */ + +public class TestCallDevirtualizationWithInfiniteLoop { + + static interface I { + public void method(); + } + + static final class A implements I { + @Override + public void method() { }; + } + + static final class B implements I { + @Override + public void method() { }; + } + + static final A a = new A(); + static final B b = new B(); + + public static void test(boolean flag) { + // Avoid executing endless loop + if (flag) { + return; + } + + // We only know after loop opts that the receiver type is B. + I recv = a; + for (int i = 0; i < 3; ++i) { + if (i > 1) { + recv = b; + } + } + // Post-parse call devirtualization will then convert below + // virtual call to a static call. + recv.method(); + + // Endless loop which does not use IO. As a result the IO + // projection of the call is removed unexpectedly. + while (true) { } + } + + public static void main(String[] args) { + test(true); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestSerialAdditions.java b/test/hotspot/jtreg/compiler/c2/TestSerialAdditions.java new file mode 100644 index 0000000000000..c52f17dd9757b --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestSerialAdditions.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2024 Red Hat and/or its affiliates. All rights reserved. + * 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 compiler.c2; + +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +import java.util.Random; + +/* + * @test + * @bug 8325495 + * @summary C2 should optimize for series of Add of unique value. e.g., a + a + ... + a => a*n + * @library /test/lib / + * @run driver compiler.c2.TestSerialAdditions + */ +public class TestSerialAdditions { + private static final Random RNG = Utils.getRandomInstance(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Run(test = { + "addTo2", + "addTo3", + "addTo4", + "shiftAndAddTo4", + "mulAndAddTo4", + "addTo5", + "addTo6", + "addTo7", + "addTo8", + "addTo16", + "addAndShiftTo16", + "addTo42", + "mulAndAddTo42", + "mulAndAddToMax", + "mulAndAddToOverflow", + "mulAndAddToZero", + "mulAndAddToMinus1", + "mulAndAddToMinus42" + }) + private void runIntTests() { + for (int a : new int[] { 0, 1, Integer.MIN_VALUE, Integer.MAX_VALUE, RNG.nextInt() }) { + Asserts.assertEQ(a * 2, addTo2(a)); + Asserts.assertEQ(a * 3, addTo3(a)); + Asserts.assertEQ(a * 4, addTo4(a)); + Asserts.assertEQ(a * 4, shiftAndAddTo4(a)); + Asserts.assertEQ(a * 4, mulAndAddTo4(a)); + Asserts.assertEQ(a * 5, addTo5(a)); + Asserts.assertEQ(a * 6, addTo6(a)); + Asserts.assertEQ(a * 7, addTo7(a)); + Asserts.assertEQ(a * 8, addTo8(a)); + Asserts.assertEQ(a * 16, addTo16(a)); + Asserts.assertEQ(a * 16, addAndShiftTo16(a)); + Asserts.assertEQ(a * 42, addTo42(a)); + Asserts.assertEQ(a * 42, mulAndAddTo42(a)); + Asserts.assertEQ(a * Integer.MAX_VALUE, mulAndAddToMax(a)); + Asserts.assertEQ(a * Integer.MIN_VALUE, mulAndAddToOverflow(a)); + Asserts.assertEQ(0, mulAndAddToZero(a)); + Asserts.assertEQ(a * -1, mulAndAddToMinus1(a)); + Asserts.assertEQ(a * -42, mulAndAddToMinus42(a)); + } + } + + @Run(test = { + "mulAndAddToIntOverflowL", + "mulAndAddToMaxL", + "mulAndAddToOverflowL" + }) + private void runLongTests() { + for (long a : new long[] { 0, 1, Long.MIN_VALUE, Long.MAX_VALUE, RNG.nextLong() }) { + Asserts.assertEQ(a * (Integer.MAX_VALUE + 1L), mulAndAddToIntOverflowL(a)); + Asserts.assertEQ(a * Long.MAX_VALUE, mulAndAddToMaxL(a)); + Asserts.assertEQ(a * Long.MIN_VALUE, mulAndAddToOverflowL(a)); + } + } + + // ----- integer tests ----- + @Test + @IR(counts = { IRNode.ADD_I, "1" }) + @IR(failOn = IRNode.LSHIFT_I) + private static int addTo2(int a) { + return a + a; // Simple additions like a + a should be kept as-is + } + + @Test + @IR(counts = { IRNode.ADD_I, "1", IRNode.LSHIFT_I, "1" }) + private static int addTo3(int a) { + return a + a + a; // a*3 => (a<<1) + a + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1" }) + private static int addTo4(int a) { + return a + a + a + a; // a*4 => a<<2 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1" }) + private static int shiftAndAddTo4(int a) { + return (a << 1) + a + a; // a*2 + a + a => a*3 + a => a*4 => a<<2 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1" }) + private static int mulAndAddTo4(int a) { + return a * 3 + a; // a*4 => a<<2 + } + + @Test + @IR(counts = { IRNode.ADD_I, "1", IRNode.LSHIFT_I, "1" }) + private static int addTo5(int a) { + return a + a + a + a + a; // a*5 => (a<<2) + a + } + + @Test + @IR(counts = { IRNode.ADD_I, "1", IRNode.LSHIFT_I, "2" }) + private static int addTo6(int a) { + return a + a + a + a + a + a; // a*6 => (a<<1) + (a<<2) + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1", IRNode.SUB_I, "1" }) + private static int addTo7(int a) { + return a + a + a + a + a + a + a; // a*7 => (a<<3) - a + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1" }) + private static int addTo8(int a) { + return a + a + a + a + a + a + a + a; // a*8 => a<<3 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1" }) + private static int addTo16(int a) { + return a + a + a + a + a + a + a + a + a + a + + a + a + a + a + a + a; // a*16 => a<<4 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1" }) + private static int addAndShiftTo16(int a) { + return (a + a) << 3; // a<<(3 + 1) => a<<4 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.MUL_I, "1" }) + private static int addTo42(int a) { + return a + a + a + a + a + a + a + a + a + a + + a + a + a + a + a + a + a + a + a + a + + a + a + a + a + a + a + a + a + a + a + + a + a + a + a + a + a + a + a + a + a + + a + a; // a*42 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.MUL_I, "1" }) + private static int mulAndAddTo42(int a) { + return a * 40 + a + a; // a*41 + a => a*42 + } + + private static final int INT_MAX_MINUS_ONE = Integer.MAX_VALUE - 1; + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1", IRNode.SUB_I, "1" }) + private static int mulAndAddToMax(int a) { + return a * INT_MAX_MINUS_ONE + a; // a*MAX => a*(MIN-1) => a*MIN - a => (a<<31) - a + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1" }) + private static int mulAndAddToOverflow(int a) { + return a * Integer.MAX_VALUE + a; // a*(MAX+1) => a*(MIN) => a<<31 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.CON_I, "1" }) + private static int mulAndAddToZero(int a) { + return a * -1 + a; // 0 + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.LSHIFT_I, "1", IRNode.SUB_I, "1" }) + private static int mulAndAddToMinus1(int a) { + return a * -2 + a; // a*-1 => a - (a<<1) + } + + @Test + @IR(failOn = IRNode.ADD_I) + @IR(counts = { IRNode.MUL_I, "1" }) + private static int mulAndAddToMinus42(int a) { + return a * -43 + a; // a*-42 + } + + // --- long tests --- + @Test + @IR(failOn = IRNode.ADD_L) + @IR(counts = { IRNode.LSHIFT_L, "1" }) + private static long mulAndAddToIntOverflowL(long a) { + return a * Integer.MAX_VALUE + a; // a*(INT_MAX+1) + } + + private static final long LONG_MAX_MINUS_ONE = Long.MAX_VALUE - 1; + + @Test + @IR(failOn = IRNode.ADD_L) + @IR(counts = { IRNode.LSHIFT_L, "1", IRNode.SUB_L, "1" }) + private static long mulAndAddToMaxL(long a) { + return a * LONG_MAX_MINUS_ONE + a; // a*MAX => a*(MIN-1) => a*MIN - 1 => (a<<63) - 1 + } + + @Test + @IR(failOn = IRNode.ADD_L) + @IR(counts = { IRNode.LSHIFT_L, "1" }) + private static long mulAndAddToOverflowL(long a) { + return a * Long.MAX_VALUE + a; // a*(MAX+1) => a*(MIN) => a<<63 + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestUnalignedAccess.java b/test/hotspot/jtreg/compiler/c2/TestUnalignedAccess.java index 033ea49e60955..d05dbad4a73ba 100644 --- a/test/hotspot/jtreg/compiler/c2/TestUnalignedAccess.java +++ b/test/hotspot/jtreg/compiler/c2/TestUnalignedAccess.java @@ -46,11 +46,20 @@ public class TestUnalignedAccess { static final Unsafe UNSAFE = Unsafe.getUnsafe(); static void sink(int x) {} + public static long lseed = 1; + public static int iseed = 2; + public static short sseed = 3; + public static byte bseed = 4; + public static long lres = lseed; + public static int ires = iseed; + public static short sres = sseed; + public static byte bres = bseed; + public static class TestLong { private static final byte[] BYTES = new byte[LEN]; private static final long rawdata = 0xbeef; - private static final long lseed = 1; + private static final long data; static { sink(2); @@ -60,10 +69,13 @@ public static class TestLong { // 1030 can't be encoded as "base + offset" mode into the instruction field. UNSAFE.putLongUnaligned(BYTES, 1030, rawdata); + lres += UNSAFE.getLongUnaligned(BYTES, 1030); // 127 can be encoded into simm9 field. - UNSAFE.putLongUnaligned(BYTES, 127, rawdata+lseed); + UNSAFE.putLongUnaligned(BYTES, 127, lres); + lres += UNSAFE.getLongUnaligned(BYTES, 127); // 1096 can be encoded into uimm12 field. - UNSAFE.putLongUnaligned(BYTES, 1096, rawdata-lseed); + UNSAFE.putLongUnaligned(BYTES, 1096, lres); + data = UNSAFE.getLongUnaligned(BYTES, 1096); } } @@ -72,7 +84,7 @@ public static class TestInt { private static final byte[] BYTES = new byte[LEN]; private static final int rawdata = 0xbeef; - private static final int iseed = 2; + private static final int data; static { sink(2); // Signed immediate byte offset: range -256 to 255 @@ -81,10 +93,13 @@ public static class TestInt { // 274 can't be encoded as "base + offset" mode into the instruction field. UNSAFE.putIntUnaligned(BYTES, 274, rawdata); + ires += UNSAFE.getIntUnaligned(BYTES, 274); // 255 can be encoded into simm9 field. - UNSAFE.putIntUnaligned(BYTES, 255, rawdata + iseed); + UNSAFE.putIntUnaligned(BYTES, 255, ires); + ires += UNSAFE.getIntUnaligned(BYTES, 255); // 528 can be encoded into uimm12 field. - UNSAFE.putIntUnaligned(BYTES, 528, rawdata - iseed); + UNSAFE.putIntUnaligned(BYTES, 528, ires); + data = UNSAFE.getIntUnaligned(BYTES, 528); } } @@ -93,7 +108,7 @@ public static class TestShort { private static final byte[] BYTES = new byte[LEN]; private static final short rawdata = (short)0xbeef; - private static final short sseed = 3; + private static final short data; static { sink(2); // Signed immediate byte offset: range -256 to 255 @@ -102,10 +117,13 @@ public static class TestShort { // 257 can't be encoded as "base + offset" mode into the instruction field. UNSAFE.putShortUnaligned(BYTES, 257, rawdata); + sres = (short) (sres + UNSAFE.getShortUnaligned(BYTES, 257)); // 253 can be encoded into simm9 field. - UNSAFE.putShortUnaligned(BYTES, 253, (short) (rawdata + sseed)); + UNSAFE.putShortUnaligned(BYTES, 253, sres); + sres = (short) (sres + UNSAFE.getShortUnaligned(BYTES, 253)); // 272 can be encoded into uimm12 field. - UNSAFE.putShortUnaligned(BYTES, 272, (short) (rawdata - sseed)); + UNSAFE.putShortUnaligned(BYTES, 272, sres); + data = UNSAFE.getShortUnaligned(BYTES, 272); } } @@ -114,7 +132,7 @@ public static class TestByte { private static final byte[] BYTES = new byte[LEN]; private static final byte rawdata = (byte)0x3f; - private static final byte bseed = 4; + private static final byte data; static { sink(2); // Signed immediate byte offset: range -256 to 255 @@ -123,34 +141,29 @@ public static class TestByte { // 272 can be encoded into simm9 field. UNSAFE.putByte(BYTES, 272, rawdata); + bres = (byte) (bres + UNSAFE.getByte(BYTES, 272)); // 53 can be encoded into simm9 field. - UNSAFE.putByte(BYTES, 53, (byte) (rawdata + bseed)); + UNSAFE.putByte(BYTES, 53, bres); + bres = (byte) (bres + UNSAFE.getByte(BYTES, 53)); // 1027 can be encoded into uimm12 field. - UNSAFE.putByte(BYTES, 1027, (byte) (rawdata - bseed)); + UNSAFE.putByte(BYTES, 1027, bres); + data = UNSAFE.getByte(BYTES, 1027); } } static void test() { TestLong ta = new TestLong(); - Asserts.assertEquals(UNSAFE.getLongUnaligned(ta.BYTES, 1030), ta.rawdata, "putUnaligned long failed!"); - Asserts.assertEquals(UNSAFE.getLongUnaligned(ta.BYTES, 127), ta.rawdata + ta.lseed, "putUnaligned long failed!"); - Asserts.assertEquals(UNSAFE.getLongUnaligned(ta.BYTES, 1096), ta.rawdata - ta.lseed, "putUnaligned long failed!"); + Asserts.assertEquals(ta.data, (ta.rawdata + lseed) * 2, "putUnaligned long failed!"); TestInt tb = new TestInt(); - Asserts.assertEquals(UNSAFE.getIntUnaligned(tb.BYTES, 274), tb.rawdata, "putUnaligned int failed!"); - Asserts.assertEquals(UNSAFE.getIntUnaligned(tb.BYTES, 255), tb.rawdata + tb.iseed, "putUnaligned int failed!"); - Asserts.assertEquals(UNSAFE.getIntUnaligned(tb.BYTES, 528), tb.rawdata - tb.iseed, "putUnaligned int failed!"); + Asserts.assertEquals(tb.data, (tb.rawdata + iseed) * 2, "putUnaligned int failed!"); TestShort tc = new TestShort(); - Asserts.assertEquals(UNSAFE.getShortUnaligned(tc.BYTES, 257), tc.rawdata, "putUnaligned short failed!"); - Asserts.assertEquals(UNSAFE.getShortUnaligned(tc.BYTES, 253), (short) (tc.rawdata + tc.sseed), "putUnaligned short failed!"); - Asserts.assertEquals(UNSAFE.getShortUnaligned(tc.BYTES, 272), (short) (tc.rawdata - tc.sseed), "putUnaligned short failed!"); + Asserts.assertEquals(tc.data, (short) (((short) (tc.rawdata + sseed)) * 2), "putUnaligned short failed!"); TestByte td = new TestByte(); - Asserts.assertEquals(UNSAFE.getByte(td.BYTES, 272), td.rawdata, "put byte failed!"); - Asserts.assertEquals(UNSAFE.getByte(td.BYTES, 53), (byte) (td.rawdata + td.bseed), "put byte failed!"); - Asserts.assertEquals(UNSAFE.getByte(td.BYTES, 1027), (byte) (td.rawdata - td.bseed), "put byte failed!"); + Asserts.assertEquals(td.data, (byte) (((byte) (td.rawdata + bseed)) * 2), "put byte failed!"); } public static void main(String[] strArr) { diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java index 23b9321fc35c1..3f82c3e00b3ac 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java @@ -261,20 +261,11 @@ private void checkstore(OutputAnalyzer output, String testType, boolean useCompr }; break; case "G1": - // a card mark volatile barrier should be generated - // before the card mark strb - // - // following the fix for 8225776 the G1 barrier is now - // scheduled out of line after the membar volatile and - // and subsequent return matches = new String[] { "membar_release \\(elided\\)", useCompressedOops ? "stlrw?" : "stlr", "membar_volatile \\(elided\\)", - "ret", - "membar_volatile", - "dmb ish", - "strb" + "ret" }; break; case "Shenandoah": @@ -332,20 +323,11 @@ private void checkcas(OutputAnalyzer output, String testType, boolean useCompres }; break; case "G1": - // a card mark volatile barrier should be generated - // before the card mark strb - // - // following the fix for 8225776 the G1 barrier is now - // scheduled out of line after the membar acquire and - // and subsequent return matches = new String[] { "membar_release \\(elided\\)", useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq", "membar_acquire \\(elided\\)", - "ret", - "membar_volatile", - "dmb ish", - "strb" + "ret" }; break; case "Shenandoah": @@ -418,20 +400,11 @@ private void checkcae(OutputAnalyzer output, String testType, boolean useCompres return; case "G1": - // a card mark volatile barrier should be generated - // before the card mark strb - // - // following the fix for 8225776 the G1 barrier is now - // scheduled out of line after the membar acquire and - // and subsequent return matches = new String[] { "membar_release \\(elided\\)", useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq", "membar_acquire \\(elided\\)", - "ret", - "membar_volatile", - "dmb ish", - "strb" + "ret" }; break; case "Shenandoah": @@ -484,20 +457,11 @@ private void checkgas(OutputAnalyzer output, String testType, boolean useCompres }; break; case "G1": - // a card mark volatile barrier should be generated - // before the card mark strb - // - // following the fix for 8225776 the G1 barrier is now - // scheduled out of line after the membar acquire and - // and subsequent return matches = new String[] { "membar_release \\(elided\\)", useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq", "membar_acquire \\(elided\\)", - "ret", - "membar_volatile", - "dmb ish", - "strb" + "ret" }; break; case "Shenandoah": diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java b/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java index 25225e86b2846..17b2817a9a29d 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestPadding.java @@ -48,7 +48,7 @@ public static void test_runner() { } @Test - @IR(counts = { IRNode.NOP, "1" }) + @IR(counts = { IRNode.NOP, "<=1" }) static int test(int i) { TestPadding tp = tpf; if (tp.b1 > 42) { // Big 'cmpb' instruction at offset 0x30 diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java b/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java index 9b91375acb73e..b5a27f3f714b3 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java @@ -29,6 +29,7 @@ * @test * @bug 8267532 * @summary check that uncommon trap is generated for unhandled catch block + * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / * @requires vm.opt.DeoptimizeALot != true * @run driver compiler.c2.irTests.TestPrunedExHandler diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java index b68ddfe2799ce..2fdbb0816ade8 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. All rights reserved. + * 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 @@ -35,7 +36,6 @@ * @test * @bug 8300258 * @key randomness - * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") * @summary C2: vectorization fails on simple ByteBuffer loop * @modules java.base/jdk.internal.misc * @library /test/lib / @@ -147,193 +147,420 @@ static private void runAndVerify3(Runnable test, int offset) { } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong1(byte[] dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong1a(byte[] dest, long[] src) { for (int i = 0; i < src.length; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, src[i]); } } - @Run(test = "testByteLong1") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong1b(byte[] dest, long[] src) { + for (int i = 0; i < src.length; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, src[i]); + } + } + + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}) + public static void testByteLong1c(byte[] dest, long[] src) { + long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit) + for (int i = 0; i < src.length - 8; i++) { + UNSAFE.putLongUnaligned(dest, base + 8 * i, src[i]); + } + } + + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong1d(byte[] dest, long[] src) { + long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit) + for (int i = 0; i < src.length - 8; i++) { + UNSAFE.putLongUnaligned(dest, base + 8L * i, src[i]); + } + } + + @Run(test = {"testByteLong1a", "testByteLong1b", "testByteLong1c", "testByteLong1d"}) public static void testByteLong1_runner() { - runAndVerify(() -> testByteLong1(byteArray, longArray), 0); + runAndVerify(() -> testByteLong1a(byteArray, longArray), 0); + runAndVerify(() -> testByteLong1b(byteArray, longArray), 0); + testByteLong1c(byteArray, longArray); + testByteLong1d(byteArray, longArray); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong2(byte[] dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong2a(byte[] dest, long[] src) { for (int i = 1; i < src.length; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), src[i]); } } - @Run(test = "testByteLong2") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong2b(byte[] dest, long[] src) { + for (int i = 1; i < src.length; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), src[i]); + } + } + + @Run(test = {"testByteLong2a", "testByteLong2b"}) public static void testByteLong2_runner() { - runAndVerify(() -> testByteLong2(byteArray, longArray), -8); + runAndVerify(() -> testByteLong2a(byteArray, longArray), -8); + runAndVerify(() -> testByteLong2b(byteArray, longArray), -8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong3(byte[] dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong3a(byte[] dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), src[i]); } } - @Run(test = "testByteLong3") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong3b(byte[] dest, long[] src) { + for (int i = 0; i < src.length - 1; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), src[i]); + } + } + + @Run(test = {"testByteLong3a", "testByteLong3b"}) public static void testByteLong3_runner() { - runAndVerify(() -> testByteLong3(byteArray, longArray), 8); + runAndVerify(() -> testByteLong3a(byteArray, longArray), 8); + runAndVerify(() -> testByteLong3b(byteArray, longArray), 8); } @Test @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}, applyIf = {"AlignVector", "false"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. // AlignVector cannot guarantee that invar is aligned. - public static void testByteLong4(byte[] dest, long[] src, int start, int stop) { + public static void testByteLong4a(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, src[i]); } } - @Run(test = "testByteLong4") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}, + applyIf = {"AlignVector", "false"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + // AlignVector cannot guarantee that invar is aligned. + public static void testByteLong4b(byte[] dest, long[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, src[i]); + } + } + + @Run(test = {"testByteLong4a", "testByteLong4b"}) public static void testByteLong4_runner() { baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET; - runAndVerify(() -> testByteLong4(byteArray, longArray, 0, size), 0); + runAndVerify(() -> testByteLong4a(byteArray, longArray, 0, size), 0); + runAndVerify(() -> testByteLong4b(byteArray, longArray, 0, size), 0); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteLong5(byte[] dest, long[] src, int start, int stop) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteLong5a(byte[] dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), src[i]); } } - @Run(test = "testByteLong5") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteLong5b(byte[] dest, long[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), src[i]); + } + } + + @Run(test = {"testByteLong5a", "testByteLong5b"}) public static void testByteLong5_runner() { baseOffset = 1; - runAndVerify(() -> testByteLong5(byteArray, longArray, 0, size-1), 8); + runAndVerify(() -> testByteLong5a(byteArray, longArray, 0, size-1), 8); + runAndVerify(() -> testByteLong5b(byteArray, longArray, 0, size-1), 8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteByte1(byte[] dest, byte[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteByte1a(byte[] dest, byte[] src) { for (int i = 0; i < src.length / 8; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte1") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteByte1b(byte[] dest, byte[] src) { + for (int i = 0; i < src.length / 8; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte1a", "testByteByte1b"}) public static void testByteByte1_runner() { - runAndVerify2(() -> testByteByte1(byteArray, byteArray), 0); + runAndVerify2(() -> testByteByte1a(byteArray, byteArray), 0); + runAndVerify2(() -> testByteByte1b(byteArray, byteArray), 0); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testByteByte2(byte[] dest, byte[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned). + // might get fixed with JDK-8325155. + public static void testByteByte2a(byte[] dest, byte[] src) { for (int i = 1; i < src.length / 8; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte2") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}, + applyIfPlatform = {"64-bit", "true"}) + // 32-bit: address has ConvL2I for cast of long to address, not supported. + public static void testByteByte2b(byte[] dest, byte[] src) { + for (int i = 1; i < src.length / 8; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte2a", "testByteByte2b"}) public static void testByteByte2_runner() { - runAndVerify2(() -> testByteByte2(byteArray, byteArray), -8); + runAndVerify2(() -> testByteByte2a(byteArray, byteArray), -8); + runAndVerify2(() -> testByteByte2b(byteArray, byteArray), -8); } @Test @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) - public static void testByteByte3(byte[] dest, byte[] src) { + public static void testByteByte3a(byte[] dest, byte[] src) { for (int i = 0; i < src.length / 8 - 1; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte3") + @Test + @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) + public static void testByteByte3b(byte[] dest, byte[] src) { + for (int i = 0; i < src.length / 8 - 1; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte3a", "testByteByte3b"}) public static void testByteByte3_runner() { - runAndVerify2(() -> testByteByte3(byteArray, byteArray), 8); + runAndVerify2(() -> testByteByte3a(byteArray, byteArray), 8); + runAndVerify2(() -> testByteByte3b(byteArray, byteArray), 8); } @Test @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) - public static void testByteByte4(byte[] dest, byte[] src, int start, int stop) { + public static void testByteByte4a(byte[] dest, byte[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte4") + @Test + @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) + public static void testByteByte4b(byte[] dest, byte[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte4a", "testByteByte4b"}) public static void testByteByte4_runner() { baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET; - runAndVerify2(() -> testByteByte4(byteArray, byteArray, 0, size), 0); + runAndVerify2(() -> testByteByte4a(byteArray, byteArray, 0, size), 0); + runAndVerify2(() -> testByteByte4b(byteArray, byteArray, 0, size), 0); } @Test @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) - public static void testByteByte5(byte[] dest, byte[] src, int start, int stop) { + public static void testByteByte5a(byte[] dest, byte[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i)); } } - @Run(test = "testByteByte5") + @Test + @IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR }) + public static void testByteByte5b(byte[] dest, byte[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i)); + } + } + + @Run(test = {"testByteByte5a", "testByteByte5b"}) public static void testByteByte5_runner() { baseOffset = 1; - runAndVerify2(() -> testByteByte5(byteArray, byteArray, 0, size-1), 8); + runAndVerify2(() -> testByteByte5a(byteArray, byteArray, 0, size-1), 8); + runAndVerify2(() -> testByteByte5b(byteArray, byteArray, 0, size-1), 8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testOffHeapLong1(long dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P(dest + 8 * (i + int_con)) + // See: JDK-8331576 + public static void testOffHeapLong1a(long dest, long[] src) { for (int i = 0; i < src.length; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * i, src[i]); } } - @Run(test = "testOffHeapLong1") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P(dest + 8L * (i + int_con)) + // See: JDK-8331576 + public static void testOffHeapLong1b(long dest, long[] src) { + for (int i = 0; i < src.length; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * i, src[i]); + } + } + + @Run(test = {"testOffHeapLong1a", "testOffHeapLong1b"}) public static void testOffHeapLong1_runner() { - runAndVerify3(() -> testOffHeapLong1(baseOffHeap, longArray), 0); + runAndVerify3(() -> testOffHeapLong1a(baseOffHeap, longArray), 0); + runAndVerify3(() -> testOffHeapLong1b(baseOffHeap, longArray), 0); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testOffHeapLong2(long dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong2a(long dest, long[] src) { for (int i = 1; i < src.length; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * (i - 1), src[i]); } } - @Run(test = "testOffHeapLong2") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong2b(long dest, long[] src) { + for (int i = 1; i < src.length; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * (i - 1), src[i]); + } + } + + @Run(test = {"testOffHeapLong2a", "testOffHeapLong2b"}) public static void testOffHeapLong2_runner() { - runAndVerify3(() -> testOffHeapLong2(baseOffHeap, longArray), -8); + runAndVerify3(() -> testOffHeapLong2a(baseOffHeap, longArray), -8); + runAndVerify3(() -> testOffHeapLong2b(baseOffHeap, longArray), -8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) - public static void testOffHeapLong3(long dest, long[] src) { + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong3a(long dest, long[] src) { for (int i = 0; i < src.length - 1; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * (i + 1), src[i]); } } - @Run(test = "testOffHeapLong3") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }) + // FAILS: adr is CastX2P + // See: JDK-8331576 + public static void testOffHeapLong3b(long dest, long[] src) { + for (int i = 0; i < src.length - 1; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * (i + 1), src[i]); + } + } + + @Run(test = {"testOffHeapLong3a", "testOffHeapLong3b"}) public static void testOffHeapLong3_runner() { - runAndVerify3(() -> testOffHeapLong3(baseOffHeap, longArray), 8); + runAndVerify3(() -> testOffHeapLong3a(baseOffHeap, longArray), 8); + runAndVerify3(() -> testOffHeapLong3b(baseOffHeap, longArray), 8); } @Test - @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, - applyIf = {"AlignVector", "false"}) + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + // applyIf = {"AlignVector", "false"}) + // FAILS: adr is CastX2P + // See: JDK-8331576 // AlignVector cannot guarantee that invar is aligned. - public static void testOffHeapLong4(long dest, long[] src, int start, int stop) { + public static void testOffHeapLong4a(long dest, long[] src, int start, int stop) { for (int i = start; i < stop; i++) { UNSAFE.putLongUnaligned(null, dest + 8 * i + baseOffset, src[i]); } } - @Run(test = "testOffHeapLong4") + @Test + @IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary + // @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" }, + // applyIf = {"AlignVector", "false"}) + // FAILS: adr is CastX2P + // See: JDK-8331576 + // AlignVector cannot guarantee that invar is aligned. + public static void testOffHeapLong4b(long dest, long[] src, int start, int stop) { + for (int i = start; i < stop; i++) { + UNSAFE.putLongUnaligned(null, dest + 8L * i + baseOffset, src[i]); + } + } + + @Run(test = {"testOffHeapLong4a", "testOffHeapLong4b"}) public static void testOffHeapLong4_runner() { baseOffset = 8; - runAndVerify3(() -> testOffHeapLong4(baseOffHeap, longArray, 0, size-1), 8); + runAndVerify3(() -> testOffHeapLong4a(baseOffHeap, longArray, 0, size-1), 8); + runAndVerify3(() -> testOffHeapLong4b(baseOffHeap, longArray, 0, size-1), 8); } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java new file mode 100644 index 0000000000000..c56ffc88778ae --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java @@ -0,0 +1,138 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2.irTests.gc; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; +import jdk.test.whitebox.gc.GC; + +import java.lang.ref.*; +import java.util.*; + +/* + * @test + * @bug 8329597 + * @summary Test that Reference.clear intrinsics are properly handled + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @requires vm.compiler2.enabled + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.gc.ReferenceClearTests + + */ +public class ReferenceClearTests { + + private static String[] args(String... add) { + List args = new ArrayList<>(); + + // Use PerMethodTrapLimit=0 to compile all branches in the intrinsics. + args.add("-XX:PerMethodTrapLimit=0"); + + // Forcefully inline all methods to reach the intrinsic code. + args.add("-XX:CompileCommand=inline,compiler.c2.irTests.gc.ReferenceClearTests::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.Reference::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.PhantomReference::*"); + + // Mix in test config code. + args.addAll(Arrays.asList(add)); + + return args.toArray(new String[0]); + } + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + + int idx = 0; + if (GC.isSelectedErgonomically() && GC.Serial.isSupported()) { + // Serial does not have SATB/keep-alive barriers at all. + // There are inter-generational barriers on stores, but they are + // folded away for null stores like clear(). + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseSerialGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Parallel.isSupported()) { + // Parallel does not have SATB/keep-alive barriers at all. + // There are inter-generational barriers on stores, but they + // should be folded away for null stores like clear(). + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseParallelGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.G1.isSupported()) { + // G1 does not have barriers in C2 IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseG1GC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Shenandoah.isSupported()) { + // Shenandoah has SATB/keep-alive barriers, but they should not be + // present clear()-s. There are load-reference barriers, which would + // confuse the tests, so we enable only SATB barriers. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:ShenandoahGCMode=passive", + "-XX:+ShenandoahSATBBarrier", + "-XX:+UseShenandoahGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Z.isSupported()) { + // Z does not have barriers in C2 IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseZGC" + ))); + } + framework.start(); + } + + static final Object REF = new Object(); + + static final SoftReference SR = new SoftReference<>(REF); + static final WeakReference WR = new WeakReference<>(REF); + static final PhantomReference PR = new PhantomReference<>(REF, null); + + // We assert there is only a single load and a single store of Reference.referent. + // This serves as signal that no GC barriers are emitted in IR. + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void soft() { + SR.clear(); + } + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void weak() { + WR.clear(); + } + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void phantom() { + PR.clear(); + } + +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java index cd3d5329771ef..69b3cb5274b57 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java @@ -1355,9 +1355,12 @@ int testReReduce(boolean cond, int x, int y) { } @Test - @IR(counts = { IRNode.ALLOC, "1" }) - // The last allocation won't be reduced because it would cause the creation - // of a nested SafePointScalarMergeNode. + // Using G1, all allocations are reduced. + @IR(applyIf = {"UseG1GC", "true"}, failOn = { IRNode.ALLOC }) + // Otherwise, the last allocation won't be reduced because it would cause + // the creation of a nested SafePointScalarMergeNode. This is caused by the + // store barrier corresponding to 'C.other = B'. + @IR(applyIf = {"UseG1GC", "false"}, counts = { IRNode.ALLOC, "1" }) int testReReduce_C2(boolean cond1, int x, int y) { return testReReduce(cond1, x, y); } @DontCompile diff --git a/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java b/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java index 068da4100bd69..87ce9a313a8b8 100644 --- a/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java +++ b/test/hotspot/jtreg/compiler/cha/StrengthReduceInterfaceCall.java @@ -24,6 +24,7 @@ /* * @test * @requires !vm.graal.enabled + * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @modules java.base/jdk.internal.org.objectweb.asm * java.base/jdk.internal.misc * java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/compiler/cha/TypeProfileFinalMethod.java b/test/hotspot/jtreg/compiler/cha/TypeProfileFinalMethod.java new file mode 100644 index 0000000000000..a81ba53af52c7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cha/TypeProfileFinalMethod.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary test c1 to record type profile with CHA optimization + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver compiler.cha.TypeProfileFinalMethod + */ +package compiler.cha; + +import java.io.File; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TypeProfileFinalMethod { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-Xbatch", "-XX:-UseOnStackReplacement", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-XX:Tier3InvocationThreshold=200", "-XX:Tier4InvocationThreshold=5000", + Launcher.class.getName()); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + System.out.println("debug output"); + System.out.println(output.getOutput()); + System.out.println("debug output end"); + output.shouldHaveExitValue(0); + output.shouldNotContain("failed to inline: virtual call"); + Pattern pattern = Pattern.compile("Child1::m.* inline "); + Matcher matcher = pattern.matcher(output.getOutput()); + int matchCnt = 0; + while (matcher.find()) { + matchCnt++; + } + Asserts.assertEquals(matchCnt, 2); // inline Child1::m() twice + } + + static class Launcher { + public static void main(String[] args) throws Exception { + addCompilerDirectives(); + int cnt = 5300; + // warmup test1 to be compiled with c1 and c2 + // and only compile test2 with c1 + for (int i = 0; i < cnt; i++) { + test1(i); + } + for (int i = 0; i < cnt; i++) { + test2(i); + } + Parent c = new TypeProfileFinalMethod.Child2(); + System.out.println("======== break CHA"); + // trigger c2 to compile test2 + for (int i = 0; i < 100; i++) { + test2(i); + } + } + + static void addCompilerDirectives() { + WhiteBox WB = WhiteBox.getWhiteBox(); + // do not inline getInstance() for test1() and test2() + String directive = "[{ match: [\"" + Launcher.class.getName() + "::test1\"]," + + "inline:[\"-" + Launcher.class.getName()+"::getInstance()\"] }]"; + WB.addCompilerDirective(directive); + + directive = "[{ match: [\"" + Launcher.class.getName() + "::test2\"]," + + "inline:[\"-" + Launcher.class.getName()+"::getInstance()\"] }]"; + WB.addCompilerDirective(directive); + + // do not inline test1() for test2() in c1 compilation + directive = "[{ match: [\"" + Launcher.class.getName() + "::test2\"]," + + "c1: { inline:[\"-" + Launcher.class.getName()+"::test1()\"] } }]"; + WB.addCompilerDirective(directive); + + // print inline tree for checking + directive = "[{ match: [\"" + Launcher.class.getName() + "::test2\"]," + + "c2: { PrintInlining: true } }]"; + WB.addCompilerDirective(directive); + } + + static int test1(int i) { + int ret = 0; + Parent ix = getInstance(); + if (i<200) { + return ix.m(); + } + for (int j = 0; j < 50; j++) { + ret += ix.m(); // the callsite we are interesting + } + return ret; + } + + static int test2(int i) { + return test1(i); + } + + static Parent getInstance() { + return new TypeProfileFinalMethod.Child1(); + } + } + + static abstract class Parent { + abstract public int m(); + } + + final static class Child1 extends Parent { + public int m() { + return 1; + } + } + + final static class Child2 extends Parent { + public int m() { + return 2; + } + } +} + + diff --git a/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java b/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java index d0b45333fb8b3..ecdb415843bf0 100644 --- a/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java +++ b/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java @@ -179,7 +179,9 @@ public static void main(String[] args) throws Exception { // Fails if code heap sizes do not add up pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+SegmentedCodeCache", "-XX:ReservedCodeCacheSize=10M", - "-XX:NonNMethodCodeHeapSize=5M", + // After fixing a round_down issue with large page sizes (JDK-8334564), + // 5M is a bit too small for NonNMethodCodeHeap + "-XX:NonNMethodCodeHeapSize=6M", "-XX:ProfiledCodeHeapSize=5M", "-XX:NonProfiledCodeHeapSize=5M", "-version"); diff --git a/test/hotspot/jtreg/compiler/codegen/TestAntiDependenciesHighMemUsage.java b/test/hotspot/jtreg/compiler/codegen/TestAntiDependenciesHighMemUsage.java new file mode 100644 index 0000000000000..b8db6581b611c --- /dev/null +++ b/test/hotspot/jtreg/compiler/codegen/TestAntiDependenciesHighMemUsage.java @@ -0,0 +1,2443 @@ +/* + * Copyright (c) 2024, 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 + * 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 8333258 + * @summary C2: high memory usage in PhaseCFG::insert_anti_dependences() + * @run main/othervm -XX:CompileOnly=TestAntiDependenciesHighMemUsage::test1 -Xcomp TestAntiDependenciesHighMemUsage + */ + +public class TestAntiDependenciesHighMemUsage { + + public static void main(String[] args) { + test1(0); + } + + private static int test1(int i) { + int v = field0000 + + field0001 + + field0002 + + field0003 + + field0004 + + field0005 + + field0006 + + field0007 + + field0008 + + field0009 + + field0010 + + field0011 + + field0012 + + field0013 + + field0014 + + field0015 + + field0016 + + field0017 + + field0018 + + field0019 + + field0020 + + field0021 + + field0022 + + field0023 + + field0024 + + field0025 + + field0026 + + field0027 + + field0028 + + field0029 + + field0030 + + field0031 + + field0032 + + field0033 + + field0034 + + field0035 + + field0036 + + field0037 + + field0038 + + field0039 + + field0040 + + field0041 + + field0042 + + field0043 + + field0044 + + field0045 + + field0046 + + field0047 + + field0048 + + field0049 + + field0050 + + field0051 + + field0052 + + field0053 + + field0054 + + field0055 + + field0056 + + field0057 + + field0058 + + field0059; + + switch (i) { + case -2140710406: + field0000 = 42; + field0001 = 42; + field0002 = 42; + field0003 = 42; + field0004 = 42; + field0005 = 42; + field0006 = 42; + field0007 = 42; + field0008 = 42; + field0009 = 42; + field0010 = 42; + field0011 = 42; + field0012 = 42; + field0013 = 42; + field0014 = 42; + field0015 = 42; + field0016 = 42; + field0017 = 42; + field0018 = 42; + field0019 = 42; + field0020 = 42; + field0021 = 42; + field0022 = 42; + field0023 = 42; + field0024 = 42; + field0025 = 42; + field0026 = 42; + field0027 = 42; + field0028 = 42; + field0029 = 42; + field0030 = 42; + field0031 = 42; + field0032 = 42; + field0033 = 42; + field0034 = 42; + field0035 = 42; + field0036 = 42; + field0037 = 42; + field0038 = 42; + field0039 = 42; + field0040 = 42; + field0041 = 42; + field0042 = 42; + field0043 = 42; + field0044 = 42; + field0045 = 42; + field0046 = 42; + field0047 = 42; + field0048 = 42; + field0049 = 42; + field0050 = 42; + field0051 = 42; + field0052 = 42; + field0053 = 42; + field0054 = 42; + field0055 = 42; + field0056 = 42; + field0057 = 42; + field0058 = 42; + field0059 = 42; + field0060 = 42; + field0061 = 42; + field0062 = 42; + field0063 = 42; + field0064 = 42; + field0065 = 42; + field0066 = 42; + field0067 = 42; + field0068 = 42; + field0069 = 42; + field0070 = 42; + field0071 = 42; + field0072 = 42; + field0073 = 42; + field0074 = 42; + field0075 = 42; + field0076 = 42; + field0077 = 42; + field0078 = 42; + field0079 = 42; + field0080 = 42; + field0081 = 42; + field0082 = 42; + field0083 = 42; + field0084 = 42; + field0085 = 42; + field0086 = 42; + field0087 = 42; + field0088 = 42; + field0089 = 42; + field0090 = 42; + field0091 = 42; + field0092 = 42; + field0093 = 42; + field0094 = 42; + field0095 = 42; + field0096 = 42; + field0097 = 42; + field0098 = 42; + field0099 = 42; + field0100 = 42; + field0101 = 42; + field0102 = 42; + field0103 = 42; + field0104 = 42; + field0105 = 42; + field0106 = 42; + field0107 = 42; + field0108 = 42; + field0109 = 42; + field0110 = 42; + field0111 = 42; + field0112 = 42; + field0113 = 42; + field0114 = 42; + field0115 = 42; + field0116 = 42; + field0117 = 42; + field0118 = 42; + field0119 = 42; + field0120 = 42; + field0121 = 42; + field0122 = 42; + field0123 = 42; + field0124 = 42; + field0125 = 42; + field0126 = 42; + field0127 = 42; + field0128 = 42; + field0129 = 42; + field0130 = 42; + field0131 = 42; + field0132 = 42; + field0133 = 42; + field0134 = 42; + field0135 = 42; + field0136 = 42; + field0137 = 42; + field0138 = 42; + field0139 = 42; + field0140 = 42; + field0141 = 42; + field0142 = 42; + field0143 = 42; + field0144 = 42; + field0145 = 42; + field0146 = 42; + field0147 = 42; + field0148 = 42; + field0149 = 42; + field0150 = 42; + field0151 = 42; + field0152 = 42; + field0153 = 42; + field0154 = 42; + field0155 = 42; + field0156 = 42; + field0157 = 42; + field0158 = 42; + field0159 = 42; + field0160 = 42; + field0161 = 42; + field0162 = 42; + field0163 = 42; + field0164 = 42; + field0165 = 42; + field0166 = 42; + field0167 = 42; + field0168 = 42; + field0169 = 42; + field0170 = 42; + field0171 = 42; + field0172 = 42; + field0173 = 42; + field0174 = 42; + field0175 = 42; + field0176 = 42; + field0177 = 42; + field0178 = 42; + field0179 = 42; + field0180 = 42; + field0181 = 42; + field0182 = 42; + field0183 = 42; + field0184 = 42; + field0185 = 42; + field0186 = 42; + field0187 = 42; + field0188 = 42; + field0189 = 42; + field0190 = 42; + field0191 = 42; + field0192 = 42; + field0193 = 42; + field0194 = 42; + field0195 = 42; + field0196 = 42; + field0197 = 42; + field0198 = 42; + field0199 = 42; + field0200 = 42; + field0201 = 42; + field0202 = 42; + field0203 = 42; + field0204 = 42; + field0205 = 42; + field0206 = 42; + field0207 = 42; + field0208 = 42; + field0209 = 42; + field0210 = 42; + field0211 = 42; + field0212 = 42; + field0213 = 42; + field0214 = 42; + field0215 = 42; + field0216 = 42; + field0217 = 42; + field0218 = 42; + field0219 = 42; + field0220 = 42; + field0221 = 42; + field0222 = 42; + field0223 = 42; + field0224 = 42; + field0225 = 42; + field0226 = 42; + field0227 = 42; + field0228 = 42; + field0229 = 42; + field0230 = 42; + field0231 = 42; + field0232 = 42; + field0233 = 42; + field0234 = 42; + field0235 = 42; + field0236 = 42; + field0237 = 42; + field0238 = 42; + field0239 = 42; + field0240 = 42; + field0241 = 42; + field0242 = 42; + field0243 = 42; + field0244 = 42; + field0245 = 42; + field0246 = 42; + field0247 = 42; + field0248 = 42; + field0249 = 42; + field0250 = 42; + field0251 = 42; + field0252 = 42; + field0253 = 42; + field0254 = 42; + field0255 = 42; + field0256 = 42; + field0257 = 42; + field0258 = 42; + field0259 = 42; + field0260 = 42; + field0261 = 42; + field0262 = 42; + field0263 = 42; + field0264 = 42; + field0265 = 42; + field0266 = 42; + field0267 = 42; + field0268 = 42; + field0269 = 42; + field0270 = 42; + field0271 = 42; + field0272 = 42; + field0273 = 42; + field0274 = 42; + field0275 = 42; + field0276 = 42; + field0277 = 42; + field0278 = 42; + field0279 = 42; + field0280 = 42; + field0281 = 42; + field0282 = 42; + field0283 = 42; + field0284 = 42; + field0285 = 42; + field0286 = 42; + field0287 = 42; + field0288 = 42; + field0289 = 42; + field0290 = 42; + field0291 = 42; + field0292 = 42; + field0293 = 42; + field0294 = 42; + field0295 = 42; + field0296 = 42; + field0297 = 42; + field0298 = 42; + field0299 = 42; + field0300 = 42; + field0301 = 42; + field0302 = 42; + field0303 = 42; + field0304 = 42; + field0305 = 42; + field0306 = 42; + field0307 = 42; + field0308 = 42; + field0309 = 42; + field0310 = 42; + field0311 = 42; + field0312 = 42; + field0313 = 42; + field0314 = 42; + field0315 = 42; + field0316 = 42; + field0317 = 42; + field0318 = 42; + field0319 = 42; + field0320 = 42; + field0321 = 42; + field0322 = 42; + field0323 = 42; + field0324 = 42; + field0325 = 42; + field0326 = 42; + field0327 = 42; + field0328 = 42; + field0329 = 42; + field0330 = 42; + field0331 = 42; + field0332 = 42; + field0333 = 42; + field0334 = 42; + field0335 = 42; + field0336 = 42; + field0337 = 42; + field0338 = 42; + field0339 = 42; + field0340 = 42; + field0341 = 42; + field0342 = 42; + field0343 = 42; + field0344 = 42; + field0345 = 42; + field0346 = 42; + field0347 = 42; + field0348 = 42; + field0349 = 42; + field0350 = 42; + field0351 = 42; + field0352 = 42; + field0353 = 42; + field0354 = 42; + field0355 = 42; + field0356 = 42; + field0357 = 42; + field0358 = 42; + field0359 = 42; + field0360 = 42; + field0361 = 42; + field0362 = 42; + field0363 = 42; + field0364 = 42; + field0365 = 42; + field0366 = 42; + field0367 = 42; + field0368 = 42; + field0369 = 42; + field0370 = 42; + field0371 = 42; + field0372 = 42; + field0373 = 42; + field0374 = 42; + field0375 = 42; + field0376 = 42; + field0377 = 42; + field0378 = 42; + field0379 = 42; + field0380 = 42; + field0381 = 42; + field0382 = 42; + field0383 = 42; + field0384 = 42; + field0385 = 42; + field0386 = 42; + field0387 = 42; + field0388 = 42; + field0389 = 42; + field0390 = 42; + field0391 = 42; + field0392 = 42; + field0393 = 42; + field0394 = 42; + field0395 = 42; + field0396 = 42; + field0397 = 42; + field0398 = 42; + field0399 = 42; + field0400 = 42; + field0401 = 42; + field0402 = 42; + field0403 = 42; + field0404 = 42; + field0405 = 42; + field0406 = 42; + field0407 = 42; + field0408 = 42; + field0409 = 42; + field0410 = 42; + field0411 = 42; + field0412 = 42; + field0413 = 42; + field0414 = 42; + field0415 = 42; + field0416 = 42; + field0417 = 42; + field0418 = 42; + field0419 = 42; + field0420 = 42; + field0421 = 42; + field0422 = 42; + field0423 = 42; + field0424 = 42; + field0425 = 42; + field0426 = 42; + field0427 = 42; + field0428 = 42; + field0429 = 42; + field0430 = 42; + field0431 = 42; + field0432 = 42; + field0433 = 42; + field0434 = 42; + field0435 = 42; + field0436 = 42; + field0437 = 42; + field0438 = 42; + field0439 = 42; + field0440 = 42; + field0441 = 42; + field0442 = 42; + field0443 = 42; + field0444 = 42; + field0445 = 42; + field0446 = 42; + field0447 = 42; + field0448 = 42; + field0449 = 42; + field0450 = 42; + field0451 = 42; + field0452 = 42; + field0453 = 42; + field0454 = 42; + field0455 = 42; + field0456 = 42; + field0457 = 42; + field0458 = 42; + field0459 = 42; + field0460 = 42; + field0461 = 42; + field0462 = 42; + field0463 = 42; + field0464 = 42; + field0465 = 42; + field0466 = 42; + field0467 = 42; + field0468 = 42; + field0469 = 42; + field0470 = 42; + field0471 = 42; + field0472 = 42; + field0473 = 42; + field0474 = 42; + field0475 = 42; + field0476 = 42; + field0477 = 42; + field0478 = 42; + field0479 = 42; + field0480 = 42; + field0481 = 42; + field0482 = 42; + field0483 = 42; + field0484 = 42; + field0485 = 42; + field0486 = 42; + field0487 = 42; + field0488 = 42; + field0489 = 42; + field0490 = 42; + field0491 = 42; + field0492 = 42; + field0493 = 42; + field0494 = 42; + field0495 = 42; + field0496 = 42; + field0497 = 42; + field0498 = 42; + field0499 = 42; + field0500 = 42; + field0501 = 42; + field0502 = 42; + field0503 = 42; + field0504 = 42; + field0505 = 42; + field0506 = 42; + field0507 = 42; + field0508 = 42; + field0509 = 42; + field0510 = 42; + field0511 = 42; + field0512 = 42; + field0513 = 42; + field0514 = 42; + field0515 = 42; + field0516 = 42; + field0517 = 42; + field0518 = 42; + field0519 = 42; + field0520 = 42; + field0521 = 42; + field0522 = 42; + field0523 = 42; + field0524 = 42; + field0525 = 42; + field0526 = 42; + field0527 = 42; + field0528 = 42; + field0529 = 42; + field0530 = 42; + field0531 = 42; + field0532 = 42; + field0533 = 42; + field0534 = 42; + field0535 = 42; + field0536 = 42; + field0537 = 42; + field0538 = 42; + field0539 = 42; + field0540 = 42; + field0541 = 42; + field0542 = 42; + field0543 = 42; + field0544 = 42; + field0545 = 42; + field0546 = 42; + field0547 = 42; + field0548 = 42; + field0549 = 42; + field0550 = 42; + field0551 = 42; + field0552 = 42; + field0553 = 42; + field0554 = 42; + field0555 = 42; + field0556 = 42; + field0557 = 42; + field0558 = 42; + field0559 = 42; + field0560 = 42; + field0561 = 42; + field0562 = 42; + field0563 = 42; + field0564 = 42; + field0565 = 42; + field0566 = 42; + field0567 = 42; + field0568 = 42; + field0569 = 42; + field0570 = 42; + field0571 = 42; + field0572 = 42; + field0573 = 42; + field0574 = 42; + field0575 = 42; + field0576 = 42; + field0577 = 42; + field0578 = 42; + field0579 = 42; + field0580 = 42; + field0581 = 42; + field0582 = 42; + field0583 = 42; + field0584 = 42; + field0585 = 42; + field0586 = 42; + field0587 = 42; + field0588 = 42; + field0589 = 42; + field0590 = 42; + field0591 = 42; + field0592 = 42; + field0593 = 42; + field0594 = 42; + field0595 = 42; + field0596 = 42; + field0597 = 42; + field0598 = 42; + field0599 = 42; + field0600 = 42; + field0601 = 42; + field0602 = 42; + field0603 = 42; + field0604 = 42; + field0605 = 42; + field0606 = 42; + field0607 = 42; + field0608 = 42; + field0609 = 42; + field0610 = 42; + field0611 = 42; + field0612 = 42; + field0613 = 42; + field0614 = 42; + field0615 = 42; + field0616 = 42; + field0617 = 42; + field0618 = 42; + field0619 = 42; + field0620 = 42; + field0621 = 42; + field0622 = 42; + field0623 = 42; + field0624 = 42; + field0625 = 42; + field0626 = 42; + field0627 = 42; + field0628 = 42; + field0629 = 42; + field0630 = 42; + field0631 = 42; + field0632 = 42; + field0633 = 42; + field0634 = 42; + field0635 = 42; + field0636 = 42; + field0637 = 42; + field0638 = 42; + field0639 = 42; + field0640 = 42; + field0641 = 42; + field0642 = 42; + field0643 = 42; + field0644 = 42; + field0645 = 42; + field0646 = 42; + field0647 = 42; + field0648 = 42; + field0649 = 42; + field0650 = 42; + field0651 = 42; + field0652 = 42; + field0653 = 42; + field0654 = 42; + field0655 = 42; + field0656 = 42; + field0657 = 42; + field0658 = 42; + field0659 = 42; + field0660 = 42; + field0661 = 42; + field0662 = 42; + field0663 = 42; + field0664 = 42; + field0665 = 42; + field0666 = 42; + field0667 = 42; + field0668 = 42; + field0669 = 42; + field0670 = 42; + field0671 = 42; + field0672 = 42; + field0673 = 42; + field0674 = 42; + field0675 = 42; + field0676 = 42; + field0677 = 42; + field0678 = 42; + field0679 = 42; + field0680 = 42; + field0681 = 42; + field0682 = 42; + field0683 = 42; + field0684 = 42; + field0685 = 42; + field0686 = 42; + field0687 = 42; + field0688 = 42; + field0689 = 42; + field0690 = 42; + field0691 = 42; + field0692 = 42; + field0693 = 42; + field0694 = 42; + field0695 = 42; + field0696 = 42; + field0697 = 42; + field0698 = 42; + field0699 = 42; + field0700 = 42; + field0701 = 42; + field0702 = 42; + field0703 = 42; + field0704 = 42; + field0705 = 42; + field0706 = 42; + field0707 = 42; + field0708 = 42; + field0709 = 42; + field0710 = 42; + field0711 = 42; + field0712 = 42; + field0713 = 42; + field0714 = 42; + field0715 = 42; + field0716 = 42; + field0717 = 42; + field0718 = 42; + field0719 = 42; + field0720 = 42; + field0721 = 42; + field0722 = 42; + field0723 = 42; + field0724 = 42; + field0725 = 42; + field0726 = 42; + field0727 = 42; + field0728 = 42; + field0729 = 42; + field0730 = 42; + field0731 = 42; + field0732 = 42; + field0733 = 42; + field0734 = 42; + field0735 = 42; + field0736 = 42; + field0737 = 42; + field0738 = 42; + field0739 = 42; + field0740 = 42; + field0741 = 42; + field0742 = 42; + field0743 = 42; + field0744 = 42; + field0745 = 42; + field0746 = 42; + field0747 = 42; + field0748 = 42; + field0749 = 42; + field0750 = 42; + field0751 = 42; + field0752 = 42; + field0753 = 42; + field0754 = 42; + field0755 = 42; + field0756 = 42; + field0757 = 42; + field0758 = 42; + field0759 = 42; + field0760 = 42; + field0761 = 42; + field0762 = 42; + field0763 = 42; + field0764 = 42; + field0765 = 42; + field0766 = 42; + field0767 = 42; + field0768 = 42; + field0769 = 42; + field0770 = 42; + field0771 = 42; + field0772 = 42; + field0773 = 42; + field0774 = 42; + field0775 = 42; + field0776 = 42; + field0777 = 42; + field0778 = 42; + field0779 = 42; + field0780 = 42; + field0781 = 42; + field0782 = 42; + field0783 = 42; + field0784 = 42; + field0785 = 42; + field0786 = 42; + field0787 = 42; + field0788 = 42; + field0789 = 42; + field0790 = 42; + field0791 = 42; + field0792 = 42; + field0793 = 42; + field0794 = 42; + field0795 = 42; + field0796 = 42; + field0797 = 42; + field0798 = 42; + field0799 = 42; + field0800 = 42; + field0801 = 42; + field0802 = 42; + field0803 = 42; + field0804 = 42; + field0805 = 42; + field0806 = 42; + field0807 = 42; + field0808 = 42; + field0809 = 42; + field0810 = 42; + field0811 = 42; + field0812 = 42; + field0813 = 42; + field0814 = 42; + field0815 = 42; + field0816 = 42; + field0817 = 42; + field0818 = 42; + field0819 = 42; + field0820 = 42; + field0821 = 42; + field0822 = 42; + field0823 = 42; + field0824 = 42; + field0825 = 42; + field0826 = 42; + field0827 = 42; + field0828 = 42; + field0829 = 42; + field0830 = 42; + field0831 = 42; + field0832 = 42; + field0833 = 42; + field0834 = 42; + field0835 = 42; + field0836 = 42; + field0837 = 42; + field0838 = 42; + field0839 = 42; + field0840 = 42; + field0841 = 42; + field0842 = 42; + field0843 = 42; + field0844 = 42; + field0845 = 42; + field0846 = 42; + field0847 = 42; + field0848 = 42; + field0849 = 42; + field0850 = 42; + field0851 = 42; + field0852 = 42; + field0853 = 42; + field0854 = 42; + field0855 = 42; + field0856 = 42; + field0857 = 42; + field0858 = 42; + field0859 = 42; + field0860 = 42; + field0861 = 42; + field0862 = 42; + field0863 = 42; + field0864 = 42; + field0865 = 42; + field0866 = 42; + field0867 = 42; + field0868 = 42; + field0869 = 42; + field0870 = 42; + field0871 = 42; + field0872 = 42; + field0873 = 42; + field0874 = 42; + field0875 = 42; + field0876 = 42; + field0877 = 42; + field0878 = 42; + field0879 = 42; + field0880 = 42; + field0881 = 42; + field0882 = 42; + field0883 = 42; + field0884 = 42; + field0885 = 42; + field0886 = 42; + field0887 = 42; + field0888 = 42; + field0889 = 42; + field0890 = 42; + field0891 = 42; + field0892 = 42; + field0893 = 42; + field0894 = 42; + field0895 = 42; + field0896 = 42; + field0897 = 42; + field0898 = 42; + field0899 = 42; + field0900 = 42; + field0901 = 42; + field0902 = 42; + field0903 = 42; + field0904 = 42; + field0905 = 42; + field0906 = 42; + field0907 = 42; + field0908 = 42; + field0909 = 42; + field0910 = 42; + field0911 = 42; + field0912 = 42; + field0913 = 42; + field0914 = 42; + field0915 = 42; + field0916 = 42; + field0917 = 42; + field0918 = 42; + field0919 = 42; + field0920 = 42; + field0921 = 42; + field0922 = 42; + field0923 = 42; + field0924 = 42; + field0925 = 42; + field0926 = 42; + field0927 = 42; + field0928 = 42; + field0929 = 42; + field0930 = 42; + field0931 = 42; + field0932 = 42; + field0933 = 42; + field0934 = 42; + field0935 = 42; + field0936 = 42; + field0937 = 42; + field0938 = 42; + field0939 = 42; + field0940 = 42; + field0941 = 42; + field0942 = 42; + field0943 = 42; + field0944 = 42; + field0945 = 42; + field0946 = 42; + field0947 = 42; + field0948 = 42; + field0949 = 42; + field0950 = 42; + field0951 = 42; + field0952 = 42; + field0953 = 42; + field0954 = 42; + field0955 = 42; + field0956 = 42; + field0957 = 42; + field0958 = 42; + field0959 = 42; + field0960 = 42; + field0961 = 42; + field0962 = 42; + field0963 = 42; + field0964 = 42; + field0965 = 42; + field0966 = 42; + field0967 = 42; + field0968 = 42; + field0969 = 42; + field0970 = 42; + field0971 = 42; + field0972 = 42; + field0973 = 42; + field0974 = 42; + field0975 = 42; + field0976 = 42; + field0977 = 42; + field0978 = 42; + field0979 = 42; + field0980 = 42; + field0981 = 42; + field0982 = 42; + field0983 = 42; + field0984 = 42; + field0985 = 42; + field0986 = 42; + field0987 = 42; + field0988 = 42; + field0989 = 42; + field0990 = 42; + field0991 = 42; + field0992 = 42; + field0993 = 42; + field0994 = 42; + field0995 = 42; + field0996 = 42; + field0997 = 42; + field0998 = 42; + field0999 = 42; + break; + case -2097348800: + break; + case -2068224216: + break; + case -2037697382: + break; + case -2004863454: + break; + case -1927368268: + break; + case -1907858975: + break; + case -1907849355: + break; + case -1874423303: + break; + case -1842766326: + break; + case -1789797270: + break; + case -1760959152: + break; + case -1691992770: + break; + case -1678813190: + break; + case -1605049009: + break; + case -1476174894: + break; + case -1377846581: + break; + case -1345530543: + break; + case -1307317230: + break; + case -1268501092: + break; + case -1220360021: + break; + case -1217415016: + break; + case -1216012752: + break; + case -1202791344: + break; + case -1197000094: + break; + case -1153521791: + break; + case -1136815094: + break; + case -1122842661: + break; + case -1097468803: + break; + case -1093178557: + break; + case -1087398572: + break; + case -1008013583: + break; + case -1001676601: + break; + case -949306426: + break; + case -912457023: + break; + case -891985903: + break; + case -883723257: + break; + case -871422185: + break; + case -766867181: + break; + case -766422255: + break; + case -650580623: + break; + case -633276745: + break; + case -632949857: + break; + case -621058352: + break; + case -616289146: + break; + case -589453283: + break; + case -555387838: + break; + case -540546990: + break; + case -526550005: + break; + case -502303438: + break; + case -408244884: + break; + case -367870439: + break; + case -342579923: + break; + case -330210563: + break; + case -329624856: + break; + case -302536977: + break; + case -287122936: + break; + case -236322890: + break; + case -227407685: + break; + case -218088061: + break; + case -180371167: + break; + case -131262666: + break; + case -5812857: + break; + case 3355: + break; + case 65759: + break; + case 110026: + break; + case 116076: + break; + case 2192268: + break; + case 2224947: + break; + case 2368702: + break; + case 2394661: + break; + case 2579998: + break; + case 2599333: + break; + case 3059181: + break; + case 3076014: + break; + case 3560141: + break; + case 3601339: + break; + case 8777024: + break; + case 28778089: + break; + case 29963587: + break; + case 57185780: + break; + case 57208314: + break; + case 57320750: + break; + case 63955982: + break; + case 64711720: + break; + case 65189916: + break; + case 65298671: + break; + case 69076575: + break; + case 74219460: + break; + case 74526880: + break; + case 78727453: + break; + case 78733291: + break; + case 192873343: + break; + case 194378184: + break; + case 246938863: + break; + case 269058788: + break; + case 289362821: + break; + case 325021616: + break; + case 353103893: + break; + case 369315063: + break; + case 375032009: + break; + case 383030819: + break; + case 438421327: + break; + case 487334413: + break; + case 491858238: + break; + case 505523517: + break; + case 516961236: + break; + case 665843328: + break; + case 671337916: + break; + case 737478748: + break; + case 738893626: + break; + case 745969447: + break; + case 770498827: + break; + case 776138553: + break; + case 828944778: + break; + case 846088000: + break; + case 850563927: + break; + case 851278306: + break; + case 873235173: + break; + case 908763827: + break; + case 933423720: + break; + case 973193329: + break; + case 997117913: + break; + case 1071332590: + break; + case 1076953756: + break; + case 1078812459: + break; + case 1133777670: + break; + case 1142656251: + break; + case 1145198778: + break; + case 1247831734: + break; + case 1260711798: + break; + case 1287805733: + break; + case 1312904398: + break; + case 1343242579: + break; + case 1391410207: + break; + case 1401244028: + break; + case 1410262602: + break; + case 1414192097: + break; + case 1428236656: + break; + case 1445374288: + break; + case 1488475261: + break; + case 1542263633: + break; + case 1592332600: + break; + case 1600636622: + break; + case 1627523232: + break; + case 1681397778: + break; + case 1721380104: + break; + case 1728372347: + break; + case 1733332192: + break; + case 1767264297: + break; + case 1790214156: + break; + case 1792749467: + break; + case 1805746613: + break; + case 1824308900: + break; + case 1830861979: + break; + case 1841735333: + break; + case 1922784394: + break; + case 1957570017: + break; + case 1958052158: + break; + case 1958247177: + break; + case 1965687765: + break; + case 1989867553: + break; + case 2000952482: + break; + case 2023747466: + break; + case 2043677302: + break; + case 2052815575: + break; + case 2082457694: + break; + case 2093211201: + break; + default: + break; + } + + return v; + } + + private static int field0000; + private static int field0001; + private static int field0002; + private static int field0003; + private static int field0004; + private static int field0005; + private static int field0006; + private static int field0007; + private static int field0008; + private static int field0009; + private static int field0010; + private static int field0011; + private static int field0012; + private static int field0013; + private static int field0014; + private static int field0015; + private static int field0016; + private static int field0017; + private static int field0018; + private static int field0019; + private static int field0020; + private static int field0021; + private static int field0022; + private static int field0023; + private static int field0024; + private static int field0025; + private static int field0026; + private static int field0027; + private static int field0028; + private static int field0029; + private static int field0030; + private static int field0031; + private static int field0032; + private static int field0033; + private static int field0034; + private static int field0035; + private static int field0036; + private static int field0037; + private static int field0038; + private static int field0039; + private static int field0040; + private static int field0041; + private static int field0042; + private static int field0043; + private static int field0044; + private static int field0045; + private static int field0046; + private static int field0047; + private static int field0048; + private static int field0049; + private static int field0050; + private static int field0051; + private static int field0052; + private static int field0053; + private static int field0054; + private static int field0055; + private static int field0056; + private static int field0057; + private static int field0058; + private static int field0059; + private static int field0060; + private static int field0061; + private static int field0062; + private static int field0063; + private static int field0064; + private static int field0065; + private static int field0066; + private static int field0067; + private static int field0068; + private static int field0069; + private static int field0070; + private static int field0071; + private static int field0072; + private static int field0073; + private static int field0074; + private static int field0075; + private static int field0076; + private static int field0077; + private static int field0078; + private static int field0079; + private static int field0080; + private static int field0081; + private static int field0082; + private static int field0083; + private static int field0084; + private static int field0085; + private static int field0086; + private static int field0087; + private static int field0088; + private static int field0089; + private static int field0090; + private static int field0091; + private static int field0092; + private static int field0093; + private static int field0094; + private static int field0095; + private static int field0096; + private static int field0097; + private static int field0098; + private static int field0099; + private static int field0100; + private static int field0101; + private static int field0102; + private static int field0103; + private static int field0104; + private static int field0105; + private static int field0106; + private static int field0107; + private static int field0108; + private static int field0109; + private static int field0110; + private static int field0111; + private static int field0112; + private static int field0113; + private static int field0114; + private static int field0115; + private static int field0116; + private static int field0117; + private static int field0118; + private static int field0119; + private static int field0120; + private static int field0121; + private static int field0122; + private static int field0123; + private static int field0124; + private static int field0125; + private static int field0126; + private static int field0127; + private static int field0128; + private static int field0129; + private static int field0130; + private static int field0131; + private static int field0132; + private static int field0133; + private static int field0134; + private static int field0135; + private static int field0136; + private static int field0137; + private static int field0138; + private static int field0139; + private static int field0140; + private static int field0141; + private static int field0142; + private static int field0143; + private static int field0144; + private static int field0145; + private static int field0146; + private static int field0147; + private static int field0148; + private static int field0149; + private static int field0150; + private static int field0151; + private static int field0152; + private static int field0153; + private static int field0154; + private static int field0155; + private static int field0156; + private static int field0157; + private static int field0158; + private static int field0159; + private static int field0160; + private static int field0161; + private static int field0162; + private static int field0163; + private static int field0164; + private static int field0165; + private static int field0166; + private static int field0167; + private static int field0168; + private static int field0169; + private static int field0170; + private static int field0171; + private static int field0172; + private static int field0173; + private static int field0174; + private static int field0175; + private static int field0176; + private static int field0177; + private static int field0178; + private static int field0179; + private static int field0180; + private static int field0181; + private static int field0182; + private static int field0183; + private static int field0184; + private static int field0185; + private static int field0186; + private static int field0187; + private static int field0188; + private static int field0189; + private static int field0190; + private static int field0191; + private static int field0192; + private static int field0193; + private static int field0194; + private static int field0195; + private static int field0196; + private static int field0197; + private static int field0198; + private static int field0199; + private static int field0200; + private static int field0201; + private static int field0202; + private static int field0203; + private static int field0204; + private static int field0205; + private static int field0206; + private static int field0207; + private static int field0208; + private static int field0209; + private static int field0210; + private static int field0211; + private static int field0212; + private static int field0213; + private static int field0214; + private static int field0215; + private static int field0216; + private static int field0217; + private static int field0218; + private static int field0219; + private static int field0220; + private static int field0221; + private static int field0222; + private static int field0223; + private static int field0224; + private static int field0225; + private static int field0226; + private static int field0227; + private static int field0228; + private static int field0229; + private static int field0230; + private static int field0231; + private static int field0232; + private static int field0233; + private static int field0234; + private static int field0235; + private static int field0236; + private static int field0237; + private static int field0238; + private static int field0239; + private static int field0240; + private static int field0241; + private static int field0242; + private static int field0243; + private static int field0244; + private static int field0245; + private static int field0246; + private static int field0247; + private static int field0248; + private static int field0249; + private static int field0250; + private static int field0251; + private static int field0252; + private static int field0253; + private static int field0254; + private static int field0255; + private static int field0256; + private static int field0257; + private static int field0258; + private static int field0259; + private static int field0260; + private static int field0261; + private static int field0262; + private static int field0263; + private static int field0264; + private static int field0265; + private static int field0266; + private static int field0267; + private static int field0268; + private static int field0269; + private static int field0270; + private static int field0271; + private static int field0272; + private static int field0273; + private static int field0274; + private static int field0275; + private static int field0276; + private static int field0277; + private static int field0278; + private static int field0279; + private static int field0280; + private static int field0281; + private static int field0282; + private static int field0283; + private static int field0284; + private static int field0285; + private static int field0286; + private static int field0287; + private static int field0288; + private static int field0289; + private static int field0290; + private static int field0291; + private static int field0292; + private static int field0293; + private static int field0294; + private static int field0295; + private static int field0296; + private static int field0297; + private static int field0298; + private static int field0299; + private static int field0300; + private static int field0301; + private static int field0302; + private static int field0303; + private static int field0304; + private static int field0305; + private static int field0306; + private static int field0307; + private static int field0308; + private static int field0309; + private static int field0310; + private static int field0311; + private static int field0312; + private static int field0313; + private static int field0314; + private static int field0315; + private static int field0316; + private static int field0317; + private static int field0318; + private static int field0319; + private static int field0320; + private static int field0321; + private static int field0322; + private static int field0323; + private static int field0324; + private static int field0325; + private static int field0326; + private static int field0327; + private static int field0328; + private static int field0329; + private static int field0330; + private static int field0331; + private static int field0332; + private static int field0333; + private static int field0334; + private static int field0335; + private static int field0336; + private static int field0337; + private static int field0338; + private static int field0339; + private static int field0340; + private static int field0341; + private static int field0342; + private static int field0343; + private static int field0344; + private static int field0345; + private static int field0346; + private static int field0347; + private static int field0348; + private static int field0349; + private static int field0350; + private static int field0351; + private static int field0352; + private static int field0353; + private static int field0354; + private static int field0355; + private static int field0356; + private static int field0357; + private static int field0358; + private static int field0359; + private static int field0360; + private static int field0361; + private static int field0362; + private static int field0363; + private static int field0364; + private static int field0365; + private static int field0366; + private static int field0367; + private static int field0368; + private static int field0369; + private static int field0370; + private static int field0371; + private static int field0372; + private static int field0373; + private static int field0374; + private static int field0375; + private static int field0376; + private static int field0377; + private static int field0378; + private static int field0379; + private static int field0380; + private static int field0381; + private static int field0382; + private static int field0383; + private static int field0384; + private static int field0385; + private static int field0386; + private static int field0387; + private static int field0388; + private static int field0389; + private static int field0390; + private static int field0391; + private static int field0392; + private static int field0393; + private static int field0394; + private static int field0395; + private static int field0396; + private static int field0397; + private static int field0398; + private static int field0399; + private static int field0400; + private static int field0401; + private static int field0402; + private static int field0403; + private static int field0404; + private static int field0405; + private static int field0406; + private static int field0407; + private static int field0408; + private static int field0409; + private static int field0410; + private static int field0411; + private static int field0412; + private static int field0413; + private static int field0414; + private static int field0415; + private static int field0416; + private static int field0417; + private static int field0418; + private static int field0419; + private static int field0420; + private static int field0421; + private static int field0422; + private static int field0423; + private static int field0424; + private static int field0425; + private static int field0426; + private static int field0427; + private static int field0428; + private static int field0429; + private static int field0430; + private static int field0431; + private static int field0432; + private static int field0433; + private static int field0434; + private static int field0435; + private static int field0436; + private static int field0437; + private static int field0438; + private static int field0439; + private static int field0440; + private static int field0441; + private static int field0442; + private static int field0443; + private static int field0444; + private static int field0445; + private static int field0446; + private static int field0447; + private static int field0448; + private static int field0449; + private static int field0450; + private static int field0451; + private static int field0452; + private static int field0453; + private static int field0454; + private static int field0455; + private static int field0456; + private static int field0457; + private static int field0458; + private static int field0459; + private static int field0460; + private static int field0461; + private static int field0462; + private static int field0463; + private static int field0464; + private static int field0465; + private static int field0466; + private static int field0467; + private static int field0468; + private static int field0469; + private static int field0470; + private static int field0471; + private static int field0472; + private static int field0473; + private static int field0474; + private static int field0475; + private static int field0476; + private static int field0477; + private static int field0478; + private static int field0479; + private static int field0480; + private static int field0481; + private static int field0482; + private static int field0483; + private static int field0484; + private static int field0485; + private static int field0486; + private static int field0487; + private static int field0488; + private static int field0489; + private static int field0490; + private static int field0491; + private static int field0492; + private static int field0493; + private static int field0494; + private static int field0495; + private static int field0496; + private static int field0497; + private static int field0498; + private static int field0499; + private static int field0500; + private static int field0501; + private static int field0502; + private static int field0503; + private static int field0504; + private static int field0505; + private static int field0506; + private static int field0507; + private static int field0508; + private static int field0509; + private static int field0510; + private static int field0511; + private static int field0512; + private static int field0513; + private static int field0514; + private static int field0515; + private static int field0516; + private static int field0517; + private static int field0518; + private static int field0519; + private static int field0520; + private static int field0521; + private static int field0522; + private static int field0523; + private static int field0524; + private static int field0525; + private static int field0526; + private static int field0527; + private static int field0528; + private static int field0529; + private static int field0530; + private static int field0531; + private static int field0532; + private static int field0533; + private static int field0534; + private static int field0535; + private static int field0536; + private static int field0537; + private static int field0538; + private static int field0539; + private static int field0540; + private static int field0541; + private static int field0542; + private static int field0543; + private static int field0544; + private static int field0545; + private static int field0546; + private static int field0547; + private static int field0548; + private static int field0549; + private static int field0550; + private static int field0551; + private static int field0552; + private static int field0553; + private static int field0554; + private static int field0555; + private static int field0556; + private static int field0557; + private static int field0558; + private static int field0559; + private static int field0560; + private static int field0561; + private static int field0562; + private static int field0563; + private static int field0564; + private static int field0565; + private static int field0566; + private static int field0567; + private static int field0568; + private static int field0569; + private static int field0570; + private static int field0571; + private static int field0572; + private static int field0573; + private static int field0574; + private static int field0575; + private static int field0576; + private static int field0577; + private static int field0578; + private static int field0579; + private static int field0580; + private static int field0581; + private static int field0582; + private static int field0583; + private static int field0584; + private static int field0585; + private static int field0586; + private static int field0587; + private static int field0588; + private static int field0589; + private static int field0590; + private static int field0591; + private static int field0592; + private static int field0593; + private static int field0594; + private static int field0595; + private static int field0596; + private static int field0597; + private static int field0598; + private static int field0599; + private static int field0600; + private static int field0601; + private static int field0602; + private static int field0603; + private static int field0604; + private static int field0605; + private static int field0606; + private static int field0607; + private static int field0608; + private static int field0609; + private static int field0610; + private static int field0611; + private static int field0612; + private static int field0613; + private static int field0614; + private static int field0615; + private static int field0616; + private static int field0617; + private static int field0618; + private static int field0619; + private static int field0620; + private static int field0621; + private static int field0622; + private static int field0623; + private static int field0624; + private static int field0625; + private static int field0626; + private static int field0627; + private static int field0628; + private static int field0629; + private static int field0630; + private static int field0631; + private static int field0632; + private static int field0633; + private static int field0634; + private static int field0635; + private static int field0636; + private static int field0637; + private static int field0638; + private static int field0639; + private static int field0640; + private static int field0641; + private static int field0642; + private static int field0643; + private static int field0644; + private static int field0645; + private static int field0646; + private static int field0647; + private static int field0648; + private static int field0649; + private static int field0650; + private static int field0651; + private static int field0652; + private static int field0653; + private static int field0654; + private static int field0655; + private static int field0656; + private static int field0657; + private static int field0658; + private static int field0659; + private static int field0660; + private static int field0661; + private static int field0662; + private static int field0663; + private static int field0664; + private static int field0665; + private static int field0666; + private static int field0667; + private static int field0668; + private static int field0669; + private static int field0670; + private static int field0671; + private static int field0672; + private static int field0673; + private static int field0674; + private static int field0675; + private static int field0676; + private static int field0677; + private static int field0678; + private static int field0679; + private static int field0680; + private static int field0681; + private static int field0682; + private static int field0683; + private static int field0684; + private static int field0685; + private static int field0686; + private static int field0687; + private static int field0688; + private static int field0689; + private static int field0690; + private static int field0691; + private static int field0692; + private static int field0693; + private static int field0694; + private static int field0695; + private static int field0696; + private static int field0697; + private static int field0698; + private static int field0699; + private static int field0700; + private static int field0701; + private static int field0702; + private static int field0703; + private static int field0704; + private static int field0705; + private static int field0706; + private static int field0707; + private static int field0708; + private static int field0709; + private static int field0710; + private static int field0711; + private static int field0712; + private static int field0713; + private static int field0714; + private static int field0715; + private static int field0716; + private static int field0717; + private static int field0718; + private static int field0719; + private static int field0720; + private static int field0721; + private static int field0722; + private static int field0723; + private static int field0724; + private static int field0725; + private static int field0726; + private static int field0727; + private static int field0728; + private static int field0729; + private static int field0730; + private static int field0731; + private static int field0732; + private static int field0733; + private static int field0734; + private static int field0735; + private static int field0736; + private static int field0737; + private static int field0738; + private static int field0739; + private static int field0740; + private static int field0741; + private static int field0742; + private static int field0743; + private static int field0744; + private static int field0745; + private static int field0746; + private static int field0747; + private static int field0748; + private static int field0749; + private static int field0750; + private static int field0751; + private static int field0752; + private static int field0753; + private static int field0754; + private static int field0755; + private static int field0756; + private static int field0757; + private static int field0758; + private static int field0759; + private static int field0760; + private static int field0761; + private static int field0762; + private static int field0763; + private static int field0764; + private static int field0765; + private static int field0766; + private static int field0767; + private static int field0768; + private static int field0769; + private static int field0770; + private static int field0771; + private static int field0772; + private static int field0773; + private static int field0774; + private static int field0775; + private static int field0776; + private static int field0777; + private static int field0778; + private static int field0779; + private static int field0780; + private static int field0781; + private static int field0782; + private static int field0783; + private static int field0784; + private static int field0785; + private static int field0786; + private static int field0787; + private static int field0788; + private static int field0789; + private static int field0790; + private static int field0791; + private static int field0792; + private static int field0793; + private static int field0794; + private static int field0795; + private static int field0796; + private static int field0797; + private static int field0798; + private static int field0799; + private static int field0800; + private static int field0801; + private static int field0802; + private static int field0803; + private static int field0804; + private static int field0805; + private static int field0806; + private static int field0807; + private static int field0808; + private static int field0809; + private static int field0810; + private static int field0811; + private static int field0812; + private static int field0813; + private static int field0814; + private static int field0815; + private static int field0816; + private static int field0817; + private static int field0818; + private static int field0819; + private static int field0820; + private static int field0821; + private static int field0822; + private static int field0823; + private static int field0824; + private static int field0825; + private static int field0826; + private static int field0827; + private static int field0828; + private static int field0829; + private static int field0830; + private static int field0831; + private static int field0832; + private static int field0833; + private static int field0834; + private static int field0835; + private static int field0836; + private static int field0837; + private static int field0838; + private static int field0839; + private static int field0840; + private static int field0841; + private static int field0842; + private static int field0843; + private static int field0844; + private static int field0845; + private static int field0846; + private static int field0847; + private static int field0848; + private static int field0849; + private static int field0850; + private static int field0851; + private static int field0852; + private static int field0853; + private static int field0854; + private static int field0855; + private static int field0856; + private static int field0857; + private static int field0858; + private static int field0859; + private static int field0860; + private static int field0861; + private static int field0862; + private static int field0863; + private static int field0864; + private static int field0865; + private static int field0866; + private static int field0867; + private static int field0868; + private static int field0869; + private static int field0870; + private static int field0871; + private static int field0872; + private static int field0873; + private static int field0874; + private static int field0875; + private static int field0876; + private static int field0877; + private static int field0878; + private static int field0879; + private static int field0880; + private static int field0881; + private static int field0882; + private static int field0883; + private static int field0884; + private static int field0885; + private static int field0886; + private static int field0887; + private static int field0888; + private static int field0889; + private static int field0890; + private static int field0891; + private static int field0892; + private static int field0893; + private static int field0894; + private static int field0895; + private static int field0896; + private static int field0897; + private static int field0898; + private static int field0899; + private static int field0900; + private static int field0901; + private static int field0902; + private static int field0903; + private static int field0904; + private static int field0905; + private static int field0906; + private static int field0907; + private static int field0908; + private static int field0909; + private static int field0910; + private static int field0911; + private static int field0912; + private static int field0913; + private static int field0914; + private static int field0915; + private static int field0916; + private static int field0917; + private static int field0918; + private static int field0919; + private static int field0920; + private static int field0921; + private static int field0922; + private static int field0923; + private static int field0924; + private static int field0925; + private static int field0926; + private static int field0927; + private static int field0928; + private static int field0929; + private static int field0930; + private static int field0931; + private static int field0932; + private static int field0933; + private static int field0934; + private static int field0935; + private static int field0936; + private static int field0937; + private static int field0938; + private static int field0939; + private static int field0940; + private static int field0941; + private static int field0942; + private static int field0943; + private static int field0944; + private static int field0945; + private static int field0946; + private static int field0947; + private static int field0948; + private static int field0949; + private static int field0950; + private static int field0951; + private static int field0952; + private static int field0953; + private static int field0954; + private static int field0955; + private static int field0956; + private static int field0957; + private static int field0958; + private static int field0959; + private static int field0960; + private static int field0961; + private static int field0962; + private static int field0963; + private static int field0964; + private static int field0965; + private static int field0966; + private static int field0967; + private static int field0968; + private static int field0969; + private static int field0970; + private static int field0971; + private static int field0972; + private static int field0973; + private static int field0974; + private static int field0975; + private static int field0976; + private static int field0977; + private static int field0978; + private static int field0979; + private static int field0980; + private static int field0981; + private static int field0982; + private static int field0983; + private static int field0984; + private static int field0985; + private static int field0986; + private static int field0987; + private static int field0988; + private static int field0989; + private static int field0990; + private static int field0991; + private static int field0992; + private static int field0993; + private static int field0994; + private static int field0995; + private static int field0996; + private static int field0997; + private static int field0998; + private static int field0999; +} diff --git a/test/hotspot/jtreg/compiler/codegen/TestAntiDependenciesHighMemUsage2.java b/test/hotspot/jtreg/compiler/codegen/TestAntiDependenciesHighMemUsage2.java new file mode 100644 index 0000000000000..3f7dbf9918bb0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codegen/TestAntiDependenciesHighMemUsage2.java @@ -0,0 +1,8444 @@ +/* + * Copyright (c) 2024, 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 + * 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 8333258 + * @summary C2: high memory usage in PhaseCFG::insert_anti_dependences() + * @run main/othervm -XX:CompileOnly=TestAntiDependenciesHighMemUsage2::test1 -XX:-ClipInlining + * -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:-UseOnStackReplacement TestAntiDependenciesHighMemUsage2 + */ + +public class TestAntiDependenciesHighMemUsage2 { + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test1(); + } + } + + private static void test1_field0000() { + int v = field0000; + v += field0001; + v += field0002; + v += field0003; + v += field0004; + v += field0005; + v += field0006; + v += field0007; + v += field0008; + v += field0009; + v += field0010; + v += field0011; + v += field0012; + v += field0013; + v += field0014; + v += field0015; + v += field0016; + v += field0017; + v += field0018; + v += field0019; + v += field0020; + v += field0021; + v += field0022; + v += field0023; + v += field0024; + v += field0025; + v += field0026; + v += field0027; + v += field0028; + v += field0029; + v += field0030; + v += field0031; + v += field0032; + v += field0033; + v += field0034; + v += field0035; + v += field0036; + v += field0037; + v += field0038; + v += field0039; + v += field0040; + v += field0041; + v += field0042; + v += field0043; + v += field0044; + v += field0045; + v += field0046; + v += field0047; + v += field0048; + v += field0049; + field0049 = v; + } + + private static void test1_field0050() { + int v = field0050; + v += field0051; + v += field0052; + v += field0053; + v += field0054; + v += field0055; + v += field0056; + v += field0057; + v += field0058; + v += field0059; + v += field0060; + v += field0061; + v += field0062; + v += field0063; + v += field0064; + v += field0065; + v += field0066; + v += field0067; + v += field0068; + v += field0069; + v += field0070; + v += field0071; + v += field0072; + v += field0073; + v += field0074; + v += field0075; + v += field0076; + v += field0077; + v += field0078; + v += field0079; + v += field0080; + v += field0081; + v += field0082; + v += field0083; + v += field0084; + v += field0085; + v += field0086; + v += field0087; + v += field0088; + v += field0089; + v += field0090; + v += field0091; + v += field0092; + v += field0093; + v += field0094; + v += field0095; + v += field0096; + v += field0097; + v += field0098; + v += field0099; + field0099 = v; + } + + private static void test1_field0100() { + int v = field0100; + v += field0101; + v += field0102; + v += field0103; + v += field0104; + v += field0105; + v += field0106; + v += field0107; + v += field0108; + v += field0109; + v += field0110; + v += field0111; + v += field0112; + v += field0113; + v += field0114; + v += field0115; + v += field0116; + v += field0117; + v += field0118; + v += field0119; + v += field0120; + v += field0121; + v += field0122; + v += field0123; + v += field0124; + v += field0125; + v += field0126; + v += field0127; + v += field0128; + v += field0129; + v += field0130; + v += field0131; + v += field0132; + v += field0133; + v += field0134; + v += field0135; + v += field0136; + v += field0137; + v += field0138; + v += field0139; + v += field0140; + v += field0141; + v += field0142; + v += field0143; + v += field0144; + v += field0145; + v += field0146; + v += field0147; + v += field0148; + v += field0149; + field0149 = v; + } + + private static void test1_field0150() { + int v = field0150; + v += field0151; + v += field0152; + v += field0153; + v += field0154; + v += field0155; + v += field0156; + v += field0157; + v += field0158; + v += field0159; + v += field0160; + v += field0161; + v += field0162; + v += field0163; + v += field0164; + v += field0165; + v += field0166; + v += field0167; + v += field0168; + v += field0169; + v += field0170; + v += field0171; + v += field0172; + v += field0173; + v += field0174; + v += field0175; + v += field0176; + v += field0177; + v += field0178; + v += field0179; + v += field0180; + v += field0181; + v += field0182; + v += field0183; + v += field0184; + v += field0185; + v += field0186; + v += field0187; + v += field0188; + v += field0189; + v += field0190; + v += field0191; + v += field0192; + v += field0193; + v += field0194; + v += field0195; + v += field0196; + v += field0197; + v += field0198; + v += field0199; + field0199 = v; + } + + private static void test1_field0200() { + int v = field0200; + v += field0201; + v += field0202; + v += field0203; + v += field0204; + v += field0205; + v += field0206; + v += field0207; + v += field0208; + v += field0209; + v += field0210; + v += field0211; + v += field0212; + v += field0213; + v += field0214; + v += field0215; + v += field0216; + v += field0217; + v += field0218; + v += field0219; + v += field0220; + v += field0221; + v += field0222; + v += field0223; + v += field0224; + v += field0225; + v += field0226; + v += field0227; + v += field0228; + v += field0229; + v += field0230; + v += field0231; + v += field0232; + v += field0233; + v += field0234; + v += field0235; + v += field0236; + v += field0237; + v += field0238; + v += field0239; + v += field0240; + v += field0241; + v += field0242; + v += field0243; + v += field0244; + v += field0245; + v += field0246; + v += field0247; + v += field0248; + v += field0249; + field0249 = v; + } + + private static void test1_field0250() { + int v = field0250; + v += field0251; + v += field0252; + v += field0253; + v += field0254; + v += field0255; + v += field0256; + v += field0257; + v += field0258; + v += field0259; + v += field0260; + v += field0261; + v += field0262; + v += field0263; + v += field0264; + v += field0265; + v += field0266; + v += field0267; + v += field0268; + v += field0269; + v += field0270; + v += field0271; + v += field0272; + v += field0273; + v += field0274; + v += field0275; + v += field0276; + v += field0277; + v += field0278; + v += field0279; + v += field0280; + v += field0281; + v += field0282; + v += field0283; + v += field0284; + v += field0285; + v += field0286; + v += field0287; + v += field0288; + v += field0289; + v += field0290; + v += field0291; + v += field0292; + v += field0293; + v += field0294; + v += field0295; + v += field0296; + v += field0297; + v += field0298; + v += field0299; + field0299 = v; + } + + private static void test1_field0300() { + int v = field0300; + v += field0301; + v += field0302; + v += field0303; + v += field0304; + v += field0305; + v += field0306; + v += field0307; + v += field0308; + v += field0309; + v += field0310; + v += field0311; + v += field0312; + v += field0313; + v += field0314; + v += field0315; + v += field0316; + v += field0317; + v += field0318; + v += field0319; + v += field0320; + v += field0321; + v += field0322; + v += field0323; + v += field0324; + v += field0325; + v += field0326; + v += field0327; + v += field0328; + v += field0329; + v += field0330; + v += field0331; + v += field0332; + v += field0333; + v += field0334; + v += field0335; + v += field0336; + v += field0337; + v += field0338; + v += field0339; + v += field0340; + v += field0341; + v += field0342; + v += field0343; + v += field0344; + v += field0345; + v += field0346; + v += field0347; + v += field0348; + v += field0349; + field0349 = v; + } + + private static void test1_field0350() { + int v = field0350; + v += field0351; + v += field0352; + v += field0353; + v += field0354; + v += field0355; + v += field0356; + v += field0357; + v += field0358; + v += field0359; + v += field0360; + v += field0361; + v += field0362; + v += field0363; + v += field0364; + v += field0365; + v += field0366; + v += field0367; + v += field0368; + v += field0369; + v += field0370; + v += field0371; + v += field0372; + v += field0373; + v += field0374; + v += field0375; + v += field0376; + v += field0377; + v += field0378; + v += field0379; + v += field0380; + v += field0381; + v += field0382; + v += field0383; + v += field0384; + v += field0385; + v += field0386; + v += field0387; + v += field0388; + v += field0389; + v += field0390; + v += field0391; + v += field0392; + v += field0393; + v += field0394; + v += field0395; + v += field0396; + v += field0397; + v += field0398; + v += field0399; + field0399 = v; + } + + private static void test1_field0400() { + int v = field0400; + v += field0401; + v += field0402; + v += field0403; + v += field0404; + v += field0405; + v += field0406; + v += field0407; + v += field0408; + v += field0409; + v += field0410; + v += field0411; + v += field0412; + v += field0413; + v += field0414; + v += field0415; + v += field0416; + v += field0417; + v += field0418; + v += field0419; + v += field0420; + v += field0421; + v += field0422; + v += field0423; + v += field0424; + v += field0425; + v += field0426; + v += field0427; + v += field0428; + v += field0429; + v += field0430; + v += field0431; + v += field0432; + v += field0433; + v += field0434; + v += field0435; + v += field0436; + v += field0437; + v += field0438; + v += field0439; + v += field0440; + v += field0441; + v += field0442; + v += field0443; + v += field0444; + v += field0445; + v += field0446; + v += field0447; + v += field0448; + v += field0449; + field0449 = v; + } + + private static void test1_field0450() { + int v = field0450; + v += field0451; + v += field0452; + v += field0453; + v += field0454; + v += field0455; + v += field0456; + v += field0457; + v += field0458; + v += field0459; + v += field0460; + v += field0461; + v += field0462; + v += field0463; + v += field0464; + v += field0465; + v += field0466; + v += field0467; + v += field0468; + v += field0469; + v += field0470; + v += field0471; + v += field0472; + v += field0473; + v += field0474; + v += field0475; + v += field0476; + v += field0477; + v += field0478; + v += field0479; + v += field0480; + v += field0481; + v += field0482; + v += field0483; + v += field0484; + v += field0485; + v += field0486; + v += field0487; + v += field0488; + v += field0489; + v += field0490; + v += field0491; + v += field0492; + v += field0493; + v += field0494; + v += field0495; + v += field0496; + v += field0497; + v += field0498; + v += field0499; + field0499 = v; + } + + private static void test1_field0500() { + int v = field0500; + v += field0501; + v += field0502; + v += field0503; + v += field0504; + v += field0505; + v += field0506; + v += field0507; + v += field0508; + v += field0509; + v += field0510; + v += field0511; + v += field0512; + v += field0513; + v += field0514; + v += field0515; + v += field0516; + v += field0517; + v += field0518; + v += field0519; + v += field0520; + v += field0521; + v += field0522; + v += field0523; + v += field0524; + v += field0525; + v += field0526; + v += field0527; + v += field0528; + v += field0529; + v += field0530; + v += field0531; + v += field0532; + v += field0533; + v += field0534; + v += field0535; + v += field0536; + v += field0537; + v += field0538; + v += field0539; + v += field0540; + v += field0541; + v += field0542; + v += field0543; + v += field0544; + v += field0545; + v += field0546; + v += field0547; + v += field0548; + v += field0549; + field0549 = v; + } + + private static void test1_field0550() { + int v = field0550; + v += field0551; + v += field0552; + v += field0553; + v += field0554; + v += field0555; + v += field0556; + v += field0557; + v += field0558; + v += field0559; + v += field0560; + v += field0561; + v += field0562; + v += field0563; + v += field0564; + v += field0565; + v += field0566; + v += field0567; + v += field0568; + v += field0569; + v += field0570; + v += field0571; + v += field0572; + v += field0573; + v += field0574; + v += field0575; + v += field0576; + v += field0577; + v += field0578; + v += field0579; + v += field0580; + v += field0581; + v += field0582; + v += field0583; + v += field0584; + v += field0585; + v += field0586; + v += field0587; + v += field0588; + v += field0589; + v += field0590; + v += field0591; + v += field0592; + v += field0593; + v += field0594; + v += field0595; + v += field0596; + v += field0597; + v += field0598; + v += field0599; + field0599 = v; + } + + private static void test1_field0600() { + int v = field0600; + v += field0601; + v += field0602; + v += field0603; + v += field0604; + v += field0605; + v += field0606; + v += field0607; + v += field0608; + v += field0609; + v += field0610; + v += field0611; + v += field0612; + v += field0613; + v += field0614; + v += field0615; + v += field0616; + v += field0617; + v += field0618; + v += field0619; + v += field0620; + v += field0621; + v += field0622; + v += field0623; + v += field0624; + v += field0625; + v += field0626; + v += field0627; + v += field0628; + v += field0629; + v += field0630; + v += field0631; + v += field0632; + v += field0633; + v += field0634; + v += field0635; + v += field0636; + v += field0637; + v += field0638; + v += field0639; + v += field0640; + v += field0641; + v += field0642; + v += field0643; + v += field0644; + v += field0645; + v += field0646; + v += field0647; + v += field0648; + v += field0649; + field0649 = v; + } + + private static void test1_field0650() { + int v = field0650; + v += field0651; + v += field0652; + v += field0653; + v += field0654; + v += field0655; + v += field0656; + v += field0657; + v += field0658; + v += field0659; + v += field0660; + v += field0661; + v += field0662; + v += field0663; + v += field0664; + v += field0665; + v += field0666; + v += field0667; + v += field0668; + v += field0669; + v += field0670; + v += field0671; + v += field0672; + v += field0673; + v += field0674; + v += field0675; + v += field0676; + v += field0677; + v += field0678; + v += field0679; + v += field0680; + v += field0681; + v += field0682; + v += field0683; + v += field0684; + v += field0685; + v += field0686; + v += field0687; + v += field0688; + v += field0689; + v += field0690; + v += field0691; + v += field0692; + v += field0693; + v += field0694; + v += field0695; + v += field0696; + v += field0697; + v += field0698; + v += field0699; + field0699 = v; + } + + private static void test1_field0700() { + int v = field0700; + v += field0701; + v += field0702; + v += field0703; + v += field0704; + v += field0705; + v += field0706; + v += field0707; + v += field0708; + v += field0709; + v += field0710; + v += field0711; + v += field0712; + v += field0713; + v += field0714; + v += field0715; + v += field0716; + v += field0717; + v += field0718; + v += field0719; + v += field0720; + v += field0721; + v += field0722; + v += field0723; + v += field0724; + v += field0725; + v += field0726; + v += field0727; + v += field0728; + v += field0729; + v += field0730; + v += field0731; + v += field0732; + v += field0733; + v += field0734; + v += field0735; + v += field0736; + v += field0737; + v += field0738; + v += field0739; + v += field0740; + v += field0741; + v += field0742; + v += field0743; + v += field0744; + v += field0745; + v += field0746; + v += field0747; + v += field0748; + v += field0749; + field0749 = v; + } + + private static void test1_field0750() { + int v = field0750; + v += field0751; + v += field0752; + v += field0753; + v += field0754; + v += field0755; + v += field0756; + v += field0757; + v += field0758; + v += field0759; + v += field0760; + v += field0761; + v += field0762; + v += field0763; + v += field0764; + v += field0765; + v += field0766; + v += field0767; + v += field0768; + v += field0769; + v += field0770; + v += field0771; + v += field0772; + v += field0773; + v += field0774; + v += field0775; + v += field0776; + v += field0777; + v += field0778; + v += field0779; + v += field0780; + v += field0781; + v += field0782; + v += field0783; + v += field0784; + v += field0785; + v += field0786; + v += field0787; + v += field0788; + v += field0789; + v += field0790; + v += field0791; + v += field0792; + v += field0793; + v += field0794; + v += field0795; + v += field0796; + v += field0797; + v += field0798; + v += field0799; + field0799 = v; + } + + private static void test1_field0800() { + int v = field0800; + v += field0801; + v += field0802; + v += field0803; + v += field0804; + v += field0805; + v += field0806; + v += field0807; + v += field0808; + v += field0809; + v += field0810; + v += field0811; + v += field0812; + v += field0813; + v += field0814; + v += field0815; + v += field0816; + v += field0817; + v += field0818; + v += field0819; + v += field0820; + v += field0821; + v += field0822; + v += field0823; + v += field0824; + v += field0825; + v += field0826; + v += field0827; + v += field0828; + v += field0829; + v += field0830; + v += field0831; + v += field0832; + v += field0833; + v += field0834; + v += field0835; + v += field0836; + v += field0837; + v += field0838; + v += field0839; + v += field0840; + v += field0841; + v += field0842; + v += field0843; + v += field0844; + v += field0845; + v += field0846; + v += field0847; + v += field0848; + v += field0849; + field0849 = v; + } + + private static void test1_field0850() { + int v = field0850; + v += field0851; + v += field0852; + v += field0853; + v += field0854; + v += field0855; + v += field0856; + v += field0857; + v += field0858; + v += field0859; + v += field0860; + v += field0861; + v += field0862; + v += field0863; + v += field0864; + v += field0865; + v += field0866; + v += field0867; + v += field0868; + v += field0869; + v += field0870; + v += field0871; + v += field0872; + v += field0873; + v += field0874; + v += field0875; + v += field0876; + v += field0877; + v += field0878; + v += field0879; + v += field0880; + v += field0881; + v += field0882; + v += field0883; + v += field0884; + v += field0885; + v += field0886; + v += field0887; + v += field0888; + v += field0889; + v += field0890; + v += field0891; + v += field0892; + v += field0893; + v += field0894; + v += field0895; + v += field0896; + v += field0897; + v += field0898; + v += field0899; + field0899 = v; + } + + private static void test1_field0900() { + int v = field0900; + v += field0901; + v += field0902; + v += field0903; + v += field0904; + v += field0905; + v += field0906; + v += field0907; + v += field0908; + v += field0909; + v += field0910; + v += field0911; + v += field0912; + v += field0913; + v += field0914; + v += field0915; + v += field0916; + v += field0917; + v += field0918; + v += field0919; + v += field0920; + v += field0921; + v += field0922; + v += field0923; + v += field0924; + v += field0925; + v += field0926; + v += field0927; + v += field0928; + v += field0929; + v += field0930; + v += field0931; + v += field0932; + v += field0933; + v += field0934; + v += field0935; + v += field0936; + v += field0937; + v += field0938; + v += field0939; + v += field0940; + v += field0941; + v += field0942; + v += field0943; + v += field0944; + v += field0945; + v += field0946; + v += field0947; + v += field0948; + v += field0949; + field0949 = v; + } + + private static void test1_field0950() { + int v = field0950; + v += field0951; + v += field0952; + v += field0953; + v += field0954; + v += field0955; + v += field0956; + v += field0957; + v += field0958; + v += field0959; + v += field0960; + v += field0961; + v += field0962; + v += field0963; + v += field0964; + v += field0965; + v += field0966; + v += field0967; + v += field0968; + v += field0969; + v += field0970; + v += field0971; + v += field0972; + v += field0973; + v += field0974; + v += field0975; + v += field0976; + v += field0977; + v += field0978; + v += field0979; + v += field0980; + v += field0981; + v += field0982; + v += field0983; + v += field0984; + v += field0985; + v += field0986; + v += field0987; + v += field0988; + v += field0989; + v += field0990; + v += field0991; + v += field0992; + v += field0993; + v += field0994; + v += field0995; + v += field0996; + v += field0997; + v += field0998; + v += field0999; + field0999 = v; + } + + private static void test1_field1000() { + int v = field1000; + v += field1001; + v += field1002; + v += field1003; + v += field1004; + v += field1005; + v += field1006; + v += field1007; + v += field1008; + v += field1009; + v += field1010; + v += field1011; + v += field1012; + v += field1013; + v += field1014; + v += field1015; + v += field1016; + v += field1017; + v += field1018; + v += field1019; + v += field1020; + v += field1021; + v += field1022; + v += field1023; + v += field1024; + v += field1025; + v += field1026; + v += field1027; + v += field1028; + v += field1029; + v += field1030; + v += field1031; + v += field1032; + v += field1033; + v += field1034; + v += field1035; + v += field1036; + v += field1037; + v += field1038; + v += field1039; + v += field1040; + v += field1041; + v += field1042; + v += field1043; + v += field1044; + v += field1045; + v += field1046; + v += field1047; + v += field1048; + v += field1049; + field1049 = v; + } + + private static void test1_field1050() { + int v = field1050; + v += field1051; + v += field1052; + v += field1053; + v += field1054; + v += field1055; + v += field1056; + v += field1057; + v += field1058; + v += field1059; + v += field1060; + v += field1061; + v += field1062; + v += field1063; + v += field1064; + v += field1065; + v += field1066; + v += field1067; + v += field1068; + v += field1069; + v += field1070; + v += field1071; + v += field1072; + v += field1073; + v += field1074; + v += field1075; + v += field1076; + v += field1077; + v += field1078; + v += field1079; + v += field1080; + v += field1081; + v += field1082; + v += field1083; + v += field1084; + v += field1085; + v += field1086; + v += field1087; + v += field1088; + v += field1089; + v += field1090; + v += field1091; + v += field1092; + v += field1093; + v += field1094; + v += field1095; + v += field1096; + v += field1097; + v += field1098; + v += field1099; + field1099 = v; + } + + private static void test1_field1100() { + int v = field1100; + v += field1101; + v += field1102; + v += field1103; + v += field1104; + v += field1105; + v += field1106; + v += field1107; + v += field1108; + v += field1109; + v += field1110; + v += field1111; + v += field1112; + v += field1113; + v += field1114; + v += field1115; + v += field1116; + v += field1117; + v += field1118; + v += field1119; + v += field1120; + v += field1121; + v += field1122; + v += field1123; + v += field1124; + v += field1125; + v += field1126; + v += field1127; + v += field1128; + v += field1129; + v += field1130; + v += field1131; + v += field1132; + v += field1133; + v += field1134; + v += field1135; + v += field1136; + v += field1137; + v += field1138; + v += field1139; + v += field1140; + v += field1141; + v += field1142; + v += field1143; + v += field1144; + v += field1145; + v += field1146; + v += field1147; + v += field1148; + v += field1149; + field1149 = v; + } + + private static void test1_field1150() { + int v = field1150; + v += field1151; + v += field1152; + v += field1153; + v += field1154; + v += field1155; + v += field1156; + v += field1157; + v += field1158; + v += field1159; + v += field1160; + v += field1161; + v += field1162; + v += field1163; + v += field1164; + v += field1165; + v += field1166; + v += field1167; + v += field1168; + v += field1169; + v += field1170; + v += field1171; + v += field1172; + v += field1173; + v += field1174; + v += field1175; + v += field1176; + v += field1177; + v += field1178; + v += field1179; + v += field1180; + v += field1181; + v += field1182; + v += field1183; + v += field1184; + v += field1185; + v += field1186; + v += field1187; + v += field1188; + v += field1189; + v += field1190; + v += field1191; + v += field1192; + v += field1193; + v += field1194; + v += field1195; + v += field1196; + v += field1197; + v += field1198; + v += field1199; + field1199 = v; + } + + private static void test1_field1200() { + int v = field1200; + v += field1201; + v += field1202; + v += field1203; + v += field1204; + v += field1205; + v += field1206; + v += field1207; + v += field1208; + v += field1209; + v += field1210; + v += field1211; + v += field1212; + v += field1213; + v += field1214; + v += field1215; + v += field1216; + v += field1217; + v += field1218; + v += field1219; + v += field1220; + v += field1221; + v += field1222; + v += field1223; + v += field1224; + v += field1225; + v += field1226; + v += field1227; + v += field1228; + v += field1229; + v += field1230; + v += field1231; + v += field1232; + v += field1233; + v += field1234; + v += field1235; + v += field1236; + v += field1237; + v += field1238; + v += field1239; + v += field1240; + v += field1241; + v += field1242; + v += field1243; + v += field1244; + v += field1245; + v += field1246; + v += field1247; + v += field1248; + v += field1249; + field1249 = v; + } + + private static void test1_field1250() { + int v = field1250; + v += field1251; + v += field1252; + v += field1253; + v += field1254; + v += field1255; + v += field1256; + v += field1257; + v += field1258; + v += field1259; + v += field1260; + v += field1261; + v += field1262; + v += field1263; + v += field1264; + v += field1265; + v += field1266; + v += field1267; + v += field1268; + v += field1269; + v += field1270; + v += field1271; + v += field1272; + v += field1273; + v += field1274; + v += field1275; + v += field1276; + v += field1277; + v += field1278; + v += field1279; + v += field1280; + v += field1281; + v += field1282; + v += field1283; + v += field1284; + v += field1285; + v += field1286; + v += field1287; + v += field1288; + v += field1289; + v += field1290; + v += field1291; + v += field1292; + v += field1293; + v += field1294; + v += field1295; + v += field1296; + v += field1297; + v += field1298; + v += field1299; + field1299 = v; + } + + private static void test1_field1300() { + int v = field1300; + v += field1301; + v += field1302; + v += field1303; + v += field1304; + v += field1305; + v += field1306; + v += field1307; + v += field1308; + v += field1309; + v += field1310; + v += field1311; + v += field1312; + v += field1313; + v += field1314; + v += field1315; + v += field1316; + v += field1317; + v += field1318; + v += field1319; + v += field1320; + v += field1321; + v += field1322; + v += field1323; + v += field1324; + v += field1325; + v += field1326; + v += field1327; + v += field1328; + v += field1329; + v += field1330; + v += field1331; + v += field1332; + v += field1333; + v += field1334; + v += field1335; + v += field1336; + v += field1337; + v += field1338; + v += field1339; + v += field1340; + v += field1341; + v += field1342; + v += field1343; + v += field1344; + v += field1345; + v += field1346; + v += field1347; + v += field1348; + v += field1349; + field1349 = v; + } + + private static void test1_field1350() { + int v = field1350; + v += field1351; + v += field1352; + v += field1353; + v += field1354; + v += field1355; + v += field1356; + v += field1357; + v += field1358; + v += field1359; + v += field1360; + v += field1361; + v += field1362; + v += field1363; + v += field1364; + v += field1365; + v += field1366; + v += field1367; + v += field1368; + v += field1369; + v += field1370; + v += field1371; + v += field1372; + v += field1373; + v += field1374; + v += field1375; + v += field1376; + v += field1377; + v += field1378; + v += field1379; + v += field1380; + v += field1381; + v += field1382; + v += field1383; + v += field1384; + v += field1385; + v += field1386; + v += field1387; + v += field1388; + v += field1389; + v += field1390; + v += field1391; + v += field1392; + v += field1393; + v += field1394; + v += field1395; + v += field1396; + v += field1397; + v += field1398; + v += field1399; + field1399 = v; + } + + private static void test1_field1400() { + int v = field1400; + v += field1401; + v += field1402; + v += field1403; + v += field1404; + v += field1405; + v += field1406; + v += field1407; + v += field1408; + v += field1409; + v += field1410; + v += field1411; + v += field1412; + v += field1413; + v += field1414; + v += field1415; + v += field1416; + v += field1417; + v += field1418; + v += field1419; + v += field1420; + v += field1421; + v += field1422; + v += field1423; + v += field1424; + v += field1425; + v += field1426; + v += field1427; + v += field1428; + v += field1429; + v += field1430; + v += field1431; + v += field1432; + v += field1433; + v += field1434; + v += field1435; + v += field1436; + v += field1437; + v += field1438; + v += field1439; + v += field1440; + v += field1441; + v += field1442; + v += field1443; + v += field1444; + v += field1445; + v += field1446; + v += field1447; + v += field1448; + v += field1449; + field1449 = v; + } + + private static void test1_field1450() { + int v = field1450; + v += field1451; + v += field1452; + v += field1453; + v += field1454; + v += field1455; + v += field1456; + v += field1457; + v += field1458; + v += field1459; + v += field1460; + v += field1461; + v += field1462; + v += field1463; + v += field1464; + v += field1465; + v += field1466; + v += field1467; + v += field1468; + v += field1469; + v += field1470; + v += field1471; + v += field1472; + v += field1473; + v += field1474; + v += field1475; + v += field1476; + v += field1477; + v += field1478; + v += field1479; + v += field1480; + v += field1481; + v += field1482; + v += field1483; + v += field1484; + v += field1485; + v += field1486; + v += field1487; + v += field1488; + v += field1489; + v += field1490; + v += field1491; + v += field1492; + v += field1493; + v += field1494; + v += field1495; + v += field1496; + v += field1497; + v += field1498; + v += field1499; + field1499 = v; + } + + private static void test1_field1500() { + int v = field1500; + v += field1501; + v += field1502; + v += field1503; + v += field1504; + v += field1505; + v += field1506; + v += field1507; + v += field1508; + v += field1509; + v += field1510; + v += field1511; + v += field1512; + v += field1513; + v += field1514; + v += field1515; + v += field1516; + v += field1517; + v += field1518; + v += field1519; + v += field1520; + v += field1521; + v += field1522; + v += field1523; + v += field1524; + v += field1525; + v += field1526; + v += field1527; + v += field1528; + v += field1529; + v += field1530; + v += field1531; + v += field1532; + v += field1533; + v += field1534; + v += field1535; + v += field1536; + v += field1537; + v += field1538; + v += field1539; + v += field1540; + v += field1541; + v += field1542; + v += field1543; + v += field1544; + v += field1545; + v += field1546; + v += field1547; + v += field1548; + v += field1549; + field1549 = v; + } + + private static void test1_field1550() { + int v = field1550; + v += field1551; + v += field1552; + v += field1553; + v += field1554; + v += field1555; + v += field1556; + v += field1557; + v += field1558; + v += field1559; + v += field1560; + v += field1561; + v += field1562; + v += field1563; + v += field1564; + v += field1565; + v += field1566; + v += field1567; + v += field1568; + v += field1569; + v += field1570; + v += field1571; + v += field1572; + v += field1573; + v += field1574; + v += field1575; + v += field1576; + v += field1577; + v += field1578; + v += field1579; + v += field1580; + v += field1581; + v += field1582; + v += field1583; + v += field1584; + v += field1585; + v += field1586; + v += field1587; + v += field1588; + v += field1589; + v += field1590; + v += field1591; + v += field1592; + v += field1593; + v += field1594; + v += field1595; + v += field1596; + v += field1597; + v += field1598; + v += field1599; + field1599 = v; + } + + private static void test1_field1600() { + int v = field1600; + v += field1601; + v += field1602; + v += field1603; + v += field1604; + v += field1605; + v += field1606; + v += field1607; + v += field1608; + v += field1609; + v += field1610; + v += field1611; + v += field1612; + v += field1613; + v += field1614; + v += field1615; + v += field1616; + v += field1617; + v += field1618; + v += field1619; + v += field1620; + v += field1621; + v += field1622; + v += field1623; + v += field1624; + v += field1625; + v += field1626; + v += field1627; + v += field1628; + v += field1629; + v += field1630; + v += field1631; + v += field1632; + v += field1633; + v += field1634; + v += field1635; + v += field1636; + v += field1637; + v += field1638; + v += field1639; + v += field1640; + v += field1641; + v += field1642; + v += field1643; + v += field1644; + v += field1645; + v += field1646; + v += field1647; + v += field1648; + v += field1649; + field1649 = v; + } + + private static void test1_field1650() { + int v = field1650; + v += field1651; + v += field1652; + v += field1653; + v += field1654; + v += field1655; + v += field1656; + v += field1657; + v += field1658; + v += field1659; + v += field1660; + v += field1661; + v += field1662; + v += field1663; + v += field1664; + v += field1665; + v += field1666; + v += field1667; + v += field1668; + v += field1669; + v += field1670; + v += field1671; + v += field1672; + v += field1673; + v += field1674; + v += field1675; + v += field1676; + v += field1677; + v += field1678; + v += field1679; + v += field1680; + v += field1681; + v += field1682; + v += field1683; + v += field1684; + v += field1685; + v += field1686; + v += field1687; + v += field1688; + v += field1689; + v += field1690; + v += field1691; + v += field1692; + v += field1693; + v += field1694; + v += field1695; + v += field1696; + v += field1697; + v += field1698; + v += field1699; + field1699 = v; + } + + private static void test1_field1700() { + int v = field1700; + v += field1701; + v += field1702; + v += field1703; + v += field1704; + v += field1705; + v += field1706; + v += field1707; + v += field1708; + v += field1709; + v += field1710; + v += field1711; + v += field1712; + v += field1713; + v += field1714; + v += field1715; + v += field1716; + v += field1717; + v += field1718; + v += field1719; + v += field1720; + v += field1721; + v += field1722; + v += field1723; + v += field1724; + v += field1725; + v += field1726; + v += field1727; + v += field1728; + v += field1729; + v += field1730; + v += field1731; + v += field1732; + v += field1733; + v += field1734; + v += field1735; + v += field1736; + v += field1737; + v += field1738; + v += field1739; + v += field1740; + v += field1741; + v += field1742; + v += field1743; + v += field1744; + v += field1745; + v += field1746; + v += field1747; + v += field1748; + v += field1749; + field1749 = v; + } + + private static void test1_field1750() { + int v = field1750; + v += field1751; + v += field1752; + v += field1753; + v += field1754; + v += field1755; + v += field1756; + v += field1757; + v += field1758; + v += field1759; + v += field1760; + v += field1761; + v += field1762; + v += field1763; + v += field1764; + v += field1765; + v += field1766; + v += field1767; + v += field1768; + v += field1769; + v += field1770; + v += field1771; + v += field1772; + v += field1773; + v += field1774; + v += field1775; + v += field1776; + v += field1777; + v += field1778; + v += field1779; + v += field1780; + v += field1781; + v += field1782; + v += field1783; + v += field1784; + v += field1785; + v += field1786; + v += field1787; + v += field1788; + v += field1789; + v += field1790; + v += field1791; + v += field1792; + v += field1793; + v += field1794; + v += field1795; + v += field1796; + v += field1797; + v += field1798; + v += field1799; + field1799 = v; + } + + private static void test1_field1800() { + int v = field1800; + v += field1801; + v += field1802; + v += field1803; + v += field1804; + v += field1805; + v += field1806; + v += field1807; + v += field1808; + v += field1809; + v += field1810; + v += field1811; + v += field1812; + v += field1813; + v += field1814; + v += field1815; + v += field1816; + v += field1817; + v += field1818; + v += field1819; + v += field1820; + v += field1821; + v += field1822; + v += field1823; + v += field1824; + v += field1825; + v += field1826; + v += field1827; + v += field1828; + v += field1829; + v += field1830; + v += field1831; + v += field1832; + v += field1833; + v += field1834; + v += field1835; + v += field1836; + v += field1837; + v += field1838; + v += field1839; + v += field1840; + v += field1841; + v += field1842; + v += field1843; + v += field1844; + v += field1845; + v += field1846; + v += field1847; + v += field1848; + v += field1849; + field1849 = v; + } + + private static void test1_field1850() { + int v = field1850; + v += field1851; + v += field1852; + v += field1853; + v += field1854; + v += field1855; + v += field1856; + v += field1857; + v += field1858; + v += field1859; + v += field1860; + v += field1861; + v += field1862; + v += field1863; + v += field1864; + v += field1865; + v += field1866; + v += field1867; + v += field1868; + v += field1869; + v += field1870; + v += field1871; + v += field1872; + v += field1873; + v += field1874; + v += field1875; + v += field1876; + v += field1877; + v += field1878; + v += field1879; + v += field1880; + v += field1881; + v += field1882; + v += field1883; + v += field1884; + v += field1885; + v += field1886; + v += field1887; + v += field1888; + v += field1889; + v += field1890; + v += field1891; + v += field1892; + v += field1893; + v += field1894; + v += field1895; + v += field1896; + v += field1897; + v += field1898; + v += field1899; + field1899 = v; + } + + private static void test1_field1900() { + int v = field1900; + v += field1901; + v += field1902; + v += field1903; + v += field1904; + v += field1905; + v += field1906; + v += field1907; + v += field1908; + v += field1909; + v += field1910; + v += field1911; + v += field1912; + v += field1913; + v += field1914; + v += field1915; + v += field1916; + v += field1917; + v += field1918; + v += field1919; + v += field1920; + v += field1921; + v += field1922; + v += field1923; + v += field1924; + v += field1925; + v += field1926; + v += field1927; + v += field1928; + v += field1929; + v += field1930; + v += field1931; + v += field1932; + v += field1933; + v += field1934; + v += field1935; + v += field1936; + v += field1937; + v += field1938; + v += field1939; + v += field1940; + v += field1941; + v += field1942; + v += field1943; + v += field1944; + v += field1945; + v += field1946; + v += field1947; + v += field1948; + v += field1949; + field1949 = v; + } + + private static void test1_field1950() { + int v = field1950; + v += field1951; + v += field1952; + v += field1953; + v += field1954; + v += field1955; + v += field1956; + v += field1957; + v += field1958; + v += field1959; + v += field1960; + v += field1961; + v += field1962; + v += field1963; + v += field1964; + v += field1965; + v += field1966; + v += field1967; + v += field1968; + v += field1969; + v += field1970; + v += field1971; + v += field1972; + v += field1973; + v += field1974; + v += field1975; + v += field1976; + v += field1977; + v += field1978; + v += field1979; + v += field1980; + v += field1981; + v += field1982; + v += field1983; + v += field1984; + v += field1985; + v += field1986; + v += field1987; + v += field1988; + v += field1989; + v += field1990; + v += field1991; + v += field1992; + v += field1993; + v += field1994; + v += field1995; + v += field1996; + v += field1997; + v += field1998; + v += field1999; + field1999 = v; + } + + private static void test1_field2000() { + int v = field2000; + v += field2001; + v += field2002; + v += field2003; + v += field2004; + v += field2005; + v += field2006; + v += field2007; + v += field2008; + v += field2009; + v += field2010; + v += field2011; + v += field2012; + v += field2013; + v += field2014; + v += field2015; + v += field2016; + v += field2017; + v += field2018; + v += field2019; + v += field2020; + v += field2021; + v += field2022; + v += field2023; + v += field2024; + v += field2025; + v += field2026; + v += field2027; + v += field2028; + v += field2029; + v += field2030; + v += field2031; + v += field2032; + v += field2033; + v += field2034; + v += field2035; + v += field2036; + v += field2037; + v += field2038; + v += field2039; + v += field2040; + v += field2041; + v += field2042; + v += field2043; + v += field2044; + v += field2045; + v += field2046; + v += field2047; + v += field2048; + v += field2049; + field2049 = v; + } + + private static void test1_field2050() { + int v = field2050; + v += field2051; + v += field2052; + v += field2053; + v += field2054; + v += field2055; + v += field2056; + v += field2057; + v += field2058; + v += field2059; + v += field2060; + v += field2061; + v += field2062; + v += field2063; + v += field2064; + v += field2065; + v += field2066; + v += field2067; + v += field2068; + v += field2069; + v += field2070; + v += field2071; + v += field2072; + v += field2073; + v += field2074; + v += field2075; + v += field2076; + v += field2077; + v += field2078; + v += field2079; + v += field2080; + v += field2081; + v += field2082; + v += field2083; + v += field2084; + v += field2085; + v += field2086; + v += field2087; + v += field2088; + v += field2089; + v += field2090; + v += field2091; + v += field2092; + v += field2093; + v += field2094; + v += field2095; + v += field2096; + v += field2097; + v += field2098; + v += field2099; + field2099 = v; + } + + private static void test1_field2100() { + int v = field2100; + v += field2101; + v += field2102; + v += field2103; + v += field2104; + v += field2105; + v += field2106; + v += field2107; + v += field2108; + v += field2109; + v += field2110; + v += field2111; + v += field2112; + v += field2113; + v += field2114; + v += field2115; + v += field2116; + v += field2117; + v += field2118; + v += field2119; + v += field2120; + v += field2121; + v += field2122; + v += field2123; + v += field2124; + v += field2125; + v += field2126; + v += field2127; + v += field2128; + v += field2129; + v += field2130; + v += field2131; + v += field2132; + v += field2133; + v += field2134; + v += field2135; + v += field2136; + v += field2137; + v += field2138; + v += field2139; + v += field2140; + v += field2141; + v += field2142; + v += field2143; + v += field2144; + v += field2145; + v += field2146; + v += field2147; + v += field2148; + v += field2149; + field2149 = v; + } + + private static void test1_field2150() { + int v = field2150; + v += field2151; + v += field2152; + v += field2153; + v += field2154; + v += field2155; + v += field2156; + v += field2157; + v += field2158; + v += field2159; + v += field2160; + v += field2161; + v += field2162; + v += field2163; + v += field2164; + v += field2165; + v += field2166; + v += field2167; + v += field2168; + v += field2169; + v += field2170; + v += field2171; + v += field2172; + v += field2173; + v += field2174; + v += field2175; + v += field2176; + v += field2177; + v += field2178; + v += field2179; + v += field2180; + v += field2181; + v += field2182; + v += field2183; + v += field2184; + v += field2185; + v += field2186; + v += field2187; + v += field2188; + v += field2189; + v += field2190; + v += field2191; + v += field2192; + v += field2193; + v += field2194; + v += field2195; + v += field2196; + v += field2197; + v += field2198; + v += field2199; + field2199 = v; + } + + private static void test1_field2200() { + int v = field2200; + v += field2201; + v += field2202; + v += field2203; + v += field2204; + v += field2205; + v += field2206; + v += field2207; + v += field2208; + v += field2209; + v += field2210; + v += field2211; + v += field2212; + v += field2213; + v += field2214; + v += field2215; + v += field2216; + v += field2217; + v += field2218; + v += field2219; + v += field2220; + v += field2221; + v += field2222; + v += field2223; + v += field2224; + v += field2225; + v += field2226; + v += field2227; + v += field2228; + v += field2229; + v += field2230; + v += field2231; + v += field2232; + v += field2233; + v += field2234; + v += field2235; + v += field2236; + v += field2237; + v += field2238; + v += field2239; + v += field2240; + v += field2241; + v += field2242; + v += field2243; + v += field2244; + v += field2245; + v += field2246; + v += field2247; + v += field2248; + v += field2249; + field2249 = v; + } + + private static void test1_field2250() { + int v = field2250; + v += field2251; + v += field2252; + v += field2253; + v += field2254; + v += field2255; + v += field2256; + v += field2257; + v += field2258; + v += field2259; + v += field2260; + v += field2261; + v += field2262; + v += field2263; + v += field2264; + v += field2265; + v += field2266; + v += field2267; + v += field2268; + v += field2269; + v += field2270; + v += field2271; + v += field2272; + v += field2273; + v += field2274; + v += field2275; + v += field2276; + v += field2277; + v += field2278; + v += field2279; + v += field2280; + v += field2281; + v += field2282; + v += field2283; + v += field2284; + v += field2285; + v += field2286; + v += field2287; + v += field2288; + v += field2289; + v += field2290; + v += field2291; + v += field2292; + v += field2293; + v += field2294; + v += field2295; + v += field2296; + v += field2297; + v += field2298; + v += field2299; + field2299 = v; + } + + private static void test1_field2300() { + int v = field2300; + v += field2301; + v += field2302; + v += field2303; + v += field2304; + v += field2305; + v += field2306; + v += field2307; + v += field2308; + v += field2309; + v += field2310; + v += field2311; + v += field2312; + v += field2313; + v += field2314; + v += field2315; + v += field2316; + v += field2317; + v += field2318; + v += field2319; + v += field2320; + v += field2321; + v += field2322; + v += field2323; + v += field2324; + v += field2325; + v += field2326; + v += field2327; + v += field2328; + v += field2329; + v += field2330; + v += field2331; + v += field2332; + v += field2333; + v += field2334; + v += field2335; + v += field2336; + v += field2337; + v += field2338; + v += field2339; + v += field2340; + v += field2341; + v += field2342; + v += field2343; + v += field2344; + v += field2345; + v += field2346; + v += field2347; + v += field2348; + v += field2349; + field2349 = v; + } + + private static void test1_field2350() { + int v = field2350; + v += field2351; + v += field2352; + v += field2353; + v += field2354; + v += field2355; + v += field2356; + v += field2357; + v += field2358; + v += field2359; + v += field2360; + v += field2361; + v += field2362; + v += field2363; + v += field2364; + v += field2365; + v += field2366; + v += field2367; + v += field2368; + v += field2369; + v += field2370; + v += field2371; + v += field2372; + v += field2373; + v += field2374; + v += field2375; + v += field2376; + v += field2377; + v += field2378; + v += field2379; + v += field2380; + v += field2381; + v += field2382; + v += field2383; + v += field2384; + v += field2385; + v += field2386; + v += field2387; + v += field2388; + v += field2389; + v += field2390; + v += field2391; + v += field2392; + v += field2393; + v += field2394; + v += field2395; + v += field2396; + v += field2397; + v += field2398; + v += field2399; + field2399 = v; + } + + private static void test1_field2400() { + int v = field2400; + v += field2401; + v += field2402; + v += field2403; + v += field2404; + v += field2405; + v += field2406; + v += field2407; + v += field2408; + v += field2409; + v += field2410; + v += field2411; + v += field2412; + v += field2413; + v += field2414; + v += field2415; + v += field2416; + v += field2417; + v += field2418; + v += field2419; + v += field2420; + v += field2421; + v += field2422; + v += field2423; + v += field2424; + v += field2425; + v += field2426; + v += field2427; + v += field2428; + v += field2429; + v += field2430; + v += field2431; + v += field2432; + v += field2433; + v += field2434; + v += field2435; + v += field2436; + v += field2437; + v += field2438; + v += field2439; + v += field2440; + v += field2441; + v += field2442; + v += field2443; + v += field2444; + v += field2445; + v += field2446; + v += field2447; + v += field2448; + v += field2449; + field2449 = v; + } + + private static void test1_field2450() { + int v = field2450; + v += field2451; + v += field2452; + v += field2453; + v += field2454; + v += field2455; + v += field2456; + v += field2457; + v += field2458; + v += field2459; + v += field2460; + v += field2461; + v += field2462; + v += field2463; + v += field2464; + v += field2465; + v += field2466; + v += field2467; + v += field2468; + v += field2469; + v += field2470; + v += field2471; + v += field2472; + v += field2473; + v += field2474; + v += field2475; + v += field2476; + v += field2477; + v += field2478; + v += field2479; + v += field2480; + v += field2481; + v += field2482; + v += field2483; + v += field2484; + v += field2485; + v += field2486; + v += field2487; + v += field2488; + v += field2489; + v += field2490; + v += field2491; + v += field2492; + v += field2493; + v += field2494; + v += field2495; + v += field2496; + v += field2497; + v += field2498; + v += field2499; + field2499 = v; + } + + private static void test1_field2500() { + int v = field2500; + v += field2501; + v += field2502; + v += field2503; + v += field2504; + v += field2505; + v += field2506; + v += field2507; + v += field2508; + v += field2509; + v += field2510; + v += field2511; + v += field2512; + v += field2513; + v += field2514; + v += field2515; + v += field2516; + v += field2517; + v += field2518; + v += field2519; + v += field2520; + v += field2521; + v += field2522; + v += field2523; + v += field2524; + v += field2525; + v += field2526; + v += field2527; + v += field2528; + v += field2529; + v += field2530; + v += field2531; + v += field2532; + v += field2533; + v += field2534; + v += field2535; + v += field2536; + v += field2537; + v += field2538; + v += field2539; + v += field2540; + v += field2541; + v += field2542; + v += field2543; + v += field2544; + v += field2545; + v += field2546; + v += field2547; + v += field2548; + v += field2549; + field2549 = v; + } + + private static void test1_field2550() { + int v = field2550; + v += field2551; + v += field2552; + v += field2553; + v += field2554; + v += field2555; + v += field2556; + v += field2557; + v += field2558; + v += field2559; + v += field2560; + v += field2561; + v += field2562; + v += field2563; + v += field2564; + v += field2565; + v += field2566; + v += field2567; + v += field2568; + v += field2569; + v += field2570; + v += field2571; + v += field2572; + v += field2573; + v += field2574; + v += field2575; + v += field2576; + v += field2577; + v += field2578; + v += field2579; + v += field2580; + v += field2581; + v += field2582; + v += field2583; + v += field2584; + v += field2585; + v += field2586; + v += field2587; + v += field2588; + v += field2589; + v += field2590; + v += field2591; + v += field2592; + v += field2593; + v += field2594; + v += field2595; + v += field2596; + v += field2597; + v += field2598; + v += field2599; + field2599 = v; + } + + private static void test1_field2600() { + int v = field2600; + v += field2601; + v += field2602; + v += field2603; + v += field2604; + v += field2605; + v += field2606; + v += field2607; + v += field2608; + v += field2609; + v += field2610; + v += field2611; + v += field2612; + v += field2613; + v += field2614; + v += field2615; + v += field2616; + v += field2617; + v += field2618; + v += field2619; + v += field2620; + v += field2621; + v += field2622; + v += field2623; + v += field2624; + v += field2625; + v += field2626; + v += field2627; + v += field2628; + v += field2629; + v += field2630; + v += field2631; + v += field2632; + v += field2633; + v += field2634; + v += field2635; + v += field2636; + v += field2637; + v += field2638; + v += field2639; + v += field2640; + v += field2641; + v += field2642; + v += field2643; + v += field2644; + v += field2645; + v += field2646; + v += field2647; + v += field2648; + v += field2649; + field2649 = v; + } + + private static void test1_field2650() { + int v = field2650; + v += field2651; + v += field2652; + v += field2653; + v += field2654; + v += field2655; + v += field2656; + v += field2657; + v += field2658; + v += field2659; + v += field2660; + v += field2661; + v += field2662; + v += field2663; + v += field2664; + v += field2665; + v += field2666; + v += field2667; + v += field2668; + v += field2669; + v += field2670; + v += field2671; + v += field2672; + v += field2673; + v += field2674; + v += field2675; + v += field2676; + v += field2677; + v += field2678; + v += field2679; + v += field2680; + v += field2681; + v += field2682; + v += field2683; + v += field2684; + v += field2685; + v += field2686; + v += field2687; + v += field2688; + v += field2689; + v += field2690; + v += field2691; + v += field2692; + v += field2693; + v += field2694; + v += field2695; + v += field2696; + v += field2697; + v += field2698; + v += field2699; + field2699 = v; + } + + private static void test1_field2700() { + int v = field2700; + v += field2701; + v += field2702; + v += field2703; + v += field2704; + v += field2705; + v += field2706; + v += field2707; + v += field2708; + v += field2709; + v += field2710; + v += field2711; + v += field2712; + v += field2713; + v += field2714; + v += field2715; + v += field2716; + v += field2717; + v += field2718; + v += field2719; + v += field2720; + v += field2721; + v += field2722; + v += field2723; + v += field2724; + v += field2725; + v += field2726; + v += field2727; + v += field2728; + v += field2729; + v += field2730; + v += field2731; + v += field2732; + v += field2733; + v += field2734; + v += field2735; + v += field2736; + v += field2737; + v += field2738; + v += field2739; + v += field2740; + v += field2741; + v += field2742; + v += field2743; + v += field2744; + v += field2745; + v += field2746; + v += field2747; + v += field2748; + v += field2749; + field2749 = v; + } + + private static void test1_field2750() { + int v = field2750; + v += field2751; + v += field2752; + v += field2753; + v += field2754; + v += field2755; + v += field2756; + v += field2757; + v += field2758; + v += field2759; + v += field2760; + v += field2761; + v += field2762; + v += field2763; + v += field2764; + v += field2765; + v += field2766; + v += field2767; + v += field2768; + v += field2769; + v += field2770; + v += field2771; + v += field2772; + v += field2773; + v += field2774; + v += field2775; + v += field2776; + v += field2777; + v += field2778; + v += field2779; + v += field2780; + v += field2781; + v += field2782; + v += field2783; + v += field2784; + v += field2785; + v += field2786; + v += field2787; + v += field2788; + v += field2789; + v += field2790; + v += field2791; + v += field2792; + v += field2793; + v += field2794; + v += field2795; + v += field2796; + v += field2797; + v += field2798; + v += field2799; + field2799 = v; + } + + private static void test1_field2800() { + int v = field2800; + v += field2801; + v += field2802; + v += field2803; + v += field2804; + v += field2805; + v += field2806; + v += field2807; + v += field2808; + v += field2809; + v += field2810; + v += field2811; + v += field2812; + v += field2813; + v += field2814; + v += field2815; + v += field2816; + v += field2817; + v += field2818; + v += field2819; + v += field2820; + v += field2821; + v += field2822; + v += field2823; + v += field2824; + v += field2825; + v += field2826; + v += field2827; + v += field2828; + v += field2829; + v += field2830; + v += field2831; + v += field2832; + v += field2833; + v += field2834; + v += field2835; + v += field2836; + v += field2837; + v += field2838; + v += field2839; + v += field2840; + v += field2841; + v += field2842; + v += field2843; + v += field2844; + v += field2845; + v += field2846; + v += field2847; + v += field2848; + v += field2849; + field2849 = v; + } + + private static void test1_field2850() { + int v = field2850; + v += field2851; + v += field2852; + v += field2853; + v += field2854; + v += field2855; + v += field2856; + v += field2857; + v += field2858; + v += field2859; + v += field2860; + v += field2861; + v += field2862; + v += field2863; + v += field2864; + v += field2865; + v += field2866; + v += field2867; + v += field2868; + v += field2869; + v += field2870; + v += field2871; + v += field2872; + v += field2873; + v += field2874; + v += field2875; + v += field2876; + v += field2877; + v += field2878; + v += field2879; + v += field2880; + v += field2881; + v += field2882; + v += field2883; + v += field2884; + v += field2885; + v += field2886; + v += field2887; + v += field2888; + v += field2889; + v += field2890; + v += field2891; + v += field2892; + v += field2893; + v += field2894; + v += field2895; + v += field2896; + v += field2897; + v += field2898; + v += field2899; + field2899 = v; + } + + private static void test1_field2900() { + int v = field2900; + v += field2901; + v += field2902; + v += field2903; + v += field2904; + v += field2905; + v += field2906; + v += field2907; + v += field2908; + v += field2909; + v += field2910; + v += field2911; + v += field2912; + v += field2913; + v += field2914; + v += field2915; + v += field2916; + v += field2917; + v += field2918; + v += field2919; + v += field2920; + v += field2921; + v += field2922; + v += field2923; + v += field2924; + v += field2925; + v += field2926; + v += field2927; + v += field2928; + v += field2929; + v += field2930; + v += field2931; + v += field2932; + v += field2933; + v += field2934; + v += field2935; + v += field2936; + v += field2937; + v += field2938; + v += field2939; + v += field2940; + v += field2941; + v += field2942; + v += field2943; + v += field2944; + v += field2945; + v += field2946; + v += field2947; + v += field2948; + v += field2949; + field2949 = v; + } + + private static void test1_field2950() { + int v = field2950; + v += field2951; + v += field2952; + v += field2953; + v += field2954; + v += field2955; + v += field2956; + v += field2957; + v += field2958; + v += field2959; + v += field2960; + v += field2961; + v += field2962; + v += field2963; + v += field2964; + v += field2965; + v += field2966; + v += field2967; + v += field2968; + v += field2969; + v += field2970; + v += field2971; + v += field2972; + v += field2973; + v += field2974; + v += field2975; + v += field2976; + v += field2977; + v += field2978; + v += field2979; + v += field2980; + v += field2981; + v += field2982; + v += field2983; + v += field2984; + v += field2985; + v += field2986; + v += field2987; + v += field2988; + v += field2989; + v += field2990; + v += field2991; + v += field2992; + v += field2993; + v += field2994; + v += field2995; + v += field2996; + v += field2997; + v += field2998; + v += field2999; + field2999 = v; + } + + private static void test1_field3000() { + int v = field3000; + v += field3001; + v += field3002; + v += field3003; + v += field3004; + v += field3005; + v += field3006; + v += field3007; + v += field3008; + v += field3009; + v += field3010; + v += field3011; + v += field3012; + v += field3013; + v += field3014; + v += field3015; + v += field3016; + v += field3017; + v += field3018; + v += field3019; + v += field3020; + v += field3021; + v += field3022; + v += field3023; + v += field3024; + v += field3025; + v += field3026; + v += field3027; + v += field3028; + v += field3029; + v += field3030; + v += field3031; + v += field3032; + v += field3033; + v += field3034; + v += field3035; + v += field3036; + v += field3037; + v += field3038; + v += field3039; + v += field3040; + v += field3041; + v += field3042; + v += field3043; + v += field3044; + v += field3045; + v += field3046; + v += field3047; + v += field3048; + v += field3049; + field3049 = v; + } + + private static void test1_field3050() { + int v = field3050; + v += field3051; + v += field3052; + v += field3053; + v += field3054; + v += field3055; + v += field3056; + v += field3057; + v += field3058; + v += field3059; + v += field3060; + v += field3061; + v += field3062; + v += field3063; + v += field3064; + v += field3065; + v += field3066; + v += field3067; + v += field3068; + v += field3069; + v += field3070; + v += field3071; + v += field3072; + v += field3073; + v += field3074; + v += field3075; + v += field3076; + v += field3077; + v += field3078; + v += field3079; + v += field3080; + v += field3081; + v += field3082; + v += field3083; + v += field3084; + v += field3085; + v += field3086; + v += field3087; + v += field3088; + v += field3089; + v += field3090; + v += field3091; + v += field3092; + v += field3093; + v += field3094; + v += field3095; + v += field3096; + v += field3097; + v += field3098; + v += field3099; + field3099 = v; + } + + private static void test1_field3100() { + int v = field3100; + v += field3101; + v += field3102; + v += field3103; + v += field3104; + v += field3105; + v += field3106; + v += field3107; + v += field3108; + v += field3109; + v += field3110; + v += field3111; + v += field3112; + v += field3113; + v += field3114; + v += field3115; + v += field3116; + v += field3117; + v += field3118; + v += field3119; + v += field3120; + v += field3121; + v += field3122; + v += field3123; + v += field3124; + v += field3125; + v += field3126; + v += field3127; + v += field3128; + v += field3129; + v += field3130; + v += field3131; + v += field3132; + v += field3133; + v += field3134; + v += field3135; + v += field3136; + v += field3137; + v += field3138; + v += field3139; + v += field3140; + v += field3141; + v += field3142; + v += field3143; + v += field3144; + v += field3145; + v += field3146; + v += field3147; + v += field3148; + v += field3149; + field3149 = v; + } + + private static void test1_field3150() { + int v = field3150; + v += field3151; + v += field3152; + v += field3153; + v += field3154; + v += field3155; + v += field3156; + v += field3157; + v += field3158; + v += field3159; + v += field3160; + v += field3161; + v += field3162; + v += field3163; + v += field3164; + v += field3165; + v += field3166; + v += field3167; + v += field3168; + v += field3169; + v += field3170; + v += field3171; + v += field3172; + v += field3173; + v += field3174; + v += field3175; + v += field3176; + v += field3177; + v += field3178; + v += field3179; + v += field3180; + v += field3181; + v += field3182; + v += field3183; + v += field3184; + v += field3185; + v += field3186; + v += field3187; + v += field3188; + v += field3189; + v += field3190; + v += field3191; + v += field3192; + v += field3193; + v += field3194; + v += field3195; + v += field3196; + v += field3197; + v += field3198; + v += field3199; + field3199 = v; + } + + private static void test1_field3200() { + int v = field3200; + v += field3201; + v += field3202; + v += field3203; + v += field3204; + v += field3205; + v += field3206; + v += field3207; + v += field3208; + v += field3209; + v += field3210; + v += field3211; + v += field3212; + v += field3213; + v += field3214; + v += field3215; + v += field3216; + v += field3217; + v += field3218; + v += field3219; + v += field3220; + v += field3221; + v += field3222; + v += field3223; + v += field3224; + v += field3225; + v += field3226; + v += field3227; + v += field3228; + v += field3229; + v += field3230; + v += field3231; + v += field3232; + v += field3233; + v += field3234; + v += field3235; + v += field3236; + v += field3237; + v += field3238; + v += field3239; + v += field3240; + v += field3241; + v += field3242; + v += field3243; + v += field3244; + v += field3245; + v += field3246; + v += field3247; + v += field3248; + v += field3249; + field3249 = v; + } + + private static void test1_field3250() { + int v = field3250; + v += field3251; + v += field3252; + v += field3253; + v += field3254; + v += field3255; + v += field3256; + v += field3257; + v += field3258; + v += field3259; + v += field3260; + v += field3261; + v += field3262; + v += field3263; + v += field3264; + v += field3265; + v += field3266; + v += field3267; + v += field3268; + v += field3269; + v += field3270; + v += field3271; + v += field3272; + v += field3273; + v += field3274; + v += field3275; + v += field3276; + v += field3277; + v += field3278; + v += field3279; + v += field3280; + v += field3281; + v += field3282; + v += field3283; + v += field3284; + v += field3285; + v += field3286; + v += field3287; + v += field3288; + v += field3289; + v += field3290; + v += field3291; + v += field3292; + v += field3293; + v += field3294; + v += field3295; + v += field3296; + v += field3297; + v += field3298; + v += field3299; + field3299 = v; + } + + private static void test1_field3300() { + int v = field3300; + v += field3301; + v += field3302; + v += field3303; + v += field3304; + v += field3305; + v += field3306; + v += field3307; + v += field3308; + v += field3309; + v += field3310; + v += field3311; + v += field3312; + v += field3313; + v += field3314; + v += field3315; + v += field3316; + v += field3317; + v += field3318; + v += field3319; + v += field3320; + v += field3321; + v += field3322; + v += field3323; + v += field3324; + v += field3325; + v += field3326; + v += field3327; + v += field3328; + v += field3329; + v += field3330; + v += field3331; + v += field3332; + v += field3333; + v += field3334; + v += field3335; + v += field3336; + v += field3337; + v += field3338; + v += field3339; + v += field3340; + v += field3341; + v += field3342; + v += field3343; + v += field3344; + v += field3345; + v += field3346; + v += field3347; + v += field3348; + v += field3349; + field3349 = v; + } + + private static void test1_field3350() { + int v = field3350; + v += field3351; + v += field3352; + v += field3353; + v += field3354; + v += field3355; + v += field3356; + v += field3357; + v += field3358; + v += field3359; + v += field3360; + v += field3361; + v += field3362; + v += field3363; + v += field3364; + v += field3365; + v += field3366; + v += field3367; + v += field3368; + v += field3369; + v += field3370; + v += field3371; + v += field3372; + v += field3373; + v += field3374; + v += field3375; + v += field3376; + v += field3377; + v += field3378; + v += field3379; + v += field3380; + v += field3381; + v += field3382; + v += field3383; + v += field3384; + v += field3385; + v += field3386; + v += field3387; + v += field3388; + v += field3389; + v += field3390; + v += field3391; + v += field3392; + v += field3393; + v += field3394; + v += field3395; + v += field3396; + v += field3397; + v += field3398; + v += field3399; + field3399 = v; + } + + private static void test1_field3400() { + int v = field3400; + v += field3401; + v += field3402; + v += field3403; + v += field3404; + v += field3405; + v += field3406; + v += field3407; + v += field3408; + v += field3409; + v += field3410; + v += field3411; + v += field3412; + v += field3413; + v += field3414; + v += field3415; + v += field3416; + v += field3417; + v += field3418; + v += field3419; + v += field3420; + v += field3421; + v += field3422; + v += field3423; + v += field3424; + v += field3425; + v += field3426; + v += field3427; + v += field3428; + v += field3429; + v += field3430; + v += field3431; + v += field3432; + v += field3433; + v += field3434; + v += field3435; + v += field3436; + v += field3437; + v += field3438; + v += field3439; + v += field3440; + v += field3441; + v += field3442; + v += field3443; + v += field3444; + v += field3445; + v += field3446; + v += field3447; + v += field3448; + v += field3449; + field3449 = v; + } + + private static void test1_field3450() { + int v = field3450; + v += field3451; + v += field3452; + v += field3453; + v += field3454; + v += field3455; + v += field3456; + v += field3457; + v += field3458; + v += field3459; + v += field3460; + v += field3461; + v += field3462; + v += field3463; + v += field3464; + v += field3465; + v += field3466; + v += field3467; + v += field3468; + v += field3469; + v += field3470; + v += field3471; + v += field3472; + v += field3473; + v += field3474; + v += field3475; + v += field3476; + v += field3477; + v += field3478; + v += field3479; + v += field3480; + v += field3481; + v += field3482; + v += field3483; + v += field3484; + v += field3485; + v += field3486; + v += field3487; + v += field3488; + v += field3489; + v += field3490; + v += field3491; + v += field3492; + v += field3493; + v += field3494; + v += field3495; + v += field3496; + v += field3497; + v += field3498; + v += field3499; + field3499 = v; + } + + private static void test1_field3500() { + int v = field3500; + v += field3501; + v += field3502; + v += field3503; + v += field3504; + v += field3505; + v += field3506; + v += field3507; + v += field3508; + v += field3509; + v += field3510; + v += field3511; + v += field3512; + v += field3513; + v += field3514; + v += field3515; + v += field3516; + v += field3517; + v += field3518; + v += field3519; + v += field3520; + v += field3521; + v += field3522; + v += field3523; + v += field3524; + v += field3525; + v += field3526; + v += field3527; + v += field3528; + v += field3529; + v += field3530; + v += field3531; + v += field3532; + v += field3533; + v += field3534; + v += field3535; + v += field3536; + v += field3537; + v += field3538; + v += field3539; + v += field3540; + v += field3541; + v += field3542; + v += field3543; + v += field3544; + v += field3545; + v += field3546; + v += field3547; + v += field3548; + v += field3549; + field3549 = v; + } + + private static void test1_field3550() { + int v = field3550; + v += field3551; + v += field3552; + v += field3553; + v += field3554; + v += field3555; + v += field3556; + v += field3557; + v += field3558; + v += field3559; + v += field3560; + v += field3561; + v += field3562; + v += field3563; + v += field3564; + v += field3565; + v += field3566; + v += field3567; + v += field3568; + v += field3569; + v += field3570; + v += field3571; + v += field3572; + v += field3573; + v += field3574; + v += field3575; + v += field3576; + v += field3577; + v += field3578; + v += field3579; + v += field3580; + v += field3581; + v += field3582; + v += field3583; + v += field3584; + v += field3585; + v += field3586; + v += field3587; + v += field3588; + v += field3589; + v += field3590; + v += field3591; + v += field3592; + v += field3593; + v += field3594; + v += field3595; + v += field3596; + v += field3597; + v += field3598; + v += field3599; + field3599 = v; + } + + private static void test1_field3600() { + int v = field3600; + v += field3601; + v += field3602; + v += field3603; + v += field3604; + v += field3605; + v += field3606; + v += field3607; + v += field3608; + v += field3609; + v += field3610; + v += field3611; + v += field3612; + v += field3613; + v += field3614; + v += field3615; + v += field3616; + v += field3617; + v += field3618; + v += field3619; + v += field3620; + v += field3621; + v += field3622; + v += field3623; + v += field3624; + v += field3625; + v += field3626; + v += field3627; + v += field3628; + v += field3629; + v += field3630; + v += field3631; + v += field3632; + v += field3633; + v += field3634; + v += field3635; + v += field3636; + v += field3637; + v += field3638; + v += field3639; + v += field3640; + v += field3641; + v += field3642; + v += field3643; + v += field3644; + v += field3645; + v += field3646; + v += field3647; + v += field3648; + v += field3649; + field3649 = v; + } + + private static void test1_field3650() { + int v = field3650; + v += field3651; + v += field3652; + v += field3653; + v += field3654; + v += field3655; + v += field3656; + v += field3657; + v += field3658; + v += field3659; + v += field3660; + v += field3661; + v += field3662; + v += field3663; + v += field3664; + v += field3665; + v += field3666; + v += field3667; + v += field3668; + v += field3669; + v += field3670; + v += field3671; + v += field3672; + v += field3673; + v += field3674; + v += field3675; + v += field3676; + v += field3677; + v += field3678; + v += field3679; + v += field3680; + v += field3681; + v += field3682; + v += field3683; + v += field3684; + v += field3685; + v += field3686; + v += field3687; + v += field3688; + v += field3689; + v += field3690; + v += field3691; + v += field3692; + v += field3693; + v += field3694; + v += field3695; + v += field3696; + v += field3697; + v += field3698; + v += field3699; + field3699 = v; + } + + private static void test1_field3700() { + int v = field3700; + v += field3701; + v += field3702; + v += field3703; + v += field3704; + v += field3705; + v += field3706; + v += field3707; + v += field3708; + v += field3709; + v += field3710; + v += field3711; + v += field3712; + v += field3713; + v += field3714; + v += field3715; + v += field3716; + v += field3717; + v += field3718; + v += field3719; + v += field3720; + v += field3721; + v += field3722; + v += field3723; + v += field3724; + v += field3725; + v += field3726; + v += field3727; + v += field3728; + v += field3729; + v += field3730; + v += field3731; + v += field3732; + v += field3733; + v += field3734; + v += field3735; + v += field3736; + v += field3737; + v += field3738; + v += field3739; + v += field3740; + v += field3741; + v += field3742; + v += field3743; + v += field3744; + v += field3745; + v += field3746; + v += field3747; + v += field3748; + v += field3749; + field3749 = v; + } + + private static void test1_field3750() { + int v = field3750; + v += field3751; + v += field3752; + v += field3753; + v += field3754; + v += field3755; + v += field3756; + v += field3757; + v += field3758; + v += field3759; + v += field3760; + v += field3761; + v += field3762; + v += field3763; + v += field3764; + v += field3765; + v += field3766; + v += field3767; + v += field3768; + v += field3769; + v += field3770; + v += field3771; + v += field3772; + v += field3773; + v += field3774; + v += field3775; + v += field3776; + v += field3777; + v += field3778; + v += field3779; + v += field3780; + v += field3781; + v += field3782; + v += field3783; + v += field3784; + v += field3785; + v += field3786; + v += field3787; + v += field3788; + v += field3789; + v += field3790; + v += field3791; + v += field3792; + v += field3793; + v += field3794; + v += field3795; + v += field3796; + v += field3797; + v += field3798; + v += field3799; + field3799 = v; + } + + private static void test1_field3800() { + int v = field3800; + v += field3801; + v += field3802; + v += field3803; + v += field3804; + v += field3805; + v += field3806; + v += field3807; + v += field3808; + v += field3809; + v += field3810; + v += field3811; + v += field3812; + v += field3813; + v += field3814; + v += field3815; + v += field3816; + v += field3817; + v += field3818; + v += field3819; + v += field3820; + v += field3821; + v += field3822; + v += field3823; + v += field3824; + v += field3825; + v += field3826; + v += field3827; + v += field3828; + v += field3829; + v += field3830; + v += field3831; + v += field3832; + v += field3833; + v += field3834; + v += field3835; + v += field3836; + v += field3837; + v += field3838; + v += field3839; + v += field3840; + v += field3841; + v += field3842; + v += field3843; + v += field3844; + v += field3845; + v += field3846; + v += field3847; + v += field3848; + v += field3849; + field3849 = v; + } + + private static void test1_field3850() { + int v = field3850; + v += field3851; + v += field3852; + v += field3853; + v += field3854; + v += field3855; + v += field3856; + v += field3857; + v += field3858; + v += field3859; + v += field3860; + v += field3861; + v += field3862; + v += field3863; + v += field3864; + v += field3865; + v += field3866; + v += field3867; + v += field3868; + v += field3869; + v += field3870; + v += field3871; + v += field3872; + v += field3873; + v += field3874; + v += field3875; + v += field3876; + v += field3877; + v += field3878; + v += field3879; + v += field3880; + v += field3881; + v += field3882; + v += field3883; + v += field3884; + v += field3885; + v += field3886; + v += field3887; + v += field3888; + v += field3889; + v += field3890; + v += field3891; + v += field3892; + v += field3893; + v += field3894; + v += field3895; + v += field3896; + v += field3897; + v += field3898; + v += field3899; + field3899 = v; + } + + private static void test1_field3900() { + int v = field3900; + v += field3901; + v += field3902; + v += field3903; + v += field3904; + v += field3905; + v += field3906; + v += field3907; + v += field3908; + v += field3909; + v += field3910; + v += field3911; + v += field3912; + v += field3913; + v += field3914; + v += field3915; + v += field3916; + v += field3917; + v += field3918; + v += field3919; + v += field3920; + v += field3921; + v += field3922; + v += field3923; + v += field3924; + v += field3925; + v += field3926; + v += field3927; + v += field3928; + v += field3929; + v += field3930; + v += field3931; + v += field3932; + v += field3933; + v += field3934; + v += field3935; + v += field3936; + v += field3937; + v += field3938; + v += field3939; + v += field3940; + v += field3941; + v += field3942; + v += field3943; + v += field3944; + v += field3945; + v += field3946; + v += field3947; + v += field3948; + v += field3949; + field3949 = v; + } + + private static void test1_field3950() { + int v = field3950; + v += field3951; + v += field3952; + v += field3953; + v += field3954; + v += field3955; + v += field3956; + v += field3957; + v += field3958; + v += field3959; + v += field3960; + v += field3961; + v += field3962; + v += field3963; + v += field3964; + v += field3965; + v += field3966; + v += field3967; + v += field3968; + v += field3969; + v += field3970; + v += field3971; + v += field3972; + v += field3973; + v += field3974; + v += field3975; + v += field3976; + v += field3977; + v += field3978; + v += field3979; + v += field3980; + v += field3981; + v += field3982; + v += field3983; + v += field3984; + v += field3985; + v += field3986; + v += field3987; + v += field3988; + v += field3989; + v += field3990; + v += field3991; + v += field3992; + v += field3993; + v += field3994; + v += field3995; + v += field3996; + v += field3997; + v += field3998; + v += field3999; + field3999 = v; + } + + private static void test1() { + int v = 0; + test1_field0000(); + test1_field0050(); + test1_field0100(); + test1_field0150(); + test1_field0200(); + test1_field0250(); + test1_field0300(); + test1_field0350(); + test1_field0400(); + test1_field0450(); + test1_field0500(); + test1_field0550(); + test1_field0600(); + test1_field0650(); + test1_field0700(); + test1_field0750(); + test1_field0800(); + test1_field0850(); + test1_field0900(); + test1_field0950(); + test1_field1000(); + test1_field1050(); + test1_field1100(); + test1_field1150(); + test1_field1200(); + test1_field1250(); + test1_field1300(); + test1_field1350(); + test1_field1400(); + test1_field1450(); + test1_field1500(); + test1_field1550(); + test1_field1600(); + test1_field1650(); + test1_field1700(); + test1_field1750(); + test1_field1800(); + test1_field1850(); + test1_field1900(); + test1_field1950(); + test1_field2000(); + test1_field2050(); + test1_field2100(); + test1_field2150(); + test1_field2200(); + test1_field2250(); + test1_field2300(); + test1_field2350(); + test1_field2400(); + test1_field2450(); + test1_field2500(); + test1_field2550(); + test1_field2600(); + test1_field2650(); + test1_field2700(); + test1_field2750(); + test1_field2800(); + test1_field2850(); + test1_field2900(); + test1_field2950(); + test1_field3000(); + test1_field3050(); + test1_field3100(); + test1_field3150(); + test1_field3200(); + test1_field3250(); + test1_field3300(); + test1_field3350(); + test1_field3400(); + test1_field3450(); + test1_field3500(); + test1_field3550(); + test1_field3600(); + test1_field3650(); + test1_field3700(); + test1_field3750(); + test1_field3800(); + test1_field3850(); + test1_field3900(); + test1_field3950(); + } + + private static int field0000; + private static int field0001; + private static int field0002; + private static int field0003; + private static int field0004; + private static int field0005; + private static int field0006; + private static int field0007; + private static int field0008; + private static int field0009; + private static int field0010; + private static int field0011; + private static int field0012; + private static int field0013; + private static int field0014; + private static int field0015; + private static int field0016; + private static int field0017; + private static int field0018; + private static int field0019; + private static int field0020; + private static int field0021; + private static int field0022; + private static int field0023; + private static int field0024; + private static int field0025; + private static int field0026; + private static int field0027; + private static int field0028; + private static int field0029; + private static int field0030; + private static int field0031; + private static int field0032; + private static int field0033; + private static int field0034; + private static int field0035; + private static int field0036; + private static int field0037; + private static int field0038; + private static int field0039; + private static int field0040; + private static int field0041; + private static int field0042; + private static int field0043; + private static int field0044; + private static int field0045; + private static int field0046; + private static int field0047; + private static int field0048; + private static int field0049; + private static int field0050; + private static int field0051; + private static int field0052; + private static int field0053; + private static int field0054; + private static int field0055; + private static int field0056; + private static int field0057; + private static int field0058; + private static int field0059; + private static int field0060; + private static int field0061; + private static int field0062; + private static int field0063; + private static int field0064; + private static int field0065; + private static int field0066; + private static int field0067; + private static int field0068; + private static int field0069; + private static int field0070; + private static int field0071; + private static int field0072; + private static int field0073; + private static int field0074; + private static int field0075; + private static int field0076; + private static int field0077; + private static int field0078; + private static int field0079; + private static int field0080; + private static int field0081; + private static int field0082; + private static int field0083; + private static int field0084; + private static int field0085; + private static int field0086; + private static int field0087; + private static int field0088; + private static int field0089; + private static int field0090; + private static int field0091; + private static int field0092; + private static int field0093; + private static int field0094; + private static int field0095; + private static int field0096; + private static int field0097; + private static int field0098; + private static int field0099; + private static int field0100; + private static int field0101; + private static int field0102; + private static int field0103; + private static int field0104; + private static int field0105; + private static int field0106; + private static int field0107; + private static int field0108; + private static int field0109; + private static int field0110; + private static int field0111; + private static int field0112; + private static int field0113; + private static int field0114; + private static int field0115; + private static int field0116; + private static int field0117; + private static int field0118; + private static int field0119; + private static int field0120; + private static int field0121; + private static int field0122; + private static int field0123; + private static int field0124; + private static int field0125; + private static int field0126; + private static int field0127; + private static int field0128; + private static int field0129; + private static int field0130; + private static int field0131; + private static int field0132; + private static int field0133; + private static int field0134; + private static int field0135; + private static int field0136; + private static int field0137; + private static int field0138; + private static int field0139; + private static int field0140; + private static int field0141; + private static int field0142; + private static int field0143; + private static int field0144; + private static int field0145; + private static int field0146; + private static int field0147; + private static int field0148; + private static int field0149; + private static int field0150; + private static int field0151; + private static int field0152; + private static int field0153; + private static int field0154; + private static int field0155; + private static int field0156; + private static int field0157; + private static int field0158; + private static int field0159; + private static int field0160; + private static int field0161; + private static int field0162; + private static int field0163; + private static int field0164; + private static int field0165; + private static int field0166; + private static int field0167; + private static int field0168; + private static int field0169; + private static int field0170; + private static int field0171; + private static int field0172; + private static int field0173; + private static int field0174; + private static int field0175; + private static int field0176; + private static int field0177; + private static int field0178; + private static int field0179; + private static int field0180; + private static int field0181; + private static int field0182; + private static int field0183; + private static int field0184; + private static int field0185; + private static int field0186; + private static int field0187; + private static int field0188; + private static int field0189; + private static int field0190; + private static int field0191; + private static int field0192; + private static int field0193; + private static int field0194; + private static int field0195; + private static int field0196; + private static int field0197; + private static int field0198; + private static int field0199; + private static int field0200; + private static int field0201; + private static int field0202; + private static int field0203; + private static int field0204; + private static int field0205; + private static int field0206; + private static int field0207; + private static int field0208; + private static int field0209; + private static int field0210; + private static int field0211; + private static int field0212; + private static int field0213; + private static int field0214; + private static int field0215; + private static int field0216; + private static int field0217; + private static int field0218; + private static int field0219; + private static int field0220; + private static int field0221; + private static int field0222; + private static int field0223; + private static int field0224; + private static int field0225; + private static int field0226; + private static int field0227; + private static int field0228; + private static int field0229; + private static int field0230; + private static int field0231; + private static int field0232; + private static int field0233; + private static int field0234; + private static int field0235; + private static int field0236; + private static int field0237; + private static int field0238; + private static int field0239; + private static int field0240; + private static int field0241; + private static int field0242; + private static int field0243; + private static int field0244; + private static int field0245; + private static int field0246; + private static int field0247; + private static int field0248; + private static int field0249; + private static int field0250; + private static int field0251; + private static int field0252; + private static int field0253; + private static int field0254; + private static int field0255; + private static int field0256; + private static int field0257; + private static int field0258; + private static int field0259; + private static int field0260; + private static int field0261; + private static int field0262; + private static int field0263; + private static int field0264; + private static int field0265; + private static int field0266; + private static int field0267; + private static int field0268; + private static int field0269; + private static int field0270; + private static int field0271; + private static int field0272; + private static int field0273; + private static int field0274; + private static int field0275; + private static int field0276; + private static int field0277; + private static int field0278; + private static int field0279; + private static int field0280; + private static int field0281; + private static int field0282; + private static int field0283; + private static int field0284; + private static int field0285; + private static int field0286; + private static int field0287; + private static int field0288; + private static int field0289; + private static int field0290; + private static int field0291; + private static int field0292; + private static int field0293; + private static int field0294; + private static int field0295; + private static int field0296; + private static int field0297; + private static int field0298; + private static int field0299; + private static int field0300; + private static int field0301; + private static int field0302; + private static int field0303; + private static int field0304; + private static int field0305; + private static int field0306; + private static int field0307; + private static int field0308; + private static int field0309; + private static int field0310; + private static int field0311; + private static int field0312; + private static int field0313; + private static int field0314; + private static int field0315; + private static int field0316; + private static int field0317; + private static int field0318; + private static int field0319; + private static int field0320; + private static int field0321; + private static int field0322; + private static int field0323; + private static int field0324; + private static int field0325; + private static int field0326; + private static int field0327; + private static int field0328; + private static int field0329; + private static int field0330; + private static int field0331; + private static int field0332; + private static int field0333; + private static int field0334; + private static int field0335; + private static int field0336; + private static int field0337; + private static int field0338; + private static int field0339; + private static int field0340; + private static int field0341; + private static int field0342; + private static int field0343; + private static int field0344; + private static int field0345; + private static int field0346; + private static int field0347; + private static int field0348; + private static int field0349; + private static int field0350; + private static int field0351; + private static int field0352; + private static int field0353; + private static int field0354; + private static int field0355; + private static int field0356; + private static int field0357; + private static int field0358; + private static int field0359; + private static int field0360; + private static int field0361; + private static int field0362; + private static int field0363; + private static int field0364; + private static int field0365; + private static int field0366; + private static int field0367; + private static int field0368; + private static int field0369; + private static int field0370; + private static int field0371; + private static int field0372; + private static int field0373; + private static int field0374; + private static int field0375; + private static int field0376; + private static int field0377; + private static int field0378; + private static int field0379; + private static int field0380; + private static int field0381; + private static int field0382; + private static int field0383; + private static int field0384; + private static int field0385; + private static int field0386; + private static int field0387; + private static int field0388; + private static int field0389; + private static int field0390; + private static int field0391; + private static int field0392; + private static int field0393; + private static int field0394; + private static int field0395; + private static int field0396; + private static int field0397; + private static int field0398; + private static int field0399; + private static int field0400; + private static int field0401; + private static int field0402; + private static int field0403; + private static int field0404; + private static int field0405; + private static int field0406; + private static int field0407; + private static int field0408; + private static int field0409; + private static int field0410; + private static int field0411; + private static int field0412; + private static int field0413; + private static int field0414; + private static int field0415; + private static int field0416; + private static int field0417; + private static int field0418; + private static int field0419; + private static int field0420; + private static int field0421; + private static int field0422; + private static int field0423; + private static int field0424; + private static int field0425; + private static int field0426; + private static int field0427; + private static int field0428; + private static int field0429; + private static int field0430; + private static int field0431; + private static int field0432; + private static int field0433; + private static int field0434; + private static int field0435; + private static int field0436; + private static int field0437; + private static int field0438; + private static int field0439; + private static int field0440; + private static int field0441; + private static int field0442; + private static int field0443; + private static int field0444; + private static int field0445; + private static int field0446; + private static int field0447; + private static int field0448; + private static int field0449; + private static int field0450; + private static int field0451; + private static int field0452; + private static int field0453; + private static int field0454; + private static int field0455; + private static int field0456; + private static int field0457; + private static int field0458; + private static int field0459; + private static int field0460; + private static int field0461; + private static int field0462; + private static int field0463; + private static int field0464; + private static int field0465; + private static int field0466; + private static int field0467; + private static int field0468; + private static int field0469; + private static int field0470; + private static int field0471; + private static int field0472; + private static int field0473; + private static int field0474; + private static int field0475; + private static int field0476; + private static int field0477; + private static int field0478; + private static int field0479; + private static int field0480; + private static int field0481; + private static int field0482; + private static int field0483; + private static int field0484; + private static int field0485; + private static int field0486; + private static int field0487; + private static int field0488; + private static int field0489; + private static int field0490; + private static int field0491; + private static int field0492; + private static int field0493; + private static int field0494; + private static int field0495; + private static int field0496; + private static int field0497; + private static int field0498; + private static int field0499; + private static int field0500; + private static int field0501; + private static int field0502; + private static int field0503; + private static int field0504; + private static int field0505; + private static int field0506; + private static int field0507; + private static int field0508; + private static int field0509; + private static int field0510; + private static int field0511; + private static int field0512; + private static int field0513; + private static int field0514; + private static int field0515; + private static int field0516; + private static int field0517; + private static int field0518; + private static int field0519; + private static int field0520; + private static int field0521; + private static int field0522; + private static int field0523; + private static int field0524; + private static int field0525; + private static int field0526; + private static int field0527; + private static int field0528; + private static int field0529; + private static int field0530; + private static int field0531; + private static int field0532; + private static int field0533; + private static int field0534; + private static int field0535; + private static int field0536; + private static int field0537; + private static int field0538; + private static int field0539; + private static int field0540; + private static int field0541; + private static int field0542; + private static int field0543; + private static int field0544; + private static int field0545; + private static int field0546; + private static int field0547; + private static int field0548; + private static int field0549; + private static int field0550; + private static int field0551; + private static int field0552; + private static int field0553; + private static int field0554; + private static int field0555; + private static int field0556; + private static int field0557; + private static int field0558; + private static int field0559; + private static int field0560; + private static int field0561; + private static int field0562; + private static int field0563; + private static int field0564; + private static int field0565; + private static int field0566; + private static int field0567; + private static int field0568; + private static int field0569; + private static int field0570; + private static int field0571; + private static int field0572; + private static int field0573; + private static int field0574; + private static int field0575; + private static int field0576; + private static int field0577; + private static int field0578; + private static int field0579; + private static int field0580; + private static int field0581; + private static int field0582; + private static int field0583; + private static int field0584; + private static int field0585; + private static int field0586; + private static int field0587; + private static int field0588; + private static int field0589; + private static int field0590; + private static int field0591; + private static int field0592; + private static int field0593; + private static int field0594; + private static int field0595; + private static int field0596; + private static int field0597; + private static int field0598; + private static int field0599; + private static int field0600; + private static int field0601; + private static int field0602; + private static int field0603; + private static int field0604; + private static int field0605; + private static int field0606; + private static int field0607; + private static int field0608; + private static int field0609; + private static int field0610; + private static int field0611; + private static int field0612; + private static int field0613; + private static int field0614; + private static int field0615; + private static int field0616; + private static int field0617; + private static int field0618; + private static int field0619; + private static int field0620; + private static int field0621; + private static int field0622; + private static int field0623; + private static int field0624; + private static int field0625; + private static int field0626; + private static int field0627; + private static int field0628; + private static int field0629; + private static int field0630; + private static int field0631; + private static int field0632; + private static int field0633; + private static int field0634; + private static int field0635; + private static int field0636; + private static int field0637; + private static int field0638; + private static int field0639; + private static int field0640; + private static int field0641; + private static int field0642; + private static int field0643; + private static int field0644; + private static int field0645; + private static int field0646; + private static int field0647; + private static int field0648; + private static int field0649; + private static int field0650; + private static int field0651; + private static int field0652; + private static int field0653; + private static int field0654; + private static int field0655; + private static int field0656; + private static int field0657; + private static int field0658; + private static int field0659; + private static int field0660; + private static int field0661; + private static int field0662; + private static int field0663; + private static int field0664; + private static int field0665; + private static int field0666; + private static int field0667; + private static int field0668; + private static int field0669; + private static int field0670; + private static int field0671; + private static int field0672; + private static int field0673; + private static int field0674; + private static int field0675; + private static int field0676; + private static int field0677; + private static int field0678; + private static int field0679; + private static int field0680; + private static int field0681; + private static int field0682; + private static int field0683; + private static int field0684; + private static int field0685; + private static int field0686; + private static int field0687; + private static int field0688; + private static int field0689; + private static int field0690; + private static int field0691; + private static int field0692; + private static int field0693; + private static int field0694; + private static int field0695; + private static int field0696; + private static int field0697; + private static int field0698; + private static int field0699; + private static int field0700; + private static int field0701; + private static int field0702; + private static int field0703; + private static int field0704; + private static int field0705; + private static int field0706; + private static int field0707; + private static int field0708; + private static int field0709; + private static int field0710; + private static int field0711; + private static int field0712; + private static int field0713; + private static int field0714; + private static int field0715; + private static int field0716; + private static int field0717; + private static int field0718; + private static int field0719; + private static int field0720; + private static int field0721; + private static int field0722; + private static int field0723; + private static int field0724; + private static int field0725; + private static int field0726; + private static int field0727; + private static int field0728; + private static int field0729; + private static int field0730; + private static int field0731; + private static int field0732; + private static int field0733; + private static int field0734; + private static int field0735; + private static int field0736; + private static int field0737; + private static int field0738; + private static int field0739; + private static int field0740; + private static int field0741; + private static int field0742; + private static int field0743; + private static int field0744; + private static int field0745; + private static int field0746; + private static int field0747; + private static int field0748; + private static int field0749; + private static int field0750; + private static int field0751; + private static int field0752; + private static int field0753; + private static int field0754; + private static int field0755; + private static int field0756; + private static int field0757; + private static int field0758; + private static int field0759; + private static int field0760; + private static int field0761; + private static int field0762; + private static int field0763; + private static int field0764; + private static int field0765; + private static int field0766; + private static int field0767; + private static int field0768; + private static int field0769; + private static int field0770; + private static int field0771; + private static int field0772; + private static int field0773; + private static int field0774; + private static int field0775; + private static int field0776; + private static int field0777; + private static int field0778; + private static int field0779; + private static int field0780; + private static int field0781; + private static int field0782; + private static int field0783; + private static int field0784; + private static int field0785; + private static int field0786; + private static int field0787; + private static int field0788; + private static int field0789; + private static int field0790; + private static int field0791; + private static int field0792; + private static int field0793; + private static int field0794; + private static int field0795; + private static int field0796; + private static int field0797; + private static int field0798; + private static int field0799; + private static int field0800; + private static int field0801; + private static int field0802; + private static int field0803; + private static int field0804; + private static int field0805; + private static int field0806; + private static int field0807; + private static int field0808; + private static int field0809; + private static int field0810; + private static int field0811; + private static int field0812; + private static int field0813; + private static int field0814; + private static int field0815; + private static int field0816; + private static int field0817; + private static int field0818; + private static int field0819; + private static int field0820; + private static int field0821; + private static int field0822; + private static int field0823; + private static int field0824; + private static int field0825; + private static int field0826; + private static int field0827; + private static int field0828; + private static int field0829; + private static int field0830; + private static int field0831; + private static int field0832; + private static int field0833; + private static int field0834; + private static int field0835; + private static int field0836; + private static int field0837; + private static int field0838; + private static int field0839; + private static int field0840; + private static int field0841; + private static int field0842; + private static int field0843; + private static int field0844; + private static int field0845; + private static int field0846; + private static int field0847; + private static int field0848; + private static int field0849; + private static int field0850; + private static int field0851; + private static int field0852; + private static int field0853; + private static int field0854; + private static int field0855; + private static int field0856; + private static int field0857; + private static int field0858; + private static int field0859; + private static int field0860; + private static int field0861; + private static int field0862; + private static int field0863; + private static int field0864; + private static int field0865; + private static int field0866; + private static int field0867; + private static int field0868; + private static int field0869; + private static int field0870; + private static int field0871; + private static int field0872; + private static int field0873; + private static int field0874; + private static int field0875; + private static int field0876; + private static int field0877; + private static int field0878; + private static int field0879; + private static int field0880; + private static int field0881; + private static int field0882; + private static int field0883; + private static int field0884; + private static int field0885; + private static int field0886; + private static int field0887; + private static int field0888; + private static int field0889; + private static int field0890; + private static int field0891; + private static int field0892; + private static int field0893; + private static int field0894; + private static int field0895; + private static int field0896; + private static int field0897; + private static int field0898; + private static int field0899; + private static int field0900; + private static int field0901; + private static int field0902; + private static int field0903; + private static int field0904; + private static int field0905; + private static int field0906; + private static int field0907; + private static int field0908; + private static int field0909; + private static int field0910; + private static int field0911; + private static int field0912; + private static int field0913; + private static int field0914; + private static int field0915; + private static int field0916; + private static int field0917; + private static int field0918; + private static int field0919; + private static int field0920; + private static int field0921; + private static int field0922; + private static int field0923; + private static int field0924; + private static int field0925; + private static int field0926; + private static int field0927; + private static int field0928; + private static int field0929; + private static int field0930; + private static int field0931; + private static int field0932; + private static int field0933; + private static int field0934; + private static int field0935; + private static int field0936; + private static int field0937; + private static int field0938; + private static int field0939; + private static int field0940; + private static int field0941; + private static int field0942; + private static int field0943; + private static int field0944; + private static int field0945; + private static int field0946; + private static int field0947; + private static int field0948; + private static int field0949; + private static int field0950; + private static int field0951; + private static int field0952; + private static int field0953; + private static int field0954; + private static int field0955; + private static int field0956; + private static int field0957; + private static int field0958; + private static int field0959; + private static int field0960; + private static int field0961; + private static int field0962; + private static int field0963; + private static int field0964; + private static int field0965; + private static int field0966; + private static int field0967; + private static int field0968; + private static int field0969; + private static int field0970; + private static int field0971; + private static int field0972; + private static int field0973; + private static int field0974; + private static int field0975; + private static int field0976; + private static int field0977; + private static int field0978; + private static int field0979; + private static int field0980; + private static int field0981; + private static int field0982; + private static int field0983; + private static int field0984; + private static int field0985; + private static int field0986; + private static int field0987; + private static int field0988; + private static int field0989; + private static int field0990; + private static int field0991; + private static int field0992; + private static int field0993; + private static int field0994; + private static int field0995; + private static int field0996; + private static int field0997; + private static int field0998; + private static int field0999; + private static int field1000; + private static int field1001; + private static int field1002; + private static int field1003; + private static int field1004; + private static int field1005; + private static int field1006; + private static int field1007; + private static int field1008; + private static int field1009; + private static int field1010; + private static int field1011; + private static int field1012; + private static int field1013; + private static int field1014; + private static int field1015; + private static int field1016; + private static int field1017; + private static int field1018; + private static int field1019; + private static int field1020; + private static int field1021; + private static int field1022; + private static int field1023; + private static int field1024; + private static int field1025; + private static int field1026; + private static int field1027; + private static int field1028; + private static int field1029; + private static int field1030; + private static int field1031; + private static int field1032; + private static int field1033; + private static int field1034; + private static int field1035; + private static int field1036; + private static int field1037; + private static int field1038; + private static int field1039; + private static int field1040; + private static int field1041; + private static int field1042; + private static int field1043; + private static int field1044; + private static int field1045; + private static int field1046; + private static int field1047; + private static int field1048; + private static int field1049; + private static int field1050; + private static int field1051; + private static int field1052; + private static int field1053; + private static int field1054; + private static int field1055; + private static int field1056; + private static int field1057; + private static int field1058; + private static int field1059; + private static int field1060; + private static int field1061; + private static int field1062; + private static int field1063; + private static int field1064; + private static int field1065; + private static int field1066; + private static int field1067; + private static int field1068; + private static int field1069; + private static int field1070; + private static int field1071; + private static int field1072; + private static int field1073; + private static int field1074; + private static int field1075; + private static int field1076; + private static int field1077; + private static int field1078; + private static int field1079; + private static int field1080; + private static int field1081; + private static int field1082; + private static int field1083; + private static int field1084; + private static int field1085; + private static int field1086; + private static int field1087; + private static int field1088; + private static int field1089; + private static int field1090; + private static int field1091; + private static int field1092; + private static int field1093; + private static int field1094; + private static int field1095; + private static int field1096; + private static int field1097; + private static int field1098; + private static int field1099; + private static int field1100; + private static int field1101; + private static int field1102; + private static int field1103; + private static int field1104; + private static int field1105; + private static int field1106; + private static int field1107; + private static int field1108; + private static int field1109; + private static int field1110; + private static int field1111; + private static int field1112; + private static int field1113; + private static int field1114; + private static int field1115; + private static int field1116; + private static int field1117; + private static int field1118; + private static int field1119; + private static int field1120; + private static int field1121; + private static int field1122; + private static int field1123; + private static int field1124; + private static int field1125; + private static int field1126; + private static int field1127; + private static int field1128; + private static int field1129; + private static int field1130; + private static int field1131; + private static int field1132; + private static int field1133; + private static int field1134; + private static int field1135; + private static int field1136; + private static int field1137; + private static int field1138; + private static int field1139; + private static int field1140; + private static int field1141; + private static int field1142; + private static int field1143; + private static int field1144; + private static int field1145; + private static int field1146; + private static int field1147; + private static int field1148; + private static int field1149; + private static int field1150; + private static int field1151; + private static int field1152; + private static int field1153; + private static int field1154; + private static int field1155; + private static int field1156; + private static int field1157; + private static int field1158; + private static int field1159; + private static int field1160; + private static int field1161; + private static int field1162; + private static int field1163; + private static int field1164; + private static int field1165; + private static int field1166; + private static int field1167; + private static int field1168; + private static int field1169; + private static int field1170; + private static int field1171; + private static int field1172; + private static int field1173; + private static int field1174; + private static int field1175; + private static int field1176; + private static int field1177; + private static int field1178; + private static int field1179; + private static int field1180; + private static int field1181; + private static int field1182; + private static int field1183; + private static int field1184; + private static int field1185; + private static int field1186; + private static int field1187; + private static int field1188; + private static int field1189; + private static int field1190; + private static int field1191; + private static int field1192; + private static int field1193; + private static int field1194; + private static int field1195; + private static int field1196; + private static int field1197; + private static int field1198; + private static int field1199; + private static int field1200; + private static int field1201; + private static int field1202; + private static int field1203; + private static int field1204; + private static int field1205; + private static int field1206; + private static int field1207; + private static int field1208; + private static int field1209; + private static int field1210; + private static int field1211; + private static int field1212; + private static int field1213; + private static int field1214; + private static int field1215; + private static int field1216; + private static int field1217; + private static int field1218; + private static int field1219; + private static int field1220; + private static int field1221; + private static int field1222; + private static int field1223; + private static int field1224; + private static int field1225; + private static int field1226; + private static int field1227; + private static int field1228; + private static int field1229; + private static int field1230; + private static int field1231; + private static int field1232; + private static int field1233; + private static int field1234; + private static int field1235; + private static int field1236; + private static int field1237; + private static int field1238; + private static int field1239; + private static int field1240; + private static int field1241; + private static int field1242; + private static int field1243; + private static int field1244; + private static int field1245; + private static int field1246; + private static int field1247; + private static int field1248; + private static int field1249; + private static int field1250; + private static int field1251; + private static int field1252; + private static int field1253; + private static int field1254; + private static int field1255; + private static int field1256; + private static int field1257; + private static int field1258; + private static int field1259; + private static int field1260; + private static int field1261; + private static int field1262; + private static int field1263; + private static int field1264; + private static int field1265; + private static int field1266; + private static int field1267; + private static int field1268; + private static int field1269; + private static int field1270; + private static int field1271; + private static int field1272; + private static int field1273; + private static int field1274; + private static int field1275; + private static int field1276; + private static int field1277; + private static int field1278; + private static int field1279; + private static int field1280; + private static int field1281; + private static int field1282; + private static int field1283; + private static int field1284; + private static int field1285; + private static int field1286; + private static int field1287; + private static int field1288; + private static int field1289; + private static int field1290; + private static int field1291; + private static int field1292; + private static int field1293; + private static int field1294; + private static int field1295; + private static int field1296; + private static int field1297; + private static int field1298; + private static int field1299; + private static int field1300; + private static int field1301; + private static int field1302; + private static int field1303; + private static int field1304; + private static int field1305; + private static int field1306; + private static int field1307; + private static int field1308; + private static int field1309; + private static int field1310; + private static int field1311; + private static int field1312; + private static int field1313; + private static int field1314; + private static int field1315; + private static int field1316; + private static int field1317; + private static int field1318; + private static int field1319; + private static int field1320; + private static int field1321; + private static int field1322; + private static int field1323; + private static int field1324; + private static int field1325; + private static int field1326; + private static int field1327; + private static int field1328; + private static int field1329; + private static int field1330; + private static int field1331; + private static int field1332; + private static int field1333; + private static int field1334; + private static int field1335; + private static int field1336; + private static int field1337; + private static int field1338; + private static int field1339; + private static int field1340; + private static int field1341; + private static int field1342; + private static int field1343; + private static int field1344; + private static int field1345; + private static int field1346; + private static int field1347; + private static int field1348; + private static int field1349; + private static int field1350; + private static int field1351; + private static int field1352; + private static int field1353; + private static int field1354; + private static int field1355; + private static int field1356; + private static int field1357; + private static int field1358; + private static int field1359; + private static int field1360; + private static int field1361; + private static int field1362; + private static int field1363; + private static int field1364; + private static int field1365; + private static int field1366; + private static int field1367; + private static int field1368; + private static int field1369; + private static int field1370; + private static int field1371; + private static int field1372; + private static int field1373; + private static int field1374; + private static int field1375; + private static int field1376; + private static int field1377; + private static int field1378; + private static int field1379; + private static int field1380; + private static int field1381; + private static int field1382; + private static int field1383; + private static int field1384; + private static int field1385; + private static int field1386; + private static int field1387; + private static int field1388; + private static int field1389; + private static int field1390; + private static int field1391; + private static int field1392; + private static int field1393; + private static int field1394; + private static int field1395; + private static int field1396; + private static int field1397; + private static int field1398; + private static int field1399; + private static int field1400; + private static int field1401; + private static int field1402; + private static int field1403; + private static int field1404; + private static int field1405; + private static int field1406; + private static int field1407; + private static int field1408; + private static int field1409; + private static int field1410; + private static int field1411; + private static int field1412; + private static int field1413; + private static int field1414; + private static int field1415; + private static int field1416; + private static int field1417; + private static int field1418; + private static int field1419; + private static int field1420; + private static int field1421; + private static int field1422; + private static int field1423; + private static int field1424; + private static int field1425; + private static int field1426; + private static int field1427; + private static int field1428; + private static int field1429; + private static int field1430; + private static int field1431; + private static int field1432; + private static int field1433; + private static int field1434; + private static int field1435; + private static int field1436; + private static int field1437; + private static int field1438; + private static int field1439; + private static int field1440; + private static int field1441; + private static int field1442; + private static int field1443; + private static int field1444; + private static int field1445; + private static int field1446; + private static int field1447; + private static int field1448; + private static int field1449; + private static int field1450; + private static int field1451; + private static int field1452; + private static int field1453; + private static int field1454; + private static int field1455; + private static int field1456; + private static int field1457; + private static int field1458; + private static int field1459; + private static int field1460; + private static int field1461; + private static int field1462; + private static int field1463; + private static int field1464; + private static int field1465; + private static int field1466; + private static int field1467; + private static int field1468; + private static int field1469; + private static int field1470; + private static int field1471; + private static int field1472; + private static int field1473; + private static int field1474; + private static int field1475; + private static int field1476; + private static int field1477; + private static int field1478; + private static int field1479; + private static int field1480; + private static int field1481; + private static int field1482; + private static int field1483; + private static int field1484; + private static int field1485; + private static int field1486; + private static int field1487; + private static int field1488; + private static int field1489; + private static int field1490; + private static int field1491; + private static int field1492; + private static int field1493; + private static int field1494; + private static int field1495; + private static int field1496; + private static int field1497; + private static int field1498; + private static int field1499; + private static int field1500; + private static int field1501; + private static int field1502; + private static int field1503; + private static int field1504; + private static int field1505; + private static int field1506; + private static int field1507; + private static int field1508; + private static int field1509; + private static int field1510; + private static int field1511; + private static int field1512; + private static int field1513; + private static int field1514; + private static int field1515; + private static int field1516; + private static int field1517; + private static int field1518; + private static int field1519; + private static int field1520; + private static int field1521; + private static int field1522; + private static int field1523; + private static int field1524; + private static int field1525; + private static int field1526; + private static int field1527; + private static int field1528; + private static int field1529; + private static int field1530; + private static int field1531; + private static int field1532; + private static int field1533; + private static int field1534; + private static int field1535; + private static int field1536; + private static int field1537; + private static int field1538; + private static int field1539; + private static int field1540; + private static int field1541; + private static int field1542; + private static int field1543; + private static int field1544; + private static int field1545; + private static int field1546; + private static int field1547; + private static int field1548; + private static int field1549; + private static int field1550; + private static int field1551; + private static int field1552; + private static int field1553; + private static int field1554; + private static int field1555; + private static int field1556; + private static int field1557; + private static int field1558; + private static int field1559; + private static int field1560; + private static int field1561; + private static int field1562; + private static int field1563; + private static int field1564; + private static int field1565; + private static int field1566; + private static int field1567; + private static int field1568; + private static int field1569; + private static int field1570; + private static int field1571; + private static int field1572; + private static int field1573; + private static int field1574; + private static int field1575; + private static int field1576; + private static int field1577; + private static int field1578; + private static int field1579; + private static int field1580; + private static int field1581; + private static int field1582; + private static int field1583; + private static int field1584; + private static int field1585; + private static int field1586; + private static int field1587; + private static int field1588; + private static int field1589; + private static int field1590; + private static int field1591; + private static int field1592; + private static int field1593; + private static int field1594; + private static int field1595; + private static int field1596; + private static int field1597; + private static int field1598; + private static int field1599; + private static int field1600; + private static int field1601; + private static int field1602; + private static int field1603; + private static int field1604; + private static int field1605; + private static int field1606; + private static int field1607; + private static int field1608; + private static int field1609; + private static int field1610; + private static int field1611; + private static int field1612; + private static int field1613; + private static int field1614; + private static int field1615; + private static int field1616; + private static int field1617; + private static int field1618; + private static int field1619; + private static int field1620; + private static int field1621; + private static int field1622; + private static int field1623; + private static int field1624; + private static int field1625; + private static int field1626; + private static int field1627; + private static int field1628; + private static int field1629; + private static int field1630; + private static int field1631; + private static int field1632; + private static int field1633; + private static int field1634; + private static int field1635; + private static int field1636; + private static int field1637; + private static int field1638; + private static int field1639; + private static int field1640; + private static int field1641; + private static int field1642; + private static int field1643; + private static int field1644; + private static int field1645; + private static int field1646; + private static int field1647; + private static int field1648; + private static int field1649; + private static int field1650; + private static int field1651; + private static int field1652; + private static int field1653; + private static int field1654; + private static int field1655; + private static int field1656; + private static int field1657; + private static int field1658; + private static int field1659; + private static int field1660; + private static int field1661; + private static int field1662; + private static int field1663; + private static int field1664; + private static int field1665; + private static int field1666; + private static int field1667; + private static int field1668; + private static int field1669; + private static int field1670; + private static int field1671; + private static int field1672; + private static int field1673; + private static int field1674; + private static int field1675; + private static int field1676; + private static int field1677; + private static int field1678; + private static int field1679; + private static int field1680; + private static int field1681; + private static int field1682; + private static int field1683; + private static int field1684; + private static int field1685; + private static int field1686; + private static int field1687; + private static int field1688; + private static int field1689; + private static int field1690; + private static int field1691; + private static int field1692; + private static int field1693; + private static int field1694; + private static int field1695; + private static int field1696; + private static int field1697; + private static int field1698; + private static int field1699; + private static int field1700; + private static int field1701; + private static int field1702; + private static int field1703; + private static int field1704; + private static int field1705; + private static int field1706; + private static int field1707; + private static int field1708; + private static int field1709; + private static int field1710; + private static int field1711; + private static int field1712; + private static int field1713; + private static int field1714; + private static int field1715; + private static int field1716; + private static int field1717; + private static int field1718; + private static int field1719; + private static int field1720; + private static int field1721; + private static int field1722; + private static int field1723; + private static int field1724; + private static int field1725; + private static int field1726; + private static int field1727; + private static int field1728; + private static int field1729; + private static int field1730; + private static int field1731; + private static int field1732; + private static int field1733; + private static int field1734; + private static int field1735; + private static int field1736; + private static int field1737; + private static int field1738; + private static int field1739; + private static int field1740; + private static int field1741; + private static int field1742; + private static int field1743; + private static int field1744; + private static int field1745; + private static int field1746; + private static int field1747; + private static int field1748; + private static int field1749; + private static int field1750; + private static int field1751; + private static int field1752; + private static int field1753; + private static int field1754; + private static int field1755; + private static int field1756; + private static int field1757; + private static int field1758; + private static int field1759; + private static int field1760; + private static int field1761; + private static int field1762; + private static int field1763; + private static int field1764; + private static int field1765; + private static int field1766; + private static int field1767; + private static int field1768; + private static int field1769; + private static int field1770; + private static int field1771; + private static int field1772; + private static int field1773; + private static int field1774; + private static int field1775; + private static int field1776; + private static int field1777; + private static int field1778; + private static int field1779; + private static int field1780; + private static int field1781; + private static int field1782; + private static int field1783; + private static int field1784; + private static int field1785; + private static int field1786; + private static int field1787; + private static int field1788; + private static int field1789; + private static int field1790; + private static int field1791; + private static int field1792; + private static int field1793; + private static int field1794; + private static int field1795; + private static int field1796; + private static int field1797; + private static int field1798; + private static int field1799; + private static int field1800; + private static int field1801; + private static int field1802; + private static int field1803; + private static int field1804; + private static int field1805; + private static int field1806; + private static int field1807; + private static int field1808; + private static int field1809; + private static int field1810; + private static int field1811; + private static int field1812; + private static int field1813; + private static int field1814; + private static int field1815; + private static int field1816; + private static int field1817; + private static int field1818; + private static int field1819; + private static int field1820; + private static int field1821; + private static int field1822; + private static int field1823; + private static int field1824; + private static int field1825; + private static int field1826; + private static int field1827; + private static int field1828; + private static int field1829; + private static int field1830; + private static int field1831; + private static int field1832; + private static int field1833; + private static int field1834; + private static int field1835; + private static int field1836; + private static int field1837; + private static int field1838; + private static int field1839; + private static int field1840; + private static int field1841; + private static int field1842; + private static int field1843; + private static int field1844; + private static int field1845; + private static int field1846; + private static int field1847; + private static int field1848; + private static int field1849; + private static int field1850; + private static int field1851; + private static int field1852; + private static int field1853; + private static int field1854; + private static int field1855; + private static int field1856; + private static int field1857; + private static int field1858; + private static int field1859; + private static int field1860; + private static int field1861; + private static int field1862; + private static int field1863; + private static int field1864; + private static int field1865; + private static int field1866; + private static int field1867; + private static int field1868; + private static int field1869; + private static int field1870; + private static int field1871; + private static int field1872; + private static int field1873; + private static int field1874; + private static int field1875; + private static int field1876; + private static int field1877; + private static int field1878; + private static int field1879; + private static int field1880; + private static int field1881; + private static int field1882; + private static int field1883; + private static int field1884; + private static int field1885; + private static int field1886; + private static int field1887; + private static int field1888; + private static int field1889; + private static int field1890; + private static int field1891; + private static int field1892; + private static int field1893; + private static int field1894; + private static int field1895; + private static int field1896; + private static int field1897; + private static int field1898; + private static int field1899; + private static int field1900; + private static int field1901; + private static int field1902; + private static int field1903; + private static int field1904; + private static int field1905; + private static int field1906; + private static int field1907; + private static int field1908; + private static int field1909; + private static int field1910; + private static int field1911; + private static int field1912; + private static int field1913; + private static int field1914; + private static int field1915; + private static int field1916; + private static int field1917; + private static int field1918; + private static int field1919; + private static int field1920; + private static int field1921; + private static int field1922; + private static int field1923; + private static int field1924; + private static int field1925; + private static int field1926; + private static int field1927; + private static int field1928; + private static int field1929; + private static int field1930; + private static int field1931; + private static int field1932; + private static int field1933; + private static int field1934; + private static int field1935; + private static int field1936; + private static int field1937; + private static int field1938; + private static int field1939; + private static int field1940; + private static int field1941; + private static int field1942; + private static int field1943; + private static int field1944; + private static int field1945; + private static int field1946; + private static int field1947; + private static int field1948; + private static int field1949; + private static int field1950; + private static int field1951; + private static int field1952; + private static int field1953; + private static int field1954; + private static int field1955; + private static int field1956; + private static int field1957; + private static int field1958; + private static int field1959; + private static int field1960; + private static int field1961; + private static int field1962; + private static int field1963; + private static int field1964; + private static int field1965; + private static int field1966; + private static int field1967; + private static int field1968; + private static int field1969; + private static int field1970; + private static int field1971; + private static int field1972; + private static int field1973; + private static int field1974; + private static int field1975; + private static int field1976; + private static int field1977; + private static int field1978; + private static int field1979; + private static int field1980; + private static int field1981; + private static int field1982; + private static int field1983; + private static int field1984; + private static int field1985; + private static int field1986; + private static int field1987; + private static int field1988; + private static int field1989; + private static int field1990; + private static int field1991; + private static int field1992; + private static int field1993; + private static int field1994; + private static int field1995; + private static int field1996; + private static int field1997; + private static int field1998; + private static int field1999; + private static int field2000; + private static int field2001; + private static int field2002; + private static int field2003; + private static int field2004; + private static int field2005; + private static int field2006; + private static int field2007; + private static int field2008; + private static int field2009; + private static int field2010; + private static int field2011; + private static int field2012; + private static int field2013; + private static int field2014; + private static int field2015; + private static int field2016; + private static int field2017; + private static int field2018; + private static int field2019; + private static int field2020; + private static int field2021; + private static int field2022; + private static int field2023; + private static int field2024; + private static int field2025; + private static int field2026; + private static int field2027; + private static int field2028; + private static int field2029; + private static int field2030; + private static int field2031; + private static int field2032; + private static int field2033; + private static int field2034; + private static int field2035; + private static int field2036; + private static int field2037; + private static int field2038; + private static int field2039; + private static int field2040; + private static int field2041; + private static int field2042; + private static int field2043; + private static int field2044; + private static int field2045; + private static int field2046; + private static int field2047; + private static int field2048; + private static int field2049; + private static int field2050; + private static int field2051; + private static int field2052; + private static int field2053; + private static int field2054; + private static int field2055; + private static int field2056; + private static int field2057; + private static int field2058; + private static int field2059; + private static int field2060; + private static int field2061; + private static int field2062; + private static int field2063; + private static int field2064; + private static int field2065; + private static int field2066; + private static int field2067; + private static int field2068; + private static int field2069; + private static int field2070; + private static int field2071; + private static int field2072; + private static int field2073; + private static int field2074; + private static int field2075; + private static int field2076; + private static int field2077; + private static int field2078; + private static int field2079; + private static int field2080; + private static int field2081; + private static int field2082; + private static int field2083; + private static int field2084; + private static int field2085; + private static int field2086; + private static int field2087; + private static int field2088; + private static int field2089; + private static int field2090; + private static int field2091; + private static int field2092; + private static int field2093; + private static int field2094; + private static int field2095; + private static int field2096; + private static int field2097; + private static int field2098; + private static int field2099; + private static int field2100; + private static int field2101; + private static int field2102; + private static int field2103; + private static int field2104; + private static int field2105; + private static int field2106; + private static int field2107; + private static int field2108; + private static int field2109; + private static int field2110; + private static int field2111; + private static int field2112; + private static int field2113; + private static int field2114; + private static int field2115; + private static int field2116; + private static int field2117; + private static int field2118; + private static int field2119; + private static int field2120; + private static int field2121; + private static int field2122; + private static int field2123; + private static int field2124; + private static int field2125; + private static int field2126; + private static int field2127; + private static int field2128; + private static int field2129; + private static int field2130; + private static int field2131; + private static int field2132; + private static int field2133; + private static int field2134; + private static int field2135; + private static int field2136; + private static int field2137; + private static int field2138; + private static int field2139; + private static int field2140; + private static int field2141; + private static int field2142; + private static int field2143; + private static int field2144; + private static int field2145; + private static int field2146; + private static int field2147; + private static int field2148; + private static int field2149; + private static int field2150; + private static int field2151; + private static int field2152; + private static int field2153; + private static int field2154; + private static int field2155; + private static int field2156; + private static int field2157; + private static int field2158; + private static int field2159; + private static int field2160; + private static int field2161; + private static int field2162; + private static int field2163; + private static int field2164; + private static int field2165; + private static int field2166; + private static int field2167; + private static int field2168; + private static int field2169; + private static int field2170; + private static int field2171; + private static int field2172; + private static int field2173; + private static int field2174; + private static int field2175; + private static int field2176; + private static int field2177; + private static int field2178; + private static int field2179; + private static int field2180; + private static int field2181; + private static int field2182; + private static int field2183; + private static int field2184; + private static int field2185; + private static int field2186; + private static int field2187; + private static int field2188; + private static int field2189; + private static int field2190; + private static int field2191; + private static int field2192; + private static int field2193; + private static int field2194; + private static int field2195; + private static int field2196; + private static int field2197; + private static int field2198; + private static int field2199; + private static int field2200; + private static int field2201; + private static int field2202; + private static int field2203; + private static int field2204; + private static int field2205; + private static int field2206; + private static int field2207; + private static int field2208; + private static int field2209; + private static int field2210; + private static int field2211; + private static int field2212; + private static int field2213; + private static int field2214; + private static int field2215; + private static int field2216; + private static int field2217; + private static int field2218; + private static int field2219; + private static int field2220; + private static int field2221; + private static int field2222; + private static int field2223; + private static int field2224; + private static int field2225; + private static int field2226; + private static int field2227; + private static int field2228; + private static int field2229; + private static int field2230; + private static int field2231; + private static int field2232; + private static int field2233; + private static int field2234; + private static int field2235; + private static int field2236; + private static int field2237; + private static int field2238; + private static int field2239; + private static int field2240; + private static int field2241; + private static int field2242; + private static int field2243; + private static int field2244; + private static int field2245; + private static int field2246; + private static int field2247; + private static int field2248; + private static int field2249; + private static int field2250; + private static int field2251; + private static int field2252; + private static int field2253; + private static int field2254; + private static int field2255; + private static int field2256; + private static int field2257; + private static int field2258; + private static int field2259; + private static int field2260; + private static int field2261; + private static int field2262; + private static int field2263; + private static int field2264; + private static int field2265; + private static int field2266; + private static int field2267; + private static int field2268; + private static int field2269; + private static int field2270; + private static int field2271; + private static int field2272; + private static int field2273; + private static int field2274; + private static int field2275; + private static int field2276; + private static int field2277; + private static int field2278; + private static int field2279; + private static int field2280; + private static int field2281; + private static int field2282; + private static int field2283; + private static int field2284; + private static int field2285; + private static int field2286; + private static int field2287; + private static int field2288; + private static int field2289; + private static int field2290; + private static int field2291; + private static int field2292; + private static int field2293; + private static int field2294; + private static int field2295; + private static int field2296; + private static int field2297; + private static int field2298; + private static int field2299; + private static int field2300; + private static int field2301; + private static int field2302; + private static int field2303; + private static int field2304; + private static int field2305; + private static int field2306; + private static int field2307; + private static int field2308; + private static int field2309; + private static int field2310; + private static int field2311; + private static int field2312; + private static int field2313; + private static int field2314; + private static int field2315; + private static int field2316; + private static int field2317; + private static int field2318; + private static int field2319; + private static int field2320; + private static int field2321; + private static int field2322; + private static int field2323; + private static int field2324; + private static int field2325; + private static int field2326; + private static int field2327; + private static int field2328; + private static int field2329; + private static int field2330; + private static int field2331; + private static int field2332; + private static int field2333; + private static int field2334; + private static int field2335; + private static int field2336; + private static int field2337; + private static int field2338; + private static int field2339; + private static int field2340; + private static int field2341; + private static int field2342; + private static int field2343; + private static int field2344; + private static int field2345; + private static int field2346; + private static int field2347; + private static int field2348; + private static int field2349; + private static int field2350; + private static int field2351; + private static int field2352; + private static int field2353; + private static int field2354; + private static int field2355; + private static int field2356; + private static int field2357; + private static int field2358; + private static int field2359; + private static int field2360; + private static int field2361; + private static int field2362; + private static int field2363; + private static int field2364; + private static int field2365; + private static int field2366; + private static int field2367; + private static int field2368; + private static int field2369; + private static int field2370; + private static int field2371; + private static int field2372; + private static int field2373; + private static int field2374; + private static int field2375; + private static int field2376; + private static int field2377; + private static int field2378; + private static int field2379; + private static int field2380; + private static int field2381; + private static int field2382; + private static int field2383; + private static int field2384; + private static int field2385; + private static int field2386; + private static int field2387; + private static int field2388; + private static int field2389; + private static int field2390; + private static int field2391; + private static int field2392; + private static int field2393; + private static int field2394; + private static int field2395; + private static int field2396; + private static int field2397; + private static int field2398; + private static int field2399; + private static int field2400; + private static int field2401; + private static int field2402; + private static int field2403; + private static int field2404; + private static int field2405; + private static int field2406; + private static int field2407; + private static int field2408; + private static int field2409; + private static int field2410; + private static int field2411; + private static int field2412; + private static int field2413; + private static int field2414; + private static int field2415; + private static int field2416; + private static int field2417; + private static int field2418; + private static int field2419; + private static int field2420; + private static int field2421; + private static int field2422; + private static int field2423; + private static int field2424; + private static int field2425; + private static int field2426; + private static int field2427; + private static int field2428; + private static int field2429; + private static int field2430; + private static int field2431; + private static int field2432; + private static int field2433; + private static int field2434; + private static int field2435; + private static int field2436; + private static int field2437; + private static int field2438; + private static int field2439; + private static int field2440; + private static int field2441; + private static int field2442; + private static int field2443; + private static int field2444; + private static int field2445; + private static int field2446; + private static int field2447; + private static int field2448; + private static int field2449; + private static int field2450; + private static int field2451; + private static int field2452; + private static int field2453; + private static int field2454; + private static int field2455; + private static int field2456; + private static int field2457; + private static int field2458; + private static int field2459; + private static int field2460; + private static int field2461; + private static int field2462; + private static int field2463; + private static int field2464; + private static int field2465; + private static int field2466; + private static int field2467; + private static int field2468; + private static int field2469; + private static int field2470; + private static int field2471; + private static int field2472; + private static int field2473; + private static int field2474; + private static int field2475; + private static int field2476; + private static int field2477; + private static int field2478; + private static int field2479; + private static int field2480; + private static int field2481; + private static int field2482; + private static int field2483; + private static int field2484; + private static int field2485; + private static int field2486; + private static int field2487; + private static int field2488; + private static int field2489; + private static int field2490; + private static int field2491; + private static int field2492; + private static int field2493; + private static int field2494; + private static int field2495; + private static int field2496; + private static int field2497; + private static int field2498; + private static int field2499; + private static int field2500; + private static int field2501; + private static int field2502; + private static int field2503; + private static int field2504; + private static int field2505; + private static int field2506; + private static int field2507; + private static int field2508; + private static int field2509; + private static int field2510; + private static int field2511; + private static int field2512; + private static int field2513; + private static int field2514; + private static int field2515; + private static int field2516; + private static int field2517; + private static int field2518; + private static int field2519; + private static int field2520; + private static int field2521; + private static int field2522; + private static int field2523; + private static int field2524; + private static int field2525; + private static int field2526; + private static int field2527; + private static int field2528; + private static int field2529; + private static int field2530; + private static int field2531; + private static int field2532; + private static int field2533; + private static int field2534; + private static int field2535; + private static int field2536; + private static int field2537; + private static int field2538; + private static int field2539; + private static int field2540; + private static int field2541; + private static int field2542; + private static int field2543; + private static int field2544; + private static int field2545; + private static int field2546; + private static int field2547; + private static int field2548; + private static int field2549; + private static int field2550; + private static int field2551; + private static int field2552; + private static int field2553; + private static int field2554; + private static int field2555; + private static int field2556; + private static int field2557; + private static int field2558; + private static int field2559; + private static int field2560; + private static int field2561; + private static int field2562; + private static int field2563; + private static int field2564; + private static int field2565; + private static int field2566; + private static int field2567; + private static int field2568; + private static int field2569; + private static int field2570; + private static int field2571; + private static int field2572; + private static int field2573; + private static int field2574; + private static int field2575; + private static int field2576; + private static int field2577; + private static int field2578; + private static int field2579; + private static int field2580; + private static int field2581; + private static int field2582; + private static int field2583; + private static int field2584; + private static int field2585; + private static int field2586; + private static int field2587; + private static int field2588; + private static int field2589; + private static int field2590; + private static int field2591; + private static int field2592; + private static int field2593; + private static int field2594; + private static int field2595; + private static int field2596; + private static int field2597; + private static int field2598; + private static int field2599; + private static int field2600; + private static int field2601; + private static int field2602; + private static int field2603; + private static int field2604; + private static int field2605; + private static int field2606; + private static int field2607; + private static int field2608; + private static int field2609; + private static int field2610; + private static int field2611; + private static int field2612; + private static int field2613; + private static int field2614; + private static int field2615; + private static int field2616; + private static int field2617; + private static int field2618; + private static int field2619; + private static int field2620; + private static int field2621; + private static int field2622; + private static int field2623; + private static int field2624; + private static int field2625; + private static int field2626; + private static int field2627; + private static int field2628; + private static int field2629; + private static int field2630; + private static int field2631; + private static int field2632; + private static int field2633; + private static int field2634; + private static int field2635; + private static int field2636; + private static int field2637; + private static int field2638; + private static int field2639; + private static int field2640; + private static int field2641; + private static int field2642; + private static int field2643; + private static int field2644; + private static int field2645; + private static int field2646; + private static int field2647; + private static int field2648; + private static int field2649; + private static int field2650; + private static int field2651; + private static int field2652; + private static int field2653; + private static int field2654; + private static int field2655; + private static int field2656; + private static int field2657; + private static int field2658; + private static int field2659; + private static int field2660; + private static int field2661; + private static int field2662; + private static int field2663; + private static int field2664; + private static int field2665; + private static int field2666; + private static int field2667; + private static int field2668; + private static int field2669; + private static int field2670; + private static int field2671; + private static int field2672; + private static int field2673; + private static int field2674; + private static int field2675; + private static int field2676; + private static int field2677; + private static int field2678; + private static int field2679; + private static int field2680; + private static int field2681; + private static int field2682; + private static int field2683; + private static int field2684; + private static int field2685; + private static int field2686; + private static int field2687; + private static int field2688; + private static int field2689; + private static int field2690; + private static int field2691; + private static int field2692; + private static int field2693; + private static int field2694; + private static int field2695; + private static int field2696; + private static int field2697; + private static int field2698; + private static int field2699; + private static int field2700; + private static int field2701; + private static int field2702; + private static int field2703; + private static int field2704; + private static int field2705; + private static int field2706; + private static int field2707; + private static int field2708; + private static int field2709; + private static int field2710; + private static int field2711; + private static int field2712; + private static int field2713; + private static int field2714; + private static int field2715; + private static int field2716; + private static int field2717; + private static int field2718; + private static int field2719; + private static int field2720; + private static int field2721; + private static int field2722; + private static int field2723; + private static int field2724; + private static int field2725; + private static int field2726; + private static int field2727; + private static int field2728; + private static int field2729; + private static int field2730; + private static int field2731; + private static int field2732; + private static int field2733; + private static int field2734; + private static int field2735; + private static int field2736; + private static int field2737; + private static int field2738; + private static int field2739; + private static int field2740; + private static int field2741; + private static int field2742; + private static int field2743; + private static int field2744; + private static int field2745; + private static int field2746; + private static int field2747; + private static int field2748; + private static int field2749; + private static int field2750; + private static int field2751; + private static int field2752; + private static int field2753; + private static int field2754; + private static int field2755; + private static int field2756; + private static int field2757; + private static int field2758; + private static int field2759; + private static int field2760; + private static int field2761; + private static int field2762; + private static int field2763; + private static int field2764; + private static int field2765; + private static int field2766; + private static int field2767; + private static int field2768; + private static int field2769; + private static int field2770; + private static int field2771; + private static int field2772; + private static int field2773; + private static int field2774; + private static int field2775; + private static int field2776; + private static int field2777; + private static int field2778; + private static int field2779; + private static int field2780; + private static int field2781; + private static int field2782; + private static int field2783; + private static int field2784; + private static int field2785; + private static int field2786; + private static int field2787; + private static int field2788; + private static int field2789; + private static int field2790; + private static int field2791; + private static int field2792; + private static int field2793; + private static int field2794; + private static int field2795; + private static int field2796; + private static int field2797; + private static int field2798; + private static int field2799; + private static int field2800; + private static int field2801; + private static int field2802; + private static int field2803; + private static int field2804; + private static int field2805; + private static int field2806; + private static int field2807; + private static int field2808; + private static int field2809; + private static int field2810; + private static int field2811; + private static int field2812; + private static int field2813; + private static int field2814; + private static int field2815; + private static int field2816; + private static int field2817; + private static int field2818; + private static int field2819; + private static int field2820; + private static int field2821; + private static int field2822; + private static int field2823; + private static int field2824; + private static int field2825; + private static int field2826; + private static int field2827; + private static int field2828; + private static int field2829; + private static int field2830; + private static int field2831; + private static int field2832; + private static int field2833; + private static int field2834; + private static int field2835; + private static int field2836; + private static int field2837; + private static int field2838; + private static int field2839; + private static int field2840; + private static int field2841; + private static int field2842; + private static int field2843; + private static int field2844; + private static int field2845; + private static int field2846; + private static int field2847; + private static int field2848; + private static int field2849; + private static int field2850; + private static int field2851; + private static int field2852; + private static int field2853; + private static int field2854; + private static int field2855; + private static int field2856; + private static int field2857; + private static int field2858; + private static int field2859; + private static int field2860; + private static int field2861; + private static int field2862; + private static int field2863; + private static int field2864; + private static int field2865; + private static int field2866; + private static int field2867; + private static int field2868; + private static int field2869; + private static int field2870; + private static int field2871; + private static int field2872; + private static int field2873; + private static int field2874; + private static int field2875; + private static int field2876; + private static int field2877; + private static int field2878; + private static int field2879; + private static int field2880; + private static int field2881; + private static int field2882; + private static int field2883; + private static int field2884; + private static int field2885; + private static int field2886; + private static int field2887; + private static int field2888; + private static int field2889; + private static int field2890; + private static int field2891; + private static int field2892; + private static int field2893; + private static int field2894; + private static int field2895; + private static int field2896; + private static int field2897; + private static int field2898; + private static int field2899; + private static int field2900; + private static int field2901; + private static int field2902; + private static int field2903; + private static int field2904; + private static int field2905; + private static int field2906; + private static int field2907; + private static int field2908; + private static int field2909; + private static int field2910; + private static int field2911; + private static int field2912; + private static int field2913; + private static int field2914; + private static int field2915; + private static int field2916; + private static int field2917; + private static int field2918; + private static int field2919; + private static int field2920; + private static int field2921; + private static int field2922; + private static int field2923; + private static int field2924; + private static int field2925; + private static int field2926; + private static int field2927; + private static int field2928; + private static int field2929; + private static int field2930; + private static int field2931; + private static int field2932; + private static int field2933; + private static int field2934; + private static int field2935; + private static int field2936; + private static int field2937; + private static int field2938; + private static int field2939; + private static int field2940; + private static int field2941; + private static int field2942; + private static int field2943; + private static int field2944; + private static int field2945; + private static int field2946; + private static int field2947; + private static int field2948; + private static int field2949; + private static int field2950; + private static int field2951; + private static int field2952; + private static int field2953; + private static int field2954; + private static int field2955; + private static int field2956; + private static int field2957; + private static int field2958; + private static int field2959; + private static int field2960; + private static int field2961; + private static int field2962; + private static int field2963; + private static int field2964; + private static int field2965; + private static int field2966; + private static int field2967; + private static int field2968; + private static int field2969; + private static int field2970; + private static int field2971; + private static int field2972; + private static int field2973; + private static int field2974; + private static int field2975; + private static int field2976; + private static int field2977; + private static int field2978; + private static int field2979; + private static int field2980; + private static int field2981; + private static int field2982; + private static int field2983; + private static int field2984; + private static int field2985; + private static int field2986; + private static int field2987; + private static int field2988; + private static int field2989; + private static int field2990; + private static int field2991; + private static int field2992; + private static int field2993; + private static int field2994; + private static int field2995; + private static int field2996; + private static int field2997; + private static int field2998; + private static int field2999; + private static int field3000; + private static int field3001; + private static int field3002; + private static int field3003; + private static int field3004; + private static int field3005; + private static int field3006; + private static int field3007; + private static int field3008; + private static int field3009; + private static int field3010; + private static int field3011; + private static int field3012; + private static int field3013; + private static int field3014; + private static int field3015; + private static int field3016; + private static int field3017; + private static int field3018; + private static int field3019; + private static int field3020; + private static int field3021; + private static int field3022; + private static int field3023; + private static int field3024; + private static int field3025; + private static int field3026; + private static int field3027; + private static int field3028; + private static int field3029; + private static int field3030; + private static int field3031; + private static int field3032; + private static int field3033; + private static int field3034; + private static int field3035; + private static int field3036; + private static int field3037; + private static int field3038; + private static int field3039; + private static int field3040; + private static int field3041; + private static int field3042; + private static int field3043; + private static int field3044; + private static int field3045; + private static int field3046; + private static int field3047; + private static int field3048; + private static int field3049; + private static int field3050; + private static int field3051; + private static int field3052; + private static int field3053; + private static int field3054; + private static int field3055; + private static int field3056; + private static int field3057; + private static int field3058; + private static int field3059; + private static int field3060; + private static int field3061; + private static int field3062; + private static int field3063; + private static int field3064; + private static int field3065; + private static int field3066; + private static int field3067; + private static int field3068; + private static int field3069; + private static int field3070; + private static int field3071; + private static int field3072; + private static int field3073; + private static int field3074; + private static int field3075; + private static int field3076; + private static int field3077; + private static int field3078; + private static int field3079; + private static int field3080; + private static int field3081; + private static int field3082; + private static int field3083; + private static int field3084; + private static int field3085; + private static int field3086; + private static int field3087; + private static int field3088; + private static int field3089; + private static int field3090; + private static int field3091; + private static int field3092; + private static int field3093; + private static int field3094; + private static int field3095; + private static int field3096; + private static int field3097; + private static int field3098; + private static int field3099; + private static int field3100; + private static int field3101; + private static int field3102; + private static int field3103; + private static int field3104; + private static int field3105; + private static int field3106; + private static int field3107; + private static int field3108; + private static int field3109; + private static int field3110; + private static int field3111; + private static int field3112; + private static int field3113; + private static int field3114; + private static int field3115; + private static int field3116; + private static int field3117; + private static int field3118; + private static int field3119; + private static int field3120; + private static int field3121; + private static int field3122; + private static int field3123; + private static int field3124; + private static int field3125; + private static int field3126; + private static int field3127; + private static int field3128; + private static int field3129; + private static int field3130; + private static int field3131; + private static int field3132; + private static int field3133; + private static int field3134; + private static int field3135; + private static int field3136; + private static int field3137; + private static int field3138; + private static int field3139; + private static int field3140; + private static int field3141; + private static int field3142; + private static int field3143; + private static int field3144; + private static int field3145; + private static int field3146; + private static int field3147; + private static int field3148; + private static int field3149; + private static int field3150; + private static int field3151; + private static int field3152; + private static int field3153; + private static int field3154; + private static int field3155; + private static int field3156; + private static int field3157; + private static int field3158; + private static int field3159; + private static int field3160; + private static int field3161; + private static int field3162; + private static int field3163; + private static int field3164; + private static int field3165; + private static int field3166; + private static int field3167; + private static int field3168; + private static int field3169; + private static int field3170; + private static int field3171; + private static int field3172; + private static int field3173; + private static int field3174; + private static int field3175; + private static int field3176; + private static int field3177; + private static int field3178; + private static int field3179; + private static int field3180; + private static int field3181; + private static int field3182; + private static int field3183; + private static int field3184; + private static int field3185; + private static int field3186; + private static int field3187; + private static int field3188; + private static int field3189; + private static int field3190; + private static int field3191; + private static int field3192; + private static int field3193; + private static int field3194; + private static int field3195; + private static int field3196; + private static int field3197; + private static int field3198; + private static int field3199; + private static int field3200; + private static int field3201; + private static int field3202; + private static int field3203; + private static int field3204; + private static int field3205; + private static int field3206; + private static int field3207; + private static int field3208; + private static int field3209; + private static int field3210; + private static int field3211; + private static int field3212; + private static int field3213; + private static int field3214; + private static int field3215; + private static int field3216; + private static int field3217; + private static int field3218; + private static int field3219; + private static int field3220; + private static int field3221; + private static int field3222; + private static int field3223; + private static int field3224; + private static int field3225; + private static int field3226; + private static int field3227; + private static int field3228; + private static int field3229; + private static int field3230; + private static int field3231; + private static int field3232; + private static int field3233; + private static int field3234; + private static int field3235; + private static int field3236; + private static int field3237; + private static int field3238; + private static int field3239; + private static int field3240; + private static int field3241; + private static int field3242; + private static int field3243; + private static int field3244; + private static int field3245; + private static int field3246; + private static int field3247; + private static int field3248; + private static int field3249; + private static int field3250; + private static int field3251; + private static int field3252; + private static int field3253; + private static int field3254; + private static int field3255; + private static int field3256; + private static int field3257; + private static int field3258; + private static int field3259; + private static int field3260; + private static int field3261; + private static int field3262; + private static int field3263; + private static int field3264; + private static int field3265; + private static int field3266; + private static int field3267; + private static int field3268; + private static int field3269; + private static int field3270; + private static int field3271; + private static int field3272; + private static int field3273; + private static int field3274; + private static int field3275; + private static int field3276; + private static int field3277; + private static int field3278; + private static int field3279; + private static int field3280; + private static int field3281; + private static int field3282; + private static int field3283; + private static int field3284; + private static int field3285; + private static int field3286; + private static int field3287; + private static int field3288; + private static int field3289; + private static int field3290; + private static int field3291; + private static int field3292; + private static int field3293; + private static int field3294; + private static int field3295; + private static int field3296; + private static int field3297; + private static int field3298; + private static int field3299; + private static int field3300; + private static int field3301; + private static int field3302; + private static int field3303; + private static int field3304; + private static int field3305; + private static int field3306; + private static int field3307; + private static int field3308; + private static int field3309; + private static int field3310; + private static int field3311; + private static int field3312; + private static int field3313; + private static int field3314; + private static int field3315; + private static int field3316; + private static int field3317; + private static int field3318; + private static int field3319; + private static int field3320; + private static int field3321; + private static int field3322; + private static int field3323; + private static int field3324; + private static int field3325; + private static int field3326; + private static int field3327; + private static int field3328; + private static int field3329; + private static int field3330; + private static int field3331; + private static int field3332; + private static int field3333; + private static int field3334; + private static int field3335; + private static int field3336; + private static int field3337; + private static int field3338; + private static int field3339; + private static int field3340; + private static int field3341; + private static int field3342; + private static int field3343; + private static int field3344; + private static int field3345; + private static int field3346; + private static int field3347; + private static int field3348; + private static int field3349; + private static int field3350; + private static int field3351; + private static int field3352; + private static int field3353; + private static int field3354; + private static int field3355; + private static int field3356; + private static int field3357; + private static int field3358; + private static int field3359; + private static int field3360; + private static int field3361; + private static int field3362; + private static int field3363; + private static int field3364; + private static int field3365; + private static int field3366; + private static int field3367; + private static int field3368; + private static int field3369; + private static int field3370; + private static int field3371; + private static int field3372; + private static int field3373; + private static int field3374; + private static int field3375; + private static int field3376; + private static int field3377; + private static int field3378; + private static int field3379; + private static int field3380; + private static int field3381; + private static int field3382; + private static int field3383; + private static int field3384; + private static int field3385; + private static int field3386; + private static int field3387; + private static int field3388; + private static int field3389; + private static int field3390; + private static int field3391; + private static int field3392; + private static int field3393; + private static int field3394; + private static int field3395; + private static int field3396; + private static int field3397; + private static int field3398; + private static int field3399; + private static int field3400; + private static int field3401; + private static int field3402; + private static int field3403; + private static int field3404; + private static int field3405; + private static int field3406; + private static int field3407; + private static int field3408; + private static int field3409; + private static int field3410; + private static int field3411; + private static int field3412; + private static int field3413; + private static int field3414; + private static int field3415; + private static int field3416; + private static int field3417; + private static int field3418; + private static int field3419; + private static int field3420; + private static int field3421; + private static int field3422; + private static int field3423; + private static int field3424; + private static int field3425; + private static int field3426; + private static int field3427; + private static int field3428; + private static int field3429; + private static int field3430; + private static int field3431; + private static int field3432; + private static int field3433; + private static int field3434; + private static int field3435; + private static int field3436; + private static int field3437; + private static int field3438; + private static int field3439; + private static int field3440; + private static int field3441; + private static int field3442; + private static int field3443; + private static int field3444; + private static int field3445; + private static int field3446; + private static int field3447; + private static int field3448; + private static int field3449; + private static int field3450; + private static int field3451; + private static int field3452; + private static int field3453; + private static int field3454; + private static int field3455; + private static int field3456; + private static int field3457; + private static int field3458; + private static int field3459; + private static int field3460; + private static int field3461; + private static int field3462; + private static int field3463; + private static int field3464; + private static int field3465; + private static int field3466; + private static int field3467; + private static int field3468; + private static int field3469; + private static int field3470; + private static int field3471; + private static int field3472; + private static int field3473; + private static int field3474; + private static int field3475; + private static int field3476; + private static int field3477; + private static int field3478; + private static int field3479; + private static int field3480; + private static int field3481; + private static int field3482; + private static int field3483; + private static int field3484; + private static int field3485; + private static int field3486; + private static int field3487; + private static int field3488; + private static int field3489; + private static int field3490; + private static int field3491; + private static int field3492; + private static int field3493; + private static int field3494; + private static int field3495; + private static int field3496; + private static int field3497; + private static int field3498; + private static int field3499; + private static int field3500; + private static int field3501; + private static int field3502; + private static int field3503; + private static int field3504; + private static int field3505; + private static int field3506; + private static int field3507; + private static int field3508; + private static int field3509; + private static int field3510; + private static int field3511; + private static int field3512; + private static int field3513; + private static int field3514; + private static int field3515; + private static int field3516; + private static int field3517; + private static int field3518; + private static int field3519; + private static int field3520; + private static int field3521; + private static int field3522; + private static int field3523; + private static int field3524; + private static int field3525; + private static int field3526; + private static int field3527; + private static int field3528; + private static int field3529; + private static int field3530; + private static int field3531; + private static int field3532; + private static int field3533; + private static int field3534; + private static int field3535; + private static int field3536; + private static int field3537; + private static int field3538; + private static int field3539; + private static int field3540; + private static int field3541; + private static int field3542; + private static int field3543; + private static int field3544; + private static int field3545; + private static int field3546; + private static int field3547; + private static int field3548; + private static int field3549; + private static int field3550; + private static int field3551; + private static int field3552; + private static int field3553; + private static int field3554; + private static int field3555; + private static int field3556; + private static int field3557; + private static int field3558; + private static int field3559; + private static int field3560; + private static int field3561; + private static int field3562; + private static int field3563; + private static int field3564; + private static int field3565; + private static int field3566; + private static int field3567; + private static int field3568; + private static int field3569; + private static int field3570; + private static int field3571; + private static int field3572; + private static int field3573; + private static int field3574; + private static int field3575; + private static int field3576; + private static int field3577; + private static int field3578; + private static int field3579; + private static int field3580; + private static int field3581; + private static int field3582; + private static int field3583; + private static int field3584; + private static int field3585; + private static int field3586; + private static int field3587; + private static int field3588; + private static int field3589; + private static int field3590; + private static int field3591; + private static int field3592; + private static int field3593; + private static int field3594; + private static int field3595; + private static int field3596; + private static int field3597; + private static int field3598; + private static int field3599; + private static int field3600; + private static int field3601; + private static int field3602; + private static int field3603; + private static int field3604; + private static int field3605; + private static int field3606; + private static int field3607; + private static int field3608; + private static int field3609; + private static int field3610; + private static int field3611; + private static int field3612; + private static int field3613; + private static int field3614; + private static int field3615; + private static int field3616; + private static int field3617; + private static int field3618; + private static int field3619; + private static int field3620; + private static int field3621; + private static int field3622; + private static int field3623; + private static int field3624; + private static int field3625; + private static int field3626; + private static int field3627; + private static int field3628; + private static int field3629; + private static int field3630; + private static int field3631; + private static int field3632; + private static int field3633; + private static int field3634; + private static int field3635; + private static int field3636; + private static int field3637; + private static int field3638; + private static int field3639; + private static int field3640; + private static int field3641; + private static int field3642; + private static int field3643; + private static int field3644; + private static int field3645; + private static int field3646; + private static int field3647; + private static int field3648; + private static int field3649; + private static int field3650; + private static int field3651; + private static int field3652; + private static int field3653; + private static int field3654; + private static int field3655; + private static int field3656; + private static int field3657; + private static int field3658; + private static int field3659; + private static int field3660; + private static int field3661; + private static int field3662; + private static int field3663; + private static int field3664; + private static int field3665; + private static int field3666; + private static int field3667; + private static int field3668; + private static int field3669; + private static int field3670; + private static int field3671; + private static int field3672; + private static int field3673; + private static int field3674; + private static int field3675; + private static int field3676; + private static int field3677; + private static int field3678; + private static int field3679; + private static int field3680; + private static int field3681; + private static int field3682; + private static int field3683; + private static int field3684; + private static int field3685; + private static int field3686; + private static int field3687; + private static int field3688; + private static int field3689; + private static int field3690; + private static int field3691; + private static int field3692; + private static int field3693; + private static int field3694; + private static int field3695; + private static int field3696; + private static int field3697; + private static int field3698; + private static int field3699; + private static int field3700; + private static int field3701; + private static int field3702; + private static int field3703; + private static int field3704; + private static int field3705; + private static int field3706; + private static int field3707; + private static int field3708; + private static int field3709; + private static int field3710; + private static int field3711; + private static int field3712; + private static int field3713; + private static int field3714; + private static int field3715; + private static int field3716; + private static int field3717; + private static int field3718; + private static int field3719; + private static int field3720; + private static int field3721; + private static int field3722; + private static int field3723; + private static int field3724; + private static int field3725; + private static int field3726; + private static int field3727; + private static int field3728; + private static int field3729; + private static int field3730; + private static int field3731; + private static int field3732; + private static int field3733; + private static int field3734; + private static int field3735; + private static int field3736; + private static int field3737; + private static int field3738; + private static int field3739; + private static int field3740; + private static int field3741; + private static int field3742; + private static int field3743; + private static int field3744; + private static int field3745; + private static int field3746; + private static int field3747; + private static int field3748; + private static int field3749; + private static int field3750; + private static int field3751; + private static int field3752; + private static int field3753; + private static int field3754; + private static int field3755; + private static int field3756; + private static int field3757; + private static int field3758; + private static int field3759; + private static int field3760; + private static int field3761; + private static int field3762; + private static int field3763; + private static int field3764; + private static int field3765; + private static int field3766; + private static int field3767; + private static int field3768; + private static int field3769; + private static int field3770; + private static int field3771; + private static int field3772; + private static int field3773; + private static int field3774; + private static int field3775; + private static int field3776; + private static int field3777; + private static int field3778; + private static int field3779; + private static int field3780; + private static int field3781; + private static int field3782; + private static int field3783; + private static int field3784; + private static int field3785; + private static int field3786; + private static int field3787; + private static int field3788; + private static int field3789; + private static int field3790; + private static int field3791; + private static int field3792; + private static int field3793; + private static int field3794; + private static int field3795; + private static int field3796; + private static int field3797; + private static int field3798; + private static int field3799; + private static int field3800; + private static int field3801; + private static int field3802; + private static int field3803; + private static int field3804; + private static int field3805; + private static int field3806; + private static int field3807; + private static int field3808; + private static int field3809; + private static int field3810; + private static int field3811; + private static int field3812; + private static int field3813; + private static int field3814; + private static int field3815; + private static int field3816; + private static int field3817; + private static int field3818; + private static int field3819; + private static int field3820; + private static int field3821; + private static int field3822; + private static int field3823; + private static int field3824; + private static int field3825; + private static int field3826; + private static int field3827; + private static int field3828; + private static int field3829; + private static int field3830; + private static int field3831; + private static int field3832; + private static int field3833; + private static int field3834; + private static int field3835; + private static int field3836; + private static int field3837; + private static int field3838; + private static int field3839; + private static int field3840; + private static int field3841; + private static int field3842; + private static int field3843; + private static int field3844; + private static int field3845; + private static int field3846; + private static int field3847; + private static int field3848; + private static int field3849; + private static int field3850; + private static int field3851; + private static int field3852; + private static int field3853; + private static int field3854; + private static int field3855; + private static int field3856; + private static int field3857; + private static int field3858; + private static int field3859; + private static int field3860; + private static int field3861; + private static int field3862; + private static int field3863; + private static int field3864; + private static int field3865; + private static int field3866; + private static int field3867; + private static int field3868; + private static int field3869; + private static int field3870; + private static int field3871; + private static int field3872; + private static int field3873; + private static int field3874; + private static int field3875; + private static int field3876; + private static int field3877; + private static int field3878; + private static int field3879; + private static int field3880; + private static int field3881; + private static int field3882; + private static int field3883; + private static int field3884; + private static int field3885; + private static int field3886; + private static int field3887; + private static int field3888; + private static int field3889; + private static int field3890; + private static int field3891; + private static int field3892; + private static int field3893; + private static int field3894; + private static int field3895; + private static int field3896; + private static int field3897; + private static int field3898; + private static int field3899; + private static int field3900; + private static int field3901; + private static int field3902; + private static int field3903; + private static int field3904; + private static int field3905; + private static int field3906; + private static int field3907; + private static int field3908; + private static int field3909; + private static int field3910; + private static int field3911; + private static int field3912; + private static int field3913; + private static int field3914; + private static int field3915; + private static int field3916; + private static int field3917; + private static int field3918; + private static int field3919; + private static int field3920; + private static int field3921; + private static int field3922; + private static int field3923; + private static int field3924; + private static int field3925; + private static int field3926; + private static int field3927; + private static int field3928; + private static int field3929; + private static int field3930; + private static int field3931; + private static int field3932; + private static int field3933; + private static int field3934; + private static int field3935; + private static int field3936; + private static int field3937; + private static int field3938; + private static int field3939; + private static int field3940; + private static int field3941; + private static int field3942; + private static int field3943; + private static int field3944; + private static int field3945; + private static int field3946; + private static int field3947; + private static int field3948; + private static int field3949; + private static int field3950; + private static int field3951; + private static int field3952; + private static int field3953; + private static int field3954; + private static int field3955; + private static int field3956; + private static int field3957; + private static int field3958; + private static int field3959; + private static int field3960; + private static int field3961; + private static int field3962; + private static int field3963; + private static int field3964; + private static int field3965; + private static int field3966; + private static int field3967; + private static int field3968; + private static int field3969; + private static int field3970; + private static int field3971; + private static int field3972; + private static int field3973; + private static int field3974; + private static int field3975; + private static int field3976; + private static int field3977; + private static int field3978; + private static int field3979; + private static int field3980; + private static int field3981; + private static int field3982; + private static int field3983; + private static int field3984; + private static int field3985; + private static int field3986; + private static int field3987; + private static int field3988; + private static int field3989; + private static int field3990; + private static int field3991; + private static int field3992; + private static int field3993; + private static int field3994; + private static int field3995; + private static int field3996; + private static int field3997; + private static int field3998; + private static int field3999; +} diff --git a/test/hotspot/jtreg/compiler/controldependency/TestAntiDependencyForPinnedLoads.java b/test/hotspot/jtreg/compiler/controldependency/TestAntiDependencyForPinnedLoads.java new file mode 100644 index 0000000000000..05673015ebe04 --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestAntiDependencyForPinnedLoads.java @@ -0,0 +1,44 @@ +/* + * 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 8337066 + * @summary Test that MergeMem is skipped when looking for stores + * @run main/othervm -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,java.lang.StringUTF16::reverse + * compiler.controldependency.TestAntiDependencyForPinnedLoads + */ + +package compiler.controldependency; + +public class TestAntiDependencyForPinnedLoads { + public static void main(String[] args) { + for(int i = 0; i < 50_000; i++) { + String str = "YYYY年MM月DD日"; + StringBuffer strBuffer = new StringBuffer(str); + String revStr = strBuffer.reverse().toString(); + if (!revStr.equals("日DD月MM年YYYY")) throw new InternalError("FAIL"); + } + } +} diff --git a/test/hotspot/jtreg/compiler/debug/TestStressBailout.java b/test/hotspot/jtreg/compiler/debug/TestStressBailout.java new file mode 100644 index 0000000000000..68610576a390f --- /dev/null +++ b/test/hotspot/jtreg/compiler/debug/TestStressBailout.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.debug; + +import java.util.Random; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Utils; + +/* + * @test + * @key stress randomness + * @bug 8330157 + * @requires vm.debug == true & vm.compiler2.enabled & (vm.opt.AbortVMOnCompilationFailure == "null" | !vm.opt.AbortVMOnCompilationFailure) + * @summary Basic tests for bailout stress flag. + * @library /test/lib / + * @run driver compiler.debug.TestStressBailout + */ + +public class TestStressBailout { + + static void runTest(int invprob) throws Exception { + String[] procArgs = {"-Xcomp", "-XX:-TieredCompilation", "-XX:+StressBailout", + "-XX:StressBailoutMean=" + invprob, "-version"}; + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(procArgs); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + out.shouldHaveExitValue(0); + } + + public static void main(String[] args) throws Exception { + Random r = Utils.getRandomInstance(); + // Likely bail out on -version, for some low Mean value. + runTest(r.nextInt(1, 10)); + // Higher value + runTest(r.nextInt(10, 1_000_000)); + } +} diff --git a/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java b/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java new file mode 100644 index 0000000000000..36ad0bf84a40b --- /dev/null +++ b/test/hotspot/jtreg/compiler/gcbarriers/TestG1BarrierGeneration.java @@ -0,0 +1,639 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.gcbarriers; + +import compiler.lib.ir_framework.*; +import java.lang.invoke.VarHandle; +import java.lang.invoke.MethodHandles; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.concurrent.ThreadLocalRandom; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test that G1 barriers are generated and optimized as expected. + * @library /test/lib / + * @requires vm.gc.G1 + * @run driver compiler.gcbarriers.TestG1BarrierGeneration + */ + +public class TestG1BarrierGeneration { + static final String PRE_ONLY = "pre"; + static final String POST_ONLY = "post"; + static final String POST_ONLY_NOT_NULL = "post notnull"; + static final String PRE_AND_POST = "pre post"; + static final String PRE_AND_POST_NOT_NULL = "pre post notnull"; + + static class Outer { + Object f; + } + + static class OuterWithVolatileField { + volatile Object f; + } + + static class OuterWithFewFields implements Cloneable { + Object f1; + Object f2; + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + static class OuterWithManyFields implements Cloneable { + Object f1; + Object f2; + Object f3; + Object f4; + Object f5; + Object f6; + Object f7; + Object f8; + Object f9; + Object f10; + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + static final VarHandle fVarHandle; + static { + MethodHandles.Lookup l = MethodHandles.lookup(); + try { + fVarHandle = l.findVarHandle(Outer.class, "f", Object.class); + } catch (Exception e) { + throw new Error(e); + } + } + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + Scenario[] scenarios = new Scenario[2*2]; + int scenarioIndex = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + scenarios[scenarioIndex] = + new Scenario(scenarioIndex, + "-XX:CompileCommand=inline,java.lang.ref.*::*", + "-XX:" + (i == 0 ? "-" : "+") + "UseCompressedOops", + "-XX:" + (j == 0 ? "-" : "+") + "ReduceInitialCardMarks"); + scenarioIndex++; + } + } + framework.addScenarios(scenarios); + framework.start(); + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testStore(Outer o, Object o1) { + o.f = o1; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testStoreNull(Outer o) { + o.f = null; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testStoreObfuscatedNull(Outer o, Object o1) { + Object o2 = o1; + for (int i = 0; i < 4; i++) { + if ((i % 2) == 0) { + o2 = null; + } + } + // o2 is null here, but this is only known to C2 after applying some + // optimizations (loop unrolling, IGVN). + o.f = o2; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testStoreNotNull(Outer o, Object o1) { + if (o1.hashCode() == 42) { + return; + } + o.f = o1; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, + phase = CompilePhase.FINAL_CODE) + public static void testStoreTwice(Outer o, Outer p, Object o1) { + o.f = o1; + p.f = o1; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testStoreVolatile(OuterWithVolatileField o, Object o1) { + o.f = o1; + } + + @Test + @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_P}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N}, + phase = CompilePhase.FINAL_CODE) + public static Outer testStoreOnNewObject(Object o1) { + Outer o = new Outer(); + o.f = o1; + return o; + } + + @Test + @IR(failOn = {IRNode.STORE_P, IRNode.STORE_N}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public static Outer testStoreNullOnNewObject() { + Outer o = new Outer(); + o.f = null; + return o; + } + + @Test + @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY_NOT_NULL, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY_NOT_NULL, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_P}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N}, + phase = CompilePhase.FINAL_CODE) + public static Outer testStoreNotNullOnNewObject(Object o1) { + if (o1.hashCode() == 42) { + return null; + } + Outer o = new Outer(); + o.f = o1; + return o; + } + + @Test + @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "2"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "2"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_P}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N}, + phase = CompilePhase.FINAL_CODE) + public static Outer testStoreOnNewObjectInTwoPaths(Object o1, boolean c) { + Outer o; + if (c) { + o = new Outer(); + o.f = o1; + } else { + o = new Outer(); + o.f = o1; + } + return o; + } + + @Run(test = {"testStore", + "testStoreNull", + "testStoreObfuscatedNull", + "testStoreNotNull", + "testStoreTwice", + "testStoreVolatile", + "testStoreOnNewObject", + "testStoreNullOnNewObject", + "testStoreNotNullOnNewObject", + "testStoreOnNewObjectInTwoPaths"}) + public void runStoreTests() { + { + Outer o = new Outer(); + Object o1 = new Object(); + testStore(o, o1); + Asserts.assertEquals(o1, o.f); + } + { + Outer o = new Outer(); + testStoreNull(o); + Asserts.assertNull(o.f); + } + { + Outer o = new Outer(); + Object o1 = new Object(); + testStoreObfuscatedNull(o, o1); + Asserts.assertNull(o.f); + } + { + Outer o = new Outer(); + Object o1 = new Object(); + testStoreNotNull(o, o1); + Asserts.assertEquals(o1, o.f); + } + { + Outer o = new Outer(); + Outer p = new Outer(); + Object o1 = new Object(); + testStoreTwice(o, p, o1); + Asserts.assertEquals(o1, o.f); + Asserts.assertEquals(o1, p.f); + } + { + OuterWithVolatileField o = new OuterWithVolatileField(); + Object o1 = new Object(); + testStoreVolatile(o, o1); + Asserts.assertEquals(o1, o.f); + } + { + Object o1 = new Object(); + Outer o = testStoreOnNewObject(o1); + Asserts.assertEquals(o1, o.f); + } + { + Outer o = testStoreNullOnNewObject(); + Asserts.assertNull(o.f); + } + { + Object o1 = new Object(); + Outer o = testStoreNotNullOnNewObject(o1); + Asserts.assertEquals(o1, o.f); + } + { + Object o1 = new Object(); + Outer o = testStoreOnNewObjectInTwoPaths(o1, ThreadLocalRandom.current().nextBoolean()); + Asserts.assertEquals(o1, o.f); + } + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testArrayStore(Object[] a, int index, Object o1) { + a[index] = o1; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testArrayStoreNull(Object[] a, int index) { + a[index] = null; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, + phase = CompilePhase.FINAL_CODE) + public static void testArrayStoreNotNull(Object[] a, int index, Object o1) { + if (o1.hashCode() == 42) { + return; + } + a[index] = o1; + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, + phase = CompilePhase.FINAL_CODE) + public static void testArrayStoreTwice(Object[] a, Object[] b, int index, Object o1) { + a[index] = o1; + b[index] = o1; + } + + @Test + @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_P}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N}, + phase = CompilePhase.FINAL_CODE) + public static Object[] testStoreOnNewArray(Object o1) { + Object[] a = new Object[10]; + // The index needs to be concrete for C2 to detect that it is safe to + // remove the pre-barrier. + a[4] = o1; + return a; + } + + @Run(test = {"testArrayStore", + "testArrayStoreNull", + "testArrayStoreNotNull", + "testArrayStoreTwice", + "testStoreOnNewArray"}) + public void runArrayStoreTests() { + { + Object[] a = new Object[10]; + Object o1 = new Object(); + testArrayStore(a, 4, o1); + Asserts.assertEquals(o1, a[4]); + } + { + Object[] a = new Object[10]; + testArrayStoreNull(a, 4); + Asserts.assertNull(a[4]); + } + { + Object[] a = new Object[10]; + Object o1 = new Object(); + testArrayStoreNotNull(a, 4, o1); + Asserts.assertEquals(o1, a[4]); + } + { + Object[] a = new Object[10]; + Object[] b = new Object[10]; + Object o1 = new Object(); + testArrayStoreTwice(a, b, 4, o1); + Asserts.assertEquals(o1, a[4]); + Asserts.assertEquals(o1, b[4]); + } + { + Object o1 = new Object(); + Object[] a = testStoreOnNewArray(o1); + Asserts.assertEquals(o1, a[4]); + } + } + + @Test + public static Object[] testCloneArrayOfObjects(Object[] a) { + Object[] a1 = null; + try { + a1 = a.clone(); + } catch (Exception e) {} + return a1; + } + + @Test + @IR(applyIf = {"ReduceInitialCardMarks", "true"}, + failOn = {IRNode.G1_STORE_P, IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"ReduceInitialCardMarks", "false", "UseCompressedOops", "false"}, + counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "2"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfAnd = {"ReduceInitialCardMarks", "false", "UseCompressedOops", "true"}, + counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "2"}, + phase = CompilePhase.FINAL_CODE) + public static OuterWithFewFields testCloneObjectWithFewFields(OuterWithFewFields o) { + Object o1 = null; + try { + o1 = o.clone(); + } catch (Exception e) {} + return (OuterWithFewFields)o1; + } + + @Test + @IR(applyIf = {"ReduceInitialCardMarks", "true"}, + counts = {IRNode.CALL_OF, "jlong_disjoint_arraycopy", "1"}) + @IR(applyIf = {"ReduceInitialCardMarks", "false"}, + counts = {IRNode.CALL_OF, "G1BarrierSetRuntime::clone", "1"}) + public static OuterWithManyFields testCloneObjectWithManyFields(OuterWithManyFields o) { + Object o1 = null; + try { + o1 = o.clone(); + } catch (Exception e) {} + return (OuterWithManyFields)o1; + } + + @Run(test = {"testCloneArrayOfObjects", + "testCloneObjectWithFewFields", + "testCloneObjectWithManyFields"}) + public void runCloneTests() { + { + Object o1 = new Object(); + Object[] a = new Object[4]; + for (int i = 0; i < 4; i++) { + a[i] = o1; + } + Object[] a1 = testCloneArrayOfObjects(a); + for (int i = 0; i < 4; i++) { + Asserts.assertEquals(o1, a1[i]); + } + } + { + Object a = new Object(); + Object b = new Object(); + OuterWithFewFields o = new OuterWithFewFields(); + o.f1 = a; + o.f2 = b; + OuterWithFewFields o1 = testCloneObjectWithFewFields(o); + Asserts.assertEquals(a, o1.f1); + Asserts.assertEquals(b, o1.f2); + } + { + Object a = new Object(); + Object b = new Object(); + Object c = new Object(); + Object d = new Object(); + Object e = new Object(); + Object f = new Object(); + Object g = new Object(); + Object h = new Object(); + Object i = new Object(); + Object j = new Object(); + OuterWithManyFields o = new OuterWithManyFields(); + o.f1 = a; + o.f2 = b; + o.f3 = c; + o.f4 = d; + o.f5 = e; + o.f6 = f; + o.f7 = g; + o.f8 = h; + o.f9 = i; + o.f10 = j; + OuterWithManyFields o1 = testCloneObjectWithManyFields(o); + Asserts.assertEquals(a, o1.f1); + Asserts.assertEquals(b, o1.f2); + Asserts.assertEquals(c, o1.f3); + Asserts.assertEquals(d, o1.f4); + Asserts.assertEquals(e, o1.f5); + Asserts.assertEquals(f, o1.f6); + Asserts.assertEquals(g, o1.f7); + Asserts.assertEquals(h, o1.f8); + Asserts.assertEquals(i, o1.f9); + Asserts.assertEquals(j, o1.f10); + } + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_COMPARE_AND_EXCHANGE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_COMPARE_AND_EXCHANGE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + static Object testCompareAndExchange(Outer o, Object oldVal, Object newVal) { + return fVarHandle.compareAndExchange(o, oldVal, newVal); + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_COMPARE_AND_SWAP_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + static boolean testCompareAndSwap(Outer o, Object oldVal, Object newVal) { + return fVarHandle.compareAndSet(o, oldVal, newVal); + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, + phase = CompilePhase.FINAL_CODE) + static Object testGetAndSet(Outer o, Object newVal) { + return fVarHandle.getAndSet(o, newVal); + } + + @Run(test = {"testCompareAndExchange", + "testCompareAndSwap", + "testGetAndSet"}) + public void runAtomicTests() { + { + Outer o = new Outer(); + Object oldVal = new Object(); + o.f = oldVal; + Object newVal = new Object(); + Object oldVal2 = testCompareAndExchange(o, oldVal, newVal); + Asserts.assertEquals(oldVal, oldVal2); + Asserts.assertEquals(o.f, newVal); + } + { + Outer o = new Outer(); + Object oldVal = new Object(); + o.f = oldVal; + Object newVal = new Object(); + boolean b = testCompareAndSwap(o, oldVal, newVal); + Asserts.assertTrue(b); + Asserts.assertEquals(o.f, newVal); + } + { + Outer o = new Outer(); + Object oldVal = new Object(); + o.f = oldVal; + Object newVal = new Object(); + Object oldVal2 = testGetAndSet(o, newVal); + Asserts.assertEquals(oldVal, oldVal2); + Asserts.assertEquals(o.f, newVal); + } + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_LOAD_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_LOAD_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + static Object testLoadSoftReference(SoftReference ref) { + return ref.get(); + } + + @Test + @IR(applyIf = {"UseCompressedOops", "false"}, + counts = {IRNode.G1_LOAD_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIf = {"UseCompressedOops", "true"}, + counts = {IRNode.G1_LOAD_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, + phase = CompilePhase.FINAL_CODE) + static Object testLoadWeakReference(WeakReference ref) { + return ref.get(); + } + + @Run(test = {"testLoadSoftReference", + "testLoadWeakReference"}) + public void runReferenceTests() { + { + Object o1 = new Object(); + SoftReference sref = new SoftReference(o1); + Object o2 = testLoadSoftReference(sref); + Asserts.assertTrue(o2 == o1 || o2 == null); + } + { + Object o1 = new Object(); + WeakReference wref = new WeakReference(o1); + Object o2 = testLoadWeakReference(wref); + Asserts.assertTrue(o2 == o1 || o2 == null); + } + } +} diff --git a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/MaterializeVirtualObjectTest.java b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/MaterializeVirtualObjectTest.java index 5855440d0b275..1dcef0c96cd64 100644 --- a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/MaterializeVirtualObjectTest.java +++ b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/MaterializeVirtualObjectTest.java @@ -27,6 +27,7 @@ * * @requires vm.jvmci & vm.compMode == "Xmixed" * @requires vm.opt.final.EliminateAllocations == true + * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * * @comment no "-Xcomp -XX:-TieredCompilation" combination allowed until JDK-8140018 is resolved * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/ClassLoaderBuilder.java b/test/hotspot/jtreg/compiler/lib/compile_framework/ClassLoaderBuilder.java new file mode 100644 index 0000000000000..2f14cfb0f0489 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/ClassLoaderBuilder.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.compile_framework; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * Build a ClassLoader that loads from classpath and {@code classesDir}. + * Helper class that generates a ClassLoader which allows loading classes + * from the classpath (see {@link Utils#getClassPaths()}) and {@code classesDir}. + *

          + * The CompileFramework compiles all its classes to a specific {@code classesDir}, + * and this generated ClassLoader thus can be used to load those classes. + */ +class ClassLoaderBuilder { + + /** + * Build a ClassLoader that loads from classpath and {@code classesDir}. + */ + public static ClassLoader build(Path classesDir) { + ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); + + try { + // Classpath for all included classes (e.g. IR Framework). + // Get all class paths, convert to URLs. + List urls = new ArrayList<>(); + for (String path : Utils.getClassPaths()) { + urls.add(new File(path).toURI().toURL()); + } + // And add in the compiled classes from this instance of CompileFramework. + urls.add(new File(classesDir.toString()).toURI().toURL()); + return URLClassLoader.newInstance(urls.toArray(URL[]::new), sysLoader); + } catch (IOException e) { + throw new CompileFrameworkException("IOException while creating ClassLoader", e); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java new file mode 100644 index 0000000000000..0f45d982af6d6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/Compile.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.compile_framework; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; +import java.util.List; +import jdk.test.lib.JDKToolFinder; + +/** + * Helper class for compilation of Java and Jasm {@link SourceCode}. + */ +class Compile { + private static final int COMPILE_TIMEOUT = 60; + + private static final String JAVA_PATH = JDKToolFinder.getJDKTool("java"); + private static final String JAVAC_PATH = JDKToolFinder.getJDKTool("javac"); + + /** + * Compile all sources in {@code javaSources}. First write them to the {@code sourceDir}, + * then compile them to class-files which are stored in {@code classesDir}. + */ + public static void compileJavaSources(List javaSources, Path sourceDir, Path classesDir) { + if (javaSources.isEmpty()) { + Utils.printlnVerbose("No java sources to compile."); + return; + } + Utils.printlnVerbose("Compiling Java sources: " + javaSources.size()); + + List javaFilePaths = writeSourcesToFiles(javaSources, sourceDir); + compileJavaFiles(javaFilePaths, classesDir); + Utils.printlnVerbose("Java sources compiled."); + } + + /** + * Compile a list of files (i.e. {@code paths}) using javac and store + * them in {@code classesDir}. + */ + private static void compileJavaFiles(List paths, Path classesDir) { + List command = new ArrayList<>(); + + command.add(JAVAC_PATH); + command.add("-classpath"); + // Note: the backslashes from windows paths must be escaped! + command.add(Utils.getEscapedClassPathAndClassesDir(classesDir)); + command.add("-d"); + command.add(classesDir.toString()); + for (Path path : paths) { + command.add(path.toAbsolutePath().toString()); + } + + executeCompileCommand(command); + } + + /** + * Compile all sources in {@code jasmSources}. First write them to the {@code sourceDir}, + * then compile them to class-files which are stored in {@code classesDir}. + */ + public static void compileJasmSources(List jasmSources, Path sourceDir, Path classesDir) { + if (jasmSources.isEmpty()) { + Utils.printlnVerbose("No jasm sources to compile."); + return; + } + Utils.printlnVerbose("Compiling jasm sources: " + jasmSources.size()); + + List jasmFilePaths = writeSourcesToFiles(jasmSources, sourceDir); + compileJasmFiles(jasmFilePaths, classesDir); + Utils.printlnVerbose("Jasm sources compiled."); + } + + /** + * Compile a list of files (i.e. {@code paths}) using asmtools jasm and store + * them in {@code classesDir}. + */ + private static void compileJasmFiles(List paths, Path classesDir) { + List command = new ArrayList<>(); + + command.add(JAVA_PATH); + command.add("-classpath"); + command.add(getAsmToolsPath()); + command.add("org.openjdk.asmtools.jasm.Main"); + command.add("-d"); + command.add(classesDir.toString()); + for (Path path : paths) { + command.add(path.toAbsolutePath().toString()); + } + + executeCompileCommand(command); + } + + /** + * Get the path of asmtools, which is shipped with JTREG. + */ + private static String getAsmToolsPath() { + for (String path : Utils.getClassPaths()) { + if (path.endsWith("jtreg.jar")) { + File jtreg = new File(path); + File dir = jtreg.getAbsoluteFile().getParentFile(); + File asmtools = new File(dir, "asmtools.jar"); + if (!asmtools.exists()) { + throw new InternalCompileFrameworkException("Found jtreg.jar in classpath, but could not find asmtools.jar"); + } + return asmtools.getAbsolutePath(); + } + } + throw new InternalCompileFrameworkException("Could not find asmtools because could not find jtreg.jar in classpath"); + } + + private static void writeCodeToFile(String code, Path path) { + Utils.printlnVerbose("File: " + path); + + // Ensure directory of the file exists. + Path dir = path.getParent(); + try { + Files.createDirectories(dir); + } catch (Exception e) { + throw new CompileFrameworkException("Could not create directory: " + dir, e); + } + + // Write to file. + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write(code); + } catch (Exception e) { + throw new CompileFrameworkException("Could not write file: " + path, e); + } + } + + /** + * Write each source in {@code sources} to a file inside {@code sourceDir}. + */ + private static List writeSourcesToFiles(List sources, Path sourceDir) { + List storedFiles = new ArrayList<>(); + for (SourceCode sourceCode : sources) { + Path path = sourceDir.resolve(sourceCode.filePathName()); + writeCodeToFile(sourceCode.code(), path); + storedFiles.add(path); + } + return storedFiles; + } + + /** + * Execute a given compilation, given as a {@code command}. + */ + private static void executeCompileCommand(List command) { + Utils.printlnVerbose("Compile command: " + String.join(" ", command)); + + ProcessBuilder builder = new ProcessBuilder(command); + builder.redirectErrorStream(true); + + String output; + int exitCode; + try { + Process process = builder.start(); + boolean exited = process.waitFor(COMPILE_TIMEOUT, TimeUnit.SECONDS); + if (!exited) { + process.destroyForcibly(); + System.out.println("Timeout: compile command: " + String.join(" ", command)); + throw new InternalCompileFrameworkException("Process timeout: compilation took too long."); + } + output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + exitCode = process.exitValue(); + } catch (IOException e) { + throw new InternalCompileFrameworkException("IOException during compilation", e); + } catch (InterruptedException e) { + throw new CompileFrameworkException("InterruptedException during compilation", e); + } + + if (exitCode != 0 || !output.isEmpty()) { + System.err.println("Compilation failed."); + System.err.println("Exit code: " + exitCode); + System.err.println("Output: '" + output + "'"); + throw new CompileFrameworkException("Compilation failed."); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java new file mode 100644 index 0000000000000..fe23d596f3c64 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFramework.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.compile_framework; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * This is the entry-point for the Compile Framework. Its purpose it to allow + * compilation and execution of Java and Jasm sources generated at runtime. + * + *

          Please reference the README.md for more details and examples. + */ +public class CompileFramework { + private final List javaSources = new ArrayList<>(); + private final List jasmSources = new ArrayList<>(); + private final Path sourceDir = Utils.makeUniqueDir("compile-framework-sources-"); + private final Path classesDir = Utils.makeUniqueDir("compile-framework-classes-"); + private ClassLoader classLoader; + + /** + * Set up a new Compile Framework instance, for a new compilation unit. + */ + public CompileFramework() {} + + /** + * Add a Java source to the compilation. + * + * @param className Class name of the class (e.g. "{@code p.xyz.YXZ}"). + * @param code Java code for the class, in the form of a {@link String}. + */ + public void addJavaSourceCode(String className, String code) { + javaSources.add(new SourceCode(className, "java", code)); + } + + /** + * Add a Jasm source to the compilation. + * + * @param className Class name of the class (e.g. "{@code p.xyz.YXZ}"). + * @param code Jasm code for the class, in the form of a {@link String}. + */ + public void addJasmSourceCode(String className, String code) { + jasmSources.add(new SourceCode(className, "jasm", code)); + } + + /** + * Compile all sources: store the sources to the {@link sourceDir} directory, compile + * Java and Jasm sources and store the generated class-files in the {@link classesDir} + * directory. + */ + public void compile() { + if (classLoader != null) { + throw new CompileFrameworkException("Cannot compile twice!"); + } + + Utils.printlnVerbose("------------------ CompileFramework: SourceCode -------------------"); + Utils.printlnVerbose(sourceCodesAsString(jasmSources)); + Utils.printlnVerbose(sourceCodesAsString(javaSources)); + + System.out.println("------------------ CompileFramework: Compilation ------------------"); + System.out.println("Source directory: " + sourceDir); + System.out.println("Classes directory: " + classesDir); + + Compile.compileJasmSources(jasmSources, sourceDir, classesDir); + Compile.compileJavaSources(javaSources, sourceDir, classesDir); + classLoader = ClassLoaderBuilder.build(classesDir); + } + + private static String sourceCodesAsString(List sourceCodes) { + StringBuilder builder = new StringBuilder(); + for (SourceCode sourceCode : sourceCodes) { + builder.append("SourceCode: ").append(sourceCode.filePathName()).append(System.lineSeparator()); + builder.append(sourceCode.code()).append(System.lineSeparator()); + } + return builder.toString(); + } + + /** + * Access a class from the compiled code. + * + * @param name Name of the class to be retrieved. + * @return The class corresponding to the {@code name}. + */ + public Class getClass(String name) { + try { + return Class.forName(name, true, classLoader); + } catch (ClassNotFoundException e) { + throw new CompileFrameworkException("Class not found:", e); + } + } + + /** + * Invoke a static method from the compiled code. + * + * @param className Class name of a compiled class. + * @param methodName Method name of the class. + * @param args List of arguments for the method invocation. + * @return Return value from the invocation. + */ + public Object invoke(String className, String methodName, Object[] args) { + Method method = findMethod(className, methodName); + + try { + return method.invoke(null, args); + } catch (IllegalAccessException e) { + throw new CompileFrameworkException("Illegal access:", e); + } catch (InvocationTargetException e) { + throw new CompileFrameworkException("Invocation target:", e); + } + } + + private Method findMethod(String className, String methodName) { + Class c = getClass(className); + Method[] methods = c.getDeclaredMethods(); + Method method = null; + + for (Method m : methods) { + if (m.getName().equals(methodName)) { + if (method != null) { + throw new CompileFrameworkException("Method name \"" + methodName + "\" not unique in class \n" + className + "\"."); + } + method = m; + } + } + + if (method == null) { + throw new CompileFrameworkException("Method \"" + methodName + "\" not found in class \n" + className + "\"."); + } + + return method; + } + + /** + * Returns the classpath appended with the {@link classesDir}, where + * the compiled classes are stored. This enables another VM to load + * the compiled classes. Note, the string is already backslash escaped, + * so that Windows paths which use backslashes can be used directly + * as strings. + * + * @return Classpath appended with the path to the compiled classes. + */ + public String getEscapedClassPathOfCompiledClasses() { + return Utils.getEscapedClassPathAndClassesDir(classesDir); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFrameworkException.java b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFrameworkException.java new file mode 100644 index 0000000000000..5c71a33a533fd --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/CompileFrameworkException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.compile_framework; + +/** + * Exception thrown in the Compilation Framework. Most likely, the user is responsible for the failure. + */ +public class CompileFrameworkException extends RuntimeException { + public CompileFrameworkException(String message) { + super("Exception in Compile Framework:" + System.lineSeparator() + message); + } + + public CompileFrameworkException(String message, Throwable e) { + super("Exception in Compile Framework:" + System.lineSeparator() + message, e); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/InternalCompileFrameworkException.java b/test/hotspot/jtreg/compiler/lib/compile_framework/InternalCompileFrameworkException.java new file mode 100644 index 0000000000000..0cfed80ce1cd6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/InternalCompileFrameworkException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.compile_framework; + +/** + * Internal exception thrown in Compilation Framework. Most likely, this is due to a bug in the CompileFramework. + */ +public class InternalCompileFrameworkException extends RuntimeException { + public InternalCompileFrameworkException(String message) { + super("Internal exception in Compile Framework, please file a bug:" + System.lineSeparator() + message); + } + + public InternalCompileFrameworkException(String message, Throwable e) { + super("Internal exception in Compile Framework, please file a bug:" + System.lineSeparator() + message, e); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/README.md b/test/hotspot/jtreg/compiler/lib/compile_framework/README.md new file mode 100644 index 0000000000000..76ddf677811c7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/README.md @@ -0,0 +1,57 @@ +# Compile Framework +The Compile Framework allows the compilation and execution of Java and Jasm sources, which are generated at runtime. + +## Motivation +We want to be able to generate Java and Jasm source code in the form of Strings at runtime, then compile them, load the classes and invoke some methods. This allows us to write more elaborate tests. For example small dedicated fuzzers that are targetted at some specific compiler optimization. + +This is more powerful than hand-written tests, as we can generalize tests and cover more examples. It can also be better than a script-generated test: those are static and often the script is not integrated with the generated test. Another limitation of a generator script is that it is only run once, creating fixed static tests. Compilation at runtime allows us to randomly generate tests each time. + +Of course we could compile at runtime without this framework, but it abstracts away the complexity of compilation, and allows the test-writer to focus on the generation of the source code. + +## How to Use the Compile Framework + +Please reference the examples found in [examples](../../../testlibrary_tests/compile_framework/examples/). Some basic tests can be found in [tests](../../../testlibrary_tests/compile_framework/tests/). + +Here a very simple example: + + // Create a new CompileFramework instance. + CompileFramework compileFramework = new CompileFramework(); + + // Add a java source file. + compileFramework.addJavaSourceCode("XYZ", ""); + + // Compile the source file. + compileFramework.compile(); + + // Object returnValue = XYZ.test(5); + Object returnValue = compileFramework.invoke("XYZ", "test", new Object[] {5}); + +### Creating a new Compile Framework Instance + +First, one must create a `new CompileFramework()`, which creates two directories: a sources and a classes directory (see `sourcesDir` and `classesDir` in [CompileFramework](./CompileFramework.java)). The sources directory is where all the sources are placed by the Compile Framework, and the classes directory is where all the compiled classes are placed by the Compile Framework. + +The Compile Framework prints the names of the directories, they are subdirectories of the JTREG scratch directory `JTWork/scratch`. + +### Adding Sources to the Compilation + +Java and Jasm sources can be added to the compilation using `compileFramework.addJavaSourceCode()` and `compileFramework.addJasmSourceCode()`. The source classes can depend on each other, and they can also use the IR Framework ([IRFrameworkJavaExample](../../../testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java)). + +When using the IR Framework, or any other library that needs to be compiled, it can be necessary to explicitly let JTREG compile that library. For example with `@compile ../../../compiler/lib/ir_framework/TestFramework.java`. Otherwise, the corresponding class files may not be available, and a corresponding failure will be encounter at class loading. + +### Compiling + +All sources are compiled with `compileFramework.compile()`. First, the sources are stored to the sources directory, then compiled, and then the class-files stored in the classes directory. The respective directory names are printed, so that the user can easily access the generated files for debugging. + +### Interacting with the Compiled Code + +The compiled code is then loaded with a `ClassLoader`. The classes can be accessed directly with `compileFramework.getClass(name)`. Specific methods can also directly be invoked with `compileFramework.invoke()`. + +Should one require the modified classpath that includes the compiled classes, this is available with `compileFramework.getEscapedClassPathOfCompiledClasses()`. This can be necessary if the test launches any other VMs that also access the compiled classes. This is for example necessary when using the IR Framework. + +### Running the Compiled Code in a New VM + +One can also run the compiled code in a new VM. For this, one has to set the classpath with `compileFramework.getEscapedClassPathOfCompiledClasses()` ([RunWithFlagsExample](../../../testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java)) + +### Verbose Printing + +For debugging purposes, one can enable verbose printing, with `-DCompileFrameworkVerbose=true`. diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/SourceCode.java b/test/hotspot/jtreg/compiler/lib/compile_framework/SourceCode.java new file mode 100644 index 0000000000000..df38e420758e6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/SourceCode.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.compile_framework; + +/** + * This class represents the source code of a specific class. + */ +record SourceCode(String className, String extension, String code) { + public String filePathName() { + StringBuilder builder = new StringBuilder(); + builder.append(className.replace('.','/')).append(".").append(extension); + return builder.toString(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java b/test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java new file mode 100644 index 0000000000000..0ac2720f33b19 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/compile_framework/Utils.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.compile_framework; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class, with many helper methods for the Compile Framework. + */ +class Utils { + private static final boolean VERBOSE = Boolean.getBoolean("CompileFrameworkVerbose"); + + /** + * Verbose printing, enabled with {@code -DCompileFrameworkVerbose=true}. + */ + public static void printlnVerbose(String s) { + if (VERBOSE) { + System.out.println(s); + } + } + + /** + * Create a temporary directory with a unique name to avoid collisions + * with multi-threading. Used to create the sources and classes directories. Since they + * are unique even across threads, the Compile Framework is multi-threading safe, i.e. + * it does not have collisions if two instances generate classes with the same name. + */ + public static Path makeUniqueDir(String prefix) { + try { + return Files.createTempDirectory(Paths.get("."), prefix); + } catch (Exception e) { + throw new InternalCompileFrameworkException("Could not set up temporary directory", e); + } + } + + /** + * Get all paths in the classpath. + */ + public static String[] getClassPaths() { + String separator = File.pathSeparator; + return System.getProperty("java.class.path").split(separator); + } + + /** + * Return the classpath, appended with the {@code classesDir}. + */ + public static String getEscapedClassPathAndClassesDir(Path classesDir) { + String cp = System.getProperty("java.class.path") + + File.pathSeparator + + classesDir.toAbsolutePath(); + // Escape the backslash for Windows paths. We are using the path in the + // command-line and Java code, so we always want it to be escaped. + return cp.replace("\\", "\\\\"); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 16f56012d3dd9..8c4b3c93343f6 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -119,13 +119,13 @@ public class IRNode { public static final String VECTOR_SIZE_32 = VECTOR_SIZE + "32"; public static final String VECTOR_SIZE_64 = VECTOR_SIZE + "64"; - private static final String TYPE_BYTE = "byte"; - private static final String TYPE_CHAR = "char"; - private static final String TYPE_SHORT = "short"; - private static final String TYPE_INT = "int"; - private static final String TYPE_LONG = "long"; - private static final String TYPE_FLOAT = "float"; - private static final String TYPE_DOUBLE = "double"; + private static final String TYPE_BYTE = "B"; + private static final String TYPE_CHAR = "C"; + private static final String TYPE_SHORT = "S"; + private static final String TYPE_INT = "I"; + private static final String TYPE_LONG = "J"; + private static final String TYPE_FLOAT = "F"; + private static final String TYPE_DOUBLE = "D"; /** * IR placeholder string to regex-for-compile-phase map. @@ -358,6 +358,11 @@ public class IRNode { beforeMatchingNameRegex(CALL, "Call.*Java"); } + public static final String CALL_OF = COMPOSITE_PREFIX + "CALL_OF" + POSTFIX; + static { + callOfNodes(CALL_OF, "Call.*"); + } + public static final String CALL_OF_METHOD = COMPOSITE_PREFIX + "CALL_OF_METHOD" + POSTFIX; static { callOfNodes(CALL_OF_METHOD, "Call.*Java"); @@ -581,6 +586,92 @@ public class IRNode { vectorNode(FMA_VD, "FmaVD", TYPE_DOUBLE); } + public static final String G1_COMPARE_AND_EXCHANGE_N_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_COMPARE_AND_EXCHANGE_N_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1CompareAndExchangeN\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_COMPARE_AND_EXCHANGE_N_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_COMPARE_AND_EXCHANGE_P_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_COMPARE_AND_EXCHANGE_P_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1CompareAndExchangeP\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_COMPARE_AND_EXCHANGE_P_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_COMPARE_AND_SWAP_N_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_COMPARE_AND_SWAP_N_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1CompareAndSwapN\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_COMPARE_AND_SWAP_N_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1CompareAndSwapP\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_ENCODE_P_AND_STORE_N = PREFIX + "G1_ENCODE_P_AND_STORE_N" + POSTFIX; + static { + machOnlyNameRegex(G1_ENCODE_P_AND_STORE_N, "g1EncodePAndStoreN"); + } + + public static final String G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1EncodePAndStoreN\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_GET_AND_SET_N_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_GET_AND_SET_N_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1GetAndSetN\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_GET_AND_SET_N_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_GET_AND_SET_P_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_GET_AND_SET_P_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1GetAndSetP\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_GET_AND_SET_P_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_LOAD_N = PREFIX + "G1_LOAD_N" + POSTFIX; + static { + machOnlyNameRegex(G1_LOAD_N, "g1LoadN"); + } + + public static final String G1_LOAD_N_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_LOAD_N_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1LoadN\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_LOAD_N_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_LOAD_P_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_LOAD_P_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1LoadP\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_LOAD_P_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_STORE_N = PREFIX + "G1_STORE_N" + POSTFIX; + static { + machOnlyNameRegex(G1_STORE_N, "g1StoreN"); + } + + public static final String G1_STORE_N_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_STORE_N_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1StoreN\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_STORE_N_WITH_BARRIER_FLAG, regex); + } + + public static final String G1_STORE_P = PREFIX + "G1_STORE_P" + POSTFIX; + static { + machOnlyNameRegex(G1_STORE_P, "g1StoreP"); + } + + public static final String G1_STORE_P_WITH_BARRIER_FLAG = COMPOSITE_PREFIX + "G1_STORE_P_WITH_BARRIER_FLAG" + POSTFIX; + static { + String regex = START + "g1StoreP\\S*" + MID + "barrier\\(\\s*" + IS_REPLACED + "\\s*\\)" + END; + machOnly(G1_STORE_P_WITH_BARRIER_FLAG, regex); + } + public static final String IF = PREFIX + "IF" + POSTFIX; static { beforeMatchingNameRegex(IF, "If\\b"); @@ -852,6 +943,11 @@ public class IRNode { vectorNode(LSHIFT_VL, "LShiftVL", TYPE_LONG); } + public static final String MACH_TEMP = PREFIX + "MACH_TEMP" + POSTFIX; + static { + machOnlyNameRegex(MACH_TEMP, "MachTemp"); + } + public static final String MACRO_LOGIC_V = PREFIX + "MACRO_LOGIC_V" + POSTFIX; static { afterBarrierExpansionToBeforeMatching(MACRO_LOGIC_V, "MacroLogicV"); @@ -1148,6 +1244,12 @@ public class IRNode { trapNodes(NULL_CHECK_TRAP, "null_check"); } + public static final String OOPMAP_WITH = COMPOSITE_PREFIX + "OOPMAP_WITH" + POSTFIX; + static { + String regex = "(#\\s*OopMap\\s*\\{.*" + IS_REPLACED + ".*\\})"; + optoOnly(OOPMAP_WITH, regex); + } + public static final String OR_VB = VECTOR_PREFIX + "OR_VB" + POSTFIX; static { vectorNode(OR_VB, "OrV", TYPE_BYTE); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java index e7db6c4844ca2..bf3021a6868f1 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.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 @@ -133,7 +133,6 @@ private String regexForVectorIRNode(String nodeRegex, VMInfo vmInfo, Comparison. } } String sizeRegex = IRNode.parseVectorNodeSize(size, type, vmInfo); - return nodeRegex.replaceAll(IRNode.IS_REPLACED, - "vector[A-Za-z]\\\\[" + sizeRegex + "\\\\]:\\\\{" + type + "\\\\}"); + return nodeRegex.replaceAll(IRNode.IS_REPLACED, "vector[A-Za-z]<" + type + "," + sizeRegex + ">"); } } diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestSafePointWithEAState.java b/test/hotspot/jtreg/compiler/longcountedloops/TestSafePointWithEAState.java new file mode 100644 index 0000000000000..a04af09570fb9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestSafePointWithEAState.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, 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 + * 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 8336702 + * @summary C2 compilation fails with "all memory state should have been processed" assert + * + * @run main/othervm TestSafePointWithEAState + * + */ + +public class TestSafePointWithEAState { + int[] b = new int[400]; + + void c() { + int e; + float f; + for (long d = 0; d < 5000; d++) { + e = 1; + while ((e += 3) < 200) { + if (d < b.length) { + for (int g = 0; g < 10000; ++g) ; + } + } + synchronized (TestSafePointWithEAState.class) { + f = new h(e).n; + } + } + } + + public static void main(String[] m) { + TestSafePointWithEAState o = new TestSafePointWithEAState(); + o.c(); + } +} + +class h { + float n; + h(float n) { + this.n = n; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java index fd5c2969074f9..c77f4f6fa2e25 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java @@ -1363,7 +1363,7 @@ static Object[] test16b(byte[] a) { static Object[] test17a(long[] a) { // Unsafe: vectorizes with profiling (not xcomp) for (int i = 0; i < RANGE; i++) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i; long v = UNSAFE.getLongUnaligned(a, adr); UNSAFE.putLongUnaligned(a, adr, v + 1); } @@ -1375,7 +1375,7 @@ static Object[] test17a(long[] a) { static Object[] test17b(long[] a) { // Not alignable for (int i = 0; i < RANGE-1; i++) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1; long v = UNSAFE.getLongUnaligned(a, adr); UNSAFE.putLongUnaligned(a, adr, v + 1); } @@ -1392,7 +1392,7 @@ static Object[] test17b(long[] a) { static Object[] test17c(long[] a) { // Unsafe: aligned vectorizes for (int i = 0; i < RANGE-1; i+=4) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i; long v0 = UNSAFE.getLongUnaligned(a, adr + 0); long v1 = UNSAFE.getLongUnaligned(a, adr + 8); UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1); @@ -1422,7 +1422,7 @@ static Object[] test17c(long[] a) { static Object[] test17d(long[] a) { // Not alignable for (int i = 0; i < RANGE-1; i+=4) { - int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1; + long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1; long v0 = UNSAFE.getLongUnaligned(a, adr + 0); long v1 = UNSAFE.getLongUnaligned(a, adr + 8); UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1); diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java index 7b95781905ead..d75db965ea3c1 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVectorFuzzer.java @@ -1090,11 +1090,11 @@ static Object[] testUU_unsafe_BasI(byte[] a) { int init = init_con_or_var(); int limit = limit_con_or_var(); int stride = stride_con(); - int scale = scale_con(); - int offset = offset1_con_or_var(); + long scale = scale_con(); + long offset = offset1_con_or_var(); for (int i = init; i < limit; i += stride) { - int adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale; + long adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale; int v = UNSAFE.getIntUnaligned(a, adr); UNSAFE.putIntUnaligned(a, adr, v + 1); } @@ -1105,19 +1105,19 @@ static Object[] testUU_unsafe_BasIH(byte[] a, byte[] b, byte[] c) { int init = init_con_or_var(); int limit = limit_con_or_var(); int stride = stride_con(); - int scale = scale_con(); - int offset1 = offset1_con_or_var(); - int offset2 = offset2_con_or_var(); - int offset3 = offset3_con_or_var(); + long scale = scale_con(); + long offset1 = offset1_con_or_var(); + long offset2 = offset2_con_or_var(); + long offset3 = offset3_con_or_var(); int h1 = hand_unrolling1_con(); int h2 = hand_unrolling2_con(); int h3 = hand_unrolling3_con(); for (int i = init; i < limit; i += stride) { - int adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale; - int adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale; - int adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale; + long adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale; + long adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale; + long adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale; if (h1 >= 1) { UNSAFE.putIntUnaligned(a, adr1 + 0*4, UNSAFE.getIntUnaligned(a, adr1 + 0*4) + 1); } if (h1 >= 2) { UNSAFE.putIntUnaligned(a, adr1 + 1*4, UNSAFE.getIntUnaligned(a, adr1 + 1*4) + 1); } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java index 65398e8adfd39..197ae08b6d882 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java @@ -172,10 +172,10 @@ public void runTest2() { static void test2(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) { for (int i = 0; i < RANGE; i+=2) { // int and float arrays are two slices. But we pretend both are of type int. - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1); - dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); - dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1); + dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); + dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); } } @@ -248,10 +248,10 @@ static void test5(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) { for (int i = 0; i < RANGE; i+=2) { // same as test2, except that reordering leads to different semantics // explanation analogue to test4 - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A - dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X - dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1); // B + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A + dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X + dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1); // B } } @@ -275,18 +275,18 @@ static void test6(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // Chain of parallelizable op and conversion - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); } } @@ -307,18 +307,18 @@ static void test7(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // Cycle involving 3 memory slices - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -340,19 +340,19 @@ static void test8(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // 2-cycle, with more ops after - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); // more stuff after - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); } } @@ -373,19 +373,19 @@ static void test9(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // 2-cycle, with more stuff before - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); // 2-cycle - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -423,18 +423,18 @@ static void test10(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, // // The cycle thus does not only go via packs, but also scalar ops. // - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; // A - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; // R: constant mismatch - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43; // S - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; // U - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; // V - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // B: moved down - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; // A + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; // R: constant mismatch + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43; // S + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; // U + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; // V + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // B: moved down + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -463,8 +463,8 @@ static void verify(String name, int[] data, int[] gold) { static void verify(String name, float[] data, float[] gold) { for (int i = 0; i < RANGE; i++) { - int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); - int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); + int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); + int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); if (datav != goldv) { throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv); } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java index 32d69689a4269..2be7d52c78087 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency2.java @@ -58,18 +58,18 @@ static void test(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb, long[] dataLa, long[] dataLb) { for (int i = 0; i < RANGE; i+=2) { // For explanation, see test 10 in TestIndependentPacksWithCyclicDependency.java - int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00); - int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; - int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43; - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10); - unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11); - float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; - float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20); - unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21); - int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01); + int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00); + int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; + int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43; + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10); + unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11); + float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; + float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20); + unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21); + int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01); } } @@ -83,8 +83,8 @@ static void verify(String name, int[] data, int[] gold) { static void verify(String name, float[] data, float[] gold) { for (int i = 0; i < RANGE; i++) { - int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); - int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i); + int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); + int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i); if (datav != goldv) { throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv); } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java index f2ed8b6aec2b3..c54a684c6911d 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestScheduleReordersScalarMemops.java @@ -124,10 +124,10 @@ static void test1(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) { for (int i = 0; i < RANGE; i+=2) { // Do the same as test0, but without int-float conversion. // This should reproduce on machines where conversion is not implemented. - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A +1 - dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X - dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y - unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] * 11); // B *11 + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A +1 + dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X + dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y + unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] * 11); // B *11 } } diff --git a/test/hotspot/jtreg/compiler/parsing/TestUnsafeArrayAccessWithNullBase.java b/test/hotspot/jtreg/compiler/parsing/TestUnsafeArrayAccessWithNullBase.java new file mode 100644 index 0000000000000..28eb4f3c165d4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestUnsafeArrayAccessWithNullBase.java @@ -0,0 +1,113 @@ +/* + * 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 8320308 + * @summary Unsafe::getShortUnaligned with base null hidden behind CheckCastPP nodes + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run main/othervm -Xbatch -XX:CompileCommand=quiet -XX:TypeProfileLevel=222 + * -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline + * -XX:CompileCommand=compileonly,compiler.parsing.TestUnsafeArrayAccessWithNullBase::test* + * -XX:-TieredCompilation compiler.parsing.TestUnsafeArrayAccessWithNullBase + * @run main compiler.parsing.TestUnsafeArrayAccessWithNullBase + */ + +package compiler.parsing; + +import java.lang.reflect.*; +import jdk.internal.misc.Unsafe; + +public class TestUnsafeArrayAccessWithNullBase { + + /* + Trigger bug when handling Unsafe.getShortUnaligned with null checks and inlined methods. + The bug appears when the method is incrementally inlined and optimized based on the argument profile information. + + Warmup Phase: By warming up with non-null values, the argument profile for the helper methods records non-null types. + - insert CheckCastPP: speculative=byte[int:>=0] for return of getSmall/getLarge + - insert CheckCastPP: speculative=byte[int:>=0] for argument `Object array` in helperSmall/helperLarge + Trigger Phase: Calling test causes LibraryCallKit::inline_unsafe_access(..) for Unsafe::getShortUnaligned to fail: + Reason: UNSAFE.getShortUnaligned(array, offset) is called with array=null, + but ConP null is now hidden by two CheckCastPP with speculative=byte[int:>=0] in the graph + */ + + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + private static final Object byteArray = new byte[1_050_000]; + + public static Object getLarge(boolean useNull) { + return useNull ? null : byteArray; + } + + public static Object getSmall(boolean useNull) { + return useNull ? null : new byte[10]; + } + + // use a helper to delay inlining of UNSAFE.getShortUnaligned + public static int helperLarge(Object array, boolean run) { + // offset >= os::vm_page_size(): LibraryCallKit::classify_unsafe_addr returns Type::AnyPtr + return run ? UNSAFE.getShortUnaligned(array, 1_049_000) : 0; // after warmup CheckCastPP: speculative=byte[int:>=0] + } + + public static int accessLargeArray(boolean useNull, boolean run) { + Object array = getLarge(useNull); // after warmup CheckCastPP: speculative=byte[int:>=0] + // getLarge() ensures null is only visible after helperLarge was (incrementally) inlined + return helperLarge(array, run); + } + + // use a helper to delay inlining of UNSAFE.getShortUnaligned + // warmup adds argument profile information for array: CheckCastPP with type non null + public static int helperSmall(Object array, boolean run) { + // 0 <= offset < os::vm_page_size(): LibraryCallKit::classify_unsafe_addr returns Type::OopPtr + return run ? UNSAFE.getShortUnaligned(array, 1) : 0; // after warmup CheckCastPP: speculative=byte[int:>=0] + } + + public static int accessSmallArray(boolean useNull, boolean run) { + Object array = getSmall(useNull); // after warmup CheckCastPP: speculative=byte[int:>=0] + return helperSmall(array, run); + } + + public static int test1(boolean run) { + return accessLargeArray(true, run); + } + + public static int test2(boolean run) { + return accessSmallArray(true, run); + } + + public static void main(String[] args) { + // Warmup to collect speculative types + for (int i = 0; i < 10_000; i++) { + accessLargeArray(false, true); + accessSmallArray(false, true); + } + + // Trigger Compilation + for (int i = 0; i < 10_000; ++i) { + test1(false); + test2(false); + } + } +} diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java b/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java index ba045814fc2dd..83bfd63c629c6 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java @@ -25,6 +25,7 @@ * @test * @bug 8073480 * @summary explicit range checks should be recognized by C2 + * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / * @modules java.base/jdk.internal.misc:+open * @build jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/compiler/runtime/safepoints/TestMachTempsAcrossSafepoints.java b/test/hotspot/jtreg/compiler/runtime/safepoints/TestMachTempsAcrossSafepoints.java new file mode 100644 index 0000000000000..ecd8f58c5ed82 --- /dev/null +++ b/test/hotspot/jtreg/compiler/runtime/safepoints/TestMachTempsAcrossSafepoints.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.runtime.safepoints; + +import compiler.lib.ir_framework.*; +import java.lang.ref.SoftReference; + +/** + * @test + * @summary Test that undefined values generated by MachTemp nodes (in this + * case, derived from G1 barriers) are not included in OopMaps. + * Extracted from java.lang.invoke.LambdaFormEditor::getInCache. + * @key randomness + * @library /test/lib / + * @requires vm.gc.G1 & vm.bits == 64 & vm.opt.final.UseCompressedOops == true + * @run driver compiler.runtime.safepoints.TestMachTempsAcrossSafepoints + */ + +public class TestMachTempsAcrossSafepoints { + + static class RefWithKey extends SoftReference { + final int key; + + public RefWithKey(int key) { + super(new Object()); + this.key = key; + } + + @DontInline + @Override + public boolean equals(Object obj) { + return obj instanceof RefWithKey that && this.key == that.key; + } + } + + public static void main(String[] args) throws Exception { + String inlineCmd = "-XX:CompileCommand=inline,java.lang.ref.SoftReference::get"; + TestFramework.runWithFlags(inlineCmd, "-XX:+StressGCM", "-XX:+StressLCM", "-XX:StressSeed=1"); + TestFramework.runWithFlags(inlineCmd, "-XX:+StressGCM", "-XX:+StressLCM"); + } + + @Test + @IR(counts = {IRNode.G1_LOAD_N, "1"}, phase = CompilePhase.FINAL_CODE) + @IR(counts = {IRNode.MACH_TEMP, ">= 1"}, phase = CompilePhase.FINAL_CODE) + @IR(counts = {IRNode.STATIC_CALL_OF_METHOD, "equals", "2"}) + @IR(failOn = {IRNode.OOPMAP_WITH, "NarrowOop"}) + static private Object test(RefWithKey key, RefWithKey[] refs) { + RefWithKey k = null; + // This loop causes the register allocator to not "rematerialize" all + // MachTemp nodes generated for the reference g1LoadN instruction below. + for (int i = 0; i < refs.length; i++) { + RefWithKey k0 = refs[0]; + if (k0.equals(key)) { + k = k0; + } + } + if (k != null && !key.equals(k)) { + return null; + } + // The MachTemp node implementing the dst TEMP operand in the g1LoadN + // instruction corresponding to k.get() can be scheduled across the + // above call to RefWithKey::equals(), due to an unfortunate interaction + // of inaccurate basic block frequency estimation (emulated in this test + // by randomizing the GCM and LCM heuristics) and call-catch cleanup. + // Since narrow pointer MachTemp nodes are typed as narrow OOPs, this + // causes the oopmap builder to include the MachTemp node definition in + // the RefWithKey::equals() return oopmap. + return (k != null) ? k.get() : null; + } + + @Run(test = "test") + @Warmup(0) + public void run() { + RefWithKey ref = new RefWithKey(42); + test(ref, new RefWithKey[]{ref}); + } +} diff --git a/test/hotspot/jtreg/compiler/types/TestBadMemSliceWithInterfaces.java b/test/hotspot/jtreg/compiler/types/TestBadMemSliceWithInterfaces.java new file mode 100644 index 0000000000000..5090c2dd6cf97 --- /dev/null +++ b/test/hotspot/jtreg/compiler/types/TestBadMemSliceWithInterfaces.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024, 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 + * 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 8340214 + * @summary C2 compilation asserts with "no node with a side effect" in PhaseIdealLoop::try_sink_out_of_loop + * + * @run main/othervm -XX:-BackgroundCompilation TestBadMemSliceWithInterfaces + * + */ + +public class TestBadMemSliceWithInterfaces { + public static void main(String[] args) { + B b = new B(); + C c = new C(); + for (int i = 0; i < 20_000; i++) { + test1(b, c, true); + test1(b, c, false); + b.field = 0; + c.field = 0; + int res = test2(b, c, true); + if (res != 42) { + throw new RuntimeException("incorrect result " + res); + } + res = test2(b, c, false); + if (res != 42) { + throw new RuntimeException("incorrect result " + res); + } + } + } + + private static void test1(B b, C c, boolean flag) { + A a; + if (flag) { + a = b; + } else { + a = c; + } + for (int i = 0; i < 1000; i++) { + a.field = 42; + } + } + + private static int test2(B b, C c, boolean flag) { + A a; + if (flag) { + a = b; + } else { + a = c; + } + int v = 0; + for (int i = 0; i < 2; i++) { + v += a.field; + a.field = 42; + } + return v; + } + + interface I { + void m(); + } + + static class A { + int field; + } + + static class B extends A implements I { + @Override + public void m() { + + } + } + + static class C extends A implements I { + @Override + public void m() { + + } + } +} diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java index 5fee59c751079..626f4c9b1f6aa 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java @@ -24,7 +24,7 @@ /* * @test * @bug 8030976 8059226 - * @requires !vm.graal.enabled + * @requires !vm.graal.enabled & (vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps) * @library /test/lib / * @modules java.base/jdk.internal.org.objectweb.asm * java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java index 931b1a10e0bd7..bef57605f5f55 100644 --- a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java @@ -244,9 +244,9 @@ static void testUnsafeAccess() throws Exception { testMismatched(Test::testZ_S, Test::changeZ); testMismatched(Test::testZ_C, Test::changeZ); testMismatched(Test::testZ_I, Test::changeZ); - testMismatched(Test::testZ_J, Test::changeZ); + testMismatched(Test::testZ_J, Test::changeZ, false, ARRAY_BOOLEAN_BASE_OFFSET == ARRAY_LONG_BASE_OFFSET); testMismatched(Test::testZ_F, Test::changeZ); - testMismatched(Test::testZ_D, Test::changeZ); + testMismatched(Test::testZ_D, Test::changeZ, false, ARRAY_BOOLEAN_BASE_OFFSET == ARRAY_DOUBLE_BASE_OFFSET); // byte[], aligned accesses testMismatched(Test::testB_Z, Test::changeB); @@ -254,9 +254,9 @@ static void testUnsafeAccess() throws Exception { testMismatched(Test::testB_S, Test::changeB); testMismatched(Test::testB_C, Test::changeB); testMismatched(Test::testB_I, Test::changeB); - testMismatched(Test::testB_J, Test::changeB); + testMismatched(Test::testB_J, Test::changeB, false, ARRAY_BYTE_BASE_OFFSET == ARRAY_LONG_BASE_OFFSET); testMismatched(Test::testB_F, Test::changeB); - testMismatched(Test::testB_D, Test::changeB); + testMismatched(Test::testB_D, Test::changeB, false, ARRAY_BYTE_BASE_OFFSET == ARRAY_DOUBLE_BASE_OFFSET); // short[], aligned accesses testMismatched(Test::testS_Z, Test::changeS); @@ -264,9 +264,9 @@ static void testUnsafeAccess() throws Exception { testMatched( Test::testS_S, Test::changeS); testMismatched(Test::testS_C, Test::changeS); testMismatched(Test::testS_I, Test::changeS); - testMismatched(Test::testS_J, Test::changeS); + testMismatched(Test::testS_J, Test::changeS, false, ARRAY_SHORT_BASE_OFFSET == ARRAY_LONG_BASE_OFFSET); testMismatched(Test::testS_F, Test::changeS); - testMismatched(Test::testS_D, Test::changeS); + testMismatched(Test::testS_D, Test::changeS, false, ARRAY_SHORT_BASE_OFFSET == ARRAY_DOUBLE_BASE_OFFSET); // char[], aligned accesses testMismatched(Test::testC_Z, Test::changeC); @@ -274,9 +274,9 @@ static void testUnsafeAccess() throws Exception { testMismatched(Test::testC_S, Test::changeC); testMatched( Test::testC_C, Test::changeC); testMismatched(Test::testC_I, Test::changeC); - testMismatched(Test::testC_J, Test::changeC); + testMismatched(Test::testC_J, Test::changeC, false, ARRAY_CHAR_BASE_OFFSET == ARRAY_LONG_BASE_OFFSET); testMismatched(Test::testC_F, Test::changeC); - testMismatched(Test::testC_D, Test::changeC); + testMismatched(Test::testC_D, Test::changeC, false, ARRAY_CHAR_BASE_OFFSET == ARRAY_DOUBLE_BASE_OFFSET); // int[], aligned accesses testMismatched(Test::testI_Z, Test::changeI); @@ -284,9 +284,9 @@ static void testUnsafeAccess() throws Exception { testMismatched(Test::testI_S, Test::changeI); testMismatched(Test::testI_C, Test::changeI); testMatched( Test::testI_I, Test::changeI); - testMismatched(Test::testI_J, Test::changeI); + testMismatched(Test::testI_J, Test::changeI, false, ARRAY_INT_BASE_OFFSET == ARRAY_LONG_BASE_OFFSET); testMismatched(Test::testI_F, Test::changeI); - testMismatched(Test::testI_D, Test::changeI); + testMismatched(Test::testI_D, Test::changeI, false, ARRAY_INT_BASE_OFFSET == ARRAY_DOUBLE_BASE_OFFSET); // long[], aligned accesses testMismatched(Test::testJ_Z, Test::changeJ); @@ -304,9 +304,9 @@ static void testUnsafeAccess() throws Exception { testMismatched(Test::testF_S, Test::changeF); testMismatched(Test::testF_C, Test::changeF); testMismatched(Test::testF_I, Test::changeF); - testMismatched(Test::testF_J, Test::changeF); + testMismatched(Test::testF_J, Test::changeF, false, ARRAY_FLOAT_BASE_OFFSET == ARRAY_LONG_BASE_OFFSET); testMatched( Test::testF_F, Test::changeF); - testMismatched(Test::testF_D, Test::changeF); + testMismatched(Test::testF_D, Test::changeF, false, ARRAY_FLOAT_BASE_OFFSET == ARRAY_DOUBLE_BASE_OFFSET); // double[], aligned accesses testMismatched(Test::testD_Z, Test::changeD); diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java index bb6185177e75f..18e7721e0d6c1 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java @@ -25,6 +25,7 @@ /* * @test * @summary Vectorization test on basic boolean operations + * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / * * @build jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeFramesTest.java b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeFramesTest.java index 1d9c8169d0433..91601479881c8 100644 --- a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeFramesTest.java +++ b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeFramesTest.java @@ -25,6 +25,7 @@ * @test DeoptimizeFramesTest * @bug 8028595 * @summary testing of WB::deoptimizeFrames() + * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/DockerBasicTest.java b/test/hotspot/jtreg/containers/docker/DockerBasicTest.java index 357eb3db49727..9233b199532bf 100644 --- a/test/hotspot/jtreg/containers/docker/DockerBasicTest.java +++ b/test/hotspot/jtreg/containers/docker/DockerBasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, 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 @@ -25,7 +25,7 @@ /* * @test * @summary Basic (sanity) test for JDK-under-test inside a docker image. - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java index 08493a7539805..43cd6ec515208 100644 --- a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java +++ b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java @@ -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 @@ -27,7 +27,7 @@ * @bug 8286030 * @key cgroups * @summary Test for hsperfdata file name conflict when two containers share the same /tmp directory - * @requires docker.support + * @requires container.support * @library /test/lib * @build WaitForFlagFile * @run driver ShareTmpDir diff --git a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java index a41dc9c39392b..c51bfa1abbb18 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, 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 @@ -26,7 +26,7 @@ * @test * @key cgroups * @summary Test JVM's CPU resource awareness when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.base/jdk.internal.platform diff --git a/test/hotspot/jtreg/containers/docker/TestCPUSets.java b/test/hotspot/jtreg/containers/docker/TestCPUSets.java index de35388f5a843..aabe82e131fe9 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUSets.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUSets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, 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 @@ -26,7 +26,7 @@ * @test * @key cgroups * @summary Test JVM's awareness of cpu sets (cpus and mems) - * @requires docker.support + * @requires container.support * @requires (os.arch != "s390x") * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/containers/docker/TestContainerInfo.java b/test/hotspot/jtreg/containers/docker/TestContainerInfo.java index 6bcb706fa7a6a..5db3c2af09850 100644 --- a/test/hotspot/jtreg/containers/docker/TestContainerInfo.java +++ b/test/hotspot/jtreg/containers/docker/TestContainerInfo.java @@ -27,7 +27,7 @@ * @test * @summary Test container info for cgroup v2 * @key cgroups - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJFREvents.java b/test/hotspot/jtreg/containers/docker/TestJFREvents.java index abc9de45db842..77c735cde00f1 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFREvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFREvents.java @@ -29,7 +29,7 @@ * when run inside Docker container, such as available CPU and memory. * Also make sure that PIDs are based on value provided by container, * not by the host system. - * @requires (docker.support & os.maxMemory >= 2g) + * @requires (container.support & os.maxMemory >= 2g) * @modules java.base/jdk.internal.platform * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java b/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java index 54ffff4e60ef3..9f9497d9c635f 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.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 @@ -27,7 +27,7 @@ * @summary Test JFR network related events inside a container; make sure * the reported host ip and host name are correctly reported within * the container. - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java b/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java index 61fad4f86be28..b751725428162 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java +++ b/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, 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,7 +25,7 @@ /* * @test * @summary Test JFR recording controlled via JMX across container boundary. - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJcmd.java b/test/hotspot/jtreg/containers/docker/TestJcmd.java index 60d4f4a9e5b46..ca8f1659fe9ef 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmd.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmd.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 * @summary Test JCMD across container boundary. The JCMD runs on a host system, * while sending commands to a JVM that runs inside a container. - * @requires docker.support + * @requires container.support * @requires vm.flagless * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java index 0aa16e8479bd2..de27f4d24e207 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.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 @@ -29,7 +29,7 @@ * and other uses. In side car pattern the main application/service container * is paired with a sidecar container by sharing certain aspects of container * namespace such as PID namespace, specific sub-directories, IPC and more. - * @requires docker.support + * @requires container.support * @requires vm.flagless * @modules java.base/jdk.internal.misc * java.management @@ -38,14 +38,23 @@ * @build EventGeneratorLoop * @run driver TestJcmdWithSideCar */ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Paths; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.regex.Pattern; import java.util.stream.Collectors; + import jdk.test.lib.Container; +import jdk.test.lib.Platform; import jdk.test.lib.Utils; import jdk.test.lib.containers.docker.Common; import jdk.test.lib.containers.docker.DockerRunOptions; @@ -60,6 +69,31 @@ public class TestJcmdWithSideCar { private static final long TIME_TO_WAIT_FOR_MAIN_METHOD_START = 50 * 1000; // milliseconds private static final String MAIN_CONTAINER_NAME = "test-container-main"; + private static final String UID = "uid"; + private static final String GID = "gid"; + + private static final Pattern ID_PATTERN = Pattern.compile("uid=(?<" + UID + ">\\d+)\\([^\\)]+\\)\\s+gid=(?<" + GID + ">\\d+).*"); + + private static final Optional USER = ProcessHandle.current().info().user().map( + user -> { + try (var br = new BufferedReader(new InputStreamReader(new ProcessBuilder("id", user).start().getInputStream()))) { + for (final var line : br.lines().toList()) { + final var m = ID_PATTERN.matcher(line); + + if (m.matches()) { + return "--user=" + m.group(UID) + ":" + m.group(GID); + } + } + } catch (IOException e) { + // do nothing... + } + + return null; + } + ); + + private static final String NET_BIND_SERVICE = "--cap-add=NET_BIND_SERVICE"; + public static void main(String[] args) throws Exception { if (!DockerTestUtils.canTestDocker()) { return; @@ -68,24 +102,33 @@ public static void main(String[] args) throws Exception { DockerTestUtils.buildJdkContainerImage(IMAGE_NAME); try { - // Start the loop process in the "main" container, then run test cases - // using a sidecar container. - MainContainer mainContainer = new MainContainer(); - mainContainer.start(); - mainContainer.waitForMainMethodStart(TIME_TO_WAIT_FOR_MAIN_METHOD_START); - - long mainProcPid = testCase01(); - - // Excluding the test case below until JDK-8228850 is fixed - // JDK-8228850: jhsdb jinfo fails with ClassCastException: - // s.j.h.oops.TypeArray cannot be cast to s.j.h.oops.Instance - // mainContainer.assertIsAlive(); - // testCase02(mainProcPid); - - mainContainer.assertIsAlive(); - testCase03(mainProcPid); + for (final boolean elevated : USER.isPresent() ? new Boolean[] { false, true } : new Boolean[] { false }) { + // Start the loop process in the "main" container, then run test cases + // using a sidecar container. + MainContainer mainContainer = new MainContainer(); + mainContainer.start(elevated); + mainContainer.waitForMainMethodStart(TIME_TO_WAIT_FOR_MAIN_METHOD_START); + + for (AttachStrategy attachStrategy : EnumSet.allOf(AttachStrategy.class)) { + if (attachStrategy == AttachStrategy.ACCESS_TMP_VIA_PROC_ROOT && + elevated && !Platform.isRoot()) { + // Elevated attach via proc/root not yet supported. + continue; + } + long mainProcPid = testCase01(attachStrategy, elevated); + + // Excluding the test case below until JDK-8228850 is fixed + // JDK-8228850: jhsdb jinfo fails with ClassCastException: + // s.j.h.oops.TypeArray cannot be cast to s.j.h.oops.Instance + // mainContainer.assertIsAlive(); + // testCase02(mainProcPid, attachStrategy, elevated); + + mainContainer.assertIsAlive(); + testCase03(mainProcPid, attachStrategy, elevated); + } - mainContainer.waitForAndCheck(TIME_TO_RUN_MAIN_PROCESS * 1000); + mainContainer.waitForAndCheck(TIME_TO_RUN_MAIN_PROCESS * 1000); + } } finally { DockerTestUtils.removeDockerImage(IMAGE_NAME); } @@ -93,21 +136,21 @@ public static void main(String[] args) throws Exception { // Run "jcmd -l" in a sidecar container, find a target process. - private static long testCase01() throws Exception { - OutputAnalyzer out = runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "-l") + private static long testCase01(AttachStrategy attachStrategy, boolean elevated) throws Exception { + OutputAnalyzer out = runSideCar(MAIN_CONTAINER_NAME, attachStrategy, elevated, "/jdk/bin/jcmd", "-l") .shouldHaveExitValue(0) .shouldContain("sun.tools.jcmd.JCmd"); long pid = findProcess(out, "EventGeneratorLoop"); if (pid == -1) { - throw new RuntimeException("Could not find specified process"); + throw new RuntimeException(attachStrategy + ": Could not find specified process"); } return pid; } // run jhsdb jinfo (jhsdb uses PTRACE) - private static void testCase02(long pid) throws Exception { - runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jhsdb", "jinfo", "--pid", "" + pid) + private static void testCase02(long pid, AttachStrategy attachStrategy, boolean elevated) throws Exception { + runSideCar(MAIN_CONTAINER_NAME, attachStrategy, elevated, "/jdk/bin/jhsdb", "jinfo", "--pid", "" + pid) .shouldHaveExitValue(0) .shouldContain("Java System Properties") .shouldContain("VM Flags"); @@ -115,11 +158,11 @@ private static void testCase02(long pid) throws Exception { // test jcmd with some commands (help, start JFR recording) // JCMD will use signal mechanism and Unix Socket - private static void testCase03(long pid) throws Exception { - runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "help") + private static void testCase03(long pid, AttachStrategy attachStrategy, boolean elevated) throws Exception { + runSideCar(MAIN_CONTAINER_NAME, attachStrategy, elevated, "/jdk/bin/jcmd", "" + pid, "help") .shouldHaveExitValue(0) .shouldContain("VM.version"); - runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "JFR.start") + runSideCar(MAIN_CONTAINER_NAME, attachStrategy, elevated, "/jdk/bin/jcmd", "" + pid, "JFR.start") .shouldHaveExitValue(0) .shouldContain("Started recording"); } @@ -127,21 +170,36 @@ private static void testCase03(long pid) throws Exception { // JCMD relies on the attach mechanism (com.sun.tools.attach), // which in turn relies on JVMSTAT mechanism, which puts its mapped - // buffers in /tmp directory (hsperfdata_). Thus, in sidecar - // we mount /tmp via --volumes-from from the main container. - private static OutputAnalyzer runSideCar(String mainContainerName, String whatToRun, - String... args) throws Exception { - List cmd = new ArrayList<>(); - String[] command = new String[] { + // buffers in /tmp directory (hsperfdata_). Thus, in the sidecar + // we have two options: + // 1. mount /tmp from the main container using --volumes-from. + // 2. access /tmp from the main container via /proc//root/tmp. + private static OutputAnalyzer runSideCar(String mainContainerName, AttachStrategy attachStrategy, boolean elevated, String whatToRun, String... args) throws Exception { + System.out.println("Attach strategy " + attachStrategy); + + List initialCommands = List.of( Container.ENGINE_COMMAND, "run", "--tty=true", "--rm", "--cap-add=SYS_PTRACE", "--sig-proxy=true", - "--pid=container:" + mainContainerName, - "--volumes-from", mainContainerName, - IMAGE_NAME, whatToRun + "--pid=container:" + mainContainerName + ); + + List attachStrategyCommands = switch (attachStrategy) { + case TMP_MOUNTED_INTO_SIDECAR -> List.of("--volumes-from", mainContainerName); + case ACCESS_TMP_VIA_PROC_ROOT -> List.of(); }; - cmd.addAll(Arrays.asList(command)); + List elevatedOpts = elevated && USER.isPresent() ? List.of(NET_BIND_SERVICE, USER.get()) : Collections.emptyList(); + + List imageAndCommand = List.of( + IMAGE_NAME, whatToRun + ); + + List cmd = new ArrayList<>(); + cmd.addAll(initialCommands); + cmd.addAll(elevatedOpts); + cmd.addAll(attachStrategyCommands); + cmd.addAll(imageAndCommand); cmd.addAll(Arrays.asList(args)); return DockerTestUtils.execute(cmd); } @@ -188,9 +246,15 @@ static class MainContainer { } }; - public Process start() throws Exception { + public Process start(final boolean elevated) throws Exception { // start "main" container (the observee) DockerRunOptions opts = commonDockerOpts("EventGeneratorLoop"); + + if (elevated && USER.isPresent()) { + opts.addDockerOpts(USER.get()); + opts.addDockerOpts(NET_BIND_SERVICE); + } + opts.addDockerOpts("--cap-add=SYS_PTRACE") .addDockerOpts("--name", MAIN_CONTAINER_NAME) .addDockerOpts("--volume", "/tmp") @@ -241,7 +305,7 @@ public void waitForAndCheck(long timeout) throws Exception { try { exitValue = p.exitValue(); } catch(IllegalThreadStateException ex) { - System.out.println("IllegalThreadStateException occured when calling exitValue()"); + System.out.println("IllegalThreadStateException occurred when calling exitValue()"); retryCount--; } } while (exitValue == -1 && retryCount > 0); @@ -253,4 +317,8 @@ public void waitForAndCheck(long timeout) throws Exception { } + private enum AttachStrategy { + TMP_MOUNTED_INTO_SIDECAR, + ACCESS_TMP_VIA_PROC_ROOT + } } diff --git a/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java b/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java index e15ab9b2b81fa..14227a7106804 100644 --- a/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java +++ b/test/hotspot/jtreg/containers/docker/TestLimitsUpdating.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +29,7 @@ * @bug 8308090 * @key cgroups * @summary Test container limits updating as they get updated at runtime without restart - * @requires docker.support + * @requires container.support * @library /test/lib * @build jdk.test.whitebox.WhiteBox LimitUpdateChecker * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar whitebox.jar jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java index 20354cf934d96..06a874e008ae5 100644 --- a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, 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 @@ -27,7 +27,7 @@ * @bug 8146115 8292083 * @key cgroups * @summary Test JVM's memory resource awareness when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.base/jdk.internal.platform diff --git a/test/hotspot/jtreg/containers/docker/TestMisc.java b/test/hotspot/jtreg/containers/docker/TestMisc.java index 5b7f96112b901..a811666999bdc 100644 --- a/test/hotspot/jtreg/containers/docker/TestMisc.java +++ b/test/hotspot/jtreg/containers/docker/TestMisc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, 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 @@ -25,7 +25,7 @@ /* * @test * @summary Test miscellanous functionality related to JVM running in docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/docker/TestPids.java b/test/hotspot/jtreg/containers/docker/TestPids.java index 2b3b2594cfa93..9b65a1b1ee880 100644 --- a/test/hotspot/jtreg/containers/docker/TestPids.java +++ b/test/hotspot/jtreg/containers/docker/TestPids.java @@ -27,7 +27,7 @@ * @test * @key cgroups * @summary Test JVM's awareness of pids controller - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/containers/systemd/TEST.properties b/test/hotspot/jtreg/containers/systemd/TEST.properties new file mode 100644 index 0000000000000..d563e5f166403 --- /dev/null +++ b/test/hotspot/jtreg/containers/systemd/TEST.properties @@ -0,0 +1,24 @@ +# +# 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. +# + +exclusiveAccess.dirs=. diff --git a/test/hotspot/jtreg/gc/TestObjectAlignmentCardSize.java b/test/hotspot/jtreg/gc/TestObjectAlignmentCardSize.java new file mode 100644 index 0000000000000..5fb46a87b5104 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestObjectAlignmentCardSize.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc; + +/* @test TestObjectAlignmentCardSize.java + * @summary Test to check correct handling of ObjectAlignmentInBytes and GCCardSizeInBytes combinations + * @requires vm.gc != "Z" + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run driver gc.TestObjectAlignmentCardSize + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class TestObjectAlignmentCardSize { + private static void runTest(int objectAlignment, int cardSize, boolean shouldSucceed) throws Exception { + OutputAnalyzer output = ProcessTools.executeTestJava( + "-XX:ObjectAlignmentInBytes=" + objectAlignment, + "-XX:GCCardSizeInBytes=" + cardSize, + "-Xmx32m", + "-Xms32m", + "-version"); + + System.out.println("Output:\n" + output.getOutput()); + + if (shouldSucceed) { + output.shouldHaveExitValue(0); + } else { + output.shouldContain("Invalid combination of GCCardSizeInBytes and ObjectAlignmentInBytes"); + output.shouldNotHaveExitValue(0); + } + } + + public static void main(String[] args) throws Exception { + runTest(8, 512, true); + runTest(128, 128, true); + runTest(256, 128, false); + runTest(256, 256, true); + runTest(256, 512, true); + } +} diff --git a/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java b/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java index 24e542881c0c3..4b69421e6e336 100644 --- a/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java +++ b/test/hotspot/jtreg/gc/arguments/TestG1HeapSizeFlags.java @@ -29,6 +29,7 @@ * @summary Tests argument processing for initial and maximum heap size for the G1 collector * @key flag-sensitive * @requires vm.gc.G1 & vm.opt.MinHeapSize == null & vm.opt.MaxHeapSize == null & vm.opt.InitialHeapSize == null + * @requires vm.compMode != "Xcomp" * @library /test/lib * @library / * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java b/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java index 544064953dfb2..ca8d1e74895b3 100644 --- a/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java +++ b/test/hotspot/jtreg/gc/arguments/TestParallelHeapSizeFlags.java @@ -30,6 +30,7 @@ * parallel collectors. * @key flag-sensitive * @requires vm.gc.Parallel & vm.opt.MinHeapSize == null & vm.opt.MaxHeapSize == null & vm.opt.InitialHeapSize == null + * @requires vm.compMode != "Xcomp" * @library /test/lib * @library / * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java b/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java index c580245a2a39e..b9cc0f231f2e6 100644 --- a/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java +++ b/test/hotspot/jtreg/gc/arguments/TestSerialHeapSizeFlags.java @@ -29,6 +29,7 @@ * @summary Tests argument processing for initial and maximum heap size for the Serial collector * @key flag-sensitive * @requires vm.gc.Serial & vm.opt.MinHeapSize == null & vm.opt.MaxHeapSize == null & vm.opt.InitialHeapSize == null + * @requires vm.compMode != "Xcomp" * @library /test/lib * @library / * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java index 1a3d07bf80d06..00e32a4136e68 100644 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java @@ -27,7 +27,7 @@ * @summary Test OOME in due to classloader leak * @requires vm.gc.Shenandoah * @library /test/lib - * @run driver TestClassLoaderLeak + * @run driver/timeout=600 TestClassLoaderLeak */ import java.util.*; diff --git a/test/hotspot/jtreg/gc/x/TestAllocateHeapAt.java b/test/hotspot/jtreg/gc/x/TestAllocateHeapAt.java index bacd7a2078e97..6a9768de7e6e2 100644 --- a/test/hotspot/jtreg/gc/x/TestAllocateHeapAt.java +++ b/test/hotspot/jtreg/gc/x/TestAllocateHeapAt.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 @@ -26,6 +26,7 @@ /* * @test TestAllocateHeapAt * @requires vm.gc.ZSinglegen & os.family == "linux" + * @requires !vm.opt.final.UseLargePages * @summary Test ZGC with -XX:AllocateHeapAt * @library /test/lib * @run main/othervm gc.x.TestAllocateHeapAt . true diff --git a/test/hotspot/jtreg/gc/z/TestAllocateHeapAt.java b/test/hotspot/jtreg/gc/z/TestAllocateHeapAt.java index 28d2ebd6aea01..2fb040840b4da 100644 --- a/test/hotspot/jtreg/gc/z/TestAllocateHeapAt.java +++ b/test/hotspot/jtreg/gc/z/TestAllocateHeapAt.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 @@ -26,6 +26,7 @@ /* * @test TestAllocateHeapAt * @requires vm.gc.ZGenerational & os.family == "linux" + * @requires !vm.opt.final.UseLargePages * @summary Test ZGC with -XX:AllocateHeapAt * @library /test/lib * @run main/othervm gc.z.TestAllocateHeapAt . true diff --git a/test/hotspot/jtreg/gc/z/TestAllocateHeapAtWithHugeTLBFS.java b/test/hotspot/jtreg/gc/z/TestAllocateHeapAtWithHugeTLBFS.java new file mode 100644 index 0000000000000..ac647bbd013fd --- /dev/null +++ b/test/hotspot/jtreg/gc/z/TestAllocateHeapAtWithHugeTLBFS.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc.z; + +/* + * @test TestAllocateHeapAtWithHugeTLBFS + * @requires vm.gc.ZGenerational & os.family == "linux" + * @summary Test ZGC with -XX:AllocateHeapAt and -XX:+UseLargePages + * @library /test/lib + * @run driver gc.z.TestAllocateHeapAtWithHugeTLBFS true + * @run driver gc.z.TestAllocateHeapAtWithHugeTLBFS false + */ + +import jdk.test.lib.process.ProcessTools; +import jtreg.SkippedException; + +import java.io.File; +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.Scanner; + +public class TestAllocateHeapAtWithHugeTLBFS { + static String find_hugetlbfs_mountpoint() { + Pattern pat = Pattern.compile("\\d+ \\d+ \\d+:\\d+ \\S+ (\\S+) [^-]*- hugetlbfs (.+)"); + try (Scanner scanner = new Scanner(new File("/proc/self/mountinfo"))) { + while (scanner.hasNextLine()) { + final Matcher mat = pat.matcher(scanner.nextLine()); + if (mat.matches() && mat.group(2).contains("pagesize=2M")) { + final Path path = Paths.get(mat.group(1)); + if (Files.isReadable(path) && + Files.isWritable(path) && + Files.isExecutable(path)) { + // Found a usable mount point. + return path.toString(); + } + } + } + } catch (FileNotFoundException e) { + System.out.println("Could not open /proc/self/mountinfo"); + } + return null; + } + public static void main(String[] args) throws Exception { + final boolean exists = Boolean.parseBoolean(args[0]); + final String directory = exists ? find_hugetlbfs_mountpoint() + : "non-existing-directory"; + if (directory == null) { + throw new SkippedException("No valid hugetlbfs mount point found"); + } + final String heapBackingFile = "Heap Backing File: " + directory; + final String failedToCreateFile = "Failed to create file " + directory; + + ProcessTools.executeTestJava( + "-XX:+UseZGC", + "-XX:+ZGenerational", + "-Xlog:gc*", + "-Xms32M", + "-Xmx32M", + "-XX:+UseLargePages", + "-XX:AllocateHeapAt=" + directory, + "-version") + .shouldContain(exists ? heapBackingFile : failedToCreateFile) + .shouldNotContain(exists ? failedToCreateFile : heapBackingFile) + .shouldHaveExitValue(exists ? 0 : 1); + } +} diff --git a/test/hotspot/jtreg/runtime/Annotations/BadContendedGroupBadCPIndex.jcod b/test/hotspot/jtreg/runtime/Annotations/BadContendedGroupBadCPIndex.jcod new file mode 100644 index 0000000000000..4802548926b42 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/BadContendedGroupBadCPIndex.jcod @@ -0,0 +1,123 @@ +/* + * 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. + */ + +/* Based on source: + * public class ContendedField { + * @Contended("group1") + * public int field; + * } + * + * We change the value of "value" so it refers to an invalid CP entry + */ + +class BadContendedGroupBadCPIndex { + 0xCAFEBABE; + 0; // minor version + 65; // version + [19] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 at 0x0A + class #4; // #2 at 0x0F + NameAndType #5 #6; // #3 at 0x12 + Utf8 "java/lang/Object"; // #4 at 0x17 + Utf8 ""; // #5 at 0x2A + Utf8 "()V"; // #6 at 0x33 + class #8; // #7 at 0x39 + Utf8 "BadContendedGroupBadCPIndex"; // #8 at 0x3C + Utf8 "field"; // #9 at 0x4D + Utf8 "I"; // #10 at 0x55 + Utf8 "RuntimeVisibleAnnotations"; // #11 at 0x59 + Utf8 "Ljdk/internal/vm/annotation/Contended;"; // #12 at 0x75 + Utf8 "value"; // #13 at 0x9E + Utf8 "group1"; // #14 at 0xA6 + Utf8 "Code"; // #15 at 0xAF + Utf8 "LineNumberTable"; // #16 at 0xB6 + Utf8 "SourceFile"; // #17 at 0xC8 + Utf8 "BadContendedGroupBadCPIndex.java"; // #18 at 0xD5 + } // Constant Pool + + 0x0021; // access [ ACC_PUBLIC ACC_SUPER ] + #7;// this_cpx + #2;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [1] { // Fields + { // field at 0xF5 + 0x0001; // access + #9; // name_index : field + #10; // descriptor_index : I + [1] { // Attributes + Attr(#11, 11) { // RuntimeVisibleAnnotations at 0xFD + [1] { // annotations + { // annotation + #12; + [1] { // element_value_pairs + { // element value pair + #13; + { // element_value + 's'; + #1400; // Changed from #14 to #1400 + } // element_value + } // element value pair + } // element_value_pairs + } // annotation + } + } // end RuntimeVisibleAnnotations + } // Attributes + } + } // Fields + + [1] { // Methods + { // method at 0x0110 + 0x0001; // access + #5; // name_index : + #6; // descriptor_index : ()V + [1] { // Attributes + Attr(#15, 29) { // Code at 0x0118 + 1; // max_stack + 1; // max_locals + Bytes[5]{ + 0x2AB70001B1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#16, 6) { // LineNumberTable at 0x012F + [1] { // line_number_table + 0 2; // at 0x013B + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + } // Methods + + [1] { // Attributes + Attr(#17, 2) { // SourceFile at 0x013D + #18; + } // end SourceFile + } // Attributes +} // end class BadContendedGroupBadCPIndex diff --git a/test/hotspot/jtreg/runtime/Annotations/BadContendedGroupWrongType.jcod b/test/hotspot/jtreg/runtime/Annotations/BadContendedGroupWrongType.jcod new file mode 100644 index 0000000000000..6fa53f939b3dd --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/BadContendedGroupWrongType.jcod @@ -0,0 +1,123 @@ +/* + * 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. + */ + +/* Based on source: + * public class ContendedField { + * @Contended("group1") + * public int field; + * } + * + * We change the value of "value" so it refers to the wrong type of CP entry + */ + +class BadContendedGroupWrongType { + 0xCAFEBABE; + 0; // minor version + 65; // version + [19] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 at 0x0A + class #4; // #2 at 0x0F + NameAndType #5 #6; // #3 at 0x12 + Utf8 "java/lang/Object"; // #4 at 0x17 + Utf8 ""; // #5 at 0x2A + Utf8 "()V"; // #6 at 0x33 + class #8; // #7 at 0x39 + Utf8 "BadContendedGroupWrongType"; // #8 at 0x3C + Utf8 "field"; // #9 at 0x4D + Utf8 "I"; // #10 at 0x55 + Utf8 "RuntimeVisibleAnnotations"; // #11 at 0x59 + Utf8 "Ljdk/internal/vm/annotation/Contended;"; // #12 at 0x75 + Utf8 "value"; // #13 at 0x9E + Utf8 "group1"; // #14 at 0xA6 + Utf8 "Code"; // #15 at 0xAF + Utf8 "LineNumberTable"; // #16 at 0xB6 + Utf8 "SourceFile"; // #17 at 0xC8 + Utf8 "BadContendedGroupWrongType.java"; // #18 at 0xD5 + } // Constant Pool + + 0x0021; // access [ ACC_PUBLIC ACC_SUPER ] + #7;// this_cpx + #2;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [1] { // Fields + { // field at 0xF5 + 0x0001; // access + #9; // name_index : field + #10; // descriptor_index : I + [1] { // Attributes + Attr(#11, 11) { // RuntimeVisibleAnnotations at 0xFD + [1] { // annotations + { // annotation + #12; + [1] { // element_value_pairs + { // element value pair + #13; + { // element_value + 's'; + #7; // Changed from #14 (utf8) to #7 (class) + } // element_value + } // element value pair + } // element_value_pairs + } // annotation + } + } // end RuntimeVisibleAnnotations + } // Attributes + } + } // Fields + + [1] { // Methods + { // method at 0x0110 + 0x0001; // access + #5; // name_index : + #6; // descriptor_index : ()V + [1] { // Attributes + Attr(#15, 29) { // Code at 0x0118 + 1; // max_stack + 1; // max_locals + Bytes[5]{ + 0x2AB70001B1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#16, 6) { // LineNumberTable at 0x012F + [1] { // line_number_table + 0 2; // at 0x013B + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + } // Methods + + [1] { // Attributes + Attr(#17, 2) { // SourceFile at 0x013D + #18; + } // end SourceFile + } // Attributes +} // end class BadContendedGroupWrongType diff --git a/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedExtraMemberAtEnd.jcod b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedExtraMemberAtEnd.jcod new file mode 100644 index 0000000000000..991c4c832b3dc --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedExtraMemberAtEnd.jcod @@ -0,0 +1,163 @@ +/* + * 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. + */ + +/* Based on source: + * + * public class DeprecatedMethod { + * @Deprecated(forRemoval=true, since="now") + * public static void m() {} + * } + * + * We add an extra junk member at the end. + */ + +class BadDeprecatedExtraMemberAtEnd { + 0xCAFEBABE; + 0; // minor version + 65; // version + [21] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 at 0x0A + class #4; // #2 at 0x0F + NameAndType #5 #6; // #3 at 0x12 + Utf8 "java/lang/Object"; // #4 at 0x17 + Utf8 ""; // #5 at 0x2A + Utf8 "()V"; // #6 at 0x33 + class #8; // #7 at 0x39 + Utf8 "BadDeprecatedExtraMemberAtEnd"; // #8 at 0x3C + Utf8 "Code"; // #9 at 0x4F + Utf8 "LineNumberTable"; // #10 at 0x56 + Utf8 "m"; // #11 at 0x68 + Utf8 "Deprecated"; // #12 at 0x6C + Utf8 "RuntimeVisibleAnnotations"; // #13 at 0x79 + Utf8 "Ljava/lang/Deprecated;"; // #14 at 0x95 + Utf8 "forRemoval"; // #15 at 0xAE + int 0x00000001; // #16 at 0xBB + Utf8 "since"; // #17 at 0xC0 + Utf8 "now"; // #18 at 0xC8 + Utf8 "SourceFile"; // #19 at 0xCE + Utf8 "BadDeprecatedExtraMemberAtEnd.java"; // #20 at 0xDB + } // Constant Pool + + 0x0021; // access [ ACC_PUBLIC ACC_SUPER ] + #7;// this_cpx + #2;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [0] { // Fields + } // Fields + + [2] { // Methods + { // method at 0xFF + 0x0001; // access + #5; // name_index : + #6; // descriptor_index : ()V + [1] { // Attributes + Attr(#9, 29) { // Code at 0x0107 + 1; // max_stack + 1; // max_locals + Bytes[5]{ + 0x2AB70001B1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x011E + [1] { // line_number_table + 0 1; // at 0x012A + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + ; + { // method at 0x012A + 0x0009; // access + #11; // name_index : m + #6; // descriptor_index : ()V + [3] { // Attributes + Attr(#9, 25) { // Code at 0x0132 + 0; // max_stack + 0; // max_locals + Bytes[1]{ + 0xB1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x0145 + [1] { // line_number_table + 0 4; // at 0x0151 + } + } // end LineNumberTable + } // Attributes + } // end Code + ; + Attr(#12, 0) { // Deprecated at 0x0151 + } // end Deprecated + ; + Attr(#13, 21) { // RuntimeVisibleAnnotations at 0x0157 (length changed from 16 to 21) + [1] { // annotations + { // annotation + #14; + [3] { // element_value_pairs <= extra pair added + { // element value pair + #15; + { // element_value + 'Z'; + #16; + } // element_value + } // element value pair + ; + { // element value pair + #17; + { // element_value + 's'; + #18; + } // element_value + } // element value pair + ; + { // Extra element value pair: now=true + #18; + { // element_value + 'Z'; + #16; + } // element_value + } // element value pair + } // element_value_pairs + } // annotation + } + } // end RuntimeVisibleAnnotations + } // Attributes + } + } // Methods + + [1] { // Attributes + Attr(#19, 2) { // SourceFile at 0x016F + #20; + } // end SourceFile + } // Attributes +} // end class BadDeprecatedExtraMemberAtEnd diff --git a/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedExtraMemberAtStart.jcod b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedExtraMemberAtStart.jcod new file mode 100644 index 0000000000000..0adf08049fd6e --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedExtraMemberAtStart.jcod @@ -0,0 +1,163 @@ +/* + * 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. + */ + +/* Based on source: + * + * public class DeprecatedMethod { + * @Deprecated(forRemoval=true, since="now") + * public static void m() {} + * } + * + * We add an extra junk member at the start. + */ + +class BadDeprecatedExtraMemberAtStart { + 0xCAFEBABE; + 0; // minor version + 65; // version + [21] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 at 0x0A + class #4; // #2 at 0x0F + NameAndType #5 #6; // #3 at 0x12 + Utf8 "java/lang/Object"; // #4 at 0x17 + Utf8 ""; // #5 at 0x2A + Utf8 "()V"; // #6 at 0x33 + class #8; // #7 at 0x39 + Utf8 "BadDeprecatedExtraMemberAtStart"; // #8 at 0x3C + Utf8 "Code"; // #9 at 0x4F + Utf8 "LineNumberTable"; // #10 at 0x56 + Utf8 "m"; // #11 at 0x68 + Utf8 "Deprecated"; // #12 at 0x6C + Utf8 "RuntimeVisibleAnnotations"; // #13 at 0x79 + Utf8 "Ljava/lang/Deprecated;"; // #14 at 0x95 + Utf8 "forRemoval"; // #15 at 0xAE + int 0x00000001; // #16 at 0xBB + Utf8 "since"; // #17 at 0xC0 + Utf8 "now"; // #18 at 0xC8 + Utf8 "SourceFile"; // #19 at 0xCE + Utf8 "BadDeprecatedExtraMemberAtStart.java"; // #20 at 0xDB + } // Constant Pool + + 0x0021; // access [ ACC_PUBLIC ACC_SUPER ] + #7;// this_cpx + #2;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [0] { // Fields + } // Fields + + [2] { // Methods + { // method at 0xFF + 0x0001; // access + #5; // name_index : + #6; // descriptor_index : ()V + [1] { // Attributes + Attr(#9, 29) { // Code at 0x0107 + 1; // max_stack + 1; // max_locals + Bytes[5]{ + 0x2AB70001B1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x011E + [1] { // line_number_table + 0 1; // at 0x012A + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + ; + { // method at 0x012A + 0x0009; // access + #11; // name_index : m + #6; // descriptor_index : ()V + [3] { // Attributes + Attr(#9, 25) { // Code at 0x0132 + 0; // max_stack + 0; // max_locals + Bytes[1]{ + 0xB1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x0145 + [1] { // line_number_table + 0 4; // at 0x0151 + } + } // end LineNumberTable + } // Attributes + } // end Code + ; + Attr(#12, 0) { // Deprecated at 0x0151 + } // end Deprecated + ; + Attr(#13, 21) { // RuntimeVisibleAnnotations at 0x0157 (length changed from 16 to 21) + [1] { // annotations + { // annotation + #14; + [3] { // element_value_pairs <= extra pair added + { // Extra element value pair: now=true + #18; + { // element_value + 'Z'; + #16; + } // element_value + } // element value pair + ; + { // element value pair + #15; + { // element_value + 'Z'; + #16; + } // element_value + } // element value pair + ; + { // element value pair + #17; + { // element_value + 's'; + #18; + } // element_value + } // element value pair + } // element_value_pairs + } // annotation + } + } // end RuntimeVisibleAnnotations + } // Attributes + } + } // Methods + + [1] { // Attributes + Attr(#19, 2) { // SourceFile at 0x016F + #20; + } // end SourceFile + } // Attributes +} // end class BadDeprecatedExtraMemberAtStart diff --git a/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedForRemovalBadCPIndex.jcod b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedForRemovalBadCPIndex.jcod new file mode 100644 index 0000000000000..e8648e0c705bb --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedForRemovalBadCPIndex.jcod @@ -0,0 +1,155 @@ +/* + * 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. + */ + +/* Based on source: + * + * public class DeprecatedMethod { + * @Deprecated(forRemoval=true, since="now") + * public static void m() {} + * } + * + * We change the CP index for the value of forRemoval to a junk value + */ + +class BadDeprecatedForRemovalBadCPIndex { + 0xCAFEBABE; + 0; // minor version + 65; // version + [21] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 at 0x0A + class #4; // #2 at 0x0F + NameAndType #5 #6; // #3 at 0x12 + Utf8 "java/lang/Object"; // #4 at 0x17 + Utf8 ""; // #5 at 0x2A + Utf8 "()V"; // #6 at 0x33 + class #8; // #7 at 0x39 + Utf8 "BadDeprecatedForRemovalBadCPIndex"; // #8 at 0x3C + Utf8 "Code"; // #9 at 0x4F + Utf8 "LineNumberTable"; // #10 at 0x56 + Utf8 "m"; // #11 at 0x68 + Utf8 "Deprecated"; // #12 at 0x6C + Utf8 "RuntimeVisibleAnnotations"; // #13 at 0x79 + Utf8 "Ljava/lang/Deprecated;"; // #14 at 0x95 + Utf8 "forRemoval"; // #15 at 0xAE + int 0x00000001; // #16 at 0xBB + Utf8 "since"; // #17 at 0xC0 + Utf8 "now"; // #18 at 0xC8 + Utf8 "SourceFile"; // #19 at 0xCE + Utf8 "BadDeprecatedForRemovalBadCPIndex.java"; // #20 at 0xDB + } // Constant Pool + + 0x0021; // access [ ACC_PUBLIC ACC_SUPER ] + #7;// this_cpx + #2;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [0] { // Fields + } // Fields + + [2] { // Methods + { // method at 0xFF + 0x0001; // access + #5; // name_index : + #6; // descriptor_index : ()V + [1] { // Attributes + Attr(#9, 29) { // Code at 0x0107 + 1; // max_stack + 1; // max_locals + Bytes[5]{ + 0x2AB70001B1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x011E + [1] { // line_number_table + 0 1; // at 0x012A + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + ; + { // method at 0x012A + 0x0009; // access + #11; // name_index : m + #6; // descriptor_index : ()V + [3] { // Attributes + Attr(#9, 25) { // Code at 0x0132 + 0; // max_stack + 0; // max_locals + Bytes[1]{ + 0xB1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x0145 + [1] { // line_number_table + 0 4; // at 0x0151 + } + } // end LineNumberTable + } // Attributes + } // end Code + ; + Attr(#12, 0) { // Deprecated at 0x0151 + } // end Deprecated + ; + Attr(#13, 16) { // RuntimeVisibleAnnotations at 0x0157 + [1] { // annotations + { // annotation + #14; + [2] { // element_value_pairs + { // element value pair + #17; + { // element_value + 's'; + #18; + } // element_value + } // element value pair + ; + { // element value pair + #15; + { // element_value + 'Z'; + #1600; // Changed from #16 to #1600 + } // element_value + } // element value pair + } // element_value_pairs + } // annotation + } + } // end RuntimeVisibleAnnotations + } // Attributes + } + } // Methods + + [1] { // Attributes + Attr(#19, 2) { // SourceFile at 0x016F + #20; + } // end SourceFile + } // Attributes +} // end class BadDeprecatedForRemovalBadCPIndex diff --git a/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedForRemovalWrongType.jcod b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedForRemovalWrongType.jcod new file mode 100644 index 0000000000000..9a2a536d664e3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedForRemovalWrongType.jcod @@ -0,0 +1,155 @@ +/* + * 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. + */ + +/* Based on source: + * + * public class DeprecatedMethod { + * @Deprecated(forRemoval=true, since="now") + * public static void m() {} + * } + * + * We change the type of forRemoval from 'Z' to 's' but don't change the value + */ + +class BadDeprecatedForRemovalWrongType { + 0xCAFEBABE; + 0; // minor version + 65; // version + [21] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 at 0x0A + class #4; // #2 at 0x0F + NameAndType #5 #6; // #3 at 0x12 + Utf8 "java/lang/Object"; // #4 at 0x17 + Utf8 ""; // #5 at 0x2A + Utf8 "()V"; // #6 at 0x33 + class #8; // #7 at 0x39 + Utf8 "BadDeprecatedForRemovalWrongType"; // #8 at 0x3C + Utf8 "Code"; // #9 at 0x4F + Utf8 "LineNumberTable"; // #10 at 0x56 + Utf8 "m"; // #11 at 0x68 + Utf8 "Deprecated"; // #12 at 0x6C + Utf8 "RuntimeVisibleAnnotations"; // #13 at 0x79 + Utf8 "Ljava/lang/Deprecated;"; // #14 at 0x95 + Utf8 "forRemoval"; // #15 at 0xAE + int 0x00000001; // #16 at 0xBB + Utf8 "since"; // #17 at 0xC0 + Utf8 "now"; // #18 at 0xC8 + Utf8 "SourceFile"; // #19 at 0xCE + Utf8 "BadDeprecatedForRemovalWrongType.java"; // #20 at 0xDB + } // Constant Pool + + 0x0021; // access [ ACC_PUBLIC ACC_SUPER ] + #7;// this_cpx + #2;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [0] { // Fields + } // Fields + + [2] { // Methods + { // method at 0xFF + 0x0001; // access + #5; // name_index : + #6; // descriptor_index : ()V + [1] { // Attributes + Attr(#9, 29) { // Code at 0x0107 + 1; // max_stack + 1; // max_locals + Bytes[5]{ + 0x2AB70001B1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x011E + [1] { // line_number_table + 0 1; // at 0x012A + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + ; + { // method at 0x012A + 0x0009; // access + #11; // name_index : m + #6; // descriptor_index : ()V + [3] { // Attributes + Attr(#9, 25) { // Code at 0x0132 + 0; // max_stack + 0; // max_locals + Bytes[1]{ + 0xB1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x0145 + [1] { // line_number_table + 0 4; // at 0x0151 + } + } // end LineNumberTable + } // Attributes + } // end Code + ; + Attr(#12, 0) { // Deprecated at 0x0151 + } // end Deprecated + ; + Attr(#13, 16) { // RuntimeVisibleAnnotations at 0x0157 + [1] { // annotations + { // annotation + #14; + [2] { // element_value_pairs + { // element value pair + #17; + { // element_value + 's'; + #18; + } // element_value + } // element value pair + ; + { // element value pair + #15; + { // element_value + 's'; // Changed from Z to s + #16; + } // element_value + } // element value pair + } // element_value_pairs + } // annotation + } + } // end RuntimeVisibleAnnotations + } // Attributes + } + } // Methods + + [1] { // Attributes + Attr(#19, 2) { // SourceFile at 0x016F + #20; + } // end SourceFile + } // Attributes +} // end class BadDeprecatedForRemovalWrongType diff --git a/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedSinceWrongType.jcod b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedSinceWrongType.jcod new file mode 100644 index 0000000000000..85863ea3b86a1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/BadDeprecatedSinceWrongType.jcod @@ -0,0 +1,155 @@ +/* + * 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. + */ + +/* Based on source: + * + * public class DeprecatedMethod { + * @Deprecated(forRemoval=true, since="now") + * public static void m() {} + * } + * + * We change the type of since from 's' to 'Z' + */ + +class BadDeprecatedSinceWrongType { + 0xCAFEBABE; + 0; // minor version + 65; // version + [21] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 at 0x0A + class #4; // #2 at 0x0F + NameAndType #5 #6; // #3 at 0x12 + Utf8 "java/lang/Object"; // #4 at 0x17 + Utf8 ""; // #5 at 0x2A + Utf8 "()V"; // #6 at 0x33 + class #8; // #7 at 0x39 + Utf8 "BadDeprecatedSinceWrongType"; // #8 at 0x3C + Utf8 "Code"; // #9 at 0x4F + Utf8 "LineNumberTable"; // #10 at 0x56 + Utf8 "m"; // #11 at 0x68 + Utf8 "Deprecated"; // #12 at 0x6C + Utf8 "RuntimeVisibleAnnotations"; // #13 at 0x79 + Utf8 "Ljava/lang/Deprecated;"; // #14 at 0x95 + Utf8 "forRemoval"; // #15 at 0xAE + int 0x00000001; // #16 at 0xBB + Utf8 "since"; // #17 at 0xC0 + Utf8 "now"; // #18 at 0xC8 + Utf8 "SourceFile"; // #19 at 0xCE + Utf8 "BadDeprecatedSinceWrongType.java"; // #20 at 0xDB + } // Constant Pool + + 0x0021; // access [ ACC_PUBLIC ACC_SUPER ] + #7;// this_cpx + #2;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [0] { // Fields + } // Fields + + [2] { // Methods + { // method at 0xFF + 0x0001; // access + #5; // name_index : + #6; // descriptor_index : ()V + [1] { // Attributes + Attr(#9, 29) { // Code at 0x0107 + 1; // max_stack + 1; // max_locals + Bytes[5]{ + 0x2AB70001B1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x011E + [1] { // line_number_table + 0 1; // at 0x012A + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + ; + { // method at 0x012A + 0x0009; // access + #11; // name_index : m + #6; // descriptor_index : ()V + [3] { // Attributes + Attr(#9, 25) { // Code at 0x0132 + 0; // max_stack + 0; // max_locals + Bytes[1]{ + 0xB1; + } + [0] { // Traps + } // end Traps + [1] { // Attributes + Attr(#10, 6) { // LineNumberTable at 0x0145 + [1] { // line_number_table + 0 4; // at 0x0151 + } + } // end LineNumberTable + } // Attributes + } // end Code + ; + Attr(#12, 0) { // Deprecated at 0x0151 + } // end Deprecated + ; + Attr(#13, 16) { // RuntimeVisibleAnnotations at 0x0157 + [1] { // annotations + { // annotation + #14; + [2] { // element_value_pairs + { // element value pair + #17; + { // element_value + 'Z'; // Changed from 's' to 'Z' + #18; + } // element_value + } // element value pair + ; + { // element value pair + #15; + { // element_value + 'Z'; + #16; + } // element_value + } // element value pair + } // element_value_pairs + } // annotation + } + } // end RuntimeVisibleAnnotations + } // Attributes + } + } // Methods + + [1] { // Attributes + Attr(#19, 2) { // SourceFile at 0x016F + #20; + } // end SourceFile + } // Attributes +} // end class BadDeprecatedSinceWrongType diff --git a/test/hotspot/jtreg/runtime/Annotations/TestBadAnnotations.java b/test/hotspot/jtreg/runtime/Annotations/TestBadAnnotations.java new file mode 100644 index 0000000000000..6617858d61bf2 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Annotations/TestBadAnnotations.java @@ -0,0 +1,64 @@ +/* + * 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 8339192 + * @summary Check that malformed annotations don't cause crashes + * @compile BadDeprecatedExtraMemberAtEnd.jcod + * BadDeprecatedExtraMemberAtStart.jcod + * BadDeprecatedSinceWrongType.jcod + * BadDeprecatedForRemovalWrongType.jcod + * BadDeprecatedForRemovalBadCPIndex.jcod + * BadContendedGroupBadCPIndex.jcod + * BadContendedGroupWrongType.jcod + * @modules java.base/jdk.internal.vm.annotation + * @run main/othervm -XX:-RestrictContended TestBadAnnotations + */ +import java.lang.annotation.Annotation; +import java.lang.reflect.*; + +// None of the malformed nnotations should cause assertion failures or +// other crashes, nor exceptions - we simply don't process them. Note that +// even if the annotation is malformed the class will still be marked as having +// that annotation; it is only the "forRemoval" state of @Deprecated, and +// the "group" value of @Contended that is potentially afffected. +// There is no API to query what annotations the VM considers applied +// to a class/field/method, so we don't try to read anything back. + +// The testcases defined reflect the changes that were made to the code under +// 8339192 - we do not try to define exhaustive tests for every potential +// malformation. Each of these test case will trigger an assert prior to +// the fix. + +public class TestBadAnnotations { + public static void main(String[] args) throws Throwable { + Class c = Class.forName("BadDeprecatedExtraMemberAtEnd"); + c = Class.forName("BadDeprecatedExtraMemberAtStart"); + c = Class.forName("BadDeprecatedSinceWrongType"); + c = Class.forName("BadDeprecatedForRemovalWrongType"); + c = Class.forName("BadDeprecatedForRemovalBadCPIndex"); + c = Class.forName("BadContendedGroupBadCPIndex"); + c = Class.forName("BadContendedGroupWrongType"); + } +} diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java b/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java index aaee80169eb06..90b309069ac4f 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java +++ b/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java @@ -22,14 +22,37 @@ */ /* - * @test + * @test VMOptionWarningExperimental * @bug 8027314 - * @summary Warn if diagnostic or experimental vm option is used and -XX:+UnlockDiagnosticVMOptions or -XX:+UnlockExperimentalVMOptions, respectively, isn't specified. Warn if develop vm option is used with product version of VM. + * @summary Warn if experimental vm option is used and -XX:+UnlockExperimentalVMOptions isn't specified. * @requires vm.flagless + * @requires ! vm.opt.final.UnlockExperimentalVMOptions * @library /test/lib * @modules java.base/jdk.internal.misc * java.management - * @run driver VMOptionWarning + * @run driver VMOptionWarning Experimental + */ + +/* @test VMOptionWarningDiagnostic + * @bug 8027314 + * @summary Warn if diagnostic vm option is used and -XX:+UnlockDiagnosticVMOptions isn't specified. + * @requires vm.flagless + * @requires ! vm.debug + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver VMOptionWarning Diagnostic + */ + +/* @test VMOptionWarningDevelop + * @bug 8027314 + * @summary Warn if develop vm option is used with product version of VM. + * @requires vm.flagless + * @requires ! vm.debug + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver VMOptionWarning Develop */ import jdk.test.lib.process.ProcessTools; @@ -38,24 +61,37 @@ public class VMOptionWarning { public static void main(String[] args) throws Exception { - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+AlwaysSafeConstructors", "-version"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldNotHaveExitValue(0); - output.shouldContain("Error: VM option 'AlwaysSafeConstructors' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions."); - - if (Platform.isDebugBuild()) { - System.out.println("Skip the rest of the tests on debug builds since diagnostic, and develop options are available on debug builds."); - return; + if (args.length != 1) { + throw new RuntimeException("wrong number of args: " + args.length); } - pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintInlining", "-version"); - output = new OutputAnalyzer(pb.start()); - output.shouldNotHaveExitValue(0); - output.shouldContain("Error: VM option 'PrintInlining' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); - - pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+VerifyStack", "-version"); - output = new OutputAnalyzer(pb.start()); - output.shouldNotHaveExitValue(0); - output.shouldContain("Error: VM option 'VerifyStack' is develop and is available only in debug version of VM."); + ProcessBuilder pb; + OutputAnalyzer output; + switch (args[0]) { + case "Experimental": { + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+AlwaysSafeConstructors", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); + output.shouldContain("Error: VM option 'AlwaysSafeConstructors' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions."); + break; + } + case "Diagnostic": { + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintInlining", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); + output.shouldContain("Error: VM option 'PrintInlining' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); + break; + } + case "Develop": { + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+VerifyStack", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); + output.shouldContain("Error: VM option 'VerifyStack' is develop and is available only in debug version of VM."); + break; + } + default: { + throw new RuntimeException("Invalid argument: " + args[0]); + } + } } } diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.java b/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.java index 73efc0e5e4605..76ea10d77cbbe 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/CreateCoredumpOnCrash.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 @@ -29,6 +29,7 @@ * java.management * jdk.internal.jvmstat/sun.jvmstat.monitor * @run driver CreateCoredumpOnCrash + * @requires vm.flagless */ import jdk.test.lib.process.ProcessTools; @@ -43,24 +44,61 @@ public static void main(String[] args) { } } + private static String ulimitString(int limit) { + String string = "ulimit -c "; + if (limit != Integer.MAX_VALUE) { + string += limit; + } else { + string += "unlimited"; + } + return string+";"; + } + public static void main(String[] args) throws Exception { runTest("-XX:-CreateCoredumpOnCrash").shouldContain("CreateCoredumpOnCrash turned off, no core file dumped") - .shouldNotHaveExitValue(0); + .shouldNotHaveExitValue(0); if (Platform.isWindows()) { // The old CreateMinidumpOnCrash option should still work runTest("-XX:-CreateMinidumpOnCrash").shouldContain("CreateCoredumpOnCrash turned off, no core file dumped") - .shouldNotHaveExitValue(0); + .shouldNotHaveExitValue(0); } else { - runTest("-XX:+CreateCoredumpOnCrash").shouldNotContain("CreateCoredumpOnCrash turned off, no core file dumped") - .shouldNotHaveExitValue(0); - } + String exec_cmd[] = {"sh", "-c", "ulimit -c"}; + OutputAnalyzer oa = new OutputAnalyzer(Runtime.getRuntime().exec(exec_cmd)); + oa.shouldHaveExitValue(0); + if (!oa.contains("0\n")) { + oa = runTest("-XX:+CreateCoredumpOnCrash"); + oa.shouldContain("Core dump will be written."); + oa.shouldNotHaveExitValue(0); + oa = runTest("-XX:+CreateCoredumpOnCrash", ulimitString(1024)); + oa.shouldContain("warning: CreateCoredumpOnCrash specified, but"); + oa.shouldNotHaveExitValue(0); + + oa = runTest("-XX:+CreateCoredumpOnCrash", ulimitString(0)); + oa.shouldContain("warning: CreateCoredumpOnCrash specified, but"); + oa.shouldNotHaveExitValue(0); + } else { + throw new Exception("ulimit is not set correctly, try 'ulimit -c unlimited' and re-run."); + } + } } + public static OutputAnalyzer runTest(String option) throws Exception { - return new OutputAnalyzer( - ProcessTools.createLimitedTestJavaProcessBuilder( - "-Xmx128m", "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", option, Crasher.class.getName()) - .start()); + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xmx128m", + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + option, Crasher.class.getName()); + return new OutputAnalyzer(pb.start()); + } + public static OutputAnalyzer runTest(String option, String limit) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xmx128m", + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + option, new String("'"+Crasher.class.getName()+"'")); + String args = ""; + for (String s:pb.command()) { + args += s+" "; + } + String exec_cmd[] = {"sh", "-c", limit+args}; + return new OutputAnalyzer(Runtime.getRuntime().exec(exec_cmd)); } } diff --git a/test/hotspot/jtreg/runtime/FieldLayout/TestFieldLayout.java b/test/hotspot/jtreg/runtime/FieldLayout/TestFieldLayout.java new file mode 100644 index 0000000000000..4066d6dd743d5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/FieldLayout/TestFieldLayout.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, Arm Limited. All rights reserved. + * 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.lang.reflect.Field; +import jdk.internal.misc.Unsafe; + +/* + * @test + * @bug 8341471 + * @summary Reversed field layout caused by unstable sorting + * @modules java.base/jdk.internal.misc + * @run main/othervm TestFieldLayout + */ + +public class TestFieldLayout { + + private static final Unsafe U = Unsafe.getUnsafe(); + + public static void main(String[] args) throws Exception { + + boolean endResult = true; + long previous = 0; + + for (Field f : Test.class.getDeclaredFields()) { + long current = U.objectFieldOffset(f); + if (current < previous) { + System.out.printf("FAILED: field %s offset %d previous %d\n", + f.getName(), current, previous); + endResult = false; + } + previous = current; + } + + System.out.println(endResult ? "Test PASSES" : "Test FAILS"); + if (!endResult) { + throw new Error("Test failed"); + } + } + + public class Test { + char a000; + char a001; + char a002; + char a003; + char a004; + char a005; + char a006; + char a007; + char a008; + char a009; + char a00a; + char a00b; + } + +} + diff --git a/test/hotspot/jtreg/runtime/Thread/TestAlwaysPreTouchStacks.java b/test/hotspot/jtreg/runtime/Thread/TestAlwaysPreTouchStacks.java index f16e0ff9da4fd..d47a07d2decb2 100644 --- a/test/hotspot/jtreg/runtime/Thread/TestAlwaysPreTouchStacks.java +++ b/test/hotspot/jtreg/runtime/Thread/TestAlwaysPreTouchStacks.java @@ -36,7 +36,7 @@ import static jdk.test.lib.Platform.isWindows; /* - * @test id=preTouch + * @test id=preTouchTest * @summary Test AlwaysPreTouchThreadStacks * @requires os.family != "aix" * @library /test/lib @@ -45,23 +45,16 @@ * @run driver TestAlwaysPreTouchStacks preTouch */ -/* - * @test id=noPreTouch - * @summary Test that only touched committed memory is reported as thread stack usage. - * @requires os.family != "aix" - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @run driver TestAlwaysPreTouchStacks noPreTouch - */ - public class TestAlwaysPreTouchStacks { // We will create a bunch of large-stacked threads to make a significant imprint on combined thread stack size - final static int MB = 1024*1024; + final static int MB = 1024 * 1024; static int memoryCeilingMB = 128; static int threadStackSizeMB = 8; static int numThreads = memoryCeilingMB / threadStackSizeMB; + static long min_stack_usage_with_pretouch = 1 * MB; + static long max_stack_usage_with_pretouch = (long)(0.75 * threadStackSizeMB * MB); + static CyclicBarrier gate = new CyclicBarrier(numThreads + 1); static private final Thread createTestThread(int num) { @@ -79,10 +72,76 @@ static private final Thread createTestThread(int num) { } }, "TestThread-" + num, threadStackSizeMB * MB); + // Let test threads run as daemons to ensure that they are still running and + // that their stacks are still allocated when the JVM shuts down and the final + // NMT report is printed. t.setDaemon(true); return t; } + private static long runPreTouchTest(boolean preTouch) throws Exception { + long reserved = 0L, committed = 0L; + ArrayList vmArgs = new ArrayList<>(); + Collections.addAll(vmArgs, + "-XX:+UnlockDiagnosticVMOptions", + "-Xmx100M", + "-XX:NativeMemoryTracking=summary", "-XX:+PrintNMTStatistics"); + if (preTouch){ + vmArgs.add("-XX:+AlwaysPreTouchStacks"); + } + if (System.getProperty("os.name").contains("Linux")) { + vmArgs.add("-XX:-UseMadvPopulateWrite"); + } + Collections.addAll(vmArgs, "TestAlwaysPreTouchStacks", "test"); + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(vmArgs); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldHaveExitValue(0); + + for (int i = 0; i < numThreads; i++) { + output.shouldContain("Alive: " + i); + } + + // If using -XX:+AlwaysPreTouchStacks, we want to see, in the final NMT printout, + // a committed thread stack size very close to reserved stack size. Like this: + // - Thread (reserved=10332400KB, committed=10284360KB) + // (thread #10021) + // (stack: reserved=10301560KB, committed=10253520KB) <<<< + // + // ... without -XX:+AlwaysPreTouchStacks, the committed/reserved ratio for thread stacks should be + // a lot lower, e.g.: + // - Thread (reserved=10332400KB, committed=331828KB) + // (thread #10021) + // (stack: reserved=10301560KB, committed=300988KB) <<< + + output.shouldMatch("- *Thread.*reserved.*committed"); + output.reportDiagnosticSummary(); + Pattern pat = Pattern.compile(".*stack: reserved=(\\d+), committed=(\\d+).*"); + boolean foundLine = false; + for (String line : output.asLines()) { + Matcher m = pat.matcher(line); + if (m.matches()) { + reserved = Long.parseLong(m.group(1)); + committed = Long.parseLong(m.group(2)); + System.out.println(">>>>> " + line + ": " + reserved + " - " + committed); + // Added sanity tests: we expect our test threads to be still alive when NMT prints its final + // report, so their stacks should dominate the NMT-reported total stack size. + long max_reserved = memoryCeilingMB * 3 * MB; + long min_reserved = memoryCeilingMB * MB; + if (reserved >= max_reserved || reserved < min_reserved) { + throw new RuntimeException("Total reserved stack sizes outside of our expectations (" + reserved + + ", expected " + min_reserved + ".." + max_reserved + ")"); + } + foundLine = true; + break; + } + } + if (!foundLine) { + throw new RuntimeException("Did not find expected NMT output"); + } + return committed; + } + public static void main(String[] args) throws Exception { if (args.length == 1 && args[0].equals("test")) { @@ -103,83 +162,23 @@ public static void main(String[] args) throws Exception { // should show up with fully - or almost fully - committed thread stacks. } else { - boolean preTouch; - if (args.length == 1 && args[0].equals("noPreTouch")){ - preTouch = false; - } else if (args.length == 1 && args[0].equals("preTouch")){ - preTouch = true; - } else { - throw new RuntimeException("Invalid test input. Must be 'preTouch' or 'noPreTouch'."); - } - ArrayList vmArgs = new ArrayList<>(); - Collections.addAll(vmArgs, - "-XX:+UnlockDiagnosticVMOptions", - "-Xmx100M", - "-XX:NativeMemoryTracking=summary", "-XX:+PrintNMTStatistics"); - if (preTouch){ - vmArgs.add("-XX:+AlwaysPreTouchStacks"); - } - if (System.getProperty("os.name").contains("Linux")) { - vmArgs.add("-XX:-UseMadvPopulateWrite"); - } - Collections.addAll(vmArgs, "TestAlwaysPreTouchStacks", "test"); - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(vmArgs); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.reportDiagnosticSummary(); - - output.shouldHaveExitValue(0); - - for (int i = 0; i < numThreads; i++) { - output.shouldContain("Alive: " + i); - } - - // If using -XX:+AlwaysPreTouchStacks, we want to see, in the final NMT printout, - // a committed thread stack size very close to reserved stack size. Like this: - // - Thread (reserved=10332400KB, committed=10284360KB) - // (thread #10021) - // (stack: reserved=10301560KB, committed=10253520KB) <<<< - // - // ... without -XX:+AlwaysPreTouchStacks, the committed/reserved ratio for thread stacks should be - // a lot lower, e.g.: - // - Thread (reserved=10332400KB, committed=331828KB) - // (thread #10021) - // (stack: reserved=10301560KB, committed=300988KB) <<< - - output.shouldMatch("- *Thread.*reserved.*committed"); - Pattern pat = Pattern.compile(".*stack: reserved=(\\d+), committed=(\\d+).*"); - boolean foundLine = false; - for (String line : output.asLines()) { - Matcher m = pat.matcher(line); - if (m.matches()) { - long reserved = Long.parseLong(m.group(1)); - long committed = Long.parseLong(m.group(2)); - System.out.println(">>>>> " + line + ": " + reserved + " - " + committed); - // This is a bit fuzzy: even with PreTouch we don't commit the full range of what NMT counts - // as thread stack. But without pre-touching, the thread stacks would be committed to about 1/5th - // of their reserved size. Requiring them to be committed for over 3/4th shows that pretouch is - // really working. - if (preTouch && (double)committed < ((double)reserved * 0.75)) { - throw new RuntimeException("Expected a higher ratio between stack committed and reserved."); - } else if (!preTouch && (double)committed > ((double)reserved * 0.50)){ - throw new RuntimeException("Expected a lower ratio between stack committed and reserved."); - } - // Added sanity tests: we expect our test threads to be still alive when NMT prints its final - // report, so their stacks should dominate the NMT-reported total stack size. - long max_reserved = memoryCeilingMB * 3 * MB; - long min_reserved = memoryCeilingMB * MB; - if (reserved >= max_reserved || reserved < min_reserved) { - throw new RuntimeException("Total reserved stack sizes outside of our expectations (" + reserved + - ", expected " + min_reserved + ".." + max_reserved + ")"); - } - foundLine = true; - break; - } - } - if (!foundLine) { - throw new RuntimeException("Did not find expected NMT output"); - } - } - + long pretouch_committed = runPreTouchTest(true); + long no_pretouch_committed = runPreTouchTest(false); + if (pretouch_committed == 0 || no_pretouch_committed == 0) { + throw new RuntimeException("Could not run with PreTouch flag."); + } + long expected_delta = numThreads * (max_stack_usage_with_pretouch - min_stack_usage_with_pretouch); + long actual_delta = pretouch_committed - no_pretouch_committed; + if (pretouch_committed <= (no_pretouch_committed + expected_delta)) { + throw new RuntimeException("Expected a higher amount of committed with pretouch stacks" + + "PreTouch amount: " + pretouch_committed + + "NoPreTouch amount: " + (no_pretouch_committed + expected_delta)); + } + if (actual_delta < expected_delta) { + throw new RuntimeException("Expected a higher delta between stack committed of with and without pretouch." + + "Expected: " + expected_delta + " Actual: " + actual_delta); + } + } } } diff --git a/test/hotspot/jtreg/runtime/Unsafe/InternalErrorTest.java b/test/hotspot/jtreg/runtime/Unsafe/InternalErrorTest.java index fcf48a7a2d775..01b051af8e550 100644 --- a/test/hotspot/jtreg/runtime/Unsafe/InternalErrorTest.java +++ b/test/hotspot/jtreg/runtime/Unsafe/InternalErrorTest.java @@ -147,7 +147,8 @@ public static void test(MappedByteBuffer buffer, Unsafe unsafe, long mapAddr, lo break; case 1: // testing Unsafe.copySwapMemory, trying to access next page after truncation. - unsafe.copySwapMemory(null, mapAddr + pageSize, new byte[4000], 16, 2000, 2); + int destOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET; + unsafe.copySwapMemory(null, mapAddr + pageSize, new byte[4000], destOffset, 2000, 2); break; case 2: // testing Unsafe.copySwapMemory, trying to access next page after truncation. diff --git a/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java b/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java index 560f42d0467da..fe618256f652f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java @@ -27,6 +27,7 @@ * @summary Class-Path: attribute in MANIFEST file * @requires vm.cds * @library /test/lib + * @compile test-classes/Hello.java * @run driver/timeout=240 ClassPathAttr */ @@ -45,6 +46,7 @@ public class ClassPathAttr { public static void main(String[] args) throws Exception { testNormalOps(); testNonExistentJars(); + testClassPathAttrJarOnCP(); } static void testNormalOps() throws Exception { @@ -197,6 +199,62 @@ static void testNonExistentJars() throws Exception { }); } + static void testClassPathAttrJarOnCP() throws Exception { + String helloJar = JarBuilder.getOrCreateHelloJar(); + String jar1 = TestCommon.getTestJar("cpattr1.jar"); + String cp = jar1 + File.pathSeparator + helloJar; + + // The cpattr1.jar contains "Class-Path: cpattr2.jar". + // The cpattr2.jar contains "Class-Path: cpattr3.jar cpattr5_123456789_223456789_323456789_42345678.jar". + // With -cp cpattr1:hello.jar, the following shared paths should be stored in the CDS archive: + // cpattr1.jar:cpattr2.jar:cpattr3.jar:cpattr5_123456789_223456789_323456789_42345678.jari:hello.jar + TestCommon.testDump(cp, TestCommon.list("Hello"), "-Xlog:class+path"); + + // Run with the same -cp apattr1.jar:hello.jar. The Hello class should be + // loaded from the archive. + TestCommon.run("-Xlog:class+path,class+load", + "-cp", cp, + "Hello") + .assertNormalExit(output -> { + output.shouldContain("Hello source: shared objects file"); + }); + + // Run with -cp apattr1.jar:cpattr2.jar:hello.jar. App classpath mismatch should be detected. + String jar2 = TestCommon.getTestJar("cpattr2.jar"); + cp = jar1 + File.pathSeparator + jar2 + File.pathSeparator + helloJar; + TestCommon.run("-Xlog:class+path,class+load", + "-cp", cp, + "Hello") + .assertAbnormalExit(output -> { + output.shouldMatch(".*APP classpath mismatch, actual: -Djava.class.path=.*cpattr1.jar.*cpattr2.jar.*hello.jar") + .shouldContain("Unable to use shared archive."); + }); + + // Run with different -cp cpattr2.jar:hello.jar. App classpath mismatch should be detected. + cp = jar2 + File.pathSeparator + helloJar; + TestCommon.run("-Xlog:class+path,class+load", + "-cp", cp, + "Hello") + .assertAbnormalExit(output -> { + output.shouldMatch(".*APP classpath mismatch, actual: -Djava.class.path=.*cpattr2.jar.*hello.jar") + .shouldContain("Unable to use shared archive."); + }); + + // Dumping with -cp cpattr1.jar:cpattr2.jar:hello.jar + // The cpattr2.jar is from the Class-Path: attribute of cpattr1.jar. + cp = jar1 + File.pathSeparator + jar2 + File.pathSeparator + helloJar; + TestCommon.testDump(cp, TestCommon.list("Hello"), "-Xlog:class+path"); + + // Run with the same -cp as dump time. The Hello class should be loaded from the archive. + TestCommon.run("-Xlog:class+path,class+load", + "-cp", cp, + "Hello") + .assertNormalExit(output -> { + output.shouldContain("Hello source: shared objects file"); + }); + + } + private static void buildCpAttr(String jarName, String manifest, String enclosingClassName, String ...testClassNames) throws Exception { String jarClassesDir = CDSTestUtils.getOutputDir() + File.separator + jarName + "_classes"; try { Files.createDirectory(Paths.get(jarClassesDir)); } catch (FileAlreadyExistsException e) { } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/DumpRuntimeClassesTest.java b/test/hotspot/jtreg/runtime/cds/appcds/DumpRuntimeClassesTest.java new file mode 100644 index 0000000000000..2e530a8d6ddab --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/DumpRuntimeClassesTest.java @@ -0,0 +1,65 @@ +/* + * 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 + * @summary Classes used by CDS at runtime should be in the archived + * @bug 8324259 + * @requires vm.cds + * @library /test/lib + * @compile test-classes/Hello.java + * @run driver DumpRuntimeClassesTest + */ + +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.cds.CDSTestUtils; + +public class DumpRuntimeClassesTest { + public static void main(String[] args) throws Exception { + // build The app + String appClass = "Hello"; + String classList = "hello.classlist"; + String archiveName = "hello.jsa"; + JarBuilder.build("hello", appClass); + String appJar = TestCommon.getTestJar("hello.jar"); + + // Dump class list + CDSTestUtils.dumpClassList(classList, "-cp", appJar, appClass); + + // Dump archive + CDSOptions opts = (new CDSOptions()) + .addPrefix("-cp", appJar, "-XX:SharedClassListFile=" + classList) + .setArchiveName(archiveName); + CDSTestUtils.createArchive(opts); + + // Run with archive and ensure all the classes used were in the archive + CDSOptions runOpts = (new CDSOptions()) + .addPrefix("-cp", appJar, "-Xlog:class+load,cds=debug") + .setArchiveName(archiveName) + .setUseVersion(false) + .addSuffix(appClass); + CDSTestUtils.runWithArchive(runOpts) + .shouldNotContain("source: jrt:/java.base"); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java new file mode 100644 index 0000000000000..409e37e10a164 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java @@ -0,0 +1,167 @@ +/* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 JetBrains s.r.o. + * 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 Verifies that CDS works with jar located in directories + * with names that need escaping + * @bug 8339460 + * @requires vm.cds + * @requires vm.cds.custom.loaders + * @requires vm.flagless + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile mypackage/Main.java mypackage/Another.java + * @run main/othervm ComplexURITest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Platform; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ComplexURITest { + final static String moduleName = "mymodule"; + + public static void main(String[] args) throws Exception { + System.setProperty("test.noclasspath", "true"); + String jarFile = JarBuilder.build(moduleName, "mypackage/Main", "mypackage/Another"); + + Path subDir = Path.of(".", "dir with space"); + Files.createDirectory(subDir); + Path newJarFilePath = subDir.resolve(moduleName + ".jar"); + Files.move(Path.of(jarFile), newJarFilePath); + jarFile = newJarFilePath.toString(); + + final String listFileName = "test-classlist.txt"; + final String staticArchiveName = "test-static.jsa"; + final String dynamicArchiveName = "test-dynamic.jsa"; + + // Verify static archive creation and use + File fileList = new File(listFileName); + delete(fileList.toPath()); + File staticArchive = new File(staticArchiveName); + delete(staticArchive.toPath()); + + createClassList(jarFile, listFileName); + if (!fileList.exists()) { + throw new RuntimeException("No class list created at " + fileList); + } + + createArchive(jarFile, listFileName, staticArchiveName); + if (!staticArchive.exists()) { + throw new RuntimeException("No shared classes archive created at " + staticArchive); + } + + useArchive(jarFile, staticArchiveName); + + // Verify dynamic archive creation and use + File dynamicArchive = new File(dynamicArchiveName); + delete(dynamicArchive.toPath()); + + createDynamicArchive(jarFile, dynamicArchiveName); + if (!dynamicArchive.exists()) { + throw new RuntimeException("No dynamic archive created at " + dynamicArchive); + } + + testDynamicArchive(jarFile, dynamicArchiveName); + } + + private static void delete(Path path) throws Exception { + if (Files.exists(path)) { + if (Platform.isWindows()) { + Files.setAttribute(path, "dos:readonly", false); + } + Files.delete(path); + } + } + + private static void createClassList(String jarFile, String list) throws Exception { + String[] launchArgs = { + "-XX:DumpLoadedClassList=" + list, + "--module-path", + jarFile, + "--module", + moduleName + "/mypackage.Main"}; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs); + OutputAnalyzer output = TestCommon.executeAndLog(pb, "create-list"); + output.shouldHaveExitValue(0); + } + + private static void createArchive(String jarFile, String list, String archive) throws Exception { + String[] launchArgs = { + "-Xshare:dump", + "-XX:SharedClassListFile=" + list, + "-XX:SharedArchiveFile=" + archive, + "--module-path", + jarFile, + "--module", + moduleName + "/mypackage.Main"}; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs); + OutputAnalyzer output = TestCommon.executeAndLog(pb, "dump-archive"); + output.shouldHaveExitValue(0); + } + + private static void useArchive(String jarFile, String archive) throws Exception { + String[] launchArgs = { + "-Xshare:on", + "-XX:SharedArchiveFile=" + archive, + "--module-path", + jarFile, + "--module", + moduleName + "/mypackage.Main"}; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs); + OutputAnalyzer output = TestCommon.executeAndLog(pb, "use-archive"); + output.shouldHaveExitValue(0); + } + + private static void createDynamicArchive(String jarFile, String archive) throws Exception { + String[] launchArgs = { + "-XX:ArchiveClassesAtExit=" + archive, + "--module-path", + jarFile, + "--module", + moduleName + "/mypackage.Main"}; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs); + OutputAnalyzer output = TestCommon.executeAndLog(pb, "dynamic-archive"); + output.shouldHaveExitValue(0); + } + + private static void testDynamicArchive(String jarFile, String archive) throws Exception { + String[] launchArgs = { + "-XX:SharedArchiveFile=" + archive, + "-XX:+PrintSharedArchiveAndExit", + "--module-path", + jarFile, + "--module", + moduleName + "/mypackage.Main"}; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs); + OutputAnalyzer output = TestCommon.executeAndLog(pb, "dynamic-archive"); + output.shouldHaveExitValue(0); + output.shouldContain("archive is valid"); + output.shouldContain(": mypackage.Main app_loader"); + output.shouldContain(": mypackage.Another unregistered_loader"); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/complexURI/mypackage/Another.java b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/mypackage/Another.java new file mode 100644 index 0000000000000..106dfd4904c7d --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/mypackage/Another.java @@ -0,0 +1,27 @@ +/* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 JetBrains s.r.o. + * 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 mypackage; + +public class Another { +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/complexURI/mypackage/Main.java b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/mypackage/Main.java new file mode 100644 index 0000000000000..fdb79e895d240 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/mypackage/Main.java @@ -0,0 +1,37 @@ +/* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 JetBrains s.r.o. + * 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 mypackage; + +import java.net.URL; +import java.net.URLClassLoader; + +public class Main { + public static void main(String[] args) throws Exception { + URL url1 = Main.class.getProtectionDomain().getCodeSource().getLocation(); + System.out.println("Will load Another from " + url1); + ClassLoader cl = URLClassLoader.newInstance(new URL[] { url1 }, null); + var anotherClass = cl.loadClass("mypackage.Another"); + System.out.println("Class " + anotherClass + " loaded successfully"); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java index 0dd4af2e4509e..e74229869edce 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/MainModuleOnly.java @@ -213,8 +213,10 @@ public static void doTest(String topArchiveName) throws Exception { "-cp", destJar.toString(), "--module-path", MODS_DIR.toString(), "-m", TEST_MODULE1 + "/" + MAIN_CLASS) - .assertAbnormalExit(output -> { - output.shouldMatch("Error: non-empty directory.*com.simple"); + // After JDK-8328313, non-empty module path directory won't be included + // in the shared paths table. + .assertNormalExit(output -> { + output.shouldNotMatch("Error: non-empty directory.*com.simple"); }); // test module path with very long length diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/MainModuleOnly.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/MainModuleOnly.java index 50191b91e1ef5..ac9bb5adc006d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/MainModuleOnly.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/MainModuleOnly.java @@ -194,8 +194,10 @@ public static void main(String... args) throws Exception { output = TestCommon.createArchive(destJar.toString(), appClasses, "--module-path", MODS_DIR.toString(), "-m", mainModule); - output.shouldHaveExitValue(1) - .shouldMatch("Error: non-empty directory.*com.simple"); + // After JDK-8328313, non-empty module path directory won't be included + // in the shared paths table. + output.shouldHaveExitValue(0) + .shouldNotMatch("Error: non-empty directory.*com.simple"); // test module path with very long length // diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java new file mode 100644 index 0000000000000..fe77714204670 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java @@ -0,0 +1,385 @@ +/* + * 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 8328313 + * @requires vm.cds & !vm.graal.enabled & vm.cds.write.archived.java.heap + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @run driver ModulePathAndFMG + * @summary test module path changes for full module graph handling. + * + */ + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class ModulePathAndFMG { + private static final String JAVA_HOME = System.getProperty("java.home"); + + private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir()); + + private static final String TEST_SRC = System.getProperty("test.src"); + + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path MODS_DIR = Paths.get("mody"); + private static final Path JMOD_DIR = Paths.get("jmod_dir"); + + // the module name of the test module + private static final String MAIN_MODULE = "com.bars"; + private static final String TEST_MODULE = "com.foos"; + private static final String DUP_MODULE = "com.foos3"; + + // the module main class + private static final String MAIN_CLASS = "com.bars.Main"; + private static final String TEST_CLASS = "com.foos.Test"; + + private static String PATH_LIBS = "modylibs"; + private static String DUP_LIBS = "duplibs"; + private static Path libsDir = null; + private static Path dupDir = null; + private static Path jmodDir = null; + private static Path mainJar = null; + private static Path testJar = null; + private static Path dupJar = null; + private static Path badJar = null; + + private static String CLASS_FOUND_MESSAGE = "com.foos.Test found"; + private static String CLASS_NOT_FOUND_MESSAGE = "java.lang.ClassNotFoundException: com.foos.Test"; + private static String FIND_EXCEPTION_MESSAGE = "java.lang.module.FindException: Module com.foos not found, required by com.bars"; + private static String MODULE_NOT_RECOGNIZED = "Module format not recognized:.*modylibs.*com.bars.JAR"; + private static String OPTIMIZE_ENABLED = "] optimized module handling: enabled"; + private static String OPTIMIZE_DISABLED = "] optimized module handling: disabled"; + private static String FMG_ENABLED = "] full module graph: enabled"; + private static String FMG_DISABLED = "] full module graph: disabled"; + private static String MAIN_FROM_JAR = "class,load.*com.bars.Main.*[.]jar"; + private static String MAIN_FROM_CDS = "class,load.*com.bars.Main.*shared objects file"; + private static String MAIN_FROM_MODULE = "class,load.*com.bars.Main.*mody/com.bars"; + private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar"; + private static String TEST_FROM_CDS = "class,load.*com.foos.Test.*shared objects file"; + private static String MAP_FAILED = "Unable to use shared archive"; + private static String PATH_SEPARATOR = File.pathSeparator; + private static String appClasses[] = {MAIN_CLASS, TEST_CLASS}; + private static String prefix[] = {"-Djava.class.path=", "-Xlog:cds,class+load,class+path=info"}; + + public static void buildTestModule() throws Exception { + + // javac -d mods/$TESTMODULE src/$TESTMODULE/** + JarBuilder.compileModule(SRC_DIR.resolve(TEST_MODULE), + MODS_DIR.resolve(TEST_MODULE), + null); + + // javac -d mods/$TESTMODULE --module-path MOD_DIR src/$TESTMODULE/** + JarBuilder.compileModule(SRC_DIR.resolve(MAIN_MODULE), + MODS_DIR.resolve(MAIN_MODULE), + MODS_DIR.toString()); + + libsDir = Files.createTempDirectory(USER_DIR, PATH_LIBS); + mainJar = libsDir.resolve(MAIN_MODULE + ".jar"); + testJar = libsDir.resolve(TEST_MODULE + ".jar"); + + // modylibs contains both modules com.foos.jar, com.bars.jar + // build com.foos.jar + String classes = MODS_DIR.resolve(TEST_MODULE).toString(); + JarBuilder.createModularJar(testJar.toString(), classes, TEST_CLASS); + + // build com.bars.jar + classes = MODS_DIR.resolve(MAIN_MODULE).toString(); + JarBuilder.createModularJar(mainJar.toString(), classes, MAIN_CLASS); + + dupDir = Files.createTempDirectory(USER_DIR, DUP_LIBS); + dupJar = dupDir.resolve(DUP_MODULE + ".jar"); + Files.copy(testJar, dupJar, StandardCopyOption.REPLACE_EXISTING); + + badJar = libsDir.resolve(MAIN_MODULE + ".JAR"); + Files.copy(mainJar, badJar, StandardCopyOption.REPLACE_EXISTING); + } + + public static void buildJmod() throws Exception { + Path jmod = Paths.get(JAVA_HOME, "bin", "jmod"); + jmodDir = Files.createDirectory(Paths.get(USER_DIR.toString() + File.separator + JMOD_DIR.toString())); + OutputAnalyzer output = ProcessTools.executeProcess(jmod.toString(), + "create", + "--class-path", Paths.get(USER_DIR.toString(), MODS_DIR.toString(), TEST_MODULE).toString(), + "--module-version", "1.0", + "--main-class", TEST_CLASS, + jmodDir.toString() + File.separator + TEST_MODULE + ".jmod"); + output.shouldHaveExitValue(0); + } + + public static void main(String... args) throws Exception { + runWithModulePath(); + runWithExplodedModule(); + runWithJmodAndBadJar(); + } + + private static void tty(String... args) { + for (String s : args) { + System.out.print(s + " "); + } + System.out.print("\n"); + } + + public static void runWithModulePath() throws Exception { + // compile the modules and create the modular jar files + buildTestModule(); + // create an archive with the classes in the modules built in the + // previous step + OutputAnalyzer output = TestCommon.createArchive( + null, appClasses, + "--module-path", + libsDir.toString(), + "-m", MAIN_MODULE); + TestCommon.checkDump(output); + + tty("1. run with CDS on, with module path same as dump time"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + libsDir.toString(), // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(FMG_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only + .shouldContain(CLASS_FOUND_MESSAGE); + }); + + tty("2. run with CDS on, with jar on path"); + TestCommon.run("-Xlog:cds", + "-Xlog:class+load", + "-cp", mainJar.toString() + PATH_SEPARATOR + testJar.toString(), + MAIN_CLASS) + .assertNormalExit(out -> { + out.shouldContain(CLASS_FOUND_MESSAGE) + .shouldMatch(MAIN_FROM_JAR) + .shouldMatch(TEST_FROM_JAR) + .shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldNotContain(FMG_ENABLED); + }); + + tty("3. run with CDS on, with --module-path, with jar should fail"); + TestCommon.run("-Xlog:cds", + "-Xlog:class+load", + "-p", libsDir.toString(), + "-cp", mainJar.toString(), + MAIN_CLASS) + .assertNormalExit(out -> { + out.shouldContain(CLASS_NOT_FOUND_MESSAGE) + .shouldMatch(MAIN_FROM_JAR) + .shouldNotContain(FMG_ENABLED) + .shouldNotContain(OPTIMIZE_ENABLED); + }); + + final String modularJarPath = mainJar.toString() + PATH_SEPARATOR + testJar.toString(); + + tty("4. run with CDS on, with modular jars specified --module-path, should pass"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + modularJarPath, // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(FMG_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldMatch(MAIN_FROM_CDS); // archived Main class is for module only + }); + + final String extraModulePath = libsDir.toString() + PATH_SEPARATOR + dupDir.toString(); + // create an archive with an extra module which is not referenced + output = TestCommon.createArchive( + null, appClasses, + "--module-path", + extraModulePath, + "-m", MAIN_MODULE); + TestCommon.checkDump(output); + + tty("5. run with CDS on, without the extra module specified in dump time, should pass"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + libsDir.toString(), // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(FMG_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only + .shouldContain(CLASS_FOUND_MESSAGE); + }); + tty("6. run with CDS on, with the extra module specified in dump time"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + extraModulePath, // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(FMG_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only + .shouldContain(CLASS_FOUND_MESSAGE); + }); + + final String extraJarPath = modularJarPath + PATH_SEPARATOR + dupJar.toString(); + + // create an archive by specifying modular jars in the --module-path with an extra module which is not referenced + output = TestCommon.createArchive( + null, appClasses, + "--module-path", + extraJarPath, + "-m", MAIN_MODULE); + TestCommon.checkDump(output); + tty("7. run with CDS on, without the extra module specified in dump time, should pass"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + modularJarPath, // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(FMG_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only + .shouldContain(CLASS_FOUND_MESSAGE); + }); + + tty("8. run with CDS on, with the extra module specified in dump time"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + extraJarPath, // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(FMG_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only + .shouldContain(CLASS_FOUND_MESSAGE); + }); + tty("9. same as test case 8 but with paths instead of modular jars in the --module-path"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + extraModulePath, // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(FMG_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldMatch(MAIN_FROM_CDS) // archived Main class is for module only + .shouldContain(CLASS_FOUND_MESSAGE); + }); + } + + public static void runWithExplodedModule() throws Exception { + // create an archive with an exploded module in the module path. + OutputAnalyzer output = TestCommon.createArchive( + null, appClasses, + "--module-path", + MODS_DIR.toString(), + "-m", MAIN_MODULE + "/" + MAIN_CLASS); + TestCommon.checkDump(output); + + tty("10. run with CDS on, with exploded module in the module path"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + MODS_DIR.toString(), // --module-path + MAIN_MODULE + "/" + MAIN_CLASS) // -m + .assertNormalExit(out -> { + out.shouldContain(FMG_DISABLED) + .shouldMatch(MAIN_FROM_MODULE) // Main class loaded from the exploded module + .shouldContain(CLASS_FOUND_MESSAGE); + }); + } + + public static void runWithJmodAndBadJar() throws Exception { + buildJmod(); + + final String modularJarPath = mainJar.toString() + PATH_SEPARATOR + testJar.toString(); + // create an archive with --module-path com.bars.jar:com.foos.jar + OutputAnalyzer output = TestCommon.createArchive( + null, appClasses, + "--module-path", + modularJarPath, + "-m", MAIN_MODULE); + TestCommon.checkDump(output); + + String runModulePath = mainJar.toString() + PATH_SEPARATOR + + jmodDir.toString() + TEST_MODULE + ".jmod"; + tty("11. run with CDS on, with module path com.bars.jar:com.foos.jmod"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + runModulePath, // --module-path + MAIN_MODULE) // -m + .assertAbnormalExit(out -> { + out.shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldNotContain(FMG_ENABLED) + .shouldContain(FIND_EXCEPTION_MESSAGE); + }); + + runModulePath += PATH_SEPARATOR + testJar.toString(); + tty("12. run with CDS on, with module path com.bars.jar:com.foos.jmod:com.foos.jar"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + runModulePath, // --module-path + MAIN_MODULE) // -m + .assertNormalExit(out -> { + out.shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(FMG_DISABLED) + .shouldContain(FMG_ENABLED) + .shouldMatch(TEST_FROM_CDS) + .shouldMatch(MAIN_FROM_CDS) + .shouldContain(CLASS_FOUND_MESSAGE); + }); + + runModulePath = badJar.toString() + PATH_SEPARATOR + testJar.toString(); + tty("13. run with CDS on, with module path com.bars.JAR:com.foos.jar"); + TestCommon.runWithModules(prefix, + null, // --upgrade-module-path + runModulePath, // --module-path + TEST_MODULE) // -m + .assertAbnormalExit(out -> { + out.shouldContain(OPTIMIZE_DISABLED) + .shouldNotContain(OPTIMIZE_ENABLED) + .shouldContain(FMG_DISABLED) + .shouldNotContain(FMG_ENABLED) + .shouldMatch(MODULE_NOT_RECOGNIZED); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java index aeb5696830df4..cf6f4cd7fd3d5 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/OptimizeModuleHandlingTest.java @@ -24,7 +24,7 @@ /** * @test - * @requires vm.cds & !vm.graal.enabled + * @requires vm.cds & !vm.graal.enabled & vm.cds.write.archived.java.heap * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @run driver OptimizeModuleHandlingTest * @summary test module path changes for optimization of @@ -64,8 +64,8 @@ public class OptimizeModuleHandlingTest { private static String CLASS_FOUND_MESSAGE = "com.foos.Test found"; private static String CLASS_NOT_FOUND_MESSAGE = "java.lang.ClassNotFoundException: com.foos.Test"; - private static String OPTIMIZE_ENABLED = "optimized module handling: enabled"; - private static String OPTIMIZE_DISABLED = "optimized module handling: disabled"; + private static String OPTIMIZE_ENABLED = "] optimized module handling: enabled"; + private static String OPTIMIZE_DISABLED = "] optimized module handling: disabled"; private static String MAIN_FROM_JAR = "class,load.*com.bars.Main.*[.]jar"; private static String MAIN_FROM_CDS = "class,load.*com.bars.Main.*shared objects file"; private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar"; @@ -154,14 +154,14 @@ public static void runWithModulePath(String... extraRuntimeArgs) throws Exceptio // Following 5 - 10 test with CDS on tty("5. run with CDS on, with module path"); - String prefix[] = {"-Djava.class.path=", "-Xlog:cds", "-Xlog:class+load"}; + String prefix[] = {"-Djava.class.path=", "-Xlog:cds,class+load,class+path=info"}; TestCommon.runWithModules(prefix, null, // --upgrade-module-path libsDir.toString(), // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldNotContain(OPTIMIZE_ENABLED) - .shouldContain(OPTIMIZE_DISABLED) + out.shouldNotContain(OPTIMIZE_DISABLED) + .shouldContain(OPTIMIZE_ENABLED) .shouldMatch(MAIN_FROM_CDS) // // archived Main class is for module only .shouldContain(CLASS_FOUND_MESSAGE); }); @@ -174,8 +174,8 @@ public static void runWithModulePath(String... extraRuntimeArgs) throws Exceptio out.shouldContain(CLASS_FOUND_MESSAGE) .shouldMatch(MAIN_FROM_CDS) .shouldMatch(TEST_FROM_CDS) - .shouldContain(OPTIMIZE_DISABLED) - .shouldNotContain(OPTIMIZE_ENABLED); + .shouldContain(OPTIMIZE_ENABLED) + .shouldNotContain(OPTIMIZE_DISABLED); }); tty("7. run with CDS on, with jar on path"); TestCommon.run("-Xlog:cds", @@ -231,7 +231,6 @@ public static void runWithModulePath(String... extraRuntimeArgs) throws Exceptio MAIN_CLASS) .assertAbnormalExit(out -> { out.shouldNotContain(CLASS_FOUND_MESSAGE) - .shouldContain(OPTIMIZE_DISABLED) // mapping info .shouldContain("shared class paths mismatch"); }); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java index 86b2c8fd425a8..2ad257476d0fb 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java @@ -108,9 +108,9 @@ public static void test(String[] args_ignored) throws Exception { testDump(1, "-XX:+UseZGC", "-XX:-UseCompressedOops", null, false); } - // incompatible GCs - testDump(2, "-XX:+UseParallelGC", "", GC_WARNING, false); - testDump(3, "-XX:+UseSerialGC", "", GC_WARNING, false); + // Dump heap objects with ParallelGC and SerialGC + testDump(2, "-XX:+UseParallelGC", "", "", false); + testDump(3, "-XX:+UseSerialGC", "", "", false); // Explicitly archive with compressed oops, run without. testDump(5, "-XX:+UseG1GC", "-XX:+UseCompressedOops", null, false); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsHumongous.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsHumongous.java index 01cebb29f9138..3e2145c782188 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsHumongous.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsHumongous.java @@ -27,6 +27,7 @@ * @summary Use a shared string allocated in a humongous G1 region. * @comment -- the following implies that G1 is used (by command-line or by default) * @requires vm.cds.write.archived.java.heap + * @requires vm.gc.G1 * * @library /test/hotspot/jtreg/runtime/cds/appcds /test/lib * @build HelloString diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsUtils.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsUtils.java index 57e39b5177aaa..4026f1d4f8414 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsUtils.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsUtils.java @@ -96,7 +96,7 @@ public static OutputAnalyzer dumpWithoutChecks(String appClasses[], String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL); String[] args = - TestCommon.concat(extraOptions, "-XX:+UseCompressedOops", "-XX:+UseG1GC", + TestCommon.concat(extraOptions, "-XX:+UseCompressedOops", "-XX:SharedArchiveConfigFile=" + TestCommon.getSourceFile(sharedDataFile)); args = TestCommon.concat(childVMOptionsPrefix, args); @@ -124,7 +124,7 @@ public static OutputAnalyzer runWithArchiveAuto(String className, String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL); String[] args = TestCommon.concat(extraOptions, - "-cp", appJar, "-XX:+UseCompressedOops", "-XX:+UseG1GC", className); + "-cp", appJar, "-XX:+UseCompressedOops", className); args = TestCommon.concat(childVMOptionsPrefix, args); OutputAnalyzer output = TestCommon.execAuto(args); @@ -143,7 +143,7 @@ public static OutputAnalyzer runWithArchive(String[] extraMatches, String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL); String[] args = TestCommon.concat(extraOptions, - "-XX:+UseCompressedOops", "-XX:+UseG1GC", className); + "-XX:+UseCompressedOops", className); args = TestCommon.concat(childVMOptionsPrefix, args); OutputAnalyzer output = TestCommon.exec(appJar, args); diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/EventsTest.java b/test/hotspot/jtreg/serviceability/dcmd/vm/EventsTest.java index b5fca58a5c5f3..9828a8990e025 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/vm/EventsTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/vm/EventsTest.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 @@ -25,6 +25,7 @@ import jdk.test.lib.dcmd.JMXExecutor; import jdk.test.lib.process.OutputAnalyzer; import org.testng.annotations.Test; +import org.testng.Assert; /* * @test @@ -38,6 +39,9 @@ */ public class EventsTest { + // MAX should be less than number of actually recorded events + private static int MAX = 9; + static String buildHeaderPattern(String logname) { return "^" + logname + ".*\\(\\d+ events\\):"; } @@ -64,9 +68,27 @@ public void run_selected(CommandExecutor executor) { output.stdoutShouldNotMatch(buildHeaderPattern("Classes unloaded")); } + public void run_max(CommandExecutor executor) { + OutputAnalyzer output = executor.execute("VM.events max=" + MAX); + long lines = output.asLines().stream().filter(x -> x.contains("Loading class")).count(); + Assert.assertTrue(lines == MAX, "There are should be " + MAX + " lines"); + } + + public void run_max_selected(CommandExecutor executor) { + OutputAnalyzer output = executor.execute("VM.events log=load max=" + MAX); + output.stdoutShouldMatch(buildHeaderPattern("Classes loaded")); + long lines = output.asLines().stream().filter(x -> x.contains("Loading class")).count(); + Assert.assertTrue(lines == MAX, "There are should be " + MAX + " lines"); + output.stdoutShouldNotMatch(buildHeaderPattern("Events")); + output.stdoutShouldNotMatch(buildHeaderPattern("Compilation")); + } + + @Test public void jmx() { run_all(new JMXExecutor()); run_selected(new JMXExecutor()); + run_max(new JMXExecutor()); + run_max_selected(new JMXExecutor()); } } diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/SystemDumpMapTest.java b/test/hotspot/jtreg/serviceability/dcmd/vm/SystemDumpMapTest.java index eb0b6bd8566ae..eab69de0a518c 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/vm/SystemDumpMapTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/vm/SystemDumpMapTest.java @@ -35,7 +35,7 @@ * @test * @summary Test of diagnostic command System.map * @library /test/lib - * @requires (os.family=="linux") + * @requires (os.family == "linux" | os.family == "windows") * @modules java.base/jdk.internal.misc * java.compiler * java.management @@ -63,11 +63,11 @@ private void run_test(CommandExecutor executor, boolean useDefaultFileName) { boolean NMTOff = output.contains("NMT is disabled"); String regexBase = ".*0x\\p{XDigit}+ - 0x\\p{XDigit}+ +\\d+"; HashSet patterns = new HashSet<>(); - for (String s: shouldMatchUnconditionally) { + for (String s: shouldMatchUnconditionally()) { patterns.add(Pattern.compile(s)); } if (!NMTOff) { // expect VM annotations if NMT is on - for (String s: shouldMatchIfNMTIsEnabled) { + for (String s: shouldMatchIfNMTIsEnabled()) { patterns.add(Pattern.compile(s)); } } diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTest.java b/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTest.java index 43857a1f039bf..6d589d17f8aea 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTest.java @@ -31,7 +31,7 @@ * @test * @summary Test of diagnostic command System.map * @library /test/lib - * @requires (os.family=="linux") + * @requires (os.family == "linux" | os.family == "windows") * @modules java.base/jdk.internal.misc * java.compiler * java.management @@ -42,11 +42,11 @@ public class SystemMapTest extends SystemMapTestBase { public void run(CommandExecutor executor) { OutputAnalyzer output = executor.execute("System.map"); boolean NMTOff = output.contains("NMT is disabled"); - for (String s: shouldMatchUnconditionally) { + for (String s: shouldMatchUnconditionally()) { output.shouldMatch(s); } if (!NMTOff) { // expect VM annotations if NMT is on - for (String s: shouldMatchIfNMTIsEnabled) { + for (String s: shouldMatchIfNMTIsEnabled()) { output.shouldMatch(s); } } diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTestBase.java b/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTestBase.java index 3d1284b09eeee..6507117df3533 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTestBase.java +++ b/test/hotspot/jtreg/serviceability/dcmd/vm/SystemMapTestBase.java @@ -22,6 +22,8 @@ * questions. */ +import jdk.test.lib.Platform; + public class SystemMapTestBase { // e.g. @@ -29,6 +31,7 @@ public class SystemMapTestBase { private static final String range = "0x\\p{XDigit}+-0x\\p{XDigit}+"; private static final String space = " +"; private static final String someSize = "\\d+"; + private static final String someNumber = "(0x\\p{XDigit}+|\\d+)"; private static final String pagesize = "(4K|8K|16K|64K|2M|16M|64M)"; private static final String prot = "[rwsxp-]+"; @@ -44,7 +47,8 @@ public class SystemMapTestBase { // java heap is either committed, non-shared, or - in case of ZGC - committed and shared. private static final String regexBase_java_heap = regexBase + "(shrd,)?com.*"; - protected static final String shouldMatchUnconditionally[] = { + + private static final String shouldMatchUnconditionally_linux[] = { // java launcher regexBase_committed + "/bin/java", // libjvm @@ -55,7 +59,7 @@ public class SystemMapTestBase { regexBase_shared_and_committed + "hsperfdata_.*" }; - protected static final String shouldMatchIfNMTIsEnabled[] = { + private static final String shouldMatchIfNMTIsEnabled_linux[] = { regexBase_java_heap + "JAVAHEAP.*", // metaspace regexBase_committed + "META.*", @@ -66,4 +70,43 @@ public class SystemMapTestBase { // Main thread stack regexBase_committed + "STACK.*main.*" }; + + // windows: + private static final String winprot = "[\\-rwxcin]*"; + private static final String wintype = "[rc]-(img|map|pvt)"; + + private static final String winbase = range + space + someSize + space + winprot + space; + + private static final String winimage = winbase + "c-img" + space + someNumber + space; + private static final String wincommitted = winbase + "(c-pvt|c-map)" + space + someNumber + space; + private static final String winreserved = winbase + "r-pvt" + space + someNumber + space; + + private static final String shouldMatchUnconditionally_windows[] = { + // java launcher + winimage + ".*[\\/\\\\]bin[\\/\\\\]java[.]exe", + // libjvm + winimage + ".*[\\/\\\\]bin[\\/\\\\].*[\\/\\\\]jvm.dll" + }; + + private static final String shouldMatchIfNMTIsEnabled_windows[] = { + wincommitted + "JAVAHEAP.*", + // metaspace + wincommitted + "META.*", + // parts of metaspace should be uncommitted + winreserved + "META.*", + // code cache + wincommitted + "CODE.*", + // Main thread stack + wincommitted + "STACK-\\d+-main.*" + }; + + private static final boolean isWindows = Platform.isWindows(); + + protected static String[] shouldMatchUnconditionally() { + return isWindows ? shouldMatchUnconditionally_windows : shouldMatchUnconditionally_linux; + } + protected static String[] shouldMatchIfNMTIsEnabled() { + return isWindows ? shouldMatchIfNMTIsEnabled_windows : shouldMatchIfNMTIsEnabled_linux; + } + } diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java index df4f9063586f4..e2c7230bc0dc9 100644 --- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java +++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java @@ -304,7 +304,17 @@ private String[] cmd(long classStart, long classStop) { "-XX:+StressMacroExpansion", "-XX:+StressIncrementalInlining", // StressSeed is uint - "-XX:StressSeed=" + rng.nextInt(Integer.MAX_VALUE))); + "-XX:StressSeed=" + rng.nextInt(Integer.MAX_VALUE), + // Do not fail on huge methods where StressGCM makes register + // allocation allocate lots of memory + "-XX:CompileCommand=memlimit,*.*,0")); + + // Use this stress mode 10% of the time as it could make some long-running compilations likely to abort. + if (rng.nextInt(10) == 0) { + Args.add("-XX:+StressBailout"); + Args.add("-XX:StressBailoutMean=100000"); + Args.add("-XX:+CaptureBailoutInformation"); + } for (String arg : CTW_EXTRA_ARGS.split(",")) { Args.add(arg); diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/CombinedJavaJasmExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/CombinedJavaJasmExample.java new file mode 100644 index 0000000000000..565c92c1b7068 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/CombinedJavaJasmExample.java @@ -0,0 +1,114 @@ +/* + * 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 + * @summary Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.CombinedJavaJasmExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +/** + * This test shows a compilation of multiple Java and Jasm source code files. + * In this example, the classes even reference each other. + */ +public class CombinedJavaJasmExample { + + // Generate a source jasm file as String + public static String generateJasm() { + return """ + package p/xyz; + + super public class XYZJasm { + public static Method test:"(I)I" + stack 20 locals 20 + { + iload_0; + iconst_2; + imul; + invokestatic Method p/xyz/XYZJava."mul3":"(I)I"; + ireturn; + } + + public static Method mul5:"(I)I" + stack 20 locals 20 + { + iload_0; + ldc 5; + imul; + ireturn; + } + } + """; + } + + // Generate a source java file as String + public static String generateJava() { + return """ + package p.xyz; + + public class XYZJava { + public static int test(int i) { + return p.xyz.XYZJasm.mul5(i * 7); + } + + public static int mul3(int i) { + return i * 3; + } + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Generate files. + comp.addJasmSourceCode("p.xyz.XYZJasm", generateJasm()); + comp.addJavaSourceCode("p.xyz.XYZJava", generateJava()); + + // Compile the source files. + comp.compile(); + + test(comp, "p.xyz.XYZJasm", "test", 11, 11 * 2 * 3); + test(comp, "p.xyz.XYZJava", "test", 13, 13 * 7 * 5); + + System.out.println("Success."); + } + + public static void test(CompileFramework comp, String className, String methodName, int input, int expected) { + Object ret = comp.invoke(className, methodName, new Object[] {input}); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i + " vs expected: " + expected); + if (i != expected) { + throw new RuntimeException("wrong value: " + i); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java new file mode 100644 index 0000000000000..11b8828d7530d --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/IRFrameworkJavaExample.java @@ -0,0 +1,156 @@ +/* + * 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 + * @summary Example test to use the Compile Framework together with the IR Framework (i.e. TestFramework). + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/ir_framework/TestFramework.java + * @run driver compile_framework.examples.IRFrameworkJavaExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; +import jdk.test.lib.Utils; +import jdk.test.lib.Platform; +import java.lang.reflect.InvocationTargetException; + +/** + * This test shows that the IR verification can be done on code compiled by the Compile Framework. + * The "@compile" command for JTREG is required so that the IRFramework is compiled, other javac + * might not compile it because it is not present in the class, only in the dynamically compiled + * code. + *

          + * Additionally, we must set the classpath for the Test-VM, so that it has access to all compiled + * classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}). + */ +public class IRFrameworkJavaExample { + + public static void main(String[] args) { + testX1(); + testX2(); + } + + // Generate a source java file as String + public static String generateX1(CompileFramework comp) { + return String.format(""" + import compiler.lib.ir_framework.*; + + public class X1 { + public static void main(String args[]) { + TestFramework framework = new TestFramework(X1.class); + framework.addFlags("-classpath", "%s"); + framework.start(); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0"}, + applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"}) + static float[] test() { + float[] a = new float[1024*8]; + for (int i = 0; i < a.length; i++) { + a[i]++; + } + return a; + } + } + """, comp.getEscapedClassPathOfCompiledClasses()); + } + + static void testX1() { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("X1", generateX1(comp)); + + // Compile the source file. + comp.compile(); + + // X1.main(); + comp.invoke("X1", "main", new Object[] {null}); + } + + // Generate a source java file as String + public static String generateX2(CompileFramework comp) { + // Example with conflicting "@IR" rules -> expect a IRViolationException. + return String.format(""" + import compiler.lib.ir_framework.*; + + public class X2 { + public static void main(String args[]) { + TestFramework framework = new TestFramework(X2.class); + framework.addFlags("-classpath", "%s"); + framework.start(); + } + + @Test + @IR(counts = {IRNode.LOAD, "> 0"}) + @IR(failOn = IRNode.LOAD) + static void test() { + } + } + """, comp.getEscapedClassPathOfCompiledClasses()); + } + + static void testX2() { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("X2", generateX2(comp)); + + // Compile the source file. + comp.compile(); + + // Load the compiled class. + Class c = comp.getClass("X2"); + + // Invoke the "X2.main" method from the compiled and loaded class. + try { + c.getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { null }); + + // Check if IR framework is expected to execute the IR rules. + if (Utils.getTestJavaOpts().length == 0 && Platform.isDebugBuild() && !Platform.isInt() && !Platform.isComp()) { + throw new RuntimeException("IRViolationException expected."); + } else { + System.out.println("Got no IRViolationException, but was also not expected."); + } + } catch (NoSuchMethodException e) { + throw new RuntimeException("No such method:", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Illegal access:", e); + } catch (InvocationTargetException e) { + Throwable t = e.getCause(); + if (t == null) { + throw new RuntimeException("IRViolationException expected:", e); + } + if (!t.getClass().getSimpleName().equals("IRViolationException")) { + throw new RuntimeException("IRViolationException expected:", e); + } + System.out.println("Success, we got a IRViolationException."); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJasmExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJasmExample.java new file mode 100644 index 0000000000000..33fe07a53970b --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJasmExample.java @@ -0,0 +1,88 @@ +/* + * 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 + * @summary Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver comile_framework.examples.MultiFileJasmExample + */ + +package comile_framework.examples; + +import compiler.lib.compile_framework.*; +import java.io.StringWriter; +import java.io.PrintWriter; + +/** + * This test shows a compilation of multiple jasm source code files. + */ +public class MultiFileJasmExample { + + // Generate a source jasm file as String + public static String generate(int i) { + StringWriter writer = new StringWriter(); + PrintWriter out = new PrintWriter(writer); + out.println("package p/xyz;"); + out.println(""); + out.println("super public class XYZ" + i + " {"); + out.println(" public static Method test:\"(I)I\""); + out.println(" stack 20 locals 20"); + out.println(" {"); + out.println(" iload_0;"); + out.println(" iconst_2;"); // every call multiplies by 2, in total 2^10 = 1024 + out.println(" imul;"); + if (i != 0) { + out.println(" invokestatic Method p/xyz/XYZ" + (i-1) + ".\"test\":\"(I)I\";"); + } + out.println(" ireturn;"); + out.println(" }"); + out.println("}"); + return writer.toString(); + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Generate 10 files. + for (int i = 0; i < 10; i++) { + comp.addJasmSourceCode("p.xyz.XYZ" + i, generate(i)); + } + + // Compile the source files. + comp.compile(); + + // Object ret = XYZ9.test(5); + Object ret = comp.invoke("p.xyz.XYZ9", "test", new Object[] { 5 }); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i); + if (i != 5 * 1024) { + throw new RuntimeException("wrong value: " + i); + } + System.out.println("Success."); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJavaExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJavaExample.java new file mode 100644 index 0000000000000..e493ebab4e8c8 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/MultiFileJavaExample.java @@ -0,0 +1,81 @@ +/* + * 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 + * @summary Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.MultiFileJavaExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; +import java.io.StringWriter; +import java.io.PrintWriter; + +/** + * This test shows a compilation of multiple java source code files. + */ +public class MultiFileJavaExample { + + // Generate a source java file as String + public static String generate(int i) { + StringWriter writer = new StringWriter(); + PrintWriter out = new PrintWriter(writer); + out.println("package p.xyz;"); + out.println(""); + out.println("public class XYZ" + i + " {"); + if (i > 0) { + out.println(" public XYZ" + (i - 1) + " xyz = new XYZ" + (i - 1) + "();"); + } + out.println(""); + out.println(" public static Object test() {"); + out.println(" return new XYZ" + i + "();"); + out.println(" }"); + out.println("}"); + return writer.toString(); + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Generate 10 files. + for (int i = 0; i < 10; i++) { + comp.addJavaSourceCode("p.xyz.XYZ" + i, generate(i)); + } + + // Compile the source files. + comp.compile(); + + // Object ret = XYZ9.test(); + Object ret = comp.invoke("p.xyz.XYZ9", "test", new Object[] {}); + + if (!ret.getClass().getSimpleName().equals("XYZ9")) { + throw new RuntimeException("wrong result:" + ret); + } + System.out.println("Success."); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java new file mode 100644 index 0000000000000..a67e6e0eb4937 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/RunWithFlagsExample.java @@ -0,0 +1,99 @@ +/* + * 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 + * @summary Example test to use the Compile Framework and run the compiled code with additional flags + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.RunWithFlagsExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * This test shows how the generated code can be compiled and invoked in a new VM. This allows + * the execution of the code with additional VM flags and options. + *

          + * The new VM must be able to locate the class files of the newly compiled code. For this we + * set the class path using {@link CompileFramework#getEscapedClassPathOfCompiledClasses}. + */ +public class RunWithFlagsExample { + + private static String generate() { + return """ + package p.xyz; + + public class X { + public static void main(String args[]) { + System.out.println("Hello world!"); + System.out.println(System.getProperty("MyMessage", "fail")); + System.err.println(args[0]); + } + } + """; + } + + public static void main(String[] args) throws Exception { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a Java source file. + comp.addJavaSourceCode("p.xyz.X", generate()); + + // Compile the source file. + comp.compile(); + + // Build command line. + String[] command = { + // Set the classpath to include our newly compiled class. + "-classpath", + comp.getEscapedClassPathOfCompiledClasses(), + // Pass additional flags here. + // "-Xbatch" is a harmless VM flag, so this example runs everywhere without issues. + "-Xbatch", + // We can also pass properties like "MyMessage". + "-DMyMessage=hello_world", + "p.xyz.X", + "hello_arg" + }; + + // Execute the command, and capture the output. + // The JTREG Java and VM options are automatically passed to the test VM. + OutputAnalyzer analyzer = ProcessTools.executeTestJava(command); + + // Verify output. + analyzer.shouldHaveExitValue(0); + analyzer.stdoutContains("Hello world!"); + analyzer.stdoutContains("hello_world"); + analyzer.stdoutContains("hello_arg"); + + // Print output to stderr. + analyzer.reportDiagnosticSummary(); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJasmExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJasmExample.java new file mode 100644 index 0000000000000..e01b45e744175 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJasmExample.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 + * @summary Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.SimpleJasmExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +/** + * This test shows a simple compilation of java source code, and its invocation. + */ +public class SimpleJasmExample { + + // Generate a source jasm file as String + public static String generate() { + return """ + super public class XYZ { + public static Method test:"(I)I" + stack 20 locals 20 + { + iload_0; + iconst_2; + imul; + ireturn; + } + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + String src = generate(); + comp.addJasmSourceCode("XYZ", src); + + // Compile the source file. + comp.compile(); + + // Object ret = XYZ.test(5); + Object ret = comp.invoke("XYZ", "test", new Object[] {5}); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i); + if (i != 10) { + throw new RuntimeException("wrong value: " + i); + } + System.out.println("Success."); + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJavaExample.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJavaExample.java new file mode 100644 index 0000000000000..5e54a6e8a08a6 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/examples/SimpleJavaExample.java @@ -0,0 +1,73 @@ +/* + * 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 + * @summary Example test to use the Compile Framework. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.examples.SimpleJavaExample + */ + +package compile_framework.examples; + +import compiler.lib.compile_framework.*; + +/** + * This test shows a simple compilation of java source code, and its invocation. + */ +public class SimpleJavaExample { + + // Generate a source java file as String + public static String generate() { + return """ + public class XYZ { + public static int test(int i) { + System.out.println("Hello from XYZ.test: " + i); + return i * 2; + } + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("XYZ", generate()); + + // Compile the source file. + comp.compile(); + + // Object ret = XYZ.test(5); + Object ret = comp.invoke("XYZ", "test", new Object[] {5}); + + // Extract return value of invocation, verify its value. + int i = (int)ret; + System.out.println("Result of call: " + i); + if (i != 10) { + throw new RuntimeException("wrong value: " + i); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJasmCompilation.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJasmCompilation.java new file mode 100644 index 0000000000000..b5b6f3e104095 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJasmCompilation.java @@ -0,0 +1,62 @@ +/* + * 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 + * @summary Example test with failing jasm compilation. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.tests.TestBadJasmCompilation + */ + +package compile_framework.tests; + +import compiler.lib.compile_framework.*; + +public class TestBadJasmCompilation { + + // Generate a source jasm file as String + public static String generate() { + return """ + super public class XYZ { + some bad code + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJasmSourceCode("XYZ", generate()); + + try { + // Compile the source file. + comp.compile(); + throw new RuntimeException("Expected compilation to fail."); + } catch (CompileFrameworkException e) { + System.out.println("Success, expected compilation to fail."); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJavaCompilation.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJavaCompilation.java new file mode 100644 index 0000000000000..1cb1d79afbcca --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestBadJavaCompilation.java @@ -0,0 +1,62 @@ +/* + * 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 + * @summary Example test with failing java compilation. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.tests.TestBadJavaCompilation + */ + +package compile_framework.tests; + +import compiler.lib.compile_framework.*; + +public class TestBadJavaCompilation { + + // Generate a source java file as String + public static String generate() { + return """ + public class XYZ { + some bad code + } + """; + } + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("XYZ", generate()); + + try { + // Compile the source file. + comp.compile(); + throw new RuntimeException("Expected compilation to fail."); + } catch (CompileFrameworkException e) { + System.out.println("Success, expected compilation to fail."); + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestConcurrentCompilation.java b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestConcurrentCompilation.java new file mode 100644 index 0000000000000..1cb902d34e4aa --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/compile_framework/tests/TestConcurrentCompilation.java @@ -0,0 +1,108 @@ +/* + * 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 + * @summary Example test with multi-threaded use of the CompileFramework. + * Tests that the source and class directories are set up correctly. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver compile_framework.tests.TestConcurrentCompilation + */ + +package compile_framework.tests; + +import compiler.lib.compile_framework.*; + +import java.util.ArrayList; +import java.util.List; + +public class TestConcurrentCompilation { + + // Generate a source java file as String + public static String generate(int i) { + return String.format(""" + public class XYZ { + public static int test() { + return %d; + } + } + """, i); + } + + public static void test(int i) { + System.out.println("Generate and compile XYZ for " + i); + CompileFramework comp = new CompileFramework(); + comp.addJavaSourceCode("XYZ", generate(i)); + comp.compile(); + + // Now, sleep to give the other threads time to compile and store their class-files. + System.out.println("Sleep for " + i); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + System.out.println("Sleep interrupted for " + i); + } + + // Now, hopefully all threads have compiled and stored their class-files. + // We can check if we get the expected result, i.e. the class-file from the current thread. + System.out.println("Run XYZ.test for " + i); + int j = (int)comp.invoke("XYZ", "test", new Object[] {}); + if (i != j) { + System.out.println("Wrong value: " + i + " vs " + j); + throw new RuntimeException("Wrong value: " + i + " vs " + j); + } + System.out.println("Success for " + i); + } + + public static class MyRunnable implements Runnable { + private int i; + + public MyRunnable(int i) { + this.i = i; + } + + public void run() { + TestConcurrentCompilation.test(i); + } + } + + public static void main(String[] args) { + System.out.println("Generating threads:"); + List threads = new ArrayList(); + for (int i = 0; i < 3; i++) { + Thread thread = new Thread(new MyRunnable(i)); + thread.start(); + threads.add(thread); + } + System.out.println("Waiting to join threads:"); + try { + for (Thread thread : threads) { + thread.join(); + } + } catch (InterruptedException e) { + throw new RuntimeException("interrupted", e); + } + System.out.println("Success."); + } +} diff --git a/test/hotspot/jtreg/vmTestbase/jit/escape/LockElision/MatMul/MatMul.java b/test/hotspot/jtreg/vmTestbase/jit/escape/LockElision/MatMul/MatMul.java index cf20b0df8a073..8d1a5fac9aefa 100644 --- a/test/hotspot/jtreg/vmTestbase/jit/escape/LockElision/MatMul/MatMul.java +++ b/test/hotspot/jtreg/vmTestbase/jit/escape/LockElision/MatMul/MatMul.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020, 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 @@ -85,7 +85,7 @@ public static void main(String[] args) { } public int run() { - log = new Log(System.out, verbose); + log = new Log(System.out); log.display("Parallel matrix multiplication test"); Matrix a = Matrix.randomMatrix(dim); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/LaunchingConnector/launchnosuspend/launchnosuspend001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/LaunchingConnector/launchnosuspend/launchnosuspend001.java index 015643f3a1892..142d6387610f6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/LaunchingConnector/launchnosuspend/launchnosuspend001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/LaunchingConnector/launchnosuspend/launchnosuspend001.java @@ -73,7 +73,6 @@ private launchnosuspend001 (String args[], PrintStream out) { argHandler = new ArgumentHandler(args); log = new Log(this.out, argHandler); - //log.enableVerbose(true); } private PrintStream out; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/IterateThroughHeap/filter_tagged/HeapFilter.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/IterateThroughHeap/filter_tagged/HeapFilter.java index 91f315ee7a968..b690955307ef7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/IterateThroughHeap/filter_tagged/HeapFilter.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/IterateThroughHeap/filter_tagged/HeapFilter.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 @@ -49,7 +49,6 @@ public int runTest(String args[], PrintStream out) { log = new Log(out, argHandler); testObjects = new Object[]{new TaggedClass(), new UntaggedClass()}; - log.enableVerbose(true); log.display("Verifying reachable objects."); status = checkStatus(status); testObjects = null; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isCollectionUsageThresholdExceeded/isexceeded001.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isCollectionUsageThresholdExceeded/isexceeded001.java index 702a5a793791a..a9e75bb7cecee 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isCollectionUsageThresholdExceeded/isexceeded001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isCollectionUsageThresholdExceeded/isexceeded001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, 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 @@ -40,7 +40,6 @@ public static void main(String[] argv) { public static int run(String[] argv, PrintStream out) { ArgumentHandler argHandler = new ArgumentHandler(argv); Log log = new Log(out, argHandler); - log.enableVerbose(true); monitor = Monitor.getMemoryMonitor(log, argHandler); List pools = monitor.getMemoryPoolMBeans(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isUsageThresholdExceeded/isexceeded001.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isUsageThresholdExceeded/isexceeded001.java index 390bfdd625188..a684c03e67a38 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isUsageThresholdExceeded/isexceeded001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/MemoryPoolMBean/isUsageThresholdExceeded/isexceeded001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2022, 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 @@ -47,7 +47,6 @@ public static void main(String[] argv) { public static int run(String[] argv, PrintStream out) { ArgumentHandler argHandler = new ArgumentHandler(argv); Log log = new Log(out, argHandler); - log.enableVerbose(true); // show log output MemoryMonitor monitor = Monitor.getMemoryMonitor(log, argHandler); List pools = monitor.getMemoryPoolMBeans(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/lowmem/lowmem001.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/lowmem/lowmem001.java index d698e5aec837f..67f0d17f56109 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/lowmem/lowmem001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/lowmem/lowmem001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, 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 @@ -58,7 +58,7 @@ public static void main(String[] args) { @Override public void run() { - Log log = new Log(System.out, true); + Log log = new Log(System.out); // System.err is duplicated into buffer // it should be empty MyStream stream = new MyStream(System.err); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java index e575018249533..99467fc033412 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java @@ -29,22 +29,15 @@ import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringReader; -import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.HashSet; import java.util.Vector; -import nsk.share.test.LazyFormatString; /** - * This class helps to print test-execution trace messages - * and filter them when execution mode is not verbose. - *

          - * Verbose mode if defined by providing -verbose command line - * option, handled by ArgumentParser. Use verbose() - * method to determine which mode is used. + * This class helps to print test-execution trace messages. *

          * Log provides with two main methods to print messages: *

            @@ -60,7 +53,6 @@ * To provide printing messages from different sources into one log * with distinct prefixes use internal Log.Logger class. * - * @see #verbose() * @see #complain(String) * @see #display(String) * @see ArgumentParser @@ -72,18 +64,6 @@ public class Log { */ private PrintStream out = null; - /** - * Is log-mode verbose? - * Always enabled. - */ - private final boolean verbose = true; - - /** - * Should log messages prefixed with timestamps? - * Always enabled. - */ - private final boolean timestamp = true; - /** * Names for trace levels */ @@ -188,41 +168,15 @@ public Log(PrintStream stream) { /** * Incarnate new Log for the given stream; and - * either for verbose or for non-verbose mode accordingly to - * the given verbose key. - */ - public Log(PrintStream stream, boolean verbose) { - this(stream); - } - - /** - * Incarnate new Log for the given stream; and - * either for verbose or for non-verbose mode accordingly to * the given argsHandler. */ public Log(PrintStream stream, ArgumentParser argsParser) { - this(stream, argsParser.verbose()); + this(stream); traceLevel = argsParser.getTraceLevel(); } ///////////////////////////////////////////////////////////////// - /** - * Return true if log mode is verbose. - */ - public boolean verbose() { - return verbose; - } - - /** - * Enable or disable verbose mode for printing messages. - */ - public void enableVerbose(boolean enable) { - if (!enable) { - throw new RuntimeException("The non-verbose logging is not supported."); - } - } - public int getTraceLevel() { return traceLevel; } @@ -266,9 +220,6 @@ public static String printExceptionToString(Object prefix, Throwable exception) @Deprecated public synchronized void println(String message) { doPrint(message); - if (!verbose()) { - keepLog(composeLine(message)); - } } /** @@ -282,9 +233,6 @@ public synchronized void println(String message) { */ @Deprecated public synchronized void comment(String message) { - if (!verbose()) { - doPrint(message); - } } /** @@ -314,17 +262,10 @@ public void trace(int level, Object message, Throwable exception) { } /** - * Print message to the assigned output stream, - * if log mode is verbose. The message will be lost, - * if execution mode is non-verbose, and there is no error messages - * printed. + * Print message to the assigned output stream. */ public synchronized void display(Object message) { - if (verbose()) { - doPrint(message.toString()); - } else { - keepLog(composeLine(message.toString())); - } + doPrint(message.toString()); } /** @@ -333,15 +274,6 @@ public synchronized void display(Object message) { * into errorsBuffer. */ public synchronized void complain(Object message) { - if (!verbose()) { - PrintStream stream = findOutStream(); - stream.println("#> "); - stream.println("#> WARNING: switching log to verbose mode,"); - stream.println("#> because error is complained"); - stream.println("#> "); - stream.flush(); - enableVerbose(true); - } String msgStr = message.toString(); printError(msgStr); @@ -406,10 +338,7 @@ private void logExceptionForFailureAnalysis(String msg) { ///////////////////////////////////////////////////////////////// /** - * Redirect log to the given stream, and switch - * log mode to verbose. - * Prints errors summary to current stream, cancel current stream - * and switches to new stream. Turns on verbose mode for new stream. + * Redirect log to the given stream. * * @deprecated This method is obsolete. */ @@ -430,20 +359,6 @@ public synchronized void clearLogBuffer() { logBuffer.clear(); } - /** - * Print all messages from log buffer which were hidden because - * of non-verbose mode, - */ - private synchronized void flushLogBuffer() { - if (!logBuffer.isEmpty()) { - PrintStream stream = findOutStream(); - for (int i = 0; i < logBuffer.size(); i++) { - stream.println(logBuffer.elementAt(i)); - } - stream.flush(); - } - } - /** * Return out stream if defined or Sytem.err otherwise; * print a warning message when System.err is used first time. @@ -468,18 +383,15 @@ private synchronized PrintStream findOutStream() { * Compose line to print possible prefixing it with timestamp. */ private String composeLine(String message) { - if (timestamp) { - long time = System.currentTimeMillis(); - long ms = time % 1000; - time /= 1000; - long secs = time % 60; - time /= 60; - long mins = time % 60; - time /= 60; - long hours = time % 24; - return "[" + hours + ":" + mins + ":" + secs + "." + ms + "] " + message; - } - return message; + long time = System.currentTimeMillis(); + long ms = time % 1000; + time /= 1000; + long secs = time % 60; + time /= 60; + long mins = time % 60; + time /= 60; + long hours = time % 24; + return "[" + hours + ":" + mins + ":" + secs + "." + ms + "] " + message; } /** @@ -513,13 +425,6 @@ private synchronized void printError(String message) { } } - /** - * Keep the given log message into logBuffer. - */ - private synchronized void keepLog(String message) { - logBuffer.addElement(message); - } - /** * This class can be used as a base for each class that use Log * for print messages and errors. diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.java b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.java index bde40cc7e2021..9899c762a7a90 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, 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 @@ -68,7 +68,7 @@ public class AODTestRunner { protected AODRunnerArgParser argParser; protected AODTestRunner(String[] args) { - log = new Log(System.out, true); + log = new Log(System.out); argParser = createArgParser(args); } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AbstractJarAgent.java b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AbstractJarAgent.java index 3fa3141a8cd9e..fb02327a42224 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AbstractJarAgent.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AbstractJarAgent.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 @@ -157,7 +157,7 @@ private void defaultInit(String[] args) { if (name == null) throw new TestBug("Agent name wasn't specified"); - log = new Log(System.out, true); + log = new Log(System.out); } /* diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/DummyTargetApplication.java b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/DummyTargetApplication.java index bb726453e9bd3..c8fb3bf266324 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/DummyTargetApplication.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/DummyTargetApplication.java @@ -39,7 +39,7 @@ */ public class DummyTargetApplication { - protected Log log = new Log(System.out, true); + protected Log log = new Log(System.out); protected AODTargetArgParser argParser; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/TargetApplicationWaitingAgents.java b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/TargetApplicationWaitingAgents.java index 6f80033dff504..0b5acfb24ba00 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/TargetApplicationWaitingAgents.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/TargetApplicationWaitingAgents.java @@ -213,7 +213,7 @@ private void initTargetApplication(String[] args) { if (targetApplicationInitialized) throw new TestBug("TargetApplication already initialized"); - log = new Log(System.out, true); + log = new Log(System.out); argParser = createArgParser(args); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Memory.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Memory.java index f5c70671e5e10..244bcf423c5bc 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Memory.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Memory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2018, 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 @@ -150,12 +150,9 @@ public static int getReferenceObjectSize() { * @return length of array */ public static int getArrayLength(long memory, long objectSize) { - int referenceSize = getReferenceSize(); int arrayExtraSize = getArrayExtraSize(); - return (int) Math.min( - (memory - arrayExtraSize) / (objectSize + referenceSize), - Integer.MAX_VALUE - ); + return (int) Math.min((memory - arrayExtraSize) / objectSize, + Integer.MAX_VALUE); } /** @@ -166,7 +163,7 @@ public static int getArrayLength(long memory, long objectSize) { * @return size of array */ public static long getArraySize(int length, long objectSize) { - return getObjectExtraSize() + length * (objectSize + getReferenceSize()); + return getArrayExtraSize() + length * objectSize; } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/JVMTITest.java b/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/JVMTITest.java index c8d26b6ea1f5e..d9b847e110dc0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/JVMTITest.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/jvmti/JVMTITest.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 @@ -87,7 +87,7 @@ static void attachAgent(String[] args) { AgentsAttacher attacher = new AgentsAttacher(Utils.findCurrentVMIdUsingJPS(jdkPath), agents, - new Log(System.out, true)); + new Log(System.out)); attacher.attachAgents(); } } diff --git a/test/hotspot/jtreg/vmTestbase/vm/compiler/coverage/parentheses/Parentheses.java b/test/hotspot/jtreg/vmTestbase/vm/compiler/coverage/parentheses/Parentheses.java index 66546e9b13f15..219af51045a23 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/compiler/coverage/parentheses/Parentheses.java +++ b/test/hotspot/jtreg/vmTestbase/vm/compiler/coverage/parentheses/Parentheses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, 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 @@ -69,7 +69,7 @@ public static void main(String[] args) throws Exception { public void run() throws IOException, ReflectiveOperationException { - log = new Log(System.out, verbose); + log = new Log(System.out); InstructionSequence instructionSequence = null; for (int i = 0; i < iterations * stressOptions.getIterationsFactor(); i++) { diff --git a/test/jdk/ProblemList-Xcomp.txt b/test/jdk/ProblemList-Xcomp.txt index 2fc09ee4df4a9..8963ead2bce8b 100644 --- a/test/jdk/ProblemList-Xcomp.txt +++ b/test/jdk/ProblemList-Xcomp.txt @@ -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 @@ -28,5 +28,5 @@ ############################################################################# java/lang/invoke/MethodHandles/CatchExceptionTest.java 8146623 generic-all -java/lang/management/MemoryMXBean/CollectionUsageThreshold.java 8318668 generic-all +java/foreign/TestUpcallStress.java 8341584 generic-all com/sun/jdi/InterruptHangTest.java 8043571 generic-all diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index e8344ae6dd393..942e15002bdcb 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -121,11 +121,17 @@ java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java 6848406 gen java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java 6848407 generic-all java/awt/Frame/MaximizedUndecorated/MaximizedUndecorated.java 8022302 generic-all java/awt/Frame/RestoreToOppositeScreen/RestoreToOppositeScreen.java 8286840 linux-all +java/awt/Frame/InitialIconifiedTest.java 7144049,8203920 macosx-all,linux-all +java/awt/Frame/ShapeNotSetSometimes/ShapeNotSetSometimes.java 8341370 macosx-all +java/awt/Frame/FocusTest.java 8341480 macosx-all java/awt/FileDialog/FileDialogIconTest/FileDialogIconTest.java 8160558 windows-all java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion.java 8060176 windows-all,macosx-all java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_1.java 8060176 windows-all,macosx-all java/awt/dnd/URIListBetweenJVMsTest/URIListBetweenJVMsTest.java 8171510 macosx-all java/awt/dnd/MissingDragExitEventTest/MissingDragExitEventTest.java 8288839 windows-x64 +java/awt/dnd/DragExitBeforeDropTest.java 8242805 macosx-all +java/awt/dnd/DragThresholdTest.java 8076299 macosx-all +java/awt/dnd/CustomDragCursorTest.java 8242805 macosx-all java/awt/Focus/ChoiceFocus/ChoiceFocus.java 8169103 windows-all,macosx-all java/awt/Focus/ClearLwQueueBreakTest/ClearLwQueueBreakTest.java 8198618 macosx-all java/awt/Focus/ConsumeNextKeyTypedOnModalShowTest/ConsumeNextKeyTypedOnModalShowTest.java 6986252 macosx-all @@ -136,7 +142,9 @@ java/awt/Focus/WrongKeyTypedConsumedTest/WrongKeyTypedConsumedTest.java 8169096 java/awt/Focus/TestDisabledAutoTransfer.java 8159871 macosx-all,windows-all java/awt/Focus/TestDisabledAutoTransferSwing.java 6962362 windows-all java/awt/Focus/ActivateOnProperAppContextTest.java 8136516 macosx-all +java/awt/Focus/FocusPolicyTest.java 7160904 linux-all java/awt/EventQueue/6980209/bug6980209.java 8198615 macosx-all +java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java 8024034 generic-all java/awt/grab/EmbeddedFrameTest1/EmbeddedFrameTest1.java 7080150 macosx-all java/awt/event/InputEvent/EventWhenTest/EventWhenTest.java 8168646 generic-all java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java 8049405 macosx-all @@ -193,6 +201,9 @@ java/awt/event/KeyEvent/ExtendedKeyCode/ExtendedKeyCodeTest.java 8169476 windows java/awt/event/KeyEvent/KeyChar/KeyCharTest.java 8169474,8224055 macosx-all,windows-all java/awt/event/KeyEvent/KeyTyped/CtrlASCII.java 8298910 linux-all +java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java 8242805 macosx-all +java/awt/dnd/DnDCursorCrashTest.java 8242805 macosx-all +java/awt/dnd/DnDClipboardDeadlockTest.java 8079553 linux-all java/awt/dnd/URIListToFileListBetweenJVMsTest/URIListToFileListBetweenJVMsTest.java 8194947 generic-all java/awt/Frame/FramesGC/FramesGC.java 8079069 macosx-all java/awt/TrayIcon/ActionCommand/ActionCommand.java 8150540 windows-all @@ -207,6 +218,9 @@ java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java 8150540,8295300 windows java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java 8150540 windows-all java/awt/TrayIcon/TrayIconPopup/TrayIconPopupClickTest.java 8150540 windows-all,macosx-all java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java 8150540 windows-all +java/awt/TrayIcon/MouseMoveTest.java 8203053 linux-all +java/awt/TrayIcon/TrayIconKeySelectTest.java 8341557 windows-all +java/awt/TrayIcon/TrayIconTest.java 8341559 generic-all java/awt/Window/ShapedAndTranslucentWindows/SetShapeAndClick.java 8197936 macosx-all java/awt/Window/ShapedAndTranslucentWindows/SetShapeDynamicallyAndClick.java 8013450 macosx-all @@ -430,6 +444,7 @@ java/awt/SplashScreen/MultiResolutionSplash/unix/UnixMultiResolutionSplashTest.j java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java 7107528 linux-all,macosx-all java/awt/Mouse/MouseDragEvent/MouseDraggedTest.java 8080676 linux-all java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersInKeyEvent.java 8157147 linux-all,windows-all,macosx-all +java/awt/Mouse/MouseClickCount.java 8017182 macosx-all java/awt/Toolkit/ToolkitPropertyTest/ToolkitPropertyTest_Enable.java 6847163 linux-all java/awt/xembed/server/RunTestXEmbed.java 7034201 linux-all java/awt/Modal/ModalFocusTransferTests/FocusTransferDialogsDocModalTest.java 8164473 linux-all @@ -463,7 +478,13 @@ java/awt/KeyboardFocusmanager/ConsumeNextMnemonicKeyTypedTest/ConsumeNextMnemoni java/awt/Window/GetScreenLocation/GetScreenLocationTest.java 8225787 linux-x64 java/awt/Dialog/MakeWindowAlwaysOnTop/MakeWindowAlwaysOnTop.java 8266243 macosx-aarch64 +java/awt/Dialog/ChoiceModalDialogTest.java 8161475 macosx-all +java/awt/Dialog/FileDialogUserFilterTest.java 8001142 generic-all + java/awt/dnd/BadSerializationTest/BadSerializationTest.java 8277817 linux-x64,windows-x64 +java/awt/dnd/DragSourceMotionListenerTest.java 8225131 windows-all +java/awt/dnd/RejectDragTest.java 7124259 macosx-all +java/awt/dnd/DnDHTMLToOutlookTest/DnDHTMLToOutlookTest.java 8027424 generic-all java/awt/GraphicsDevice/DisplayModes/UnknownRefrshRateTest.java 8286436 macosx-aarch64 java/awt/image/multiresolution/MultiresolutionIconTest.java 8291979 linux-x64,windows-all java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java 8305061 macosx-x64 @@ -515,6 +536,8 @@ java/io/pathNames/GeneralWin32.java 8180264 windows- com/sun/management/OperatingSystemMXBean/GetProcessCpuLoad.java 8030957 aix-all com/sun/management/OperatingSystemMXBean/GetSystemCpuLoad.java 8030957 aix-all +com/sun/management/DiagnosticCommandMBean/DcmdMBeanPermissionsTest.java 8340401 windows-all + java/lang/management/MemoryMXBean/Pending.java 8158837 generic-all java/lang/management/MemoryMXBean/PendingAllGC.sh 8158837 generic-all @@ -560,14 +583,19 @@ java/net/Socket/asyncClose/Race.java 8317801 aix-ppc6 # jdk_nio +java/nio/Buffer/LimitDirectMemory.java 8340728 generic-all + java/nio/channels/AsynchronousSocketChannel/StressLoopback.java 8211851 aix-ppc64 java/nio/channels/Channels/SocketChannelStreams.java 8317838 aix-ppc64 -java/nio/channels/DatagramChannel/AdaptorMulticasting.java 8308807 aix-ppc64 +java/nio/channels/DatagramChannel/AdaptorMulticasting.java 8308807,8144003 aix-ppc64,macosx-all java/nio/channels/DatagramChannel/AfterDisconnect.java 8308807 aix-ppc64 java/nio/channels/DatagramChannel/ManySourcesAndTargets.java 8264385 macosx-aarch64 java/nio/channels/DatagramChannel/Unref.java 8233437 generic-all +java/nio/channels/DatagramChannel/BasicMulticastTests.java 8144003 macosx-all +java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java 8144003 macosx-all +java/nio/channels/DatagramChannel/Promiscuous.java 8144003 macosx-all ############################################################################ @@ -657,7 +685,6 @@ javax/swing/AbstractButton/6711682/bug6711682.java 8060765 windows-all,macosx-al javax/swing/JFileChooser/6396844/TwentyThousandTest.java 8198003 generic-all javax/swing/JFileChooser/8194044/FileSystemRootTest.java 8327236 windows-all javax/swing/JPopupMenu/6800513/bug6800513.java 7184956 macosx-all -javax/swing/JTabbedPane/8007563/Test8007563.java 8051591 generic-all javax/swing/JTabbedPane/4624207/bug4624207.java 8064922 macosx-all javax/swing/SwingUtilities/TestBadBreak/TestBadBreak.java 8160720 generic-all javax/swing/JFileChooser/6798062/bug6798062.java 8146446 windows-all @@ -714,6 +741,7 @@ com/sun/jdi/InvokeHangTest.java 8218463 linux-al ############################################################################ # jdk_util +java/util/zip/CloseInflaterDeflaterTest.java 8339216 linux-s390x ############################################################################ @@ -786,3 +814,10 @@ java/awt/FullScreen/TranslucentWindow/TranslucentWindow.java 8258103 linux-all java/awt/Focus/FrameMinimizeTest/FrameMinimizeTest.java 8016266 linux-x64 java/awt/Frame/SizeMinimizedTest.java 8305915 linux-x64 java/awt/PopupMenu/PopupHangTest.java 8340022 windows-all +java/awt/Focus/MinimizeNonfocusableWindowTest.java 8024487 windows-all +java/awt/Focus/InactiveFocusRace.java 8023263 linux-all +java/awt/List/HandlingKeyEventIfMousePressedTest.java 6848358 macosx-all,windows-all +java/awt/Checkbox/CheckboxBoxSizeTest.java 8340870 windows-all +java/awt/Checkbox/CheckboxIndicatorSizeTest.java 8340870 windows-all +java/awt/Checkbox/CheckboxNullLabelTest.java 8340870 windows-all +java/awt/dnd/WinMoveFileToShellTest.java 8341665 windows-all diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index 6198d33214293..c8db6b89a71c7 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -100,7 +100,7 @@ requires.properties= \ vm.jvmci.enabled \ vm.jvmti \ vm.cpu.features \ - docker.support \ + container.support \ systemd.support \ release.implementor \ jdk.containerized \ diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 0c6b13fdca057..e7ee8990f94fb 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -1,4 +1,4 @@ -# 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 @@ -91,7 +91,8 @@ tier3 = \ :jdk_svc \ -:jdk_svc_sanity \ -:svc_tools \ - :jdk_jpackage + :jdk_jpackage \ + :jdk_since_checks # Everything not in other tiers tier4 = \ @@ -381,7 +382,8 @@ jdk_svc = \ jdk_foreign = \ java/foreign \ jdk/internal/reflect/CallerSensitive/CheckCSMs.java \ - -java/foreign/TestMatrix.java + -java/foreign/TestMatrix.java \ + -java/foreign/TestUpcallStress.java jdk_vector = \ jdk/incubator/vector @@ -665,3 +667,7 @@ jdk_containers_extended = \ jdk_core_no_security = \ :jdk_core \ -:jdk_security + +# Set of tests for `@since` checks in source code documentation +jdk_since_checks = \ + tools/sincechecker/modules/java_base/CheckSince_javaBase.java diff --git a/test/jdk/build/AbsPathsInImage.java b/test/jdk/build/AbsPathsInImage.java index 712f990507d19..229094a0920e3 100644 --- a/test/jdk/build/AbsPathsInImage.java +++ b/test/jdk/build/AbsPathsInImage.java @@ -40,7 +40,7 @@ * @bug 8226346 * @summary Check all output files for absolute path fragments * @requires !vm.debug - * @run main AbsPathsInImage + * @run main/othervm -Xmx900m AbsPathsInImage */ public class AbsPathsInImage { diff --git a/test/jdk/com/sun/jdi/EATests.java b/test/jdk/com/sun/jdi/EATests.java index adeb21741438f..b2a2cad49db7f 100644 --- a/test/jdk/com/sun/jdi/EATests.java +++ b/test/jdk/com/sun/jdi/EATests.java @@ -158,6 +158,19 @@ * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks * -XX:LockingMode=0 * -XX:GuaranteedAsyncDeflationInterval=1000 + * + * @bug 8341819 + * @comment Regression test for re-locking racing with deflation with LM_LIGHTWEIGHT. + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=2 + * -XX:GuaranteedAsyncDeflationInterval=1 */ /** diff --git a/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java b/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java index 90a2a7b981565..e03923b6c3442 100644 --- a/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java +++ b/test/jdk/com/sun/jndi/dns/ConfigTests/Timeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2022, 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 @@ -29,7 +29,6 @@ import java.net.InetSocketAddress; import java.net.SocketTimeoutException; import java.time.Duration; -import java.time.Instant; import jdk.test.lib.net.URIBuilder; @@ -40,7 +39,7 @@ * number of retries. * @library ../lib/ /test/lib * @modules java.base/sun.security.util - * @run main Timeout + * @run main/othervm Timeout */ public class Timeout extends DNSTestBase { @@ -48,8 +47,12 @@ public class Timeout extends DNSTestBase { private static final int TIMEOUT = 250; // try 5 times per server private static final int RETRIES = 5; + // DnsClient retries again with increased timeout if left + // timeout is less than this value, and max retry attempts + // is not reached + private static final int DNS_CLIENT_MIN_TIMEOUT = 0; - private Instant startTime; + private long startTime; public Timeout() { setLocalServer(false); @@ -81,7 +84,7 @@ public void runTest() throws Exception { setContext(new InitialDirContext(env())); // Any request should fail after timeouts have expired. - startTime = Instant.now(); + startTime = System.nanoTime(); context().getAttributes(""); throw new RuntimeException( @@ -92,28 +95,35 @@ public void runTest() throws Exception { @Override public boolean handleException(Exception e) { if (e instanceof CommunicationException) { - Duration elapsedTime = Duration.between(startTime, Instant.now()); + Duration elapsedTime = Duration.ofNanos(System.nanoTime() - startTime); if (!(((CommunicationException) e) .getRootCause() instanceof SocketTimeoutException)) { return false; } - Duration expectedTime = Duration.ofMillis(TIMEOUT) - .multipliedBy((1 << RETRIES) - 1); + Duration minAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + .minus(Duration.ofMillis(DNS_CLIENT_MIN_TIMEOUT * RETRIES)); + Duration maxAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + // max allowed timeout value is set to 2 * expected timeout + .multipliedBy(2); + DNSTestUtils.debug("Elapsed (ms): " + elapsedTime.toMillis()); - DNSTestUtils.debug("Expected (ms): " + expectedTime.toMillis()); + String expectedRangeMsg = "%s - %s" + .formatted(minAllowedTime.toMillis(), maxAllowedTime.toMillis()); + DNSTestUtils.debug("Expected range (ms): " + expectedRangeMsg); // Check that elapsed time is as long as expected, and - // not more than 50% greater. - if (elapsedTime.compareTo(expectedTime) >= 0 && - elapsedTime.multipliedBy(2) - .compareTo(expectedTime.multipliedBy(3)) <= 0) { + // not more than 2 times greater. + if (elapsedTime.compareTo(minAllowedTime) >= 0 && + elapsedTime.compareTo(maxAllowedTime) <= 0) { System.out.println("elapsed time is as long as expected."); return true; } throw new RuntimeException( - "Failed: timeout in " + elapsedTime.toMillis() - + " ms, expected" + expectedTime.toMillis() + "ms"); + "Failed: timeout in " + elapsedTime.toMillis() + + " ms, expected to be in a range (ms): " + expectedRangeMsg); } return super.handleException(e); diff --git a/test/jdk/com/sun/jndi/dns/ConfigTests/TimeoutWithEmptyDatagrams.java b/test/jdk/com/sun/jndi/dns/ConfigTests/TimeoutWithEmptyDatagrams.java new file mode 100644 index 0000000000000..9fb8954c99c54 --- /dev/null +++ b/test/jdk/com/sun/jndi/dns/ConfigTests/TimeoutWithEmptyDatagrams.java @@ -0,0 +1,196 @@ +/* + * 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 jdk.test.lib.net.URIBuilder; + +import javax.naming.CommunicationException; +import javax.naming.Context; +import javax.naming.directory.InitialDirContext; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/* + * @test + * @bug 8339538 + * @summary Tests that DnsClient correctly calculates left timeout in + * presence of empty datagram packets. + * @library ../lib /test/lib + * @modules java.base/sun.security.util + * @run main/othervm TimeoutWithEmptyDatagrams + */ + +public class TimeoutWithEmptyDatagrams extends DNSTestBase { + // initial timeout = 1/4 sec + private static final int TIMEOUT = 250; + // try 5 times per server + private static final int RETRIES = 5; + // DnsClient retries again with increased timeout if left + // timeout is less than this value, and max retry attempts + // is not reached + private static final int DNS_CLIENT_MIN_TIMEOUT = 0; + + public TimeoutWithEmptyDatagrams() { + setLocalServer(false); + } + + public static void main(String[] args) throws Exception { + new TimeoutWithEmptyDatagrams().run(args); + } + + /* + * Tests that we can set the initial UDP timeout interval and the + * number of retries. + */ + @Override + public void runTest() throws Exception { + // Create a DatagramSocket and bind it to the loopback address to simulate + // UDP DNS server that doesn't respond + try (DatagramSocket ds = new DatagramSocket(new InetSocketAddress( + InetAddress.getLoopbackAddress(), 0))) { + CountDownLatch gotClientAddress = new CountDownLatch(1); + AtomicReference clientAddress = new AtomicReference<>(); + AtomicBoolean stopTestThreads = new AtomicBoolean(); + + String allQuietUrl = URIBuilder.newBuilder() + .scheme("dns") + .loopback() + .port(ds.getLocalPort()) + .build() + .toString(); + + // Run a virtual thread that receives client request packets and extracts + // sender address from them. + Thread receiverThread = Thread.ofVirtual().start(() -> { + while (!stopTestThreads.get()) { + try { + DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); + ds.receive(packet); + System.err.println("Got packet from " + packet.getSocketAddress()); + boolean hasClientAddress = clientAddress.get() != null; + clientAddress.set(packet.getSocketAddress()); + if (!hasClientAddress) { + gotClientAddress.countDown(); + } + } catch (IOException e) { + if (!stopTestThreads.get()) { + throw new RuntimeException(e); + } else { + return; + } + } + } + }); + + // Run a virtual thread that will send an empty packets via server socket + // that should wake up the selector on a client side. + Thread wakeupThread = Thread.ofVirtual().start(() -> { + try { + long timeout = Math.max(1, TIMEOUT / 4); + // wait for a first packet on a server socket + gotClientAddress.await(); + + // Now start sending empty packets until we get a notification + // from client part to stop sending + while (!stopTestThreads.get()) { + System.err.println("Server timeout = " + timeout); + TimeUnit.MILLISECONDS.sleep(timeout); + System.err.println("Sending wakeup packet to " + clientAddress.get()); + var wakeupPacket = new DatagramPacket(new byte[0], 0); + wakeupPacket.setSocketAddress(clientAddress.get()); + ds.send(wakeupPacket); + timeout += Math.max(1, timeout / 2); + } + } catch (IOException ioe) { + throw new RuntimeException("Test machinery failure", ioe); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted during wakeup packets sending"); + } finally { + System.err.println("Server thread exiting"); + } + }); + + long startTime = 0; + try { + env().put(Context.PROVIDER_URL, allQuietUrl); + env().put("com.sun.jndi.dns.timeout.initial", String.valueOf(TIMEOUT)); + env().put("com.sun.jndi.dns.timeout.retries", String.valueOf(RETRIES)); + setContext(new InitialDirContext(env())); + + startTime = System.nanoTime(); + context().getAttributes(""); + + // Any request should fail after timeouts have expired. + throw new RuntimeException("Failed: getAttributes succeeded unexpectedly"); + } catch (CommunicationException ce) { + // We need to catch CommunicationException outside the test framework + // flow because wakeupThread.join() can take some time that could + // increase measured timeout + long endTime = System.nanoTime(); + Duration elapsedTime = Duration.ofNanos(endTime - startTime); + if (ce.getRootCause() instanceof SocketTimeoutException) { + + Duration minAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + .minus(Duration.ofMillis(DNS_CLIENT_MIN_TIMEOUT * RETRIES)); + Duration maxAllowedTime = Duration.ofMillis(TIMEOUT) + .multipliedBy((1 << RETRIES) - 1) + // max allowed timeout value is set to 2 * expected timeout + .multipliedBy(2); + + DNSTestUtils.debug("Elapsed (ms): " + elapsedTime.toMillis()); + String expectedRangeMsg = "%s - %s" + .formatted(minAllowedTime.toMillis(), maxAllowedTime.toMillis()); + DNSTestUtils.debug("Expected range (ms): " + expectedRangeMsg); + + // Check that elapsed time is as long as expected, and + // not more than 2 times greater. + if (elapsedTime.compareTo(minAllowedTime) >= 0 && + elapsedTime.compareTo(maxAllowedTime) <= 0) { + System.out.println("elapsed time is as long as expected."); + } else { + throw new RuntimeException( + "Failed: timeout in " + elapsedTime.toMillis() + + " ms, expected to be in a range (ms): " + expectedRangeMsg); + } + } else { + throw ce; + } + } finally { + stopTestThreads.set(true); + wakeupThread.join(); + ds.close(); + receiverThread.join(); + } + } + } +} diff --git a/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java b/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java index 3cc9f40b8957c..ba6fffd067025 100644 --- a/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java +++ b/test/jdk/com/sun/management/DiagnosticCommandMBean/DcmdMBeanTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, 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,7 +23,7 @@ /* * @test - * @bug 7150256 + * @bug 7150256 8338603 * @summary Basic Test for the DiagnosticCommandMBean * @author Frederic Parain, Shanliang JIANG * @@ -68,10 +68,14 @@ public static void main(String[] args) throws Exception { System.out.println("Description:" + info.getDescription()); MBeanOperationInfo[] opInfo = info.getOperations(); System.out.println("Operations:"); + int operationFailures = 0; for (int i = 0; i < opInfo.length; i++) { - printOperation(opInfo[i]); + operationFailures += printOperation(opInfo[i]); System.out.println("\n@@@@@@\n"); } + if (operationFailures > 0) { + throw new RuntimeException("FAILED. " + operationFailures + " operations found with non-standard parameter types."); + } } finally { try { cc.close(); @@ -83,7 +87,12 @@ public static void main(String[] args) throws Exception { System.out.println("Test passed"); } - static void printOperation(MBeanOperationInfo info) { + /** + * Print an Operation, and check for any non-standard parameter types. + * Return the number of failed parameters, so the caller can signal to fail the test. + */ + static int printOperation(MBeanOperationInfo info) { + int failures = 0; System.out.println("Name: "+info.getName()); System.out.println("Description: "+info.getDescription()); System.out.println("Return Type: "+info.getReturnType()); @@ -100,8 +109,16 @@ static void printOperation(MBeanOperationInfo info) { Descriptor desc3 = (Descriptor)desc2.getFieldValue(desc2.getFieldNames()[j]); for(int k=0; k */ - permission java.io.FilePermission "<>", "read,write,delete"; + /* + * To read configuration file in META-INF/services, write/delete .attach_pid, + * and read symbolic link of /proc/self/ns/mnt. + */ + permission java.io.FilePermission "<>", "read,write,delete,readlink"; }; diff --git a/test/jdk/java/awt/Button/BadActionEventTest/BadActionEventTest.java b/test/jdk/java/awt/Button/BadActionEventTest/BadActionEventTest.java new file mode 100644 index 0000000000000..53aac7ab78751 --- /dev/null +++ b/test/jdk/java/awt/Button/BadActionEventTest/BadActionEventTest.java @@ -0,0 +1,93 @@ +/* + * 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 4530087 + * @summary Test if double-clicking causes ActionEvent on underlying button + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BadActionEventTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class BadActionEventTest implements ActionListener { + private static Button showBtn; + private static Button listBtn; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1) Click on 'Show File Dialog' to bring up the FileDialog window. + (If necessary, change to a directory with files (not just directories) in it.) + 2) Move the FileDialog so that one of the file names (again, a file, NOT a directory) in the list is + directly over the 'ActionListener' button. + 3) Double-click on the file name over the button. The FileDialog will disappear. + 4) If the 'ActionListener' button receives an ActionEvent, the test fails and a + message to that effect will be printed. + Otherwise, the test passes. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(BadActionEventTest::createUI) + .logArea(2) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("BadActionEventTest"); + frame.setLayout(new GridLayout(1, 2)); + frame.setSize(400, 200); + showBtn = new Button("Show File Dialog"); + listBtn = new Button("ActionListener"); + showBtn.setSize(200, 200); + listBtn.setSize(200, 200); + showBtn.addActionListener(new BadActionEventTest()); + listBtn.addActionListener(new BadActionEventTest()); + frame.add(showBtn); + frame.add(listBtn); + return frame; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == showBtn) { + FileDialog fd = new FileDialog(new Frame()); + fd.setVisible(true); + } else if (e.getSource() == listBtn) { + listBtn.setBackground(Color.red); + listBtn.setLabel("TEST FAILS!"); + PassFailJFrame.log("*TEST FAILS!* ActionListener got ActionEvent! *TEST FAILS!*"); + } + } +} diff --git a/test/jdk/java/awt/CardLayout/RemoveComponentTest/RemoveComponentTest.java b/test/jdk/java/awt/CardLayout/RemoveComponentTest/RemoveComponentTest.java new file mode 100644 index 0000000000000..0a23a98953302 --- /dev/null +++ b/test/jdk/java/awt/CardLayout/RemoveComponentTest/RemoveComponentTest.java @@ -0,0 +1,166 @@ +/* + * 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 + * 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 4546123 + * @summary CardLayout becomes unusable after deleting an element + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RemoveComponentTest + */ + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Frame; +import java.awt.Insets; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JLabel; +import javax.swing.JPanel; + +public class RemoveComponentTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + You should see a frame titled "Test Frame For + RemoveComponentTest". Try to select a few different panels from + the second menu. Make sure your last choice is the red panel. + Then click close (in first menu). After that you should be able + to select any panels except red one. + If that is the case, the test passes. Otherwise, the test failed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(RemoveComponentTest::createUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + TestFrame frame = new TestFrame(); + frame.setSize(200, 200); + return frame; + } +} + +class TestFrame extends Frame implements ActionListener { + public Panel aPanel; + public TestPanel pageRed; + public TestPanel pageGreen; + public TestPanel pageBlue; + public String currentSelection = ""; + + public MenuItem mi; + public CardLayout theCardLayout; + + + public TestFrame() { + super("Test Frame For RemoveComponentTest"); + + setBackground(Color.black); + setLayout(new BorderLayout(5, 5)); + + MenuBar mb = new MenuBar(); + + Menu fileMenu = new Menu("File"); + Menu pageMenu = new Menu("Pages"); + + mi = new MenuItem("Close"); + mi.addActionListener(this); + fileMenu.add(mi); + + mi = new MenuItem("Red"); + mi.addActionListener(this); + pageMenu.add(mi); + + mi = new MenuItem("Green"); + mi.addActionListener(this); + pageMenu.add(mi); + + mi = new MenuItem("Blue"); + mi.addActionListener(this); + pageMenu.add(mi); + + mb.add(fileMenu); + mb.add(pageMenu); + + setMenuBar(mb); + + aPanel = new Panel(); + theCardLayout = new CardLayout(); + + aPanel.setLayout(theCardLayout); + + pageRed = new TestPanel("PageRed", Color.red); + pageGreen = new TestPanel("PageGreen", Color.green); + pageBlue = new TestPanel("PageBlue", Color.blue); + + aPanel.add("PageRed", pageRed); + aPanel.add("PageGreen", pageGreen); + aPanel.add("PageBlue", pageBlue); + + add("Center", aPanel); + setSize(getPreferredSize()); + } + + public Insets getInsets() { + return new Insets(47, 9, 9, 9); + } + + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("Red")) { + theCardLayout.show(aPanel, "PageRed"); + currentSelection = "PageRed"; + } else if (e.getActionCommand().equals("Green")) { + theCardLayout.show(aPanel, "PageGreen"); + } else if (e.getActionCommand().equals("Blue")) { + theCardLayout.show(aPanel, "PageBlue"); + } else if (e.getActionCommand().equals("Close")) { + PassFailJFrame.log("Closing"); + + if (currentSelection.equals("PageRed")) { + PassFailJFrame.log("Remove page red"); + theCardLayout.removeLayoutComponent(pageRed); + } + } + } +} + +class TestPanel extends JPanel { + private String pageName; + + TestPanel(String pageName, Color color) { + setBackground(color); + add(new JLabel(pageName)); + } +} diff --git a/test/jdk/java/awt/Checkbox/CheckboxBoxSizeTest.java b/test/jdk/java/awt/Checkbox/CheckboxBoxSizeTest.java new file mode 100644 index 0000000000000..0d500e5daa16d --- /dev/null +++ b/test/jdk/java/awt/Checkbox/CheckboxBoxSizeTest.java @@ -0,0 +1,71 @@ +/* + * 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.Checkbox; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; + +/* + * @test + * @bug 4410522 + * @requires (os.family == "windows") + * @summary The box size of the Checkbox control should be the same as + * in Windows native applications. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CheckboxBoxSizeTest + */ + +public class CheckboxBoxSizeTest { + private static final String INSTRUCTIONS = """ + This test must be run at UI Scale of 100% AND + 150% or greater. + Compare the size of box to any of native apps on Windows + (Eg. Font Dialog Settings on Word). + They should be the same. + + If the sizes are same Press PASS, else Press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("CheckboxBoxSizeTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(CheckboxBoxSizeTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("CheckboxBoxSizeTest"); + Panel panel = new Panel(new FlowLayout()); + Checkbox checkbox = new Checkbox("Compare the box size"); + panel.add(checkbox); + frame.add(panel); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/Checkbox/CheckboxIndicatorSizeTest.java b/test/jdk/java/awt/Checkbox/CheckboxIndicatorSizeTest.java new file mode 100644 index 0000000000000..3456e7e040d17 --- /dev/null +++ b/test/jdk/java/awt/Checkbox/CheckboxIndicatorSizeTest.java @@ -0,0 +1,168 @@ +/* + * 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. + */ + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4090493 + * @summary Test for Checkbox indicator size + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CheckboxIndicatorSizeTest + */ + +public class CheckboxIndicatorSizeTest implements ActionListener { + private static final String INSTRUCTIONS = """ + Indicator size of Checkbox depends + on the platform font used to render the label. + + In the frame you can see a group of checkboxes + and radio buttons. + Verify that all checkboxes and radio buttons have + indicators of the same size and proportional to + the uiScale and/or font-size. + + Use menu to change the font size and the indicators + should scale proportionally. + + If there is a bug, the checkbox/radiobutton with + dingbats label will have a smaller indicator. + + Press PASS if the above conditions are true else Press FAIL. + """; + private static Frame frame; + private static Panel testPanel; + + public static void main(String[] args) throws Exception { + + CheckboxIndicatorSizeTest obj = new CheckboxIndicatorSizeTest(); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 3) + .columns(60) + .testUI(obj::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private Frame createAndShowUI() { + frame = new Frame("CheckboxIndicatorSizeTest"); + + testPanel = new Panel(new GridLayout(0, 1)); + testPanel.setFont(new Font("Dialog", Font.PLAIN, 12)); + frame.add(testPanel); + + MenuBar menuBar = new MenuBar(); + Menu fontSizeMenu = new Menu("FontSize"); + + MenuItem size10 = new MenuItem("10"); + size10.addActionListener(this); + fontSizeMenu.add(size10); + + MenuItem size12 = new MenuItem("12"); + size12.addActionListener(this); + fontSizeMenu.add(size12); + + MenuItem size14 = new MenuItem("14"); + size14.addActionListener(this); + fontSizeMenu.add(size14); + + MenuItem size18 = new MenuItem("18"); + size18.addActionListener(this); + fontSizeMenu.add(size18); + + MenuItem size24 = new MenuItem("24"); + size24.addActionListener(this); + fontSizeMenu.add(size24); + + MenuItem size36 = new MenuItem("36"); + size36.addActionListener(this); + fontSizeMenu.add(size36); + + menuBar.add(fontSizeMenu); + frame.setMenuBar(menuBar); + + Checkbox cbEnglishOnly + = new Checkbox("Toggle", true); + Checkbox cbDingbatsOnly + = new Checkbox("\u274a\u274b\u274c\u274d", true); + Checkbox cbEnglishDingbats + = new Checkbox("Toggle \u274a\u274d", true); + Checkbox cbDingbatsEnglish + = new Checkbox("\u274a\u274d toggle", true); + + CheckboxGroup radioGroup = new CheckboxGroup(); + Checkbox rbEnglishOnly + = new Checkbox("Radio", true, radioGroup); + Checkbox rbDingbatsOnly + = new Checkbox("\u274a\u274b\u274c\u274d", false, radioGroup); + Checkbox rbEnglishDingbats + = new Checkbox("Radio \u274a\u274d", false, radioGroup); + Checkbox rbDingbatsEnglish + = new Checkbox("\u274a\u274d radio", false, radioGroup); + + Label cbLabel = new Label("Checkboxes"); + cbLabel.setBackground(Color.YELLOW); + testPanel.add(cbLabel); + testPanel.add(cbEnglishOnly); + testPanel.add(cbDingbatsOnly); + testPanel.add(cbEnglishDingbats); + testPanel.add(cbDingbatsEnglish); + + Label rbLabel = new Label("Radio buttons"); + rbLabel.setBackground(Color.YELLOW); + testPanel.add(rbLabel); + testPanel.add(rbEnglishOnly); + testPanel.add(rbDingbatsOnly); + testPanel.add(rbEnglishDingbats); + testPanel.add(rbDingbatsEnglish); + + frame.pack(); + return frame; + } + + @Override + public void actionPerformed(ActionEvent e) { + String sizeStr = e.getActionCommand(); + int size = Integer.parseInt(sizeStr); + Font oldFont = testPanel.getFont(); + Font newFont = new Font(oldFont.getName(), oldFont.getStyle(), size); + testPanel.setFont(newFont); + frame.pack(); + frame.setVisible(true); + } +} diff --git a/test/jdk/java/awt/Checkbox/CheckboxNullLabelTest.java b/test/jdk/java/awt/Checkbox/CheckboxNullLabelTest.java new file mode 100644 index 0000000000000..c5cb7c278297a --- /dev/null +++ b/test/jdk/java/awt/Checkbox/CheckboxNullLabelTest.java @@ -0,0 +1,95 @@ +/* + * 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 + * 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.CheckboxGroup; +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Panel; + +/* + * @test + * @bug 4383735 + * @summary Checkbox buttons are too small with java 1.3 and 1.4 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CheckboxNullLabelTest + */ + +public class CheckboxNullLabelTest { + private static final String INSTRUCTIONS = """ + Please look at the frame titled 'CheckboxNullLabelTest'. + Check if all the check boxes in each group + (of 3 check boxes) have the same size. + + If the size of labeled check box is NOT the same as + the size of non-labeled Press FAIL otherwise Press PASS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(35) + .testUI(CheckboxNullLabelTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame f = new Frame("CheckboxNullLabelTest"); + f.setLayout(new BorderLayout()); + f.add(new CheckboxTest(Color.gray, new Font(null, 0, 12)), "North"); + f.add(new CheckboxTest(Color.green, new Font(null, 0, 18)), "South"); + f.add(new CheckboxTest(Color.red, new Font(null, 0, 24)), "East"); + f.add(new CheckboxTest(Color.white, new Font(null, 0, 30)), "West"); + f.add(new CheckboxTest(f.getBackground(), new Font(null, 0, 36)), "Center"); + f.setSize(600, 450); + return f; + } + + private static class CheckboxTest extends Panel { + Checkbox cb1, cb2, cb3; + + CheckboxTest(Color background, Font font) { + setBackground(background); + CheckboxGroup cbg = new CheckboxGroup(); + + cb1 = new Checkbox(null, cbg, true); + cb1.setFont(font); + + cb2 = new Checkbox("", cbg, true); + cb2.setFont(font); + + cb3 = new Checkbox("Label", cbg, false); + cb3.setFont(font); + + add(cb1); + add(cb2); + add(cb3); + } + } +} diff --git a/test/jdk/java/awt/Checkbox/CheckboxPreferredSizeTest.java b/test/jdk/java/awt/Checkbox/CheckboxPreferredSizeTest.java new file mode 100644 index 0000000000000..dd61b52aaedbe --- /dev/null +++ b/test/jdk/java/awt/Checkbox/CheckboxPreferredSizeTest.java @@ -0,0 +1,67 @@ +/* + * 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 + * 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.Checkbox; +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; + +/* + * @test + * @bug 4304049 + * @summary tests that Checkbox fits into its preferred size entirely + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CheckboxPreferredSizeTest + */ + +public class CheckboxPreferredSizeTest { + private static final String INSTRUCTIONS = """ + As the test starts, ensure that the + whole checkbox with all its text is visible. + If the checkbox is entirely visible, press PASS else, + press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(35) + .testUI(CheckboxPreferredSizeTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("Checkbox Preferred Size Test"); + frame.setBackground(Color.BLUE); + Checkbox box = new Checkbox("Checkbox_With_Some_Size"); + box.setFont(new Font("Helvetica", Font.PLAIN, 36)); + box.setBackground(Color.RED); + frame.add(box); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/Checkbox/DynamicChangeTest/DynamicChangeTest.java b/test/jdk/java/awt/Checkbox/DynamicChangeTest/DynamicChangeTest.java new file mode 100644 index 0000000000000..b62255efa4589 --- /dev/null +++ b/test/jdk/java/awt/Checkbox/DynamicChangeTest/DynamicChangeTest.java @@ -0,0 +1,72 @@ +/* + * 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 + * 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 6225679 + * @summary Tests that checkbox changes into radiobutton dynamically + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DynamicChangeTest + */ + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Frame; +import java.awt.GridLayout; + +public class DynamicChangeTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test is primarily for Windows platform, but should pass + on other platforms as well. Ensure that 'This is checkbox' is + checkbox, and 'This is radiobutton' is radiobutton. + If it is so, press pass else fail. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(DynamicChangeTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Dynamic Change Checkbox Test"); + f.setSize(200, 200); + + f.setLayout(new GridLayout(2, 1)); + Checkbox ch1 = new Checkbox("This is checkbox", + new CheckboxGroup(), true); + f.add(ch1); + Checkbox ch2 = new Checkbox("This is radiobutton", null, true); + f.add(ch2); + + ch1.setCheckboxGroup(null); + ch2.setCheckboxGroup(new CheckboxGroup()); + return f; + } +} diff --git a/test/jdk/java/awt/Choice/CheckChoiceTest.java b/test/jdk/java/awt/Choice/CheckChoiceTest.java new file mode 100644 index 0000000000000..3e2e06b1c9557 --- /dev/null +++ b/test/jdk/java/awt/Choice/CheckChoiceTest.java @@ -0,0 +1,92 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Choice; +import java.awt.Frame; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 4151949 + * @summary Verifies that Components are reshaped to their preferred size + * when their Container is packed. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CheckChoiceTest + */ + +public class CheckChoiceTest { + + private static JComponent componentToFocus; + + private static final String INSTRUCTIONS = """ + Verify that the widths of the Choice components are all the same + and that none is the minimum possible size. + (The Choices should be at least as wide as the Frame.) + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("CheckChoiceTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 3) + .columns(45) + .testUI(CheckChoiceTest::createAndShowUI) + .splitUIBottom(CheckChoiceTest::createComponentToFocus) + .build(); + + // focus away from the window with choices + Thread.sleep(300); + SwingUtilities.invokeAndWait(() -> componentToFocus.requestFocus()); + + passFailJFrame.awaitAndCheck(); + } + + private static JComponent createComponentToFocus() { + componentToFocus = new JPanel(); + return componentToFocus; + } + + private static Frame createAndShowUI() { + Frame f = new Frame("Check Choice"); + f.setLayout(new BorderLayout()); + + Choice choice1 = new Choice(); + Choice choice2 = new Choice(); + Choice choice3 = new Choice(); + + f.add(choice1, BorderLayout.NORTH); + f.add(choice3, BorderLayout.CENTER); + f.add(choice2, BorderLayout.SOUTH); + f.pack(); + + choice1.add("I am Choice, yes I am : 0"); + choice2.add("I am the same, yes I am : 0"); + choice3.add("I am the same, yes I am : 0"); + + return f; + } +} diff --git a/test/jdk/java/awt/Choice/ChoiceBigTest.java b/test/jdk/java/awt/Choice/ChoiceBigTest.java new file mode 100644 index 0000000000000..be82ed2a40db1 --- /dev/null +++ b/test/jdk/java/awt/Choice/ChoiceBigTest.java @@ -0,0 +1,70 @@ +/* + * 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.Choice; +import java.awt.Frame; +import java.awt.FlowLayout; +import java.awt.Window; + +/* + * @test + * @bug 4288285 + * @summary Verifies choice works with many items + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ChoiceBigTest + */ + +public class ChoiceBigTest { + private static final String INSTRUCTIONS = """ + Click the Choice button, press Pass if: + + - all looks good. + - if you can select the item 1000 + + Otherwise press Fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ChoiceBigTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 3) + .columns(45) + .testUI(ChoiceBigTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Window createAndShowUI() { + Frame frame = new Frame("Check Choice"); + frame.setLayout(new FlowLayout()); + Choice choice = new Choice(); + frame.setSize(400, 200); + for (int i = 1; i < 1001; ++i) { + choice.add("I am Choice, yes I am : " + i); + } + frame.add(choice); + return frame; + } +} diff --git a/test/jdk/java/awt/Choice/ChoiceDragEventsInside.java b/test/jdk/java/awt/Choice/ChoiceDragEventsInside.java new file mode 100644 index 0000000000000..dde773f19289c --- /dev/null +++ b/test/jdk/java/awt/Choice/ChoiceDragEventsInside.java @@ -0,0 +1,210 @@ +/* + * 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 + * 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 6251983 6722236 + * @summary MouseDragged events not triggered for Choice when dragging it with left mouse button + * @key headful + * @run main ChoiceDragEventsInside + */ + +import java.awt.Choice; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.lang.reflect.InvocationTargetException; + +public class ChoiceDragEventsInside extends Frame { + Robot robot; + Choice choice1; + Point pt; + Dimension size; + volatile boolean mouseDragged = false; + volatile boolean mouseDraggedOutside = false; + + public void setupUI() { + setTitle("Choce Drag Events Inside"); + choice1 = new Choice(); + for (int i = 1; i < 50; i++) { + choice1.add("item-0" + i); + } + choice1.setForeground(Color.red); + choice1.setBackground(Color.red); + choice1.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent me) { + System.out.println(me); + } + + public void mouseDragged(MouseEvent me) { + System.out.println(me); + mouseDragged = true; + if (me.getY() < 0) { + mouseDraggedOutside = true; + } + } + } + ); + add(choice1); + setLayout(new FlowLayout()); + setSize(200, 200); + setLocationRelativeTo(null); + setVisible(true); + validate(); + } + + public void start() { + try { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.setAutoDelay(50); + robot.delay(100); + EventQueue.invokeAndWait(() -> { + pt = choice1.getLocationOnScreen(); + size = choice1.getSize(); + }); + testDragInsideChoice(InputEvent.BUTTON1_MASK); + testDragInsideChoiceList(InputEvent.BUTTON1_MASK); + testDragOutsideChoice(InputEvent.BUTTON1_MASK); + } catch (Throwable e) { + throw new RuntimeException("Test failed. Exception thrown: " + e); + } + } + + public void testDragInsideChoice(int button) { + robot.mouseMove(pt.x + size.width / 2, pt.y + size.height / 2); + robot.delay(100); + robot.mousePress(button); + robot.mouseRelease(button); + robot.delay(200); + robot.mousePress(button); + robot.mouseRelease(button); + robot.delay(200); + + //close opened choice + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.delay(200); + + robot.mouseMove(pt.x + size.width / 4, pt.y + size.height / 2); + robot.mousePress(button); + + dragMouse(pt.x + size.width / 4, pt.y + size.height / 2, + pt.x + size.width * 3 / 4, pt.y + size.height / 2); + robot.mouseRelease(button); + robot.delay(200); + if (!mouseDragged) { + throw new RuntimeException("Test failed. Choice should generate MouseDragged events inside Choice itself"); + } else { + System.out.println("Stage 1 passed. Choice generates MouseDragged events inside Choice itself"); + } + mouseDragged = false; + //close opened choice + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.delay(200); + } + + public void testDragInsideChoiceList(int button) { + robot.mouseMove(pt.x + size.width / 2, pt.y + size.height / 2); + robot.delay(100); + robot.mousePress(button); + robot.mouseRelease(button); + robot.delay(200); + + robot.mouseMove(pt.x + size.width / 2, pt.y + 5 * size.height); + robot.delay(200); + robot.mousePress(button); + + dragMouse(pt.x + size.width / 2, pt.y + 5 * size.height, + pt.x + size.width / 2, pt.y + 8 * size.height); + robot.mouseRelease(button); + robot.delay(200); + if (mouseDragged) { + throw new RuntimeException("Test failed. Choice shouldn't generate MouseDragged events inside Choice's list"); + } else { + System.out.println("Stage 2 passed. Choice doesn't generate MouseDragged events inside Choice's list"); + } + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.delay(200); + mouseDragged = false; + } + + public void testDragOutsideChoice(int button) { + pt = choice1.getLocationOnScreen(); + robot.mouseMove(pt.x + size.width / 2, pt.y + size.height / 2); + robot.delay(100); + + robot.mousePress(button); + //drag mouse outside of Choice + dragMouse(pt.x + size.width / 2, pt.y + size.height / 2, + pt.x + size.width / 2, pt.y - 3 * size.height); + robot.mouseRelease(button); + robot.delay(200); + if (!mouseDragged || !mouseDraggedOutside) { + throw new RuntimeException("Test failed. Choice should generate MouseDragged events outside Choice"); + } else { + System.out.println("Stage 3 passed. Choice generates MouseDragged events outside Choice"); + } + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.delay(200); + mouseDragged = false; + } + + public void dragMouse(int x0, int y0, int x1, int y1) { + int curX = x0; + int curY = y0; + int dx = x0 < x1 ? 1 : -1; + int dy = y0 < y1 ? 1 : -1; + + while (curX != x1) { + curX += dx; + robot.mouseMove(curX, curY); + } + while (curY != y1) { + curY += dy; + robot.mouseMove(curX, curY); + } + } + + public static void main(final String[] args) throws InterruptedException, + InvocationTargetException { + ChoiceDragEventsInside app = new ChoiceDragEventsInside(); + try { + EventQueue.invokeAndWait(app::setupUI); + app.start(); + } finally { + EventQueue.invokeAndWait(app::dispose); + } + } +} diff --git a/test/jdk/java/awt/Choice/ChoiceFocusTest.java b/test/jdk/java/awt/Choice/ChoiceFocusTest.java new file mode 100644 index 0000000000000..3ca895f88e6e9 --- /dev/null +++ b/test/jdk/java/awt/Choice/ChoiceFocusTest.java @@ -0,0 +1,81 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Choice; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Window; + +/* + * @test + * @bug 4927930 + * @summary Verify that the focus is set to the selected item after calling the java.awt.Choice.select() method + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ChoiceFocusTest + */ + +public class ChoiceFocusTest { + + private static final String INSTRUCTIONS = """ + 1. Use the mouse to select Item 5 in the Choice list. + 2. Click on the Choice. Item5 is now selected and highlighted. This is the correct behavior. + 3. Select Item 1 in the Choice list. + 4. Click the "choice.select(5)" button. This causes a call to Choice.select(5). Item 5 is now selected. + 5. Click on the Choice. + 6. If the cursor and focus are on item 5, the test passes. Otherwise, it fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ChoiceFocusTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 3) + .columns(50) + .testUI(ChoiceFocusTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Window createAndShowUI() { + Panel panel = new Panel(); + Choice choice = new Choice(); + Button button = new Button("choice.select(5);"); + + for (int i = 0; i < 10; i++) { + choice.add(String.valueOf(i)); + } + + button.addActionListener(e -> choice.select(5)); + + panel.add(button); + panel.add(choice); + + Frame frame = new Frame("ChoiceFocusTest"); + frame.add(panel); + frame.pack(); + + return frame; + } +} diff --git a/test/jdk/java/awt/Choice/ChoiceInLWTest/ChoiceInLWTest.java b/test/jdk/java/awt/Choice/ChoiceInLWTest/ChoiceInLWTest.java new file mode 100644 index 0000000000000..9d1ad19549189 --- /dev/null +++ b/test/jdk/java/awt/Choice/ChoiceInLWTest/ChoiceInLWTest.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4130788 + * @summary Choice components move unexpectedly when in lightweight containers + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ChoiceInLWTest + */ + +import java.awt.BorderLayout; +import java.awt.Choice; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.reflect.InvocationTargetException; + +public class ChoiceInLWTest extends Frame implements Runnable { + private final Choice choices; + static final String INSTRUCTIONS = """ + After test starts wait for two seconds and open a choice. + If choice's popup obscures the label above it press Fail. + Otherwise press Pass. + """; + + public ChoiceInLWTest() { + setLayout(new BorderLayout()); + Container lwCont = new Container(); + lwCont.setLayout(new FlowLayout()); + choices = new Choice(); + choices.add("This is just a token item to get a nice width."); + lwCont.add(choices); + add("Center", lwCont); + Label label = new Label("You should see an unobscured Choice below."); + label.setAlignment(Label.CENTER); + add("North", label); + addChoiceItem(); + addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + super.windowOpened(e); + new Thread(ChoiceInLWTest.this).start(); + } + }); + pack(); + } + + private void addChoiceItem() { + choices.add("Adding an item used to move the Choice!"); + } + + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException ignore) { + } + addChoiceItem(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Choice in LW Container Test") + .testUI(ChoiceInLWTest::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + + } +} diff --git a/test/jdk/java/awt/Choice/ChoiceMouseEventTest.java b/test/jdk/java/awt/Choice/ChoiceMouseEventTest.java new file mode 100644 index 0000000000000..9603d5c763de4 --- /dev/null +++ b/test/jdk/java/awt/Choice/ChoiceMouseEventTest.java @@ -0,0 +1,123 @@ +/* + * 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 + * 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 4319246 + * @summary Tests that MouseReleased, MouseClicked and MouseDragged are triggered on choice + * @key headful + * @run main ChoiceMouseEventTest + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Choice; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.lang.reflect.InvocationTargetException; + + +public class ChoiceMouseEventTest extends Frame { + static volatile boolean mousePressed = false; + static volatile boolean mouseReleased = false; + static volatile boolean mouseClicked = false; + Choice choice = new Choice(); + static Point location; + static Dimension size; + + public void setupGUI() { + setTitle("Choice Mouse Event Test"); + this.setLayout(new BorderLayout()); + choice.add("item-1"); + choice.add("item-2"); + choice.add("item-3"); + choice.add("item-4"); + add("Center", choice); + choice.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + mouseClicked = true; + } + + @Override + public void mousePressed(MouseEvent e) { + mousePressed = true; + } + + @Override + public void mouseReleased(MouseEvent e) { + mouseReleased = true; + } + }); + setLocationRelativeTo(null); + setSize(400, 200); + setVisible(true); + } + + public Point _location() { + return choice.getLocationOnScreen(); + } + + public Dimension _size() { + return choice.getSize(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + ChoiceMouseEventTest test = new ChoiceMouseEventTest(); + try { + EventQueue.invokeAndWait(test::setupGUI); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.delay(1000); + robot.waitForIdle(); + EventQueue.invokeAndWait(() -> { + location = test._location(); + size = test._size(); + }); + robot.waitForIdle(); + robot.mouseMove(location.x + size.width - 10, location.y + (size.height / 2)); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(2000); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(2000); + robot.waitForIdle(); + if (!mouseClicked || !mousePressed || !mouseReleased) { + throw new RuntimeException(String.format("One of the events not arrived: " + + "mouseClicked = %b, mousePressed = %b, mouseReleased = %b", + mouseClicked, mousePressed, mouseReleased)); + } + } finally { + if (test != null) { + EventQueue.invokeAndWait(test::dispose); + } + } + } +} + diff --git a/test/jdk/java/awt/Choice/ChoicePosTest.java b/test/jdk/java/awt/Choice/ChoicePosTest.java new file mode 100644 index 0000000000000..c585e4709bbe5 --- /dev/null +++ b/test/jdk/java/awt/Choice/ChoicePosTest.java @@ -0,0 +1,137 @@ +/* + * 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. + */ + +import java.awt.Choice; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +/* + * @test + * @bug 4075194 + * @summary 4075194, Choice may not be displayed at the location requested + * @key headful + */ + +public class ChoicePosTest { + + private static Robot robot; + private static Frame frame; + private static final int GAP = 10; + private static volatile Choice c1,c2; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(ChoicePosTest::createAndShowGUI); + + robot = new Robot(); + robot.waitForIdle(); + robot.delay(500); + + captureAndTestChoices(); + } finally { + EventQueue.invokeAndWait(frame::dispose); + } + + System.out.println("Passed"); + } + + private static void createAndShowGUI() { + frame = new Frame("ChoicePosTest"); + Insets insets = frame.getInsets(); + frame.setSize( insets.left + 400 + insets.right, insets.top + 400 + insets.bottom ); + frame.setBackground(Color.RED); + frame.setLayout(null); + frame.setLocationRelativeTo(null); + + c1 = new Choice(); + c1.setBackground(Color.GREEN); + frame.add( c1 ); + c1.setBounds( 20, 50, 100, 100 ); + + c2 = new Choice(); + c2.setBackground(Color.GREEN); + frame.add(c2); + c2.addItem("One"); + c2.addItem("Two"); + c2.addItem("Three"); + c2.setBounds( 125, 50, 100, 100 ); + + frame.validate(); + frame.setVisible(true); + } + + private static void captureAndTestChoices() { + Point c1loc = c1.getLocationOnScreen(); + Point c2loc = c2.getLocationOnScreen(); + + int startX = c1loc.x - GAP; + int startY = c1loc.y - GAP; + int captureWidth = c2loc.x + c2.getWidth() + GAP - startX; + int captureHeight = c2loc.y + c2.getHeight() + GAP - startY; + + BufferedImage bi = robot.createScreenCapture( + new Rectangle(startX, startY, captureWidth, captureHeight) + ); + + int redPix = Color.RED.getRGB(); + + int lastNonRedCount = 0; + + for (int y = 0; y < captureHeight; y++) { + int nonRedCount = 0; + for (int x = 0; x < captureWidth; x++) { + int pix = bi.getRGB(x, y); + if (pix != redPix) { + nonRedCount++; + } + } + + if (nonRedCount > 0 && lastNonRedCount > 0) { + if (lastNonRedCount - nonRedCount > 0) { + System.err.printf( + "Failed at %d, nonRedCount: %d lastNonRedCount: %d\n", + y, nonRedCount, lastNonRedCount + ); + + try { + ImageIO.write(bi, "png", new File("choices.png")); + } catch (IOException ignored) { + } + + throw new RuntimeException("Choices are not aligned"); + } + } + + lastNonRedCount = nonRedCount; + } + } +} diff --git a/test/jdk/java/awt/Choice/ChoiceRemoveTest.java b/test/jdk/java/awt/Choice/ChoiceRemoveTest.java new file mode 100644 index 0000000000000..6de66db936276 --- /dev/null +++ b/test/jdk/java/awt/Choice/ChoiceRemoveTest.java @@ -0,0 +1,111 @@ +/* + * 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 4079027 + * @summary Removing an item dynamically from a Choice object breaks lower items. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ChoiceRemoveTest + */ + +import java.awt.Choice; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.Panel; +import java.awt.event.ItemEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.reflect.InvocationTargetException; + +public class ChoiceRemoveTest extends Frame { + Choice selector; + static final String INSTRUCTIONS = """ + After window 'Choice Remove Test' appears wait for three seconds + and then click on the choice. In popup there should be no + 'Choice A' variant. Try selecting each variant with mouse + and verify by the log that the correct variant gets selected. + If after selecting item in the list the correct item gets selected + and correct item name appears in the log press Pass otherwise press Fail. + """; + + public static void main(String[] argv) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Test Instructions") + .testUI(ChoiceRemoveTest::new) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } + + public ChoiceRemoveTest() { + super("Choice Remove Test"); + Panel p; + Label prompt; + + addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + super.windowOpened(e); + new Thread(() -> { + try { + Thread.sleep(2000); + } catch (InterruptedException ignore) { + } + removeFirst(); + }).start(); + } + }); + + setLayout(new GridLayout()); + p = new Panel(); + + prompt = new Label("Select different items including the last one"); + p.add(prompt); + + selector = new Choice(); + selector.add("Choice A"); + selector.add("Choice B"); + selector.add("Choice C"); + selector.add("Choice D"); + selector.add("Choice E"); + selector.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + Object selected = e.getItem(); + PassFailJFrame.log(selected.toString()); + } + }); + p.add(selector); + add(p); + pack(); + } + + public void removeFirst() { + selector.remove("Choice A"); + } +} diff --git a/test/jdk/java/awt/Choice/DeadlockTest.java b/test/jdk/java/awt/Choice/DeadlockTest.java new file mode 100644 index 0000000000000..fdc6d94e6ba81 --- /dev/null +++ b/test/jdk/java/awt/Choice/DeadlockTest.java @@ -0,0 +1,113 @@ +/* + * 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.BorderLayout; +import java.awt.Choice; +import java.awt.Frame; +import jdk.test.lib.Platform; + +/* + * @test + * @bug 4134619 + * @summary Tests that the EventDispatchThread doesn't deadlock with + * user threads which are modifying a Choice component. + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jdk.test.lib.Platform + * @run main/manual DeadlockTest + */ + +public class DeadlockTest extends Thread { + + static volatile Choice choice1; + static volatile Choice choice2; + static volatile Choice choice3; + static volatile Frame frame; + static int itemCount = 0; + + private static final boolean isWindows = Platform.isWindows(); + + private static final String INSTRUCTIONS = """ + Click on the top Choice component and hold the mouse still briefly. + Then, without releasing the mouse button, move the cursor to a menu + item and then again hold the mouse still briefly. + %s + Release the button and repeat this process. + + Verify that this does not cause a deadlock + or crash within a reasonable amount of time. + """.formatted( + isWindows + ? "(menu can automatically collapse sometimes, this is ok)\n" + : "" + + ) ; + + public static void main(String[] args) throws Exception { + DeadlockTest deadlockTest = new DeadlockTest(); + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("DeadlockTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(deadlockTest::createAndShowUI) + .build(); + + deadlockTest.start(); + + passFailJFrame.awaitAndCheck(); + } + + public Frame createAndShowUI() { + frame = new Frame("Check Choice"); + frame.setLayout(new BorderLayout()); + choice1 = new Choice(); + choice2 = new Choice(); + choice3 = new Choice(); + frame.add(choice1, BorderLayout.NORTH); + frame.add(choice3, BorderLayout.CENTER); + frame.add(choice2, BorderLayout.SOUTH); + frame.pack(); + return frame; + } + + public void run() { + while (true) { + if (choice1 != null && itemCount < 40) { + choice1.add("I am Choice, yes I am : " + itemCount * itemCount); + choice2.add("I am the same, yes I am : " + itemCount * itemCount); + choice3.add("I am the same, yes I am : " + itemCount * itemCount); + itemCount++; + } + if (itemCount >= 20 && choice1 != null && + choice1.getItemCount() > 0) { + choice1.removeAll(); + choice2.removeAll(); + choice3.removeAll(); + itemCount = 0; + } + frame.validate(); + try { + Thread.sleep(1000); + } catch (Exception ignored) {} + } + } +} diff --git a/test/jdk/java/awt/Choice/DisabledList.java b/test/jdk/java/awt/Choice/DisabledList.java new file mode 100644 index 0000000000000..d028527303f7a --- /dev/null +++ b/test/jdk/java/awt/Choice/DisabledList.java @@ -0,0 +1,79 @@ +/* + * 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 + * 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.Choice; +import java.awt.Frame; +import java.awt.Window; +import java.awt.event.ItemEvent; + +/* + * @test + * @bug 6476183 + * @summary Drop down of a Choice changed to enabled state has a disabled like appearance + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DisabledList + */ + +public class DisabledList { + + private static final String INSTRUCTIONS = """ + 1) Select the checkbox + 2) Open Choice + 3) Drag mouse over the scrollbar or drag out it the choice + 4) If choice's items become disabled press fail, otherwise pass + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DisabledList Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 3) + .columns(45) + .testUI(DisabledList::createAndShowUI) + .logArea(4) + .build() + .awaitAndCheck(); + } + + private static Window createAndShowUI() { + Frame frame = new Frame("DisabledList"); + frame.setSize(200, 200); + frame.validate(); + Checkbox checkbox = new Checkbox("checkbox"); + final Choice choice = new Choice(); + choice.setEnabled(false); + for (int i = 0; i < 15; i++) { + choice.addItem("Item" + i); + } + checkbox.addItemListener(event -> { + PassFailJFrame.log("CheckBox.itemStateChanged occurred"); + choice.setEnabled(event.getStateChange() == ItemEvent.SELECTED); + }); + frame.add(BorderLayout.NORTH, checkbox); + frame.add(BorderLayout.CENTER, choice); + return frame; + } +} diff --git a/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_DragOut.java b/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_DragOut.java new file mode 100644 index 0000000000000..aed1543774575 --- /dev/null +++ b/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_DragOut.java @@ -0,0 +1,74 @@ +/* + * 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 + * 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 6367251 + * @summary 2 items are highlighted when pressing, dragging the mouse inside the choice, XToolkit + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiItemSelected_DragOut + */ + +import java.awt.Choice; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; + +public class MultiItemSelected_DragOut extends Frame { + static final String INSTRUCTIONS = """ + 1) Open Choice. + 2) Start drag from first item to second or third one. + 3) Without releasing left mouse button + press and release right mouse button. + 4) Release left mouse button. + 5) Open choice again. + 6) If there is only one selection cursor + in the dropdown list press Pass otherwise press Fail. + """; + + public MultiItemSelected_DragOut() { + Choice choice = new Choice(); + + for (int i = 1; i < 10; i++) { + choice.add("item " + i); + } + add(choice); + choice.addItemListener(ie -> System.out.println(ie)); + + setLayout(new FlowLayout()); + setSize(200, 200); + validate(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("MultiItemSelected Drag Out Test") + .testUI(MultiItemSelected_DragOut::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_KeySelect.java b/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_KeySelect.java new file mode 100644 index 0000000000000..9e930f9923b81 --- /dev/null +++ b/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_KeySelect.java @@ -0,0 +1,75 @@ +/* + * 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 + * 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 6367251 + * @summary 2 items are highlighted when dragging inside and press ESC or ENTER + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiItemSelected_KeySelect + */ + +import java.awt.Choice; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; + +public class MultiItemSelected_KeySelect extends Frame { + static final String INSTRUCTIONS = """ + 1) Open Choice. + 2) Start drag from first item to another one. + 3) Without releasing the mouse button press ESC key. + 4) Open choice again. + 5) Verify that there is only one + selection cursor in the dropdown list. + 6) Repeat steps 2-5 once again but this time + press ENTER key instead of ESC. + 7) If in both scenarios there is only one selection cursor + press Pass otherwise press Fail. + """; + + public MultiItemSelected_KeySelect() { + Choice choice = new Choice(); + + for (int i = 1; i < 10; i++) { + choice.add("item " + i); + } + add(choice); + choice.addItemListener(ie -> System.out.println(ie)); + setLayout(new FlowLayout()); + setSize(200, 200); + validate(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("MultiItemSelected Key Select Test") + .testUI(MultiItemSelected_KeySelect::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_UpDown.java b/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_UpDown.java new file mode 100644 index 0000000000000..5904d98a9081f --- /dev/null +++ b/test/jdk/java/awt/Choice/MultiItemSelected/MultiItemSelected_UpDown.java @@ -0,0 +1,76 @@ +/* + * 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 + * 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 6367251 + * @summary 2 items are highlighted when dragging outside and press UP or DOWN + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiItemSelected_UpDown + */ + +import java.awt.Choice; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; + +public class MultiItemSelected_UpDown extends Frame { + static final String INSTRUCTIONS = """ + 1) Open Choice. + 2) Start drag from first item to another one. + 3) Without interrupting drag + move mouse cursor outside the choice popup. + 4) Press UP, DOWN key several times to position + selection cursor to a different item. + 5) Release mouse button. + 6) If popup is closed upon mouse button release open Choice again. + 7) Verify that there is only one + selection cursor in the dropdown list. + 8) If true then press Pass, otherwise press Fail. + """; + + public MultiItemSelected_UpDown() { + Choice choice = new Choice(); + + for (int i = 1; i < 20; i++) { + choice.add(" item " + i); + } + add(choice); + choice.addItemListener(ie -> System.out.println(ie)); + setLayout(new FlowLayout()); + setSize(200, 200); + validate(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("MultiItemSelected Up/Down Test") + .testUI(MultiItemSelected_UpDown::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Choice/PopupMenuOnChoiceArea.java b/test/jdk/java/awt/Choice/PopupMenuOnChoiceArea.java new file mode 100644 index 0000000000000..2a56d7281ee44 --- /dev/null +++ b/test/jdk/java/awt/Choice/PopupMenuOnChoiceArea.java @@ -0,0 +1,106 @@ +/* + * 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 + * 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 6240046 + * @summary REG:Choice's Drop-down does not disappear when clicking somewhere, after popup menu is disposed-XTkt + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PopupMenuOnChoiceArea + */ + + +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.PopupMenu; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.lang.reflect.InvocationTargetException; + +public class PopupMenuOnChoiceArea extends Frame { + static final String INSTRUCTIONS = """ + You would see a window named 'Popup menu on choice area' + with Choice in it. Move the mouse pointer to the choice. + Click right mouse button on it. + You should see a popup menu with 'File' in it. + Close this popup menu by pressing Esc. + Click the left mouse button on the Choice. + You should see a Choice drop-down menu. + Move mouse pointer into drop-down menu. + Click right mouse button on any item in it. + If you see a 'File' popup menu press Fail. + If Choice drop-down closes instead press Pass. + """; + + public PopupMenuOnChoiceArea() { + super("Popup menu on choice area"); + this.setLayout(new FlowLayout()); + Choice choice = new Choice(); + choice.add("item-1"); + choice.add("item-2"); + choice.add("item-3"); + choice.add("item-4"); + add("Center", choice); + Menu fileMenu = new Menu("File"); + Menu open = new Menu("Open"); + Menu save = new Menu("save"); + CheckboxMenuItem exit = new CheckboxMenuItem("Exit"); + fileMenu.add(open); + fileMenu.add(save); + fileMenu.add(exit); + final PopupMenu pop = new PopupMenu(); + pop.setLabel("This is a popup menu"); + pop.setName("a menu"); + pop.add(fileMenu); + choice.add(pop); + choice.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (me.isPopupTrigger()) { + pop.show(me.getComponent(), me.getX(), me.getY()); + } + } + + public void mouseReleased(MouseEvent me) { + if (me.isPopupTrigger()) { + pop.show(me.getComponent(), me.getX(), me.getY()); + } + } + }); + setSize(200, 200); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Test Instructions") + .testUI(PopupMenuOnChoiceArea::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Choice/RepaintAfterRemoveLastItemTest/RepaintAfterRemoveLastItemTest.java b/test/jdk/java/awt/Choice/RepaintAfterRemoveLastItemTest/RepaintAfterRemoveLastItemTest.java new file mode 100644 index 0000000000000..20acc66b6bda1 --- /dev/null +++ b/test/jdk/java/awt/Choice/RepaintAfterRemoveLastItemTest/RepaintAfterRemoveLastItemTest.java @@ -0,0 +1,77 @@ +/* + * 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 + * 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 6292186 + * @summary Choice is not refreshed properly when the last item gets removed + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RepaintAfterRemoveLastItemTest + */ + +import java.awt.Button; +import java.awt.Choice; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.InvocationTargetException; + +public class RepaintAfterRemoveLastItemTest extends Frame implements ActionListener { + Choice ch = new Choice(); + + static final String INSTRUCTIONS = """ + Press on the 'remove' button after that if the choice does not display + 'only item' press Pass. If 'only item' is still displayed press Fail. + """; + + public RepaintAfterRemoveLastItemTest() { + ch.add("only item"); + add(ch); + + Button b = new Button("remove"); + add(b); + b.addActionListener(this); + setLayout(new FlowLayout()); + setSize(200, 200); + validate(); + } + + public void actionPerformed(ActionEvent ae) { + if (ch.getItemCount() != 0) { + ch.remove(0); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Repaint After Remove Test") + .testUI(RepaintAfterRemoveLastItemTest::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Choice/ScrollbarFlickers.java b/test/jdk/java/awt/Choice/ScrollbarFlickers.java new file mode 100644 index 0000000000000..c35d4900134fa --- /dev/null +++ b/test/jdk/java/awt/Choice/ScrollbarFlickers.java @@ -0,0 +1,85 @@ +/* + * 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 + * 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 6405707 + * @summary Choice popup & scrollbar gets Flickering when mouse is pressed & drag on the scrollbar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollbarFlickers + */ + +import java.awt.BorderLayout; +import java.awt.Choice; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; + +public class ScrollbarFlickers extends Frame { + static final String INSTRUCTIONS = """ + Open the choice popup. Select any item in it and + drag it with the mouse above or below the choice. + Keep the choice opened. + Continue dragging the mouse outside of the choice + making content of the popup scroll. + If you see that scrollbar flickers press Fail. + Otherwise press Pass. + """; + + public ScrollbarFlickers() { + super("Scrollbar Flickering Test"); + Choice ch = new Choice(); + setLayout(new BorderLayout()); + ch.add("Praveen"); + ch.add("Mohan"); + ch.add("Rakesh"); + ch.add("Menon"); + ch.add("Girish"); + ch.add("Ramachandran"); + ch.add("Elancheran"); + ch.add("Subramanian"); + ch.add("Raju"); + ch.add("Pallath"); + ch.add("Mayank"); + ch.add("Joshi"); + ch.add("Sundar"); + ch.add("Srinivas"); + ch.add("Mandalika"); + ch.add("Suresh"); + ch.add("Chandar"); + add(ch); + setSize(200, 200); + validate(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Test Instructions") + .testUI(ScrollbarFlickers::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Choice/SetFontTest.java b/test/jdk/java/awt/Choice/SetFontTest.java new file mode 100644 index 0000000000000..f38a34a7ed278 --- /dev/null +++ b/test/jdk/java/awt/Choice/SetFontTest.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Choice; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Panel; + +/* + * @test + * @bug 4293346 + * @summary Checks that Choice does update its dimensions on font change + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetFontTest + */ + +public class SetFontTest { + + private static final String INSTRUCTIONS = """ + Choice component used to not update its dimension on font change. + Select one of fonts on the choice pull down list. + Pull down the list after the font change; if items in the list are + shown correctly the test is passed, otherwise it failed. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("SetFontTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(SetFontTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("SetFontTest"); + Choice choice = new Choice(); + frame.setBounds(100, 400, 400, 100); + choice.addItem("dummy"); + choice.addItem("Set LARGE Font"); + choice.addItem("Set small Font"); + choice.addItem("addNewItem"); + choice.addItem("deleteItem"); + + choice.addItemListener(e -> { + if (e.getItem().toString().equals("addNewItem")) { + choice.addItem("very very very very long item"); + frame.validate(); + } else if (e.getItem().toString().equals("deleteItem")) { + if (choice.getItemCount() > 4) { + choice.remove(4); + frame.validate(); + } + } else if (e.getItem().toString().equals("Set LARGE Font")) { + choice.setFont(new Font("Dialog", Font.PLAIN, 24)); + frame.validate(); + } else if (e.getItem().toString().equals("Set small Font")) { + choice.setFont(new Font("Dialog", Font.PLAIN, 10)); + frame.validate(); + } + }); + Panel panel = new Panel(); + panel.add(choice); + frame.add(panel, BorderLayout.CENTER); + return frame; + } +} diff --git a/test/jdk/java/awt/Component/BackgroundColorTest/InitialBackgroundSettingTest.java b/test/jdk/java/awt/Component/BackgroundColorTest/InitialBackgroundSettingTest.java new file mode 100644 index 0000000000000..3bed6f106c59a --- /dev/null +++ b/test/jdk/java/awt/Component/BackgroundColorTest/InitialBackgroundSettingTest.java @@ -0,0 +1,132 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4148334 + * @summary tests that background color is initially set correctly. + * @requires os.family == "windows" + * @key headful + * @run main InitialBackgroundSettingTest + */ +import java.awt.Button; +import java.awt.Choice; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Scrollbar; +import java.lang.reflect.InvocationTargetException; + +public class InitialBackgroundSettingTest { + Frame frame; + TextField tf; + TextArea ta; + Choice choice; + List list; + Scrollbar bar; + Button button; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + InitialBackgroundSettingTest test= new InitialBackgroundSettingTest(); + try { + EventQueue.invokeAndWait(test::setupGUI); + EventQueue.invokeAndWait(test::test); + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } + + public void setupGUI () { + frame = new Frame("InitialBackgroundSettingTest frame"); + tf = new TextField("I am the TextField"); + ta = new TextArea("I am the TextArea"); + choice = new Choice(); + list = new List(); + bar = new Scrollbar(Scrollbar.HORIZONTAL); + button = new Button("I am the button"); + frame.setBackground(Color.red); + frame.setLayout(new GridLayout(7, 1)); + frame.add(button); + frame.add(bar); + frame.add(choice); + frame.add(list); + frame.add(tf); + frame.add(ta); + frame.setVisible(true); + frame.setBounds (400, 0, 300, 300); + } + + public void test() { + boolean passed = true; + System.out.println("Button background color is:" + + button.getBackground()); + if (Color.red.equals(button.getBackground())) { + System.err.println("Button background is red"); + passed = false; + } + System.out.println("Scrollbar background color is:" + + bar.getBackground()); + if (Color.red.equals(bar.getBackground())) { + System.err.println("ScrollBar background is red"); + passed = false; + } + System.out.println("Choice background color is:" + + choice.getBackground()); + if (Color.red.equals(choice.getBackground())) { + System.err.println("Choice background is red"); + passed = false; + } + System.out.println("List background color is:" + + list.getBackground()); + if (Color.red.equals(list.getBackground())) { + System.err.println("List background is red"); + passed = false; + } + System.out.println("TextField background color is:" + + tf.getBackground()); + if (Color.red.equals(tf.getBackground())) { + System.err.println("TextField background is red"); + passed = false; + } + System.out.println("TextArea background color is:" + + ta.getBackground()); + if (Color.red.equals(ta.getBackground())) { + System.err.println("TextArea background is red"); + passed = false; + } + + if (!passed) { + throw new RuntimeException("One or more component inherited" + + " background from a Frame"); + } + } + + public void dispose() { + frame.dispose(); + } +} diff --git a/test/jdk/java/awt/Component/ComponentEventTest.java b/test/jdk/java/awt/Component/ComponentEventTest.java new file mode 100644 index 0000000000000..bfbfed336a017 --- /dev/null +++ b/test/jdk/java/awt/Component/ComponentEventTest.java @@ -0,0 +1,357 @@ +/* + * 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.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.Point; +import java.awt.Robot; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.InputEvent; +import java.lang.reflect.InvocationTargetException; + +import jdk.test.lib.Platform; + +/* + * @test + * @key headful + * @bug 8333403 + * @summary Test performs various operations to check components events are triggered properly. + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main ComponentEventTest + */ +public class ComponentEventTest { + + private static final int DELAY = 500; + + private static Frame frame; + private static Robot robot; + + private static Component[] components; + + private static volatile Point centerPoint; + + private static volatile boolean componentHidden; + private static volatile boolean componentShown; + private static volatile boolean componentMoved; + private static volatile boolean componentResized; + + private static final ComponentListener componentListener = + new ComponentListener() { + + @Override + public void componentShown(ComponentEvent e) { + System.out.println("ComponentShown: " + e.getSource()); + componentShown = true; + } + + @Override + public void componentResized(ComponentEvent e) { + System.out.println("ComponentResized: " + e.getSource()); + componentResized = true; + } + + @Override + public void componentMoved(ComponentEvent e) { + System.out.println("ComponentMoved: " + e.getSource()); + componentMoved = true; + } + + @Override + public void componentHidden(ComponentEvent e) { + System.out.println("ComponentHidden: " + e.getSource()); + componentHidden = true; + } + }; + + private static void initializeGUI() { + frame = new Frame("Component Event Test"); + frame.setLayout(new FlowLayout()); + + Panel panel = new Panel(); + Button button = new Button("Button"); + Label label = new Label("Label"); + List list = new List(); + list.add("One"); + list.add("Two"); + list.add("Three"); + Choice choice = new Choice(); + choice.add("Red"); + choice.add("Orange"); + choice.add("Yellow"); + Checkbox checkbox = new Checkbox("Checkbox"); + Scrollbar scrollbar = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255); + TextField textfield = new TextField(15); + TextArea textarea = new TextArea(5, 15); + + components = new Component[] { panel, button, label, list, choice, + checkbox, scrollbar, textfield, textarea, frame }; + + for (int i = 0; i < components.length - 1; i++) { + components[i].addComponentListener(componentListener); + frame.add(components[i]); + } + frame.addComponentListener(componentListener); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + + EventQueue.invokeAndWait(ComponentEventTest::initializeGUI); + robot.waitForIdle(); + robot.delay(DELAY); + + doTest(); + + System.out.println("Test PASSED"); + } finally { + EventQueue.invokeAndWait(ComponentEventTest::disposeFrame); + } + } + + private static void doTest() + throws InvocationTargetException, InterruptedException { + // Click the frame to ensure it gains focus + clickFrame(); + + robot.delay(DELAY); + + for (int i = 0; i < components.length; i++) { + for (boolean state : new boolean[] { true, false }) { + doTest(components[i], state); + } + } + + robot.delay(DELAY); + + System.out.println("Iconify frame"); + resetValues(); + testIconifyFrame(); + + System.out.println("Deiconify frame"); + resetValues(); + testDeiconifyFrame(); + } + + private static void clickFrame() + throws InvocationTargetException, InterruptedException { + EventQueue.invokeAndWait(() -> { + Point location = frame.getLocationOnScreen(); + Dimension size = frame.getSize(); + centerPoint = new Point(location.x + size.width / 2, + location.y + size.height / 2); + }); + + robot.mouseMove(centerPoint.x, centerPoint.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } + + private static void testIconifyFrame() + throws InvocationTargetException, InterruptedException { + EventQueue.invokeAndWait(() -> frame.setExtendedState(Frame.ICONIFIED)); + + robot.waitForIdle(); + robot.delay(DELAY); + if (componentShown || componentHidden || componentMoved + || componentResized) { + throw new RuntimeException( + "ComponentEvent triggered when frame is iconified"); + } + } + + private static void testDeiconifyFrame() + throws InvocationTargetException, InterruptedException { + EventQueue.invokeAndWait(() -> frame.setExtendedState(Frame.NORMAL)); + + robot.waitForIdle(); + robot.delay(DELAY); + + /* + * Because of the different behavior between MS Windows and other OS, we + * receive native events WM_SIZE and WM_MOVE on Windows when the frame + * state changes from iconified to normal. AWT sends these events to + * components when it receives the events from the native system. See + * JDK-6754618 for more information. + */ + + if (componentShown || componentHidden) { + throw new RuntimeException( + "FAIL: componentShown or componentHidden triggered " + + "when frame set to normal"); + } + + if (Platform.isWindows() && (!componentMoved || !componentResized)) { + throw new RuntimeException( + "FAIL: componentMoved or componentResized wasn't triggered " + + "when frame set to normal"); + } + if (!Platform.isWindows() && (componentMoved || componentResized)) { + throw new RuntimeException( + "FAIL: componentMoved or componentResized triggered " + + "when frame set to normal"); + } + } + + private static void doTest(final Component currentComponent, boolean enable) + throws InvocationTargetException, InterruptedException { + + System.out.println("Component " + currentComponent); + System.out.println(" enabled " + enable); + + EventQueue.invokeAndWait(() -> { + currentComponent.setEnabled(enable); + revalidateFrame(); + }); + + robot.delay(DELAY); + + resetValues(); + EventQueue.invokeAndWait(() -> { + currentComponent.setVisible(false); + revalidateFrame(); + }); + + robot.delay(DELAY); + if (!componentHidden) { + throw new RuntimeException("FAIL: ComponentHidden not triggered for" + + currentComponent.getClass()); + } + + resetValues(); + EventQueue.invokeAndWait(() -> { + currentComponent.setVisible(false); + revalidateFrame(); + }); + + robot.delay(DELAY); + if (componentHidden) { + throw new RuntimeException("FAIL: ComponentHidden triggered when " + + "setVisible(false) called for a hidden " + + currentComponent.getClass()); + } + + resetValues(); + EventQueue.invokeAndWait(() -> { + currentComponent.setVisible(true); + revalidateFrame(); + }); + + robot.delay(DELAY); + if (!componentShown) { + throw new RuntimeException("FAIL: ComponentShown not triggered for " + + currentComponent.getClass()); + } + + resetValues(); + EventQueue.invokeAndWait(() -> { + currentComponent.setVisible(true); + revalidateFrame(); + }); + + robot.delay(DELAY); + if (componentShown) { + throw new RuntimeException("FAIL: ComponentShown triggered when " + + "setVisible(true) called for a shown " + + currentComponent.getClass()); + } + + resetValues(); + EventQueue.invokeAndWait(() -> { + currentComponent.setLocation(currentComponent.getLocation().x + 1, + currentComponent.getLocation().y); + revalidateFrame(); + }); + + robot.delay(DELAY); + if (!componentMoved) { + throw new RuntimeException("FAIL: ComponentMoved not triggered for " + + currentComponent.getClass()); + } + + resetValues(); + EventQueue.invokeAndWait(() -> { + currentComponent.setSize(currentComponent.getSize().width + 1, + currentComponent.getSize().height); + revalidateFrame(); + }); + + robot.delay(DELAY); + if (!componentResized) { + throw new RuntimeException("FAIL: ComponentResized not triggered " + + "when size increases for " + currentComponent.getClass()); + } + + resetValues(); + EventQueue.invokeAndWait(() -> { + currentComponent.setSize(currentComponent.getSize().width - 1, + currentComponent.getSize().height); + revalidateFrame(); + }); + + robot.delay(DELAY); + if (!componentResized) { + throw new RuntimeException("FAIL: ComponentResized not triggered " + + "when size decreases for " + currentComponent.getClass()); + } + + System.out.println("\n"); + } + + private static void revalidateFrame() { + frame.invalidate(); + frame.validate(); + } + + private static void resetValues() { + componentShown = false; + componentHidden = false; + componentMoved = false; + componentResized = false; + } + + private static void disposeFrame() { + if (frame != null) { + frame.dispose(); + } + } +} diff --git a/test/jdk/java/awt/Component/ComponentLeakTest/ComponentLeakTest.java b/test/jdk/java/awt/Component/ComponentLeakTest/ComponentLeakTest.java new file mode 100644 index 0000000000000..2936880dde6c6 --- /dev/null +++ b/test/jdk/java/awt/Component/ComponentLeakTest/ComponentLeakTest.java @@ -0,0 +1,769 @@ +/* + * 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 + * 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 + * @requires (os.family != "linux") + * @bug 4119609 4149812 4136116 4171960 4170095 4294016 4343272 + * @summary This test verifies that java.awt objects are being garbage + * collected correctly. That is, it ensures that unneeded + * references (such as JNI global refs or refs in static arrays) + * do not remain after the object is disposed. + * @run main/othervm ComponentLeakTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Canvas; +import java.awt.CardLayout; +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.LayoutManager; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.Map; +import java.util.HashMap; + +public class ComponentLeakTest { + + public static void main(String[] args) { + final int iter = 5; + + for(int count = 0; count < iter; count++) { + MainFrame f = new MainFrame(); + MainWindow w = new MainWindow(f); + MainDialog d = new MainDialog(f); + TestFileDialog fd = new TestFileDialog(f, "TestFileDialog"); + fd.addNotify(); // fd.show() hangs + + fd.dispose(); + d.dispose(); + w.dispose(); + f.dispose(); + } + + // Test layout managers + Frame border = new Frame(); + border.setLayout(new BorderLayout()); + Frame card = new Frame(); + card.setLayout(new CardLayout()); + Frame flow = new Frame(); + flow.setLayout(new FlowLayout()); + Frame gridBag = new Frame(); + gridBag.setLayout(new GridBagLayout()); + Frame grid = new Frame(); + grid.setLayout(new GridLayout(1, 2)); + + for (int count = 0; count < iter; count++) { + border.add(new BorderTestButton("BorderTest"), + BorderLayout.WEST); + border.add(new BorderTestButton("BorderTest"), + BorderLayout.EAST); + card.add(new CardTestButton("CardTest"), "card0"); + card.add(new CardTestButton("CardTest"), "card1"); + flow.add(new FlowTestButton()); + flow.add(new FlowTestButton()); + gridBag.add(new GridBagTestButton(), new GridBagConstraints()); + gridBag.add(new GridBagTestButton(), new GridBagConstraints()); + grid.add(new GridTestButton()); + grid.add(new GridTestButton()); + + border.removeAll(); + card.removeAll(); + flow.removeAll(); + gridBag.removeAll(); + grid.removeAll(); + } + + gc(5); + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + } + + freeReferences(); + reportLeaks(); + System.err.println("Test passed."); + } + + public static void initWindow(Window w) { + w.setSize(600, 400); + w.setLayout(new FlowLayout()); + + // peered components + w.add(new TestButton("Button")); + w.add(new TestCanvas()); + w.add(new TestCheckbox("Checkbox", true)); + TestChoice choice = new TestChoice(); + choice.add("Choice 1"); + choice.add("Choice Two"); + w.add(choice); + w.add(new TestLabel("Label")); + TestList list = new TestList(); + list.add("List 1"); + list.add("List Two"); + w.add(list); + w.add(new TestScrollbar(Scrollbar.VERTICAL)); + w.add(new TestScrollbar(Scrollbar.HORIZONTAL)); + TestScrollPane scrollpane = new TestScrollPane(); + scrollpane.add(new TestButton("Button in a scrollpane")); + w.add(scrollpane); + w.add(new TestTextArea("TextArea", 3, 30)); + w.add(new TestTextField("TextField")); + + // nested components + TestPanel panel1 = new TestPanel(); + panel1.setLayout(new FlowLayout()); + panel1.setBackground(Color.red); + w.add(panel1); + + panel1.add(new TestButton("level 2")); + + Panel panel2 = new Panel(); + panel2.setLayout(new FlowLayout()); + panel2.setBackground(Color.green); + panel1.add(panel2); + + panel2.add(new TestButton("level 3")); + + w.add(new TestLightweight("Lightweight")); + } + + private static ReferenceQueue queue = new ReferenceQueue(); + private static Map refs = new HashMap(); + + public static void register(Object obj) { + PhantomReference ref = new PhantomReference(obj, queue); + refs.put(ref, obj.getClass().getName()); + } + + private static void gc() { + System.gc(); + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + throw new RuntimeException("Test was interrupted"); + } + } + + private static void gc(int num) { + for (; num > 0; num--) { + gc(); + } + } + + public static void freeReferences() { + System.err.println("Total references: " + refs.size()); + boolean wasFreed = false; + do { + Object[] arr = new Object[2000]; + gc(5); + Reference ref = null; + wasFreed = false; + while ((ref = queue.poll()) != null) { + refs.remove(ref); + wasFreed = true; + gc(); + } + } while (wasFreed); + } + + public static void reportLeaks() { + for (Reference ref : refs.keySet()) { + System.err.println("Leaked " + refs.get(ref)); + } + + if (refs.size() > 0) { + throw new RuntimeException("Some references remained: " + refs.size()); + } + } +} + +class TestFrame extends Frame { + public TestFrame() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestFrame(String title) { + super(title); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestWindow extends Window { + public TestWindow(Frame owner) { + super(owner); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestWindow(Window owner) { + super(owner); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestDialogL extends Dialog { + public TestDialogL(Frame owner) { + super(owner); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestDialogL(Frame owner, boolean modal) { + super(owner, modal); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestDialogL(Frame owner, String title) { + super(owner, title); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestDialogL(Frame owner, String title, boolean modal) { + super(owner, title, modal); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestDialogL(Dialog owner) { + super(owner); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestDialogL(Dialog owner, String title) { + super(owner, title); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestDialogL(Dialog owner, String title, boolean modal) { + super(owner, title, modal); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestFileDialog extends FileDialog { + public TestFileDialog(Frame parent) { + super(parent); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestFileDialog(Frame parent, String title) { + super(parent, title); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestFileDialog(Frame parent, String title, int mode) { + super(parent, title, mode); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestButton extends Button { + public TestButton() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestButton(String title) { + super(title); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestCanvas extends Canvas { + int width = 100; + int height = 100; + + public TestCanvas() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestCanvas(GraphicsConfiguration config) { + super(config); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public void paint(Graphics g) { + g.setColor(Color.blue); + g.fillRoundRect(10, 10, 50, 50, 15, 30); + g.setColor(Color.red); + g.fillOval(70, 70, 25, 25); + } + + public Dimension getPreferredSize() { + return new Dimension(width, height); + } +} + +class TestCheckbox extends Checkbox { + public TestCheckbox() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestCheckbox(String label) { + super(label); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestCheckbox(String label, boolean state) { + super(label, state); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestCheckbox(String label, boolean state, CheckboxGroup group) { + super(label, state, group); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestCheckbox(String label, CheckboxGroup group, boolean state) { + super(label, group, state); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestChoice extends Choice { + public TestChoice() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestLabel extends Label { + public TestLabel() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestLabel(String text) { + super(text); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestLabel(String text, int align) { + super(text, align); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestList extends List { + public TestList() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestList(int rows) { + super(rows); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestList(int rows, boolean multipleMode) { + super(rows, multipleMode); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestScrollbar extends Scrollbar { + public TestScrollbar() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestScrollbar(int orientation) { + super(orientation); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestScrollbar(int orient, int val, int visible, int min, int max) { + super(orient, val, visible, min, max); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestScrollPane extends ScrollPane { + public TestScrollPane() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestScrollPane(int policy) { + super(policy); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestTextField extends TextField { + public TestTextField() { + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } + + public TestTextField(String text) { + super(text); + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } + + public TestTextField(int columns) { + super(columns); + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } + + public TestTextField(String text, int columns) { + super(text, columns); + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestTextArea extends TextArea { + public TestTextArea() { + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } + + public TestTextArea(String text) { + super(text); + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } + + public TestTextArea(int rows, int columns) { + super(rows, columns); + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } + + public TestTextArea(String text, int rows, int columns) { + super(text, rows, columns); + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } + + public TestTextArea(String text, int rows, int columns, int bars) { + super(text, rows, columns, bars); + ComponentLeakTest.register(this); + requestFocus(); + setDropTarget(new TestDropTarget(this)); + } +} + +class TestPanel extends Panel { + public TestPanel() { + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } + + public TestPanel(LayoutManager layout) { + super(layout); + ComponentLeakTest.register(this); + setDropTarget(new TestDropTarget(this)); + } +} +class TestMenu extends Menu { + public TestMenu() { + ComponentLeakTest.register(this); + } + + public TestMenu(String label) { + super(label); + ComponentLeakTest.register(this); + } + + public TestMenu(String label, boolean tearOff) { + super(label, tearOff); + ComponentLeakTest.register(this); + } +} + +class TestMenuItem extends MenuItem { + public TestMenuItem() { + ComponentLeakTest.register(this); + } + public TestMenuItem(String label) { + super(label); + ComponentLeakTest.register(this); + } + + public TestMenuItem(String label, MenuShortcut s) { + super(label, s); + ComponentLeakTest.register(this); + } +} + +class TestMenuBar extends MenuBar { + public TestMenuBar() { + ComponentLeakTest.register(this); + } +} + +class TestPopupMenu extends PopupMenu { + public TestPopupMenu() { + ComponentLeakTest.register(this); + } + + public TestPopupMenu(String label) { + super(label); + ComponentLeakTest.register(this); + } +} + +class TestCheckboxMenuItem extends CheckboxMenuItem { + public TestCheckboxMenuItem() { + ComponentLeakTest.register(this); + } + + public TestCheckboxMenuItem(String label) { + super(label); + ComponentLeakTest.register(this); + } + + public TestCheckboxMenuItem(String label, boolean state) { + super(label, state); + ComponentLeakTest.register(this); + } +} + +class BorderTestButton extends Button { + public BorderTestButton() { + ComponentLeakTest.register(this); + } + + public BorderTestButton(String title) { + super(title); + ComponentLeakTest.register(this); + } +} + +class CardTestButton extends Button { + public CardTestButton() { + ComponentLeakTest.register(this); + } + + public CardTestButton(String title) { + super(title); + ComponentLeakTest.register(this); + } +} + +class FlowTestButton extends Button { + public FlowTestButton() { + ComponentLeakTest.register(this); + } + + public FlowTestButton(String title) { + super(title); + ComponentLeakTest.register(this); + } +} + +class GridBagTestButton extends Button { + public GridBagTestButton() { + ComponentLeakTest.register(this); + } + + public GridBagTestButton(String title) { + super(title); + ComponentLeakTest.register(this); + } +} + +class GridTestButton extends Button { + public GridTestButton() { + ComponentLeakTest.register(this); + } + + public GridTestButton(String title) { + super(title); + ComponentLeakTest.register(this); + } +} + +class TestLightweight extends Component { + String label; + int width = 100; + int height = 30; + + public TestLightweight(String label) { + this.label = label; + ComponentLeakTest.register(this); + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.orange); + g.fillRect(0, 0, d.width, d.height); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() { + return new Dimension(width,height); + } +} + +class TestDropTarget extends DropTarget { + public TestDropTarget(Component comp) { + super(comp, new DropTargetListener() { + public void dragEnter(DropTargetDragEvent dtde) {} + public void dragOver(DropTargetDragEvent dtde) {} + public void dropActionChanged(DropTargetDragEvent dtde) {} + public void dragExit(DropTargetEvent dte) {} + public void drop(DropTargetDropEvent dtde) {} + }); + ComponentLeakTest.register(this); + } +} + +class MainWindow extends TestWindow { + public MainWindow(Frame f) { + super(f); + ComponentLeakTest.initWindow(this); + setVisible(true); + + TestPopupMenu popup = new TestPopupMenu("hi"); + add(popup); + popup.show(this, 5, 5); + } +} + +class MainDialog extends TestDialogL { + public MainDialog(Frame f) { + super(f, "MainDialog", false); + ComponentLeakTest.initWindow(this); + setVisible(true); + + TestPopupMenu popup = new TestPopupMenu("hi"); + add(popup); + popup.show(this, 5, 5); + } +} + +class MainFrame extends TestFrame { + public MainFrame(){ + super("Component Leak Test MainFrame"); + + ComponentLeakTest.initWindow(this); + + TestMenu menu = new TestMenu("Print"); + TestMenu menu2 = new TestMenu("File"); + TestMenu menu3 = new TestMenu("Edit"); + TestMenu menu4 = new TestMenu("ReallyReallyReallyReallyReallyReallyReallyReally" + + "ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLong"); + menu2.setFont(new Font("SansSerif", Font.BOLD, 20)); + menu2.setEnabled(false); + menu3.setFont(new Font("Monospaced", Font.ITALIC, 18)); + menu3.setEnabled(false); + menu4.setEnabled(false); + TestMenuItem itemPrinter = new TestMenuItem("foobar"); + TestMenuItem itemScreen = new TestMenuItem("baz"); + TestCheckboxMenuItem itemCheck = new TestCheckboxMenuItem("yep"); + menu.add(itemPrinter); + menu.add(itemScreen); + menu.add(itemCheck); + TestMenuBar menuBar = new TestMenuBar(); + menuBar.add( menu ); + menuBar.add( menu2 ); + menuBar.add( menu3 ); + menuBar.add( menu4 ); + setMenuBar(menuBar); + + setVisible(true); + + TestPopupMenu popup = new TestPopupMenu("hi"); + add(popup); + popup.show(this, 5, 5); + } +} diff --git a/test/jdk/java/awt/Component/ComponentSerializationTest/ComponentSerializationTest.java b/test/jdk/java/awt/Component/ComponentSerializationTest/ComponentSerializationTest.java new file mode 100644 index 0000000000000..ddf7efffdbdfc --- /dev/null +++ b/test/jdk/java/awt/Component/ComponentSerializationTest/ComponentSerializationTest.java @@ -0,0 +1,354 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4146452 + * @summary Tests serialization of peered and lightweight Components. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ComponentSerializationTest + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.CheckboxMenuItem; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import javax.swing.JPanel; + +public class ComponentSerializationTest extends JPanel { + private MainFrame mf; + private MainWindow mw; + private MainDialog md; + private MainFileDialog mfd; + private static final String INSTRUCTIONS = """ + A Frame, a Window, and a Dialog should appear. From the Frame's + "Serialize" menu, select "Serialize!". Another Frame, Window, and + Dialog should appear exactly on top of the existing ones. The state + and functionality of the two sets of Windows should be identical. If + any errors or exceptions appear in the log area, or if the second set of + Windows is different from the first, the test fails. Otherwise, the + test passes. + """; + + private static final ArrayList toDispose = new ArrayList<>(); + + public ComponentSerializationTest() { + mf = new MainFrame(); + toDispose.add(mf); + mw = new MainWindow(mf); + toDispose.add(mw); + md = new MainDialog(mf); + toDispose.add(md); + mfd = new MainFileDialog(mf); + toDispose.add(mfd); + } + + public static void main(String[] argc) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Component Serialization Test") + .splitUI(ComponentSerializationTest::new) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + for (Window w : toDispose) { + if (w != null) { + EventQueue.invokeAndWait(w::dispose); + } + } + } + + private void initWindow(Window w) { + w.setSize(600, 400); + w.setLayout(new FlowLayout()); + + // peered components + w.add(new Button("Button")); + w.add(new TestCanvas()); + w.add(new Checkbox("Checkbox", true)); + Choice choice = new Choice(); + choice.add("Choice 1"); + choice.add("Choice Two"); + w.add(choice); + w.add(new Label("Label")); + List list = new List(); + list.add("List 1"); + list.add("List Two"); + w.add(list); + w.add(new Scrollbar(Scrollbar.VERTICAL)); + w.add(new Scrollbar(Scrollbar.HORIZONTAL)); + ScrollPane scrollpane = new ScrollPane(); + scrollpane.add(new Button("Button in a scrollpane")); + w.add(scrollpane); + w.add(new TextArea("TextArea", 3, 30)); + w.add(new TextField("TextField")); + + // nested components + Panel panel1 = new Panel(); + panel1.setLayout(new FlowLayout()); + panel1.setBackground(Color.red); + w.add(panel1); + + panel1.add(new Button("level 2")); + + Panel panel2 = new Panel(); + panel2.setLayout(new FlowLayout()); + panel2.setBackground(Color.green); + panel1.add(panel2); + + panel2.add(new Button("level 3")); + + // lightweight components + w.add(new LWButton("LWbutton") ); + + // overlapping components + w.add(new ZOrderPanel()); + } + + class MainWindow extends Window { + public MainWindow(Frame f) { + super(f); + initWindow(this); + setLocation(650, 0); + setVisible(true); + } + } + + class MainDialog extends Dialog { + public MainDialog(Frame f) { + super(f, "MainDialog", false); + initWindow(this); + setLocation(0, 450); + setVisible(true); + } + } + + class MainFileDialog extends FileDialog { + public MainFileDialog(Frame f) { + super(f, "MainFileDialog", FileDialog.SAVE); + setLocation(650, 450); + addNotify(); + } + } + + class MainFrame extends Frame { + public MainFrame() { + super("ComponentSerializationTest"); + initWindow(this); + + Menu menu = new Menu("Serialize"); + Menu menu2 = new Menu("File"); + Menu menu3 = new Menu("Edit"); + Menu menu4 = new Menu("ReallyReallyReallyReallyReallyReallyReallyReally" + + "ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLong"); + menu2.setFont(new Font("SansSerif", Font.BOLD, 20)); + menu2.setEnabled(false); + menu3.setFont(new Font("Monospaced", Font.ITALIC, 18)); + menu3.setEnabled(false); + menu4.setEnabled(false); + MenuItem itemSerialize = new MenuItem("Serialize!"); + CheckboxMenuItem itemCheck = new CheckboxMenuItem("Check me"); + menu.add(itemSerialize); + menu.add(itemCheck); + MenuBar menuBar = new MenuBar(); + menuBar.add(menu); + menuBar.add(menu2); + menuBar.add(menu3); + menuBar.add(menu4); + setMenuBar(menuBar); + + itemSerialize.addActionListener(new ActionSerialize()); + + setLocation(0, 0); + setVisible(true); + } + } + + class ActionSerialize implements ActionListener { + public void actionPerformed(ActionEvent ev) { + Frame f2 = null; + Window w2 = null; + Dialog d2 = null; + FileDialog fd2 = null; + + try { + FileOutputStream fos = new FileOutputStream("tmp"); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(mf); + oos.writeObject(mw); + oos.writeObject(md); + oos.writeObject(mfd); + oos.flush(); + + FileInputStream fis = new FileInputStream("tmp"); + ObjectInputStream ois = new ObjectInputStream(fis); + f2 = (Frame)ois.readObject(); + w2 = (Window)ois.readObject(); + d2 = (Dialog)ois.readObject(); + fd2= (FileDialog)ois.readObject(); + } catch (Exception e) { + PassFailJFrame.log(e.getMessage()); + } + + if (f2 == null || w2 == null || d2 == null || fd2 == null) { + PassFailJFrame.log("ERROR: one of the components was not deserialized."); + PassFailJFrame.log("frame = " + f2); + PassFailJFrame.log("window = " + w2); + PassFailJFrame.log("dialog = " + d2); + PassFailJFrame.log("file dalog = " + fd2); + } + + if (f2 != null) { + toDispose.add(f2); + f2.setVisible(true); + } + if (w2 != null) { + toDispose.add(w2); + w2.setVisible(true); + } + if (d2 != null) { + toDispose.add(d2); + d2.setVisible(true); + } + if (fd2 != null) { + toDispose.add(fd2); + fd2.addNotify(); + } + } + } + + class LWButton extends Component { + String label; + int width = 100; + int height = 30; + + public LWButton(String label) { + super(); + this.label = label; + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.orange); + g.fillRect(0, 0, d.width, d.height); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + } + + class TestCanvas extends Canvas { + int width = 100; + int height = 100; + + public void paint(Graphics g) { + g.setColor(Color.blue); + g.fillRoundRect(10, 10, 50, 50, 15, 30); + g.setColor(Color.red); + g.fillOval(70, 70, 25, 25); + } + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + } + + class ZOrderPanel extends Panel { + public ZOrderPanel() { + setLayout(null); + + Component first, second, third, fourth; + + show(); + first = makeBox("Second", Color.blue, -1); + second = makeBox("First", Color.yellow, 0); + fourth = makeBox("Fourth", Color.red, 2); + third = makeBox("Third", Color.green, 3); + remove(third); + add(third, 2); + validate(); + add(new LWButton("LWButton"), 0); + } + + public Dimension preferredSize() { + return new Dimension(260, 80); + } + + public void layout() { + int i, n; + Insets ins = insets(); + n = countComponents(); + for (i = n - 1; i >= 0; i--) { + Component p = getComponent(i); + p.reshape(ins.left + 40 * i, ins.top + 5 * i, 60, 60); + } + } + + public Component makeBox(String s, Color c, int index) { + Label l = new Label(s); + l.setBackground(c); + l.setAlignment(Label.RIGHT); + add(l, index); + validate(); + return l; + } + } +} diff --git a/test/jdk/java/awt/Component/FlickeringOnScroll/FlickeringOnScroll.java b/test/jdk/java/awt/Component/FlickeringOnScroll/FlickeringOnScroll.java new file mode 100644 index 0000000000000..2119ae7bcc01e --- /dev/null +++ b/test/jdk/java/awt/Component/FlickeringOnScroll/FlickeringOnScroll.java @@ -0,0 +1,139 @@ +/* + * 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 + * 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 6347994 + * @summary REG: Scrollbar, Choice, Checkbox flickers and grays out when scrolling, XToolkit + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FlickeringOnScroll + */ + +import java.awt.BorderLayout; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.lang.reflect.InvocationTargetException; + +public class FlickeringOnScroll extends Frame { + + static final String INSTRUCTIONS = """ + There are five components in the frame: + Scrollbars(vertical and horizontal), a Choice, + a Checkbox and a TextArea + 1) Drag the thumbs of each Scrollbar. + 2) Do the same with Choice's scrollbar. + 3) Focus on Checkbox and press left mouse button or SPACE repeatedly. + 4) Right click inside TextArea and navigate through all menu items + in PopupMenu using the arrow keys. + If you notice some component or its scrollbar flickers on + key/mouse press or drag, press Fail. Otherwise press Pass. + """; + + public FlickeringOnScroll() { + Choice ch = new Choice(); + ch.add("Praveen"); + ch.add("Mohan"); + ch.add("Rakesh"); + ch.add("Menon"); + ch.add("Girish"); + ch.add("Ramachandran"); + ch.add("Elancheran"); + ch.add("Subramanian"); + ch.add("Raju"); + ch.add("Pallath"); + ch.add("Mayank"); + ch.add("Joshi"); + ch.add("Sundar"); + ch.add("Srinivas"); + ch.add("Mandalika"); + Checkbox chb = new Checkbox ("Checkbox", false); + TextArea ta = new TextArea("Text Area"); + Panel panel = new Panel(); + PopupMenu popup = new PopupMenu("Popup"); + MenuItem mi1 = new MenuItem("mi1"); + MenuItem mi2 = new MenuItem("mi2"); + MenuItem mi3 = new MenuItem("mi3"); + MenuItem mi4 = new MenuItem("mi4"); + + setTitle("Flickering Scroll Area Testing Frame"); + setLayout(new FlowLayout()); + add(ch); + add(chb); + add(ta); + + panel.setLayout(new BorderLayout()); + panel.setPreferredSize(new Dimension(200, 200)); + add(panel); + panel.add("Center",new java.awt.Label("Scrollbar flickering test..." ,java.awt.Label.CENTER)); + panel.add("South",new Scrollbar(Scrollbar.HORIZONTAL, 0, 100, 0, 255)); + panel.add("East",new Scrollbar(Scrollbar.VERTICAL, 0, 100, 0, 255)); + + ta.add(popup); + popup.add (mi1); + popup.add (mi2); + popup.add (mi3); + popup.add (mi4); + + ta.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (me.isPopupTrigger()) { + if (popup != null) { + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + } + public void mouseReleased(MouseEvent me) { + if (me.isPopupTrigger()) { + if (popup != null) { + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + } + }); + + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Scroll Area Flickering Repaint") + .testUI(FlickeringOnScroll::new) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Component/FocusRepaintTest/FocusRepaintTest.java b/test/jdk/java/awt/Component/FocusRepaintTest/FocusRepaintTest.java new file mode 100644 index 0000000000000..ecffdfda6135d --- /dev/null +++ b/test/jdk/java/awt/Component/FocusRepaintTest/FocusRepaintTest.java @@ -0,0 +1,83 @@ +/* + * 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 + * 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 4079435 + * @summary Calling repaint() in focus handlers messes up the window. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FocusRepaintTest + */ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.lang.reflect.InvocationTargetException; + +public class FocusRepaintTest extends Frame implements FocusListener { + static final String INSTRUCTIONS = """ + Hit the tab key repeatedly in the Test window. + If any of the buttons disappear press Fail, otherwise press Pass. + """; + + public FocusRepaintTest() { + setTitle("Test"); + setLayout(new FlowLayout()); + setSize(200, 100); + Button b1 = new Button("Close"); + Button b2 = new Button("Button"); + add(b1); + add(b2); + b1.setSize(50, 30); + b2.setSize(50, 30); + b1.addFocusListener(this); + b2.addFocusListener(this); + } + + public void focusGained(FocusEvent e) { + Button b = (Button) e.getSource(); + PassFailJFrame.log("Focus gained for " + b.getLabel()); + b.repaint(); + } + + public void focusLost(FocusEvent e) { + Button b = (Button) e.getSource(); + PassFailJFrame.log("Focus lost for " + b.getLabel()); + b.repaint(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Focus Repaint") + .testUI(FocusRepaintTest::new) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Component/ListDoubleIndentTest/ListDoubleIndentTest.java b/test/jdk/java/awt/Component/ListDoubleIndentTest/ListDoubleIndentTest.java new file mode 100644 index 0000000000000..4c6c1248950e4 --- /dev/null +++ b/test/jdk/java/awt/Component/ListDoubleIndentTest/ListDoubleIndentTest.java @@ -0,0 +1,148 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4185460 + * @summary Container list the indentation is 2x the indent param value + * @key headful + * @run main ListDoubleIndentTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PrintStream; +import java.io.PipedOutputStream; + +import java.lang.reflect.InvocationTargetException; +import java.util.Vector; + +public class ListDoubleIndentTest { + public static void main(final String[] args) throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(new ListDoubleIndentTest()::performTest); + } + + public void performTest() { + boolean bReturn = false; + int iCompCount = 0; + int iNotEqual = 0; + int iIndentWrong = 0; + System.out.println("Test: Check indentation"); + Vector v = new Vector(); + String sLine; + String sReturn; + String sExpTrim; + Button b1, b2, b3, b4, b5; + Frame f = null; + + try { + f = new Frame("ListDoubleIndentTest"); + + f.add(b1 = new Button("North"), BorderLayout.NORTH, 0); + f.add(b2 = new Button("South"), BorderLayout.SOUTH, 1); + f.add(b3 = new Button("East"), BorderLayout.EAST, 2); + f.add(b4 = new Button("West"), BorderLayout.WEST, 3); + f.add(b5 = new Button("Center"), BorderLayout.CENTER, -1); + + String[] sExpected = {f.toString(), b1.toString(), b2.toString(), + b3.toString(), b4.toString(), b5.toString()}; + + iCompCount = f.getComponentCount(); + System.out.println("Component count: " + iCompCount); + + for (int j = 0; j <= 10; j++) { + PipedInputStream pin = new PipedInputStream(); + PrintStream output = new PrintStream(new PipedOutputStream(pin), true); + BufferedReader input = new BufferedReader(new InputStreamReader(pin)); + + f.list(output, j); + + output.flush(); + output.close(); + + while ((sLine = input.readLine()) != null) { + v.addElement(sLine); + } + + for (int i = 0; i < v.size(); i++) { + sReturn = (String)v.elementAt(i); + sExpTrim = sExpected[i].trim(); + + if (!(sExpTrim.equals(sReturn.trim()))) { + System.out.println("iNotEqual"); + ++iNotEqual; + } + + int iSpace = sReturn.lastIndexOf(' ') + 1; + + if (i == 0) { + System.out.println("Indent set at: " + j); + System.out.println("Indent return: " + iSpace); + if (iSpace != j) { + System.out.println("iIndentWrong1"); + ++iIndentWrong; + } + } else { + if (iSpace != (j + 1)) { + System.out.println(iSpace + "; " + j); + ++iIndentWrong; + } + } + System.out.println(sReturn); + } + v.removeAllElements(); + v.trimToSize(); + } + + if (iNotEqual == 0 && iIndentWrong == 0) { + bReturn = true; + } else { + bReturn = false; + } + + } catch(IOException e) { + bReturn = false; + System.out.println ("Unexpected Exception thrown: " + e.getMessage()); + e.printStackTrace(); + } finally { + if (f != null) { + f.dispose(); + } + } + + if (bReturn) { + System.out.println("Test for Container.list Passed"); + } else { + System.out.println("Test for Container.list Failed"); + throw new RuntimeException("Test FAILED"); + } + } +} diff --git a/test/jdk/java/awt/Component/MinMaxSizeDefensive/GetSizesTest.java b/test/jdk/java/awt/Component/MinMaxSizeDefensive/GetSizesTest.java new file mode 100644 index 0000000000000..2b4954879380a --- /dev/null +++ b/test/jdk/java/awt/Component/MinMaxSizeDefensive/GetSizesTest.java @@ -0,0 +1,113 @@ +/* + * 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 4783989 + @summary get(Preferred|Minimum|Maximum)Size() must not return a reference. + The object copy of Dimension class needed. + @key headful + @run main GetSizesTest +*/ + +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.lang.reflect.InvocationTargetException; + +public class GetSizesTest extends Frame { + Button b; + + public static void main(final String[] args) throws InterruptedException, + InvocationTargetException { + GetSizesTest app = new GetSizesTest(); + EventQueue.invokeAndWait(() -> { + try { + app.init(); + app.start(); + } finally { + app.dispose(); + } + }); + } + + public void init() { + b = new Button("button"); + add(b); + } + + public void start () { + setSize(200, 200); + setLocationRelativeTo(null); + setVisible(true); + validate(); + + System.out.println("Test set for Container (Frame)."); + + Dimension dimPref = getPreferredSize(); + dimPref.setSize(101, 101); + if (getPreferredSize().equals(new Dimension(101, 101))) { + throw new RuntimeException("Test Failed for: " + dimPref); + } + System.out.println("getPreferredSize() Passed."); + + Dimension dimMin = getMinimumSize(); + dimMin.setSize(101, 101); + if (getMinimumSize().equals(new Dimension(101, 101))) { + throw new RuntimeException("Test Failed for: " + dimMin); + } + System.out.println("getMinimumSize() Passed."); + + Dimension dimMax = getMaximumSize(); + dimMax.setSize(101, 101); + if (getMaximumSize().equals(new Dimension(101, 101))) { + throw new RuntimeException("Test Failed for: " + dimMax); + } + System.out.println("getMaximumSize() Passed."); + + System.out.println("Test set for Component (Button)."); + + dimPref = b.getPreferredSize(); + dimPref.setSize(33, 33); + if (b.getPreferredSize().equals(new Dimension(33, 33))) { + throw new RuntimeException("Test Failed for: " + dimPref); + } + System.out.println("getPreferredSize() Passed."); + + dimMin = b.getMinimumSize(); + dimMin.setSize(33, 33); + if (b.getMinimumSize().equals(new Dimension(33, 33))) { + throw new RuntimeException("Test Failed for: " + dimMin); + } + System.out.println("getMinimumSize() Passed."); + + dimMax = b.getMaximumSize(); + dimMax.setSize(33, 33); + if (b.getMaximumSize().equals(new Dimension(33, 33))) { + throw new RuntimeException("Test Failed for: " + dimMax); + } + System.out.println("getMaximumSize() Passed."); + System.out.println("GetSizesTest Succeeded."); + } +} diff --git a/test/jdk/java/awt/Component/PaintGlitchTest/PaintGlitchTest.java b/test/jdk/java/awt/Component/PaintGlitchTest/PaintGlitchTest.java new file mode 100644 index 0000000000000..867d82d326297 --- /dev/null +++ b/test/jdk/java/awt/Component/PaintGlitchTest/PaintGlitchTest.java @@ -0,0 +1,225 @@ +/* + * 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 + * 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 4045781 + * @summary Exposed/damaged canvases don't always update correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PaintGlitchTest + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +public class PaintGlitchTest extends Frame { + static final String INSTRUCTIONS = """ + 1. Click on the 'Painting Glitch Test' window and select from + its menu a content type (text, gradient, fill, + AWT components, Swing components etc.). + 2. Select 'Modal Dialog...' to create a dialog. + 3. Drag the dialog over the content very fast + for 10 seconds or so - make sure you + keep dragging while the content is painting. + 4. Verify that the area exposed by the drag (the damaged regions) + always update properly no white areas or bits of the dialog + should be left after the drag operation is + completed (i.e. after you let go of the mouse). + 5. Repeat for all other content types. + 6. If for any content type the damaged dialog is not properly + repainted press Fail. Otherwise press Pass. + """; + + public PaintGlitchTest() { + super("Painting Glitch Test"); + + TextPanel textPanel = new TextPanel(); + GradientPanel gradientPanel = new GradientPanel(); + ComponentPanel componentPanel = new ComponentPanel(); + SwingPanel swingPanel = new SwingPanel(); + + add(textPanel); + + MenuBar menubar = new MenuBar(); + Menu testMenu = new Menu("Test"); + testMenu.add(makeContentItem("Text Lines", textPanel) ); + testMenu.add(makeContentItem("Gradient Fill", gradientPanel) ); + testMenu.add(makeContentItem("AWT Components", componentPanel) ); + testMenu.add(makeContentItem("Swing Components", swingPanel) ); + testMenu.addSeparator(); + MenuItem dialogItem = new MenuItem("Modal Dialog..."); + dialogItem.addActionListener(ev -> new ObscuringDialog(PaintGlitchTest.this).show()); + testMenu.add(dialogItem); + testMenu.addSeparator(); + menubar.add(testMenu); + + setMenuBar(menubar); + setSize(400,300); + } + + public static void main(String args[]) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Repaint Glitch") + .testUI(PaintGlitchTest::new) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } + + public MenuItem makeContentItem(String title, final Component content) { + MenuItem menuItem = new MenuItem(title); + menuItem.addActionListener( + ev -> { + remove(0); + add(content); + validate(); + } + ); + + return menuItem; + } +} + +class GradientPanel extends Canvas { + public void paint(Graphics g) { + long ms = System.currentTimeMillis(); + // just paint something that'll take a while + int x, y; + int width = getSize().width; + int height = getSize().height; + int step = 8; + + for (x = 0; x < width; x += step) { + for (y = 0; y < height; y += step) { + int red = (255 * y) / height; + int green = (255 * x * y) / (width * height); + int blue = (255 * x) / width; + + Color color = new Color(red, green, blue); + g.setColor(color); + g.fillRect(x, y, step, step); + } + } + long time = System.currentTimeMillis() - ms; + PassFailJFrame.log("GradientPanel paint took " + time + " ms"); + } + + public Dimension getPreferredSize() { + return new Dimension(200,1000); + } +} + +class TextPanel extends Canvas { + public void paint(Graphics g) { + long ms = System.currentTimeMillis(); + Font font = new Font("SanSerif", Font.ITALIC, 12); + + g.setFont(font); + // just paint something that'll take a while + int x, y; + int height = getHeight(); + int step = 16; + + for (x = y = 0; y < height; y += step) { + g.drawString(y + " : The quick brown fox jumps over the lazy dog. " + + "The rain in Spain falls mainly on the plain.", x, y); + } + long time = System.currentTimeMillis() - ms; + PassFailJFrame.log("TextPanel paint took " + time + " ms"); + } + + public Dimension getPreferredSize() { + return new Dimension(640,1000); + } +} + +class ComponentPanel extends Panel { + ComponentPanel() { + add(new Label("Label")); + add(new Button("Button")); + add(new Checkbox("Checkbox")); + Choice c = new Choice(); + c.add("choice"); + java.awt.List l = new java.awt.List(); + l.add("list"); + add(new Scrollbar()); + add(new TextField("TextField")); + add(new TextArea("TextArea")); + add(new Panel()); + add(new Canvas()); + } +} + +class SwingPanel extends JPanel { + SwingPanel() { + add(new JLabel("JLabel")); + add(new JButton("JButton")); + add(new JCheckBox("JCheckBox")); + JComboBox c = new JComboBox(); + JList l = new JList(); + add(new JScrollBar()); + add(new JTextField("This is a JTextField with some text in it to make it longer.")); + add(new JTextArea("This is a JTextArea with some text in it to make it longer.")); + } +} + +class ObscuringDialog extends Dialog { + ObscuringDialog(Frame f) { + super(f, "Obscuring Dialog"); + Button ok = new Button("OK, go away"); + ok.addActionListener(ev -> dispose()); + add(ok); + pack(); + } +} diff --git a/test/jdk/java/awt/Component/ProcessEvent/ProcessEvent.java b/test/jdk/java/awt/Component/ProcessEvent/ProcessEvent.java new file mode 100644 index 0000000000000..3dfd5a0403804 --- /dev/null +++ b/test/jdk/java/awt/Component/ProcessEvent/ProcessEvent.java @@ -0,0 +1,455 @@ +/* + * 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 4292099 + * @summary AWT Event delivery to processEvent + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ProcessEvent + */ + +import java.awt.AWTEvent; +import java.awt.AWTEventMulticaster; +import java.awt.Adjustable; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.ItemSelectable; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; +import java.lang.reflect.InvocationTargetException; + +public class ProcessEvent extends Frame { + + static final String INSTRUCTIONS = """ + Press each of the four buttons for ActionEvent, AdjustmentEvent, + ItemEvent and TextEvent. If a message for each corresponding event + appears in the log area and says the event listener was + called, then press Pass otherwise press Fail. + """; + ActionBtn af; + AdjustmentBtn adjf; + ItemBtn itf; + TextBtn txtf; + + public ProcessEvent() { + setLayout(new FlowLayout()); + add(af = new ActionBtn()); + af.setBackground(Color.green); + + add(adjf = new AdjustmentBtn()); + adjf.setBackground(Color.green); + + add(itf = new ItemBtn()); + itf.setBackground(Color.green); + + add(txtf = new TextBtn()); + txtf.setBackground(Color.green); + + // These action listeners simply provide feedback of when + // the event is delivered properly. + af.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + PassFailJFrame.log(af.getText() + + ": action listener called: " + + ae.toString()); + } + }); + + adjf.addAdjustmentListener(new AdjustmentListener() { + public void adjustmentValueChanged(AdjustmentEvent ae) { + PassFailJFrame.log(adjf.getText() + + ": adjustment listener called: " + + ae.toString()); + } + }); + + itf.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + PassFailJFrame.log(itf.getText() + + ": item listener called: " + + e.toString()); + } + }); + + txtf.addTextListener(new TextListener() { + public void textValueChanged(TextEvent e) { + PassFailJFrame.log(txtf.getText() + + ": text listener called: " + + e.toString()); + } + }); + + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Process Events Test") + .testUI(ProcessEvent::new) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} + +class ButtonComponent extends Component implements ItemSelectable, Adjustable { + + transient protected TextListener textListener; + transient ActionListener actionListener; + transient AdjustmentListener adjustmentListener; + transient ItemListener itemListener; + String actionCommand = null; + + String text = null; + + public ButtonComponent(String label) { + super(); + text = label; + } + + public String getText() { + return text; + } + + public Dimension getPreferredSize() { + return new Dimension(200, 30); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public String getActionCommand() { + if (actionCommand == null) + return getText(); + else + return actionCommand; + } + + public void setActionCommand(String ac) { + actionCommand = ac; + } + + // ActionEvent listener support + + public synchronized void addActionListener(ActionListener l) { + if (l == null) { + return; + } + enableEvents(AWTEvent.ACTION_EVENT_MASK); + actionListener = AWTEventMulticaster.add(actionListener, l); + } + + public synchronized void removeActionListener(ActionListener l) { + if (l == null) { + return; + } + actionListener = AWTEventMulticaster.remove(actionListener, l); + } + + // AdjustmentEvent listener support + + public synchronized void addAdjustmentListener(AdjustmentListener l) { + if (l == null) { + return; + } + enableEvents(AWTEvent.ADJUSTMENT_EVENT_MASK); + adjustmentListener = AWTEventMulticaster.add(adjustmentListener, l); + } + + public synchronized void removeAdjustmentListener(AdjustmentListener l) { + if (l == null) { + return; + } + adjustmentListener = AWTEventMulticaster.remove(adjustmentListener, l); + } + + // ItemEvent listener support + + public synchronized void addItemListener(ItemListener l) { + if (l == null) { + return; + } + enableEvents(AWTEvent.ITEM_EVENT_MASK); + itemListener = AWTEventMulticaster.add(itemListener, l); + } + + public synchronized void removeItemListener(ItemListener l) { + if (l == null) { + return; + } + itemListener = AWTEventMulticaster.remove(itemListener, l); + } + + // TextEvent listener support + + public synchronized void addTextListener(TextListener l) { + if (l == null) { + return; + } + enableEvents(AWTEvent.TEXT_EVENT_MASK); + textListener = AWTEventMulticaster.add(textListener, l); + } + + public synchronized void removeTextListener(TextListener l) { + if (l == null) { + return; + } + textListener = AWTEventMulticaster.remove(textListener, l); + } + + // Implement the processEvent and processXXXEvent methods to + // handle reception and processing of the event types. + + protected void processEvent(AWTEvent e) { + if (e instanceof ActionEvent) { + processActionEvent((ActionEvent) e); + return; + } + if (e instanceof AdjustmentEvent) { + processAdjustmentEvent((AdjustmentEvent) e); + return; + } + if (e instanceof ItemEvent) { + processItemEvent((ItemEvent) e); + return; + } + if (e instanceof TextEvent) { + processTextEvent((TextEvent) e); + return; + } + super.processEvent(e); + } + + protected void processActionEvent(ActionEvent e) { + if (actionListener != null) { + actionListener.actionPerformed(e); + } + } + + protected void processAdjustmentEvent(AdjustmentEvent e) { + if (adjustmentListener != null) { + adjustmentListener.adjustmentValueChanged(e); + } + } + + protected void processItemEvent(ItemEvent e) { + if (itemListener != null) { + itemListener.itemStateChanged(e); + } + } + + protected void processTextEvent(TextEvent e) { + if (textListener != null) { + textListener.textValueChanged(e); + } + } + + public void paint(Graphics g) { + Dimension dim = getSize(); + g.clearRect(0, 0, dim.width, dim.height); + g.setColor(getForeground()); + g.drawString(text, 2, dim.height - 2); + } + + /** + * Returns the selected items or null if no items are selected. + */ + public Object[] getSelectedObjects() { + return null; + } + + /** + * Gets the orientation of the adjustable object. + */ + public int getOrientation() { + return 0; + } + + /** + * Gets the minimum value of the adjustable object. + */ + public int getMinimum() { + return 0; + } + + /** + * Sets the minimum value of the adjustable object. + * + * @param min the minimum value + */ + public void setMinimum(int min) { + } + + /** + * Gets the maximum value of the adjustable object. + */ + public int getMaximum() { + return 0; + } + + /** + * Sets the maximum value of the adjustable object. + * + * @param max the maximum value + */ + public void setMaximum(int max) { + } + + /** + * Gets the unit value increment for the adjustable object. + */ + public int getUnitIncrement() { + return 0; + } + + /** + * Sets the unit value increment for the adjustable object. + * + * @param u the unit increment + */ + public void setUnitIncrement(int u) { + } + + /** + * Gets the block value increment for the adjustable object. + */ + public int getBlockIncrement() { + return 0; + } + + /** + * Sets the block value increment for the adjustable object. + * + * @param b the block increment + */ + public void setBlockIncrement(int b) { + } + + /** + * Gets the length of the propertional indicator. + */ + public int getVisibleAmount() { + return 0; + } + + /** + * Sets the length of the proportionl indicator of the + * adjustable object. + * + * @param v the length of the indicator + */ + public void setVisibleAmount(int v) { + } + + /** + * Gets the current value of the adjustable object. + */ + public int getValue() { + return 0; + } + + /** + * Sets the current value of the adjustable object. This + * value must be within the range defined by the minimum and + * maximum values for this object. + * + * @param v the current value + */ + public void setValue(int v) { + } + +} + +class ActionBtn extends ButtonComponent { + public ActionBtn() { + super("ActionEvent"); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + ActionEvent ae = new ActionEvent(e.getSource(), + ActionEvent.ACTION_PERFORMED, + getActionCommand()); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ae); + } + }); + } +} + +class AdjustmentBtn extends ButtonComponent { + public AdjustmentBtn() { + super("AdjustmentEvent"); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + AdjustmentEvent ae = new AdjustmentEvent((Adjustable) e.getSource(), + AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED, + 1, 1); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ae); + } + }); + } +} + +class ItemBtn extends ButtonComponent { + public ItemBtn() { + super("ItemEvent"); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + ItemEvent ae = new ItemEvent((ItemSelectable) e.getSource(), + ItemEvent.ITEM_STATE_CHANGED, + e.getSource(), 1); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ae); + } + }); + } +} + +class TextBtn extends ButtonComponent { + public TextBtn() { + super("TextEvent"); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + TextEvent ae = new TextEvent(e.getSource(), + TextEvent.TEXT_VALUE_CHANGED); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ae); + } + }); + } +} diff --git a/test/jdk/java/awt/Component/SetFontOrBackground/SetBgrFnt.java b/test/jdk/java/awt/Component/SetFontOrBackground/SetBgrFnt.java new file mode 100644 index 0000000000000..02707ad578475 --- /dev/null +++ b/test/jdk/java/awt/Component/SetFontOrBackground/SetBgrFnt.java @@ -0,0 +1,123 @@ +/* + * 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 4906548 4921849 + * @summary Checks that setFont and setBackground have immediate effect + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetBgrFnt + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.lang.reflect.InvocationTargetException; + +public class SetBgrFnt extends Frame { + static final String INSTRUCTIONS = """ + 1. Press a button marked 'Switch fonts' + fonts in three components below (a Button, a Checkbox + and a Label) must change immediately. + + 2. Press a button marked 'Switch background' + background of three components and canvas must change. + MacOS is an exception - AWT buttons on macOS so not + change color so on macOS only canvas, checkbox + and a label should change background. + + If this is the behavior that you observe press Pass, + otherwise press Fail. + """; + Label la; + Button bu, bu1, bu2; + Checkbox cb; + Font font1, font2; + Canvas ca; + boolean bToggleFont = true; + boolean bToggleBg = true; + + public SetBgrFnt() { + bu = new Button("Switch fonts"); + bu1 = new Button("Switch background"); + bu2 = new Button("I'm a button"); + cb = new Checkbox("Checkbox I am"); + la = new Label("I am a label"); + ca = new Canvas(); + font1 = new Font("Serif", Font.ITALIC, 22); + font2 = new Font("SansSerif", Font.PLAIN, 10); + la.setFont(font1); + cb.setFont(font1); + bu2.setFont(font1); + bu.addActionListener(ae -> { + if (bToggleFont) { + la.setFont(font2); + cb.setFont(font2); + bu2.setFont(font2); + } else { + la.setFont(font1); + cb.setFont(font1); + bu2.setFont(font1); + } + bToggleFont = !bToggleFont; + }); + + bu1.addActionListener(ae -> { + if (bToggleBg) { + ca.setBackground(Color.YELLOW); + setBackground(Color.YELLOW); + } else { + ca.setBackground(Color.GREEN); + setBackground(Color.GREEN); + } + bToggleBg = !bToggleBg; + }); + + setLayout(new GridLayout(8, 1)); + add(bu); + add(bu1); + add(new Label()); + add("South", la); + add("South", bu2); + add("South", cb); + add("South", ca); + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Set Font and Background Test") + .testUI(SetBgrFnt::new) + .instructions(INSTRUCTIONS) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Component/ZOrderTest/ZOrderTest.java b/test/jdk/java/awt/Component/ZOrderTest/ZOrderTest.java new file mode 100644 index 0000000000000..15003f753a6fe --- /dev/null +++ b/test/jdk/java/awt/Component/ZOrderTest/ZOrderTest.java @@ -0,0 +1,159 @@ +/* + * 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 + * 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 4059430 + * @summary Test for component z-ordering consistency + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ZOrderTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Panel; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public class ZOrderTest { + public final static String INSTRUCTIONS = """ + The ZOrderTest creates two frames. + - Frame 1 has components added to an intermediate panel + - Frame 2 has components added directly to the frame itself + Verify that the components are in the correct z-order. Lower numbered + components should overlap higher numbered ones (e.g. component zero should + appear on top of component one). + Both frames should have the same component ordering, and this ordering should + be the same on all supported operating systems. + """; + + public static void main(String [] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Component ZOrder Test") + .testUI(ZOrderTest::makeFrames) + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } + + private static List makeFrames() { + Frame frame, frame2; + + // test adding components to panel on a frame + frame = new Frame("ZOrderTest(1) for 4059430"); + frame.pack(); + frame.show(); + Panel panel = new ZOrderPanel(); + frame.setBounds(0, 0, 500, 350); + frame.setLayout(new GridLayout()); + frame.add(panel); + doTest(panel); + + // test adding components directly to frame + frame2 = new ZOrderTestFrame("ZOrderTest(2) for 4059430"); + frame2.pack(); + frame2.show(); + frame2.setBounds(80, 80, 500, 350); + doTest(frame2); + + return List.of(frame, frame2); + } + + /* + * This tests various boundary conditions with z-ordering + * - inserting at the top of the z-order + * - inserting at the bottom of the z-order + * - inserting in the middle of the z-order + */ + private static void doTest(Container cont) { + Component compZero, compOne, compTwo, compThree, compFour; + + compZero = makeBox(cont, "Comp One", Color.blue, -1); + // insert on top + compOne = makeBox(cont, "Comp Zero", Color.yellow, 0); + // put at the back + compThree = makeBox(cont, "Comp Three", Color.red, 2); + // insert in last position + compTwo = makeBox(cont, "Comp Two", Color.green, 3); + // swap compTwo and compThree to correct positions + cont.remove(compTwo); + cont.add(compTwo, 2); + // one more test of adding to the end + compFour = makeBox(cont, "Comp Four", Color.magenta, -1); + // re-validate so components cascade into proper place + cont.validate(); + } + + private static Component makeBox(Container cont, String s, Color c, int index) { + Label l = new Label(s); + l.setBackground(c); + l.setAlignment(Label.RIGHT); + if (index == -1) { + cont.add(l); // semantically equivalent to -1, but why not test this too + } else { + cont.add(l, index); + } + cont.validate(); + return l; + } + + /** + * Cascades components across the container so + * that they overlap, demonstrating their z-ordering + */ + static void doCascadeLayout(Container cont) { + int i, n; + Insets ins = cont.insets(); + n = cont.countComponents(); + for (i = n - 1; i >= 0; i--) { + Component comp = cont.getComponent(i); + comp.reshape(ins.left + 75 * i, ins.top + 30 * i, 100, 100); + } + } +} + +class ZOrderPanel extends Panel { + public void layout() { + ZOrderTest.doCascadeLayout(this); + } +} + +class ZOrderTestFrame extends Frame +{ + public ZOrderTestFrame(String title) { + super(title); + } + + public void layout() { + ZOrderTest.doCascadeLayout(this); + } +} diff --git a/test/jdk/java/awt/Cursor/BlockedWindowTest/BlockedWindowTest.java b/test/jdk/java/awt/Cursor/BlockedWindowTest/BlockedWindowTest.java new file mode 100644 index 0000000000000..f1313dbb74246 --- /dev/null +++ b/test/jdk/java/awt/Cursor/BlockedWindowTest/BlockedWindowTest.java @@ -0,0 +1,105 @@ +/* + * 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 + * 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 6391547 + * @summary Test if the JTextField's cursor is changed when there is a modal dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BlockedWindowTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Cursor; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +class MyDialog extends Dialog implements ActionListener { + MyDialog(Frame owner) { + super(owner, "Modal dialog", true); + setBounds(owner.getX() + 150, owner.getY() + 150, 100, 100); + setLayout(new BorderLayout()); + Button b = new Button("Close"); + add(b, "South"); + b.addActionListener(this); + setVisible(true); + } + + public void actionPerformed(ActionEvent ae) { + setVisible(false); + this.dispose(); + } +} + +class MyFrame extends Frame implements ActionListener { + Dialog d; + + public MyFrame() { + super("ManualYesNoTest"); + Button b = new Button("Click here"); + TextField tf = new TextField("A text field"); + b.addActionListener(this); + setLayout(new BorderLayout()); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + add(b, "South"); + add(tf, "North"); + setSize(300, 300); + } + + public void actionPerformed(ActionEvent ae) { + d = new MyDialog(this); + } +} + +public class BlockedWindowTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Verify that the hand cursor is displayed over the window and + text cursor over TextField. + Click the button in the window to display a modal dialog. + Verify that default cursor is displayed over the window + and over TextField now. + Then close modal dialog and verify that hand cursor is + displayed over window and text cursor over TextField. + If so, press PASS, else press FAIL. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(BlockedWindowTest::createUI) + .build() + .awaitAndCheck(); + } + + public static MyFrame createUI() { + MyFrame f = new MyFrame(); + return f; + } +} diff --git a/test/jdk/java/awt/Cursor/CursorDragTest/ListDragCursor.java b/test/jdk/java/awt/Cursor/CursorDragTest/ListDragCursor.java new file mode 100644 index 0000000000000..32923f1d78b0a --- /dev/null +++ b/test/jdk/java/awt/Cursor/CursorDragTest/ListDragCursor.java @@ -0,0 +1,84 @@ +/* + * 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 + * 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 4313052 + * @summary Test cursor changes after mouse dragging ends + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListDragCursor + */ + +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.List; +import java.awt.Panel; +import java.awt.TextArea; + +public class ListDragCursor { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Move mouse to the TextArea. + 2. Press the left mouse button. + 3. Drag mouse to the list. + 4. Release the left mouse button. + + If the mouse cursor starts as a Text Line Cursor and changes + to a regular Pointer Cursor, then Hand Cursor when hovering + the list, pass the test. This test fails if the cursor does + not update at all when pointing over the different components. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ListDragCursor::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame frame = new Frame("Cursor change after drag"); + Panel panel = new Panel(); + + List list = new List(2); + list.add("List1"); + list.add("List2"); + list.add("List3"); + list.add("List4"); + list.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + TextArea textArea = new TextArea(3, 5); + textArea.setCursor(new Cursor(Cursor.TEXT_CURSOR)); + + panel.add(textArea); + panel.add(list); + + frame.add(panel); + frame.setBounds(300, 100, 300, 150); + return frame; + } +} diff --git a/test/jdk/java/awt/Cursor/CursorUpdateTest/CursorUpdateTest.java b/test/jdk/java/awt/Cursor/CursorUpdateTest/CursorUpdateTest.java new file mode 100644 index 0000000000000..33e2a3cb166a1 --- /dev/null +++ b/test/jdk/java/awt/Cursor/CursorUpdateTest/CursorUpdateTest.java @@ -0,0 +1,118 @@ +/* + * 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 + * 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 5097531 + * @summary Make sure the cursor updates correctly under certain + * circumstances even when the EDT is busy + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CursorUpdateTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; + +public class CursorUpdateTest { + final static String progress = "|/-\\"; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Check the following: + 1. Cursor must be crosshair when hovering the mouse over the + blue square. + 2. Crosshair cursor must not flicker. + 3. The cursor must be "I-beam" when hovering the mouse over the + button. + 4. Click the button - it will display "Busy" message and a + rotating bar for 5 seconds. The cursor must change to + hourglass. + 5. (Windows only) While the cursor is on the button, press Alt. + The cursor must change to normal shape. Pressing Alt again + must revert it back to I-beam. + 6. Move the mouse out of the button and back onto it. The cursor + must update correctly (hourglass over the button, normal + over the frame) even when the button displays "busy". + Do not try to check (1) or (5) when the button displays + "Busy" - this is not required. + Pass if all the steps are as behave as described. Otherwise, + fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(CursorUpdateTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame(); + f.setLayout(new FlowLayout()); + Button b = new Button("Button"); + f.add(b); + b.setCursor(new Cursor(Cursor.TEXT_CURSOR)); + Component c = new MyComponent(); + f.add(c); + c.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); + b.addActionListener(e -> { + String oldLabel = b.getLabel(); + Cursor oldCursor = b.getCursor(); + b.setCursor(new Cursor(Cursor.WAIT_CURSOR)); + try { + for (int i = 0; i < 50; i++) { + b.setLabel("Busy " + progress.charAt(i % 4)); + Thread.sleep(100); + } + } catch (InterruptedException ex) { + } + b.setCursor(oldCursor); + b.setLabel(oldLabel); + }); + return f; + } +} + +class MyComponent extends Component { + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getSize().width, getSize().height); + } + + public MyComponent() { + setBackground(Color.blue); + } + + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } +} diff --git a/test/jdk/java/awt/Cursor/CustomCursorTest/CustomCursorTest.java b/test/jdk/java/awt/Cursor/CustomCursorTest/CustomCursorTest.java new file mode 100644 index 0000000000000..32c1fdc150622 --- /dev/null +++ b/test/jdk/java/awt/Cursor/CustomCursorTest/CustomCursorTest.java @@ -0,0 +1,137 @@ +/* + * 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 4174035 4106384 4205805 + * @summary Test for functionality of Custom Cursor + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CustomCursorTest + */ + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JLabel; + +import static java.awt.image.BufferedImage.TYPE_INT_ARGB; + +public class CustomCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test is for switching between a custom cursor and the + system cursor. + + 1. Click on the test window panel to change from the default + system cursor to the custom red square cursor + 2. Verify that the square cursor shows when the panel is clicked + 3. Verify that the square cursor is colored red + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(CustomCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + JFrame f = new JFrame("Custom Cursor Test"); + CustomCursorPanel c = null; + try { + c = new CustomCursorPanel(); + } catch (IOException e) { + e.printStackTrace(); + } + + f.setIconImage(c.getImage()); + f.getContentPane().add(c); + f.setSize(400, 400); + return f; + } +} + +class CustomCursorPanel extends Panel { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Image image; + Cursor cursor; + boolean flip = false; + + public CustomCursorPanel() throws IOException { + generateRedSquareCursor(); + + image = toolkit.getImage(System.getProperty("test.classes", ".") + + java.io.File.separator + "square_cursor.gif"); + + setBackground(Color.green); + cursor = toolkit.createCustomCursor(image, new Point(0, 0), "custom"); + + JLabel c = (JLabel) add(new JLabel("click to switch between " + + "red square and default cursors")); + c.setBackground(Color.white); + c.setForeground(Color.red); + + addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (!flip) { + setCursor(cursor); + flip = true; + } else { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + flip = false; + } + } + }); + } + + public Image getImage() { + return image; + } + + private static void generateRedSquareCursor() throws IOException { + Path p = Path.of(System.getProperty("test.classes", ".")); + BufferedImage bImg = new BufferedImage(35, 34, TYPE_INT_ARGB); + Graphics2D cg = bImg.createGraphics(); + cg.setColor(Color.RED); + cg.fillRect(0, 0, 35, 34); + ImageIO.write(bImg, "png", new File(p + java.io.File.separator + + "square_cursor.gif")); + } +} diff --git a/test/jdk/java/awt/Cursor/HiddenDialogParentTest/HiddenDialogParentTest.java b/test/jdk/java/awt/Cursor/HiddenDialogParentTest/HiddenDialogParentTest.java new file mode 100644 index 0000000000000..7450f4ec3bb77 --- /dev/null +++ b/test/jdk/java/awt/Cursor/HiddenDialogParentTest/HiddenDialogParentTest.java @@ -0,0 +1,72 @@ +/* + * 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 + * 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 5079694 + * @summary Test if JDialog respects setCursor + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HiddenDialogParentTest + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; + +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.border.LineBorder; + +public class HiddenDialogParentTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + You can see a label area in the center of JDialog. + Verify that the cursor is a hand cursor in this area. + If so, press pass, else press fail. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HiddenDialogParentTest::createUI) + .build() + .awaitAndCheck(); + } + + public static JDialog createUI() { + JDialog dialog = new JDialog(); + dialog.setTitle("JDialog Cursor Test"); + dialog.setLayout(new BorderLayout()); + JLabel centerLabel = new JLabel("Cursor should be a hand in this " + + "label area"); + centerLabel.setBorder(new LineBorder(Color.BLACK)); + centerLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + dialog.add(centerLabel, BorderLayout.CENTER); + dialog.setSize(300, 200); + + return dialog; + } +} diff --git a/test/jdk/java/awt/Cursor/InvalidImageCustomCursorTest/InvalidImageCustomCursorTest.java b/test/jdk/java/awt/Cursor/InvalidImageCustomCursorTest/InvalidImageCustomCursorTest.java new file mode 100644 index 0000000000000..9877df342ec60 --- /dev/null +++ b/test/jdk/java/awt/Cursor/InvalidImageCustomCursorTest/InvalidImageCustomCursorTest.java @@ -0,0 +1,95 @@ +/* + * 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 4212593 + * @summary The Toolkit.createCustomCursor does not check absence of the + * image of cursor + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InvalidImageCustomCursorTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Toolkit; + +public class InvalidImageCustomCursorTest { + static Cursor cursor; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Press 'Hide' button to hide (set transparent) cursor for the + green panel. Move the pointer over the green panel - pointer + should disappear. Press 'Default' button to set default cursor + for the green panel. + + If you see any exceptions or cursor is not transparent, + test failed, otherwise it passed. + """; + + Toolkit tk = Toolkit.getDefaultToolkit(); + Image image = tk.getImage("NON_EXISTING_FILE.gif"); + Point p = new Point(0, 0); + + cursor = tk.createCustomCursor(image, p, "Test"); + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(InvalidImageCustomCursorTest::createUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Invalid Cursor Image Test"); + f.setLayout(new BorderLayout()); + f.setSize(200, 200); + + Button def = new Button("Default"); + Button hide = new Button("Hide"); + Panel panel = new Panel(); + + def.addActionListener(e -> panel.setCursor(Cursor.getDefaultCursor())); + hide.addActionListener(e -> panel.setCursor(cursor)); + + panel.setBackground(Color.green); + panel.setSize(100, 100); + f.add("Center", panel); + f.add("North", hide); + f.add("South", def); + + return f; + } +} diff --git a/test/jdk/java/awt/Cursor/JPanelCursorTest/JPanelCursorTest.java b/test/jdk/java/awt/Cursor/JPanelCursorTest/JPanelCursorTest.java new file mode 100644 index 0000000000000..8acd622e59212 --- /dev/null +++ b/test/jdk/java/awt/Cursor/JPanelCursorTest/JPanelCursorTest.java @@ -0,0 +1,122 @@ +/* + * 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 4114073 + * @summary Test for setCursor in a JPanel when added to a JFrame's contentPane + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual JPanelCursorTest + */ + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.border.BevelBorder; + +public class JPanelCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test checks for setCursor in a JPanel when added to a + JFrame's contentPane. + + 1. Verify that the cursor in the left side of the test window + is a default cursor. + 2. Verify that the cursor changes to the crosshair cursor when + pointing over the button. + 3. Verify that the cursor changes to the hand cursor when in + the right side of the splitpane (and not on the button). + + If true, then pass the test. Otherwise, fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(JPanelCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + JFrame frame = new JFrame(); + + JSplitPane j = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + ExtJComponent pane = new ExtJComponent(); + + CursorBugPanel panel = new CursorBugPanel(); + + j.setLeftComponent(pane); + j.setRightComponent(panel); + j.setContinuousLayout(true); + + frame.getContentPane().add("Center", j); + pane.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + frame.pack(); + return frame; + } +} + +class ExtJComponent extends JComponent { + public ExtJComponent() { + super(); + setOpaque(true); + setBackground(Color.green); + setForeground(Color.red); + setBorder(new BevelBorder(BevelBorder.RAISED)); + } + public void paintComponent(Graphics g) { + g.drawString("Default", 20, 30); + } + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } +} + +class CursorBugPanel extends JPanel { + public CursorBugPanel () { + // BUG: fails to set cursor for panel + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + // Create a button + JButton button = new JButton("Crosshair"); + + // Sets cursor for button, no problem + button.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + add(button); + } + + public void paintComponent(Graphics g) { + g.drawString("Hand", 20, 60); + } +} diff --git a/test/jdk/java/awt/Cursor/NullCursorTest/NullCursorTest.java b/test/jdk/java/awt/Cursor/NullCursorTest/NullCursorTest.java new file mode 100644 index 0000000000000..c2398a80eb2b2 --- /dev/null +++ b/test/jdk/java/awt/Cursor/NullCursorTest/NullCursorTest.java @@ -0,0 +1,154 @@ +/* + * 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 4111379 + * @summary Test for setting cursor to null for lightweight components + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NullCursorTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class NullCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Hover over each colored area as described: + Green area shows a CrossCursor. + Red area shows a TextCursor. + Yellow area shows a HandCursor. + 2. Click once in red area, then: + Green area shows a HandCursor. + Red area shows a BusyCursor. + Yellow area shows a HandCursor. + 3. Click again in red area, then: + Green area shows a CrossCursor. + Red area shows a HandCursor. + Yellow area shows a HandCursor. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(NullCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Null Cursor Test Frame"); + f.setSize(200, 200); + final Container p = f; + p.setName("parent"); + p.setLayout(null); + + final Component green = p.add(new Component() { + public void paint(Graphics g) { + Rectangle r = getBounds(); + g.setColor(Color.green); + g.fillRect(0, 0, r.width, r.height); + } + }); + green.setName("green"); + green.setBackground(Color.red); + green.setBounds(50, 50, 75, 75); + green.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + + Container h = new Container() { + public void paint(Graphics g) { + Rectangle r = getBounds(); + g.setColor(Color.yellow); + g.fillRect(0, 0, r.width, r.height); + super.paint(g); + } + }; + h.setBounds(15, 25, 150, 150); + h.setName("container"); + h.setBackground(Color.yellow); + h.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + final Component red = new Component() { + public void paint(Graphics g) { + Rectangle r = getBounds(); + Color c = getBackground(); + g.setColor(c); + g.fillRect(0, 0, r.width, r.height); + super.paint(g); + } + }; + red.setName("red"); + red.setBackground(Color.red); + red.setBounds(10, 10, 120, 120); + red.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + + final Button b = (Button)h.add(new Button("Test")); + b.setBounds(10, 10, 40, 20); + h.add(red); + p.add(h); + + b.addActionListener(new ActionListener() { + boolean f = false; + public void actionPerformed(ActionEvent e) { + if (f) { + b.setCursor(null); + } else { + b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + f = !f; + } + }); + red.addMouseListener(new MouseAdapter() { + boolean f = true; + + public void mouseClicked(MouseEvent e) { + Component c = (Component)e.getSource(); + if (f) { + c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + p.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + green.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + f = false; + } else { + c.setCursor(null); + p.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + green.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + f = true; + } + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/Cursor/SetCursorTest/SetCursorTest.java b/test/jdk/java/awt/Cursor/SetCursorTest/SetCursorTest.java new file mode 100644 index 0000000000000..87f028eb4f617 --- /dev/null +++ b/test/jdk/java/awt/Cursor/SetCursorTest/SetCursorTest.java @@ -0,0 +1,82 @@ +/* + * 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 4160080 + * @summary Test setCursor() on lightweight components when event is generated + * by a button + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetCursorTest + */ + +import java.awt.Cursor; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + + +public class SetCursorTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test checks for the behavior of setCursor() when called in + a JFrame's JButton action event. + + 1. Click the "OK" button in the test window. + 2. Verify that the cursor changes to the waiting cursor instead + of the default system cursor. + + If true, then pass the test. Otherwise, fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(SetCursorTest::createUI) + .build() + .awaitAndCheck(); + } + + public static myFrame createUI() { + myFrame f = new myFrame(); + return f; + } +} + +class myFrame extends JFrame { + public myFrame() { + super("SetCursor With Button Test"); + setSize(200, 200); + + final JPanel p = new JPanel(); + final JButton okButton = new JButton("OK"); + okButton.addActionListener(e -> + setCursor(new Cursor(Cursor.WAIT_CURSOR))); + + p.add(okButton); + getContentPane().add(p); + } +} diff --git a/test/jdk/java/awt/Desktop/ActionSupportTest.java b/test/jdk/java/awt/Desktop/ActionSupportTest.java new file mode 100644 index 0000000000000..e5cdec0e42246 --- /dev/null +++ b/test/jdk/java/awt/Desktop/ActionSupportTest.java @@ -0,0 +1,201 @@ +/* + * 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 + * 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 6255196 + * @key headful + * @summary Verifies the supported actions on different platforms. + * @library /test/lib + * @run main/othervm ActionSupportTest + */ + +import java.awt.Desktop; +import java.io.File; +import java.net.URI; +import javax.swing.JMenuBar; +import jtreg.SkippedException; + +import static java.awt.desktop.QuitStrategy.NORMAL_EXIT; + +public class ActionSupportTest { + + public static void main(String[] args) { + final File file = new File("nonExistentFile"); + final URI uri = URI.create("nonExistentSchema:anything"); + final StringBuilder error = new StringBuilder(); + + if (!Desktop.isDesktopSupported()) { + throw new SkippedException("Class java.awt.Desktop is not supported on " + + "current platform. Farther testing will not be performed"); + } + + Desktop desktop = Desktop.getDesktop(); + for (Desktop.Action action : Desktop.Action.values()) { + boolean supported = desktop.isSupported(action); + + try { + switch (action) { + case OPEN: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.open(file); + break; + case EDIT: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.edit(file); + break; + case PRINT: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.print(file); + break; + case MAIL: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.mail(uri); + break; + case BROWSE: + if (supported) { + continue; // prevent native message about strange schema + } + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.browse(uri); + break; + case APP_EVENT_FOREGROUND: + case APP_EVENT_HIDDEN: + case APP_EVENT_REOPENED: + case APP_EVENT_SCREEN_SLEEP: + case APP_EVENT_SYSTEM_SLEEP: + case APP_EVENT_USER_SESSION: + continue; // Has no effect if SystemEventListener's sub-type + // is unsupported on the current platform. + case APP_ABOUT: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setAboutHandler(e -> { + }); + break; + case APP_PREFERENCES: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setPreferencesHandler(e -> { + }); + break; + case APP_OPEN_FILE: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setOpenFileHandler(e -> { + }); + break; + case APP_PRINT_FILE: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setPrintFileHandler(e -> { + }); + break; + case APP_OPEN_URI: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setOpenURIHandler(e -> { + }); + break; + case APP_QUIT_HANDLER: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setQuitHandler((e, response) -> { + }); + break; + case APP_QUIT_STRATEGY: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setQuitStrategy(NORMAL_EXIT); + break; + case APP_SUDDEN_TERMINATION: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.enableSuddenTermination(); + break; + case APP_REQUEST_FOREGROUND: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.requestForeground(true); + break; + case APP_HELP_VIEWER: + if (supported) { + continue; // prevent open separate window + } + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.openHelpViewer(); + break; + case APP_MENU_BAR: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.setDefaultMenuBar(new JMenuBar()); + break; + case BROWSE_FILE_DIR: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.browseFileDirectory(file); + break; + case MOVE_TO_TRASH: + // if not supported, an UnsupportedOperationException will be thrown. + // if supported, other exception might be thrown. + desktop.moveToTrash(file); + break; + } + // no exception has been thrown. + if (!supported) { + error.append("Action " + action.name() + " is an " + + "unsupported operation, but no exception has been thrown\n"); + } + } catch (UnsupportedOperationException uoe) { + if (!supported) { + System.out.println("Action " + action.name() + "is not supported."); + } else { + error.append("Action " + action.name() + " is a " + + "supported operation, " + + "but UnsupportedOperationException has been thrown\n"); + } + } catch (Exception e) { + if (supported) { + System.out.println("Action " + action.name() + "supported."); + } else { + error.append("Action " + action.name() + " is an " + + "unsupported operation, but " + + "UnsupportedOperationException has not been thrown\n"); + } + } + } + + if (!error.isEmpty()) { + System.err.println(error); + throw new RuntimeException("One or more tests failed. " + + "Look at the error output for details"); + } + System.out.println("Test completed"); + } +} diff --git a/test/jdk/java/awt/Desktop/BrowseTest.java b/test/jdk/java/awt/Desktop/BrowseTest.java new file mode 100644 index 0000000000000..1bdccace3fc87 --- /dev/null +++ b/test/jdk/java/awt/Desktop/BrowseTest.java @@ -0,0 +1,90 @@ +/* + * 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 + * 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 6255196 + * @summary Verifies the function of method browse(java.net.URI uri). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BrowseTest + */ + +import java.awt.Desktop; +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import javax.swing.JPanel; + +public class BrowseTest extends JPanel { + static final String INSTRUCTIONS = """ + This test could launch default file manager to open user's home + directory, and default web browser to show the URL of java vendor. + After test execution close the native file manager and web browser + windows if they were launched by test. + Also check output for any unexpected EXCEPTIONS, + if you see any failure messages press Fail otherwise press Pass. + """; + + public BrowseTest() { + if (!Desktop.isDesktopSupported()) { + PassFailJFrame.log("Class java.awt.Desktop is not supported on " + + "current platform. Farther testing will not be performed"); + PassFailJFrame.forcePass(); + } + + Desktop desktop = Desktop.getDesktop(); + + URI dirURI = new File(System.getProperty("user.home")).toURI(); + URI webURI = URI.create(System.getProperty("java.vendor.url", "http://www.java.com")); + boolean failed = false; + try { + PassFailJFrame.log("Try to browse " + dirURI + " ..."); + desktop.browse(dirURI); + PassFailJFrame.log("Succeed.\n"); + } catch (Exception e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + try { + PassFailJFrame.log("Try to browse " + webURI + " ..."); + desktop.browse(webURI); + PassFailJFrame.log("Succeed.\n"); + } catch (Exception e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Browser Test") + .splitUI(BrowseTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Desktop/DesktopSupportTest.java b/test/jdk/java/awt/Desktop/DesktopSupportTest.java new file mode 100644 index 0000000000000..ec8b82ba5ef21 --- /dev/null +++ b/test/jdk/java/awt/Desktop/DesktopSupportTest.java @@ -0,0 +1,59 @@ +/* + * 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 + * 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 6255196 + * @key headful + * @summary Verifies if class Desktop is supported on current platform. + * @run main DesktopSupportTest + */ + +import java.awt.Desktop; + +public class DesktopSupportTest { + public static void main(String[] args) { + boolean supported = Desktop.isDesktopSupported(); + try { + Desktop desktop = Desktop.getDesktop(); + if (!supported) { + throw new RuntimeException("UnsupportedOperationException " + + "should be thrown, as this class is not supported " + + "on current platform."); + } + } catch (UnsupportedOperationException uoe) { + if (supported) { + throw new RuntimeException("UnsupportedOperationException " + + "should NOT be thrown, as this class is supported " + + "on current platform."); + } + } catch (Exception e) { + if (!supported) { + throw new RuntimeException("UnsupportedOperationException " + + "should be thrown, as this class is not supported " + + "on current platform. But " + e.getClass().getName() + + " has been thrown instead."); + } + } + } +} diff --git a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java new file mode 100644 index 0000000000000..3f161f7fcaf90 --- /dev/null +++ b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java @@ -0,0 +1,152 @@ +/* + * 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 + * 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 printer + * @bug 6255196 + * @summary Verifies the function of methods edit(java.io.File file) and + * print(java.io.File file) + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual EditAndPrintTest + */ + +import java.awt.Desktop; +import java.awt.Desktop.Action; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JPanel; + +public class EditAndPrintTest extends JPanel { + + static final String INSTRUCTIONS = """ + This test tries to edit and print a directory, which will expectedly raise IOException. + Then this test would edit and print a .txt file, which should be successful. + After test execution close the editor if it was launched by test. + If you see any EXCEPTION messages in the output press FAIL. + """; + + public EditAndPrintTest() { + if (!Desktop.isDesktopSupported()) { + PassFailJFrame.log("Class java.awt.Desktop is not supported on " + + "current platform. Further testing will not be performed"); + PassFailJFrame.forcePass(); + } + + Desktop desktop = Desktop.getDesktop(); + + if (!desktop.isSupported(Action.PRINT) && !desktop.isSupported(Action.EDIT)) { + PassFailJFrame.log("Neither EDIT nor PRINT actions are supported. Nothing to test."); + PassFailJFrame.forcePass(); + } + + /* + * Part 1: print or edit a directory, which should throw an IOException. + */ + File userHome = new File(System.getProperty("user.home")); + try { + if (desktop.isSupported(Action.EDIT)) { + PassFailJFrame.log("Trying to edit " + userHome); + desktop.edit(userHome); + PassFailJFrame.log("No exception has been thrown for editing " + + "directory " + userHome.getPath()); + PassFailJFrame.log("Test failed."); + } else { + PassFailJFrame.log("Action EDIT is unsupported."); + } + } catch (IOException e) { + PassFailJFrame.log("Expected IOException is caught."); + } + + try { + if (desktop.isSupported(Action.PRINT)) { + PassFailJFrame.log("Trying to print " + userHome); + desktop.print(userHome); + PassFailJFrame.log("No exception has been thrown for printing " + + "directory " + userHome.getPath()); + PassFailJFrame.log("Test failed."); + } else { + PassFailJFrame.log("Action PRINT is unsupported.\n"); + } + } catch (IOException e) { + PassFailJFrame.log("Expected IOException is caught."); + } + + /* + * Part 2: print or edit a normal .txt file, which may succeed if there + * is associated application to print or edit the given file. It fails + * otherwise. + */ + // Create a temp .txt file for test. + String testFilePath = System.getProperty("java.io.tmpdir") + File.separator + "JDIC-test.txt"; + File testFile = null; + try { + PassFailJFrame.log("Creating temporary file."); + testFile = File.createTempFile("JDIC-test", ".txt", new File(System.getProperty("java.io.tmpdir"))); + testFile.deleteOnExit(); + FileWriter writer = new FileWriter(testFile); + writer.write("This is a temp file used to test print() method of Desktop."); + writer.flush(); + writer.close(); + } catch (java.io.IOException ioe){ + PassFailJFrame.log("EXCEPTION: " + ioe.getMessage()); + PassFailJFrame.forceFail("Failed to create temp file for testing."); + } + + try { + if (desktop.isSupported(Action.EDIT)) { + PassFailJFrame.log("Try to edit " + testFile); + desktop.edit(testFile); + PassFailJFrame.log("Succeed."); + } + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + try { + if (desktop.isSupported(Action.PRINT)) { + PassFailJFrame.log("Trying to print " + testFile); + desktop.print(testFile); + PassFailJFrame.log("Succeed."); + } + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + } + + public static void main(String args[]) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Edit and Print test") + .splitUI(EditAndPrintTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(60) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Desktop/MailTest.java b/test/jdk/java/awt/Desktop/MailTest.java new file mode 100644 index 0000000000000..15e5c0769a0dc --- /dev/null +++ b/test/jdk/java/awt/Desktop/MailTest.java @@ -0,0 +1,116 @@ +/* + * 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 + * 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 6255196 + * @summary Verifies the function of methods mail() and mail(java.net.URI uri). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MailTest + */ + +import java.awt.Desktop; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import javax.swing.JPanel; + +public class MailTest extends JPanel { + + static final String INSTRUCTIONS = """ + This test could launch the mail client to compose mail + with and without filling the message fields. + After test execution close the mail composing windows if they + were launched by test. + If you see any unexpected EXCEPTION messages in the output + press Fail. Otherwise press Pass. + """; + + private MailTest() { + if (!Desktop.isDesktopSupported()) { + PassFailJFrame.log("Class java.awt.Desktop is not supported on " + + "current platform. Farther testing will not be performed"); + PassFailJFrame.forcePass(); + } + + Desktop desktop = Desktop.getDesktop(); + if (!desktop.isSupported(Desktop.Action.MAIL)) { + PassFailJFrame.log("Action.MAIL is not supported."); + PassFailJFrame.forcePass(); + } + + /* + * Part 1: launch the mail composing window without a mailto URI. + */ + try { + desktop.mail(); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + /* + * Part 2: launch the mail composing window with a mailto URI. + */ + URI testURI = null; + try { + testURI = new URI("mailto", "foo@bar.com?subject=test subject" + + "&cc=foocc@bar.com&body=test body", null); + desktop.mail(testURI); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } catch (java.net.URISyntaxException use) { + // Should not reach here. + PassFailJFrame.log("EXCEPTION: " + use.getMessage()); + } + + /* + * Part 3: try to launch the mail composing window with a URI with a + * scheme which is not "mailto": + * http://java.net. + * An IOException should be thrown in this case. + */ + try { + testURI = URI.create("http://java.com"); + PassFailJFrame.log("Try to mail: " + testURI); + desktop.mail(testURI); + } catch (IllegalArgumentException e) { + PassFailJFrame.log("Caught expected IllegalArgumentException"); + } catch (IOException ioe) { + PassFailJFrame.log("EXCEPTION: " + ioe.getMessage()); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Mail Test") + .splitUI(MailTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/Desktop/OpenTest.java b/test/jdk/java/awt/Desktop/OpenTest.java new file mode 100644 index 0000000000000..1ed29067d50e1 --- /dev/null +++ b/test/jdk/java/awt/Desktop/OpenTest.java @@ -0,0 +1,114 @@ +/* + * 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 + * 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 6255196 + * @summary Verifies the function of method open(java.io.File file). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual/othervm OpenTest + */ + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import javax.swing.JPanel; + +public class OpenTest extends JPanel { + + static final String INSTRUCTIONS = """ + This test could open the user's home directory and a .txt file. + After test execution, close the native application windows that + are used to open the directory and .txt file if they were launched + by the test. + If you see any unexpected EXCEPTION messages in the output press Fail. + Otherwise press Pass. + """; + + public OpenTest() { + if (!Desktop.isDesktopSupported()) { + PassFailJFrame.log("Class java.awt.Desktop is not supported on " + + "current platform. Further testing will not be performed"); + PassFailJFrame.forcePass(); + } + + Desktop desktop = Desktop.getDesktop(); + + /* + * Part 1: open a directory, which should launch the system default + * file explorer. + * + * On Windows platforms, the default file explorer is explorer; + * on UNIX platforms with Gnome installed and running, the default + * file explorer is Nautilus. + */ + File userHome = new File(System.getProperty("user.home")); + + try { + PassFailJFrame.log("Try to open " + userHome); + desktop.open(userHome); + PassFailJFrame.log("Succeed."); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + + /* + * Part 2: open a normal .txt file, which should launch the registered + * application for .txt files. + */ + // Create a temp .txt file for test. + File testFile = null; + try { + PassFailJFrame.log("Creating temporary file"); + testFile = File.createTempFile("JDIC-test", ".txt", + new File(System.getProperty("java.io.tmpdir"))); + testFile.deleteOnExit(); + } catch (java.io.IOException ioe) { + PassFailJFrame.log("EXCEPTION: " + ioe.getMessage()); + PassFailJFrame.log("Failed to create test file"); + } + + try { + PassFailJFrame.log("Try to open " + testFile); + desktop.open(testFile); + PassFailJFrame.log("Succeed."); + } catch (IOException e) { + PassFailJFrame.log("EXCEPTION: " + e.getMessage()); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Mail Test") + .splitUI(OpenTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/DesktopProperties/FontSmoothing.java b/test/jdk/java/awt/DesktopProperties/FontSmoothing.java new file mode 100644 index 0000000000000..d3879d4893ff2 --- /dev/null +++ b/test/jdk/java/awt/DesktopProperties/FontSmoothing.java @@ -0,0 +1,93 @@ +/* + * 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. + */ + +import java.awt.Frame; +import java.awt.Toolkit; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/* + * @test + * @bug 4808569 + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary add desktop property for the Windows XP or later font smoothing settings + * @run main/manual FontSmoothing + */ + +public class FontSmoothing { + + private static final String PROP_NAME = "win.text.fontSmoothingType"; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test should be run on Windows XP or later. + + On Windows 11: + 1. Open Run dialog by typing 'run' in search bar. + 2. Type 'cttune' and press Ok. + 3. Uncheck the "Turn On ClearType" checkbox and follow next instructions on screen. + 4. Repeat Step 1-2. + 5. Check the "Turn On ClearType" checkbox and follow next instructions on screen. + 6. Take a look at the output window to determine if the test passed or failed. + """; + + PassFailJFrame.builder() + .title("FontSmoothing Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testTimeOut(5) + .testUI(FontSmoothing::createUI) + .logArea(8) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("FontSmoothing Test"); + f.setSize(50, 50); + + Object value = Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty: " + PROP_NAME + " = " + value + "\n"); + + Toolkit.getDefaultToolkit().addPropertyChangeListener(PROP_NAME, new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + PassFailJFrame.log("PropertyChangeEvent: " + e.getPropertyName() + + "\n old value=" + e.getOldValue() + + "\n new value=" + e.getNewValue()); + + Integer value = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty:" + PROP_NAME + "=" + value); + + if (value.equals((Integer) e.getNewValue())) { + PassFailJFrame.log("test PASSED"); + } else { + PassFailJFrame.log("test FAILED"); + } + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/DesktopProperties/ThreeDBackgroundColor.java b/test/jdk/java/awt/DesktopProperties/ThreeDBackgroundColor.java new file mode 100644 index 0000000000000..26792e79a92d1 --- /dev/null +++ b/test/jdk/java/awt/DesktopProperties/ThreeDBackgroundColor.java @@ -0,0 +1,100 @@ +/* + * 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 + * 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.Frame; +import java.awt.Toolkit; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +/* + * @test + * @bug 4368193 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "windows") + * @summary Toolkit's getDesktopProperty returns stale values on Microsoft Windows + * @run main/manual ThreeDBackgroundColor + */ + +public class ThreeDBackgroundColor { + + private static final String PROP_NAME = "win.3d.backgroundColor"; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + On Windows 10: + 1. Open Windows Settings, in the search bar type + 'high contrast', in the list of suggestions choose option + 'Turn high contrast on or off' + 2. In the High contrast control panel click on the on/off switch + to initialize High contrast mode + 3. Wait for the High contrast mode to finish initialization + 4. Click on the same switch again to turn off High contrast mode + + On Windows 11: + 1. Open Windows settings, in the search bar type + 'Contrast Theme'. + 2. Select any value from 'Contrast themes' dropdown menu and press 'Apply'. + 3. Wait for the High contrast mode to finish initialization + 4. Select 'None' from 'Contrast themes' dropdown menu to revert the changes. + + Take a look at the output window to determine if the test passed or failed."""; + + PassFailJFrame.builder() + .title("ThreeDBackgroundColor Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testTimeOut(5) + .testUI(ThreeDBackgroundColor::createUI) + .logArea(8) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("ThreeDBackgroundColor Test"); + f.setSize(50, 50); + + Object value = Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty:" + PROP_NAME + "=" + value); + + Toolkit.getDefaultToolkit().addPropertyChangeListener(PROP_NAME, new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + PassFailJFrame.log("PropertyChangeEvent: " + e.getPropertyName() + + "\n old value=" + e.getOldValue() + + "\n new value=" + e.getNewValue()); + + Color value = (Color) Toolkit.getDefaultToolkit().getDesktopProperty(PROP_NAME); + PassFailJFrame.log("toolkit.getDesktopProperty:" + PROP_NAME + "=" + value); + if (value.equals((Color) e.getNewValue())) { + PassFailJFrame.log("test PASSED"); + } else { + PassFailJFrame.log("test FAILED"); + } + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java b/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java new file mode 100644 index 0000000000000..97ce5a83a96b7 --- /dev/null +++ b/test/jdk/java/awt/Dialog/ChoiceModalDialogTest.java @@ -0,0 +1,140 @@ +/* + * 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 + * 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 6213128 + * @key headful + * @summary Tests that choice is releasing input capture when a modal + * dialog is shown + * @run main ChoiceModalDialogTest + */ + +import java.awt.Choice; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class ChoiceModalDialogTest { + static Frame f; + static Dialog d; + static volatile boolean keyOK; + static volatile boolean mouseOK; + static TextField tf; + static Choice c; + + public static void main(String[] args) throws Exception { + Robot r; + try { + r = new Robot(); + r.setAutoDelay(100); + EventQueue.invokeAndWait(() -> { + f = new Frame("Frame"); + c = new Choice(); + f.setBounds(100, 300, 300, 200); + f.setLayout(new FlowLayout()); + tf = new TextField(3); + f.add(tf); + + c.add("1"); + c.add("2"); + c.add("3"); + c.add("4"); + f.add(c); + + tf.addFocusListener(new FocusAdapter() { + public void focusLost(FocusEvent ev) { + d = new Dialog(f, "Dialog", true); + d.setBounds(300, 300, 200, 150); + d.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent ev) { + keyOK = true; + } + }); + d.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent ev) { + mouseOK = true; + } + }); + d.setVisible(true); + } + }); + + f.setVisible(true); + f.toFront(); + }); + r.waitForIdle(); + r.delay(1000); + EventQueue.invokeAndWait(() -> { + r.mouseMove(tf.getLocationOnScreen().x + tf.getSize().width / 2, + tf.getLocationOnScreen().y + tf.getSize().height / 2); + }); + r.waitForIdle(); + r.delay(500); + EventQueue.invokeAndWait(() -> { + r.mouseMove(c.getLocationOnScreen().x + c.getSize().width - 4, + c.getLocationOnScreen().y + c.getSize().height / 2); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + }); + r.waitForIdle(); + r.delay(500); + EventQueue.invokeAndWait(() -> { + r.mouseMove(d.getLocationOnScreen().x + d.getSize().width / 2, + d.getLocationOnScreen().y + d.getSize().height / 2); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.keyPress(KeyEvent.VK_A); + r.keyRelease(KeyEvent.VK_A); + }); + r.waitForIdle(); + r.delay(500); + if (!mouseOK) { + throw new RuntimeException("Test Failed due to Mouse release failure!"); + } + if (!keyOK) { + throw new RuntimeException("Test Failed due to Key release failure!"); + } + System.out.println("Test Passed!"); + } finally { + EventQueue.invokeAndWait(() -> { + if (d != null) { + d.dispose(); + } + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Dialog/DefaultIconTest.java b/test/jdk/java/awt/Dialog/DefaultIconTest.java new file mode 100644 index 0000000000000..8d2ec8c406f0a --- /dev/null +++ b/test/jdk/java/awt/Dialog/DefaultIconTest.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +import java.awt.Dialog; +import java.awt.Frame; + +/* + * @test + * @bug 4964237 + * @requires (os.family == "windows") + * @summary Win: Changing theme changes java dialogs title icon + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultIconTest + */ + +public class DefaultIconTest { + static String instructions = """ + This test shows frame and two dialogs + Change windows theme. Resizable dialog should retain default icon + Non-resizable dialog should retain no icon + Press PASS if icons look correct, FAIL otherwise + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ShownModalDialogSerializationTest Instructions") + .instructions(instructions) + .testTimeOut(5) + .rows(10) + .columns(35) + .testUI(DefaultIconTest::createGUIs) + .build() + .awaitAndCheck(); + } + + public static Frame createGUIs() { + Frame f = new Frame("DefaultIconTest"); + f.setSize(200, 100); + Dialog d1 = new Dialog(f, "Resizable Dialog, should show default icon"); + d1.setSize(200, 100); + d1.setVisible(true); + d1.setLocation(0, 150); + Dialog d2 = new Dialog(f, "Non-resizable dialog, should have no icon"); + d2.setSize(200, 100); + d2.setVisible(true); + d2.setResizable(false); + d2.setLocation(0, 300); + return f; + } +} diff --git a/test/jdk/java/awt/Dialog/DialogBackgroundTest.java b/test/jdk/java/awt/Dialog/DialogBackgroundTest.java new file mode 100644 index 0000000000000..793782fc43be8 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogBackgroundTest.java @@ -0,0 +1,153 @@ +/* + * 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 4255230 4191946 + * @summary Tests to verify Dialog inherits background from its owner + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogBackgroundTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class DialogBackgroundTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Perform the following steps: + 1) Select "New Frame" from the "File" menu of the + "TreeCopy Frame #1" frame. + 2) Select "Configure" from the "File" menu in the + *new* frame. + If label text "This is a label:" in the appeared + "Configuration Dialog" dialog has a grey background + test PASSES, otherwise it FAILS + """; + TreeCopy treeCopy = new TreeCopy(++TreeCopy.windowCount, null); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(treeCopy) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class TreeCopy extends Frame implements ActionListener { + TextField tfRoot; + ConfigDialog configDlg; + MenuItem miConfigure = new MenuItem("Configure..."); + MenuItem miNewWindow = new MenuItem("New Frame"); + static int windowCount = 0; + Window parent; + + public TreeCopy(int windowNum, Window myParent) { + super(); + setTitle("TreeCopy Frame #" + windowNum); + MenuBar mb = new MenuBar(); + Menu m = new Menu("File"); + configDlg = new ConfigDialog(this); + parent = myParent; + + m.add(miConfigure); + m.add(miNewWindow); + miConfigure.addActionListener(this); + miNewWindow.addActionListener(this); + mb.add(m); + setMenuBar(mb); + m.addActionListener(this); + + tfRoot = new TextField(); + tfRoot.setEditable(false); + add(tfRoot); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent we) { + dispose(); + } + }); + + setSize(200, 100); + setLocationRelativeTo(parent); + } + + public void actionPerformed(ActionEvent ae) { + Object source = ae.getSource(); + + if (source == miConfigure) { + configDlg.setVisible(true); + if (configDlg.getBackground() != configDlg.labelColor) + PassFailJFrame.log("FAIL: Test failed!!!"); + } else if (source == miNewWindow) { + new TreeCopy(++windowCount, this).setVisible(true); + } + } +} + +class ConfigDialog extends Dialog implements ActionListener { + public Button okButton; + public Button cancelButton; + public Label l2; + public Color labelColor; + + public ConfigDialog(Frame parent) { + super(parent, "Configuration Dialog"); + okButton = new Button("OK"); + cancelButton = new Button("Cancel"); + l2 = new Label("This is a label:"); + + setLayout(new FlowLayout()); + add(l2); + add(okButton); + add(cancelButton); + + okButton.addActionListener(this); + cancelButton.addActionListener(this); + + pack(); + labelColor = l2.getBackground(); + } + + public void actionPerformed(ActionEvent ae) { + dispose(); + } +} diff --git a/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java b/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java new file mode 100644 index 0000000000000..06debe28fc551 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogIconTest/DialogIconTest.java @@ -0,0 +1,91 @@ +/* + * 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 + * 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.Frame; +import java.awt.Image; +import java.awt.Label; +import java.awt.MediaTracker; +import java.awt.Toolkit; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4779641 + * @summary Test to verify that Non-resizable dialogs should not show icons + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogIconTest + */ + +public class DialogIconTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. This is a Windows-only test of Dialog icons + 2. You can see a frame with a swing icon and two dialogs that it + owns. The resizable dialog should have the same icon as the + frame. The non-resizable dialog should have no icon at all + 3. Press PASS if this is true, press FAIL otherwise + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static List initialize() { + Frame f = new Frame("Parent frame"); + f.setBounds(50, 50, 200, 200); + + Dialog dr = new Dialog(f, "Resizable Dialog"); + dr.setLocation(100, 100); + dr.add(new Label("Should inherit icon from parent")); + dr.pack(); + + Dialog dn = new Dialog(f, "NON Resizable Dialog"); + dn.setLocation(150, 150); + dn.add(new Label("Should have no icon")); + dn.pack(); + dn.setResizable(false); + + String fileName = System.getProperty("test.src") + + System.getProperty("file.separator") + "swing.small.gif"; + + Image icon = Toolkit.getDefaultToolkit().createImage(fileName); + MediaTracker tracker = new MediaTracker(f); + tracker.addImage(icon, 0); + try { + tracker.waitForAll(); + } catch (InterruptedException ie) { + throw new RuntimeException("MediaTracker addImage Interrupted!"); + } + f.setIconImage(icon); + return List.of(f, dn, dr); + } +} diff --git a/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif b/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif new file mode 100644 index 0000000000000..14a489ff4e7df Binary files /dev/null and b/test/jdk/java/awt/Dialog/DialogIconTest/swing.small.gif differ diff --git a/test/jdk/java/awt/Dialog/DialogInitialResizability.java b/test/jdk/java/awt/Dialog/DialogInitialResizability.java new file mode 100644 index 0000000000000..7ecde39c4add1 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogInitialResizability.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; + +/* + * @test + * @bug 4912551 + * @summary Checks that with resizable set to false before show() + * dialog can not be resized. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogInitialResizability + */ + +public class DialogInitialResizability { + static String instructions = """ + When this test is run a dialog will display (setResizable Test). + This dialog should not be resizable. + + Additionally ensure that there are NO componentResized events in the log section. + If the above conditions are true, then Press PASS else FAIL. + """; + + private static final Dimension INITIAL_SIZE = new Dimension(400, 150); + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DialogInitialResizability") + .instructions(instructions) + .testTimeOut(5) + .rows((int) instructions.lines().count() + 2) + .columns(40) + .testUI(DialogInitialResizability::createGUI) + .logArea() + .build() + .awaitAndCheck(); + } + + public static MyDialog createGUI() { + Frame f = new Frame("invisible dialog owner"); + + MyDialog ld = new MyDialog(f); + ld.setBounds(100, 100, INITIAL_SIZE.width, INITIAL_SIZE.height); + ld.setResizable(false); + + PassFailJFrame.log("Dialog isResizable is set to: " + ld.isResizable()); + PassFailJFrame.log("Dialog Initial Size " + ld.getSize()); + return ld; + } + + private static class MyDialog extends Dialog implements ComponentListener { + public MyDialog(Frame f) { + super(f, "setResizable test", false); + this.addComponentListener(this); + } + + public void componentResized(ComponentEvent e) { + if (!e.getComponent().getSize().equals(INITIAL_SIZE)) { + PassFailJFrame.log("Component Resized. Test Failed!!"); + } + } + + public void componentMoved(ComponentEvent e) { + } + + public void componentShown(ComponentEvent e) { + } + + public void componentHidden(ComponentEvent e) { + } + } +} diff --git a/test/jdk/java/awt/Dialog/DialogModalityTest.java b/test/jdk/java/awt/Dialog/DialogModalityTest.java new file mode 100644 index 0000000000000..d8ac9e4620b42 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogModalityTest.java @@ -0,0 +1,114 @@ +/* + * 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.BorderLayout; +import java.awt.Button; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Event; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4058370 + * @summary Test to verify Modality of Dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogModalityTest + */ + +public class DialogModalityTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. When the test is running, there will be a Frame, a Modal Dialog + and a Window that is Modal Dialog's parent. + 2. Verify that it is impossible to bring up the menu in Frame before + closing the Modal Dialog. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static List initialize() { + Frame f = new Frame("Parent Frame"); + DialogTest dlg = new DialogTest(f, "Modal Dialog"); + f.add(new Button("push me")); + f.setSize(200, 200); + f.setLocation(210, 1); + dlg.setBounds(210, 203, 200, 200); + return List.of(f, dlg); + } +} + +class DialogTest extends Dialog { + Button closeButton; + Frame parent; + + public DialogTest(Frame parent, String title) { + this(parent, title, true); + } + + public DialogTest(Frame parent, String title, boolean modal) { + super(parent, title, modal); + this.parent = parent; + setLayout(new BorderLayout()); + Panel buttonPanel = new Panel(); + closeButton = new Button("Close"); + buttonPanel.add(closeButton); + add("Center", buttonPanel); + pack(); + } + + public boolean action(Event e, Object arg) { + if (e.target == closeButton) { + Dialog dialog = null; + Component c = (Component) e.target; + + while (c != null && !(c instanceof Dialog)) { + c = c.getParent(); + } + + if (c != null) { + dialog = (Dialog) c; + } + + if (dialog == null) { + return false; + } + + dialog.setVisible(false); + dialog.dispose(); + return true; + } + return false; + } +} diff --git a/test/jdk/java/awt/Dialog/DialogResizeTest.java b/test/jdk/java/awt/Dialog/DialogResizeTest.java new file mode 100644 index 0000000000000..e57a529806872 --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogResizeTest.java @@ -0,0 +1,118 @@ +/* + * 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. + */ + +import java.awt.Checkbox; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.lang.Exception; +import java.lang.String; +import java.lang.System; + +/* + * @test + * @bug 4115213 + * @summary Test to verify Checks that with resizable set to false, + * dialog can not be resized + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogResizeTest + */ + +public class DialogResizeTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. When this test is run a dialog will display (setResizable Test) + Click on the checkbox to change the dialog resizable state + 2. For both dialog resizable states (resizable, non-resizable) try to + change the size of the dialog. When isResizable is true the dialog + is resizable. When isResizable is false the dialog is non-resizable + 3. If this is the behavior that you observe, the test has passed, Press + the Pass button. Otherwise the test has failed, Press the Fail button + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Dialog initialize() { + Frame f = new Frame("Owner Frame"); + MyDialog ld = new MyDialog(f); + ld.setBounds(100, 100, 400, 150); + ld.setResizable(false); + System.out.println("isResizable is set to: " + ld.isResizable()); + return ld; + } +} + +class MyDialog extends Dialog implements ItemListener { + String sText = "Tests java.awt.Dialog.setResizable method"; + TextArea ta = new TextArea(sText, 2, 40, TextArea.SCROLLBARS_NONE); + + public MyDialog(Frame f) { + + super(f, "setResizable test", false); + + Panel cbPanel = new Panel(); + cbPanel.setLayout(new FlowLayout()); + + Panel taPanel = new Panel(); + taPanel.setLayout(new FlowLayout()); + taPanel.add(ta); + + Checkbox cb = new Checkbox("Check this box to change the dialog's " + + "resizable state", null, isResizable()); + cb.setState(false); + cb.addItemListener(this); + cbPanel.add(cb); + + add("North", taPanel); + add("South", cbPanel); + pack(); + } + + public void itemStateChanged(ItemEvent evt) { + setResizable(evt.getStateChange() == ItemEvent.SELECTED); + + boolean bResizeState = isResizable(); + PassFailJFrame.log("isResizable is set to: " + bResizeState); + + if (isResizable()) { + ta.setText("dialog is resizable (isResizable = " + bResizeState + ")"); + } else { + ta.setText("dialog is NOT resizable (isResizable = " + bResizeState + ")"); + } + } +} diff --git a/test/jdk/java/awt/Dialog/DialogResizeTest2.java b/test/jdk/java/awt/Dialog/DialogResizeTest2.java new file mode 100644 index 0000000000000..3124719637a1d --- /dev/null +++ b/test/jdk/java/awt/Dialog/DialogResizeTest2.java @@ -0,0 +1,105 @@ +/* + * 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.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GridLayout; + +/* + * @test + * @bug 4172302 + * @summary Test to make sure non-resizable Dialogs can be resized with the + * setSize() method. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DialogResizeTest2 + */ + +public class DialogResizeTest2 { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This tests the programmatic resizability of non-resizable Dialogs + Even when a Dialog is set to be non-resizable, it should be + programmatically resizable using the setSize() method. + + 1. Initially the Dialog will be resizable. Try using the \\"Smaller\\" + and \\"Larger\\" buttons to verify that the Dialog resizes correctly + 2. Then, click the \\"Toggle\\" button to make the Dialog non-resizable + 3. Again, verify that clicking the \\"Larger\\" and \\"Smaller\\" buttons + causes the Dialog to get larger and smaller. If the Dialog does + not change size, or does not re-layout correctly, the test FAILS + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Parent Frame"); + frame.add(new Button("Button")); + frame.setSize(100, 100); + new dlg(frame).setVisible(true); + return frame; + } + + static class dlg extends Dialog { + public dlg(Frame f_) { + super(f_, "Dialog", false); + setSize(200, 200); + Button bLarger = new Button("Larger"); + bLarger.addActionListener(e -> setSize(400, 400)); + Button bSmaller = new Button("Smaller"); + bSmaller.addActionListener(e -> setSize(200, 100)); + Button bCheck = new Button("Resizable?"); + bCheck.addActionListener(e -> { + if (isResizable()) { + PassFailJFrame.log("Dialog is resizable"); + } else { + PassFailJFrame.log("Dialog is not resizable"); + } + }); + Button bToggle = new Button("Toggle"); + bToggle.addActionListener(e -> { + if (isResizable()) { + setResizable(false); + PassFailJFrame.log("Dialog is now not resizable"); + } else { + setResizable(true); + PassFailJFrame.log("Dialog is now resizable"); + } + }); + setLayout(new GridLayout(1, 4)); + add(bSmaller); + add(bLarger); + add(bCheck); + add(bToggle); + } + } +} diff --git a/test/jdk/java/awt/Dialog/EnabledResetTest.java b/test/jdk/java/awt/Dialog/EnabledResetTest.java new file mode 100644 index 0000000000000..d71c9b1801b22 --- /dev/null +++ b/test/jdk/java/awt/Dialog/EnabledResetTest.java @@ -0,0 +1,145 @@ +/* + * 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 4232374 + * @summary Tests that dismissing a modal dialog does not enable + * disabled components + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual EnabledResetTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class EnabledResetTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Press "Create Child" twice to create three windows + Verify that the parent windows are disabled + 2. Press "Create Modal Dialog" + Verify that the parent windows are disabled + 3. Press "enable" + Verify that no windows accept mouse events + 4. Press "ok" + Verify that the first window is still disabled + If all the verifications are done, then test is + PASSED, else test fails. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new ChildDialog(1, null)) + .build() + .awaitAndCheck(); + } +} + +class ChildDialog extends Frame implements ActionListener { + Window parent; + int id; + Button b, c, d; + + public ChildDialog(int frameNumber, Window myParent) { + super(); + id = frameNumber; + parent = myParent; + + setTitle("Frame Number " + id); + + b = new Button("Dismiss me"); + c = new Button("Create Child"); + d = new Button("Create Modal Dialog"); + + setLayout(new BorderLayout()); + add("North", c); + add("Center", d); + add("South", b); + pack(); + + b.addActionListener(this); + c.addActionListener(this); + d.addActionListener(this); + } + + public void setVisible(boolean b) { + if (parent != null) { + if (b) { + parent.setEnabled(false); + } else { + parent.setEnabled(true); + parent.requestFocus(); + } + } + + super.setVisible(b); + } + + public void dispose() { + if (parent != null) { + parent.setEnabled(true); + parent.requestFocus(); + } + super.dispose(); + } + + + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == c) { + (new ChildDialog(id + 1, this)).setVisible(true); + } else if (evt.getSource() == d) { + Dialog D = new Dialog(this, "Modal Dialog "); + D.setLayout(new FlowLayout()); + Button b = new Button("ok"); + Button e = new Button("enable"); + D.add(b); + D.add(e); + D.setModal(true); + D.pack(); + b.addActionListener(this); + e.addActionListener(this); + D.setVisible(true); + } else if (evt.getSource() == b) { + dispose(); + } else if (evt.getSource() instanceof Button) { + if ("ok".equals(evt.getActionCommand())) { + Button target = (Button) evt.getSource(); + Window w = (Window) target.getParent(); + w.dispose(); + } + if ("enable".equals(evt.getActionCommand())) { + parent.setEnabled(true); + } + } + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java b/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java new file mode 100644 index 0000000000000..d4670cceb601e --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogGetFileTest.java @@ -0,0 +1,78 @@ +/* + * 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 4414105 + * @summary Tests that FileDialog returns null when cancelled + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogGetFileTest + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.Frame; + +public class FileDialogGetFileTest { + static FileDialog fd; + static Frame frame; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Open FileDialog from "Show File Dialog" button. + 2. Click cancel button without selecting any file/folder. + 3. If FileDialog.getFile return null then test PASSES, + else test FAILS automatically. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(4) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + frame = new Frame("FileDialog GetFile test"); + fd = new FileDialog(frame); + fd.setFile("FileDialogGetFileTest.html"); + fd.setBounds(100, 100, 400, 400); + Button showBtn = new Button("Show File Dialog"); + frame.add(showBtn); + frame.pack(); + showBtn.addActionListener(e -> { + fd.setVisible(true); + if (fd.getFile() != null) { + PassFailJFrame.forceFail("Test failed: FileDialog returned non-null value"); + } else { + PassFailJFrame.log("Test Passed!"); + } + }); + return frame; + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/FileDialogIconTest.java b/test/jdk/java/awt/Dialog/FileDialogIconTest/FileDialogIconTest.java new file mode 100644 index 0000000000000..e8505681a82a5 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogIconTest/FileDialogIconTest.java @@ -0,0 +1,258 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/* + * @test + * @bug 4035189 + * @summary Test to verify that PIT File Dialog icon not matching with + * the new java icon (frame Icon) - PIT build + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogIconTest + */ + +public class FileDialogIconTest { + public static Frame frame; + public static Image image; + public static List images; + static String fileBase; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Select the Image for a Dialog and Frame using either + Load/Save/Just Dialog. + 2. Set the Icon Image/s to Frame and Dialog. Verify that the + Icon is set for the respective Frame and Dialog. + If selected Icon is set to Frame and Dialog press PASS + else FAIL. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static void setImagesToFD(java.util.List listIcon) { + FileDialogIconTest.images = listIcon; + } + + public static void setImagesToFrame(java.util.List listIcon) { + frame.setIconImages(listIcon); + } + + public static void setImageToFD(Image img) { + FileDialogIconTest.image = img; + } + + public static void setImageToFrame(Image img) { + frame.setIconImage(img); + } + + public static Frame initialize() { + frame = new Frame("FileDialogIconTest"); + Button setImageButton1 = new Button("setIconImageToFrame"); + Button setImageButton2 = new Button("setIconImageToDialog"); + Button setImageButton3 = new Button("setIconImagesToFrame"); + Button setImageButton4 = new Button("setIconImagesToDialog"); + Button setImageButton5 = new Button("setIconBufferedImagesToDialog"); + Button setImageButton6 = new Button("setIconBufferedImagesToFrame"); + + if (System.getProperty("test.src") == null) { + fileBase = ""; + } else { + fileBase = System.getProperty("test.src") + System.getProperty("file.separator"); + } + + final String fileName = fileBase + "loading-msg.gif"; + + setImageButton1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + setImageToFrame(image); + PassFailJFrame.log("Loaded image . setting to frame"); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + setImageButton2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image = Toolkit.getDefaultToolkit().getImage(fileName); + setImageToFD(image); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + setImageButton3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image; + java.util.List list = new java.util.ArrayList(); + for (int i = 1; i <= 4; i++) { + String fileName = fileBase + "T" + i + ".gif"; + image = Toolkit.getDefaultToolkit().getImage(fileName); + PassFailJFrame.log("Loaded image " + fileName + ". setting to the list for frame"); + list.add(image); + } + setImagesToFrame(list); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + setImageButton4.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + try { + Image image; + List list = new ArrayList<>(); + for (int i = 1; i <= 4; i++) { + String fileName = fileBase + "T" + i + ".gif"; + image = Toolkit.getDefaultToolkit().getImage(fileName); + PassFailJFrame.log("Loaded image " + fileName + ". setting to the list for dialog"); + list.add(image); + } + setImagesToFD(list); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + setImageButton5.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + List list = new ArrayList<>(); + try { + Robot robot = new Robot(); + Rectangle rectangle; + for (int i = 1; i <= 4; i++) { + rectangle = new Rectangle(i * 10, i * 10, i * 10 + 40, i * 10 + 40); + java.awt.image.BufferedImage image = robot.createScreenCapture(rectangle); + robot.delay(100); + list.add(image); + } + } catch (Throwable t) { + t.printStackTrace(); + } + PassFailJFrame.log("Captured images and set to the list for dialog"); + } + }); + + setImageButton6.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + List list = new ArrayList<>(); + try { + Robot robot = new Robot(); + Rectangle rectangle; + for (int i = 1; i <= 4; i++) { + rectangle = new Rectangle(i * 10, i * 10, i * 10 + 40, i * 10 + 40); + java.awt.image.BufferedImage image = robot.createScreenCapture(rectangle); + robot.delay(100); + list.add(image); + } + } catch (Throwable t) { + t.printStackTrace(); + } + PassFailJFrame.log("Captured images and set to the list for frame"); + } + }); + + Button buttonLoad = new Button("Load Dialog"); + Button buttonSave = new Button("Save Dialog"); + Button buttonSimple = new Button("Just Dialog"); + buttonLoad.addActionListener(new MyActionListener(FileDialog.LOAD, "LOAD")); + buttonSave.addActionListener(new MyActionListener(FileDialog.SAVE, "SAVE")); + buttonSimple.addActionListener(new MyActionListener(-1, "")); + + frame.setSize(400, 400); + frame.setLayout(new FlowLayout()); + frame.add(buttonLoad); + frame.add(buttonSave); + frame.add(buttonSimple); + frame.add(setImageButton1); + frame.add(setImageButton2); + frame.add(setImageButton3); + frame.add(setImageButton4); + frame.pack(); + return frame; + } +} + +class MyActionListener implements ActionListener { + int id; + String name; + + public MyActionListener(int id, String name) { + this.id = id; + this.name = name; + } + + public void actionPerformed(ActionEvent ae) { + try { + FileDialog filedialog; + if (id == -1 && Objects.equals(name, "")) { + filedialog = new FileDialog(FileDialogIconTest.frame); + } else { + filedialog = new FileDialog(FileDialogIconTest.frame, name, id); + } + if (FileDialogIconTest.image != null) { + filedialog.setIconImage(FileDialogIconTest.image); + } + + if (FileDialogIconTest.images != null) { + filedialog.setIconImages(FileDialogIconTest.images); + } + filedialog.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif new file mode 100644 index 0000000000000..24a7dea299fd1 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T1.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif new file mode 100644 index 0000000000000..1f3fd66328ba6 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T2.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T3.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T3.gif new file mode 100644 index 0000000000000..af6d626058a67 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T3.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif new file mode 100644 index 0000000000000..67876264d8f7f Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/T4.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif b/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif new file mode 100644 index 0000000000000..6c7570a6d8435 Binary files /dev/null and b/test/jdk/java/awt/Dialog/FileDialogIconTest/loading-msg.gif differ diff --git a/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java b/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java new file mode 100644 index 0000000000000..80ff9ed0bde60 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogUserFilterTest.java @@ -0,0 +1,150 @@ +/* + * 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.Button; +import java.awt.Checkbox; +import java.awt.Component; +import java.awt.Container; +import java.awt.Event; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; +import java.io.File; +import java.io.FilenameFilter; + +/* + * @test + * @bug 4293697 4416433 4417139 4409600 + * @summary Test to verify that user filter always gets called on changing the + * directory in FileDialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogUserFilterTest + */ + +public class FileDialogUserFilterTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Enter a mask into the field, a directory into + the field (or leave the default values). + 2. Then click the button, file dialog will appear. + Output of the user filter will be shown in the output + area. Enter several different directories to the file dialog + via double-clicking on the directory list. The output + area should show some filtering output on each directory + change. If any output was only given on dialog startup, + the test is FAILED. + 3. Look at the list of files accepted by the filter. + If some files do not match the filter, + the test is FAILED. + 4. Open dialog with an empty filter. + Enter some directories with a lot of files (like /usr/bin). + If dialog crashes the test is FAILED. + Enter the directory that contain files and other directories. + If the directories are shown in the files box along with files + then the test is FAILED. + 5. Click in checkbox 'do not use filter', make it checked. + Open dialog, enter the directory with some files. + If no files is shown in the File list box (while you are sure + there are some files there) the test is FAILED + Otherwise it is PASSED." + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new DialogFilterTest()) + .build() + .awaitAndCheck(); + } +} + +class DialogFilterTest extends Frame implements FilenameFilter { + FileDialog fd; + static TextField tfDirectory = new TextField(); + static TextField tfFile = new TextField(); + static TextField tfFilter = new TextField(); + static Checkbox useFilterCheck = new Checkbox("do not use filter"); + + public DialogFilterTest() { + setTitle("File Dialog User Filter test"); + add("North", new Button("Load")); + Panel p = new Panel(); + p.setLayout(new GridBagLayout()); + addRow(p, new Label("directory:", Label.RIGHT), tfDirectory); + addRow(p, new Label("file:", Label.RIGHT), tfFile); + addRow(p, new Label("filter:", Label.RIGHT), tfFilter); + addRow(p, new Label(""), useFilterCheck); + tfFilter.setText(".java"); + tfDirectory.setText("."); + add("Center", p); + setSize(300, 200); + } + + static void addRow(Container cont, Component c1, Component c2) { + GridBagLayout gbl = (GridBagLayout) cont.getLayout(); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + cont.add(c1); + gbl.setConstraints(c1, c); + + c.gridwidth = GridBagConstraints.REMAINDER; + c.weightx = 1.0; + cont.add(c2); + gbl.setConstraints(c2, c); + } + + public boolean accept(File dir, String name) { + System.out.println("File " + dir + " String " + name); + if (fd.getMode() == FileDialog.LOAD) { + return name.lastIndexOf(tfFilter.getText()) > 0; + } + return true; + } + + public boolean action(Event evt, Object what) { + boolean load = "Load".equals(what); + + if (load || "Save".equals(what)) { + fd = new FileDialog(new Frame(), null, + load ? FileDialog.LOAD : FileDialog.SAVE); + fd.setDirectory(tfDirectory.getText()); + fd.setFile(tfFile.getText()); + if (!useFilterCheck.getState()) { + fd.setFilenameFilter(this); + } + fd.setVisible(true); + tfDirectory.setText(fd.getDirectory()); + tfFile.setText(fd.getFile()); + + return true; + } + return false; + } +} diff --git a/test/jdk/java/awt/Dialog/FileDialogWrongNameCrash.java b/test/jdk/java/awt/Dialog/FileDialogWrongNameCrash.java new file mode 100644 index 0000000000000..f3d83a6849465 --- /dev/null +++ b/test/jdk/java/awt/Dialog/FileDialogWrongNameCrash.java @@ -0,0 +1,72 @@ +/* + * 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 + * 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; + +/* + * @test + * @bug 4779118 + * @summary Tests that FileDialog with wrong initial file name + * doesn't crash when Open button is pressed. + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FileDialogWrongNameCrash + */ + +public class FileDialogWrongNameCrash { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + (This is Windows only test) + 1. You should see a frame 'Frame' with button 'Load'. Press button.", + 2. You should see 'Load file' dialog, select any file and press 'Open'", + (not 'Cancel'!!!). If Java doesn't crash - press PASS, else FAIL + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static Frame initialize() { + Frame frame = new Frame("File Dialog Wrong Name Crash Test"); + Button fileButton = new Button("Load"); + fileButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + final java.awt.FileDialog selector = + new java.awt.FileDialog(frame); + selector.setFile("Z:\\O2 XDA\\LogiTest\\\\Testcase.xml"); + selector.setVisible(true); + } + }); + frame.add(fileButton); + frame.setSize(100, 60); + return frame; + } +} diff --git a/test/jdk/java/awt/Dialog/GetLocationTest_1.java b/test/jdk/java/awt/Dialog/GetLocationTest_1.java new file mode 100644 index 0000000000000..a373f931cf72a --- /dev/null +++ b/test/jdk/java/awt/Dialog/GetLocationTest_1.java @@ -0,0 +1,129 @@ +/* + * 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. + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4168481 + * @summary Test to verify Dialog getLocation() regression on Solaris + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual GetLocationTest_1 + */ + +public class GetLocationTest_1 { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click in in the blue square and the yellow window should come + up with the top left by the cursor + 2. If you see this correct behavior press PASS. If you see that + the yellow window location is offset by some inset, press FAIL + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } + + public static Dialog initialize() { + Frame f = new Frame("Owner Frame"); + ColorComponent blue = new ColorComponent(); + blue.setBackground(Color.blue); + blue.setSize(50, 50); + + final Dialog dialog = new Dialog(f, "GetLocation test"); + dialog.setLocation(300, 300); + System.out.println("Dialog location = " + dialog.getLocation()); + blue.setLocation(50, 50); + dialog.setLayout(null); + dialog.add(blue); + dialog.setSize(200, 200); + + final ColorWindow w = new ColorWindow(f); + w.setSize(50, 50); + w.setBackground(Color.yellow); + + blue.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + PassFailJFrame.log("Dialog location = " + dialog.getLocation()); + Point p = e.getPoint(); + Component c = e.getComponent(); + PassFailJFrame.log("Position = " + p); + convertPointToScreen(p, c); + PassFailJFrame.log("Converted to = " + p); + w.setLocation(p.x, p.y); + w.setVisible(true); + } + }); + return dialog; + } + + static class ColorComponent extends Component { + public void paint(Graphics g) { + g.setColor(getBackground()); + Rectangle bounds = getBounds(); + g.fillRect(0, 0, bounds.width, bounds.height); + } + } + + static class ColorWindow extends Window { + ColorWindow(Frame f) { + super(f); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + Rectangle bounds = getBounds(); + g.fillRect(0, 0, bounds.width, bounds.height); + } + } + + public static void convertPointToScreen(Point p, Component c) { + do { + Point b = c.getLocation(); + PassFailJFrame.log("Adding " + b + " for " + c); + p.x += b.x; + p.y += b.y; + + if (c instanceof java.awt.Window) { + break; + } + c = c.getParent(); + } while (c != null); + } +} diff --git a/test/jdk/java/awt/Dialog/HideDialogTest.java b/test/jdk/java/awt/Dialog/HideDialogTest.java new file mode 100644 index 0000000000000..78d0344a6cf8e --- /dev/null +++ b/test/jdk/java/awt/Dialog/HideDialogTest.java @@ -0,0 +1,138 @@ +/* + * 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 + * 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.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4048664 4065506 4122094 4171979 + * @summary Test if Dialog can be successfully hidden, see that no other app + * comes to front, see if hide + dispose causes assertion failure + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HideDialogTest + */ + +public class HideDialogTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. A Frame should appear with a "test" button in it + 2. Click on the "test" button. A Dialog will appear with a "dismiss" button + and a "dismiss-with-dispose" button + 3. First, click on the "dismiss-with-dispose" button. Verify that + no assertion failure appears. + 4. Now, click on the "dismiss" button. The Dialog should go away. + 5. Repeat from (2) 10-20 times. + 6. When the dialog goes away check that the frame window does not briefly + get obscured by another app or repaint it's entire area. There should be + no flicker at all in areas obscured by the dialog. (4065506 4122094) + If there is the test fails. + 7. If the Dialog is successfully hidden each time, the test passed. If the + Dialog did not hide, the test failed (4048664). + + NOTE: When the dialog does not go away (meaning the bug has manifested itself), + the "dismiss-with-dispose" button can be used to get rid of it. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(new MyFrame()) + .build() + .awaitAndCheck(); + } +} + +class MyDialog extends Dialog { + public MyDialog(Frame f) { + super(f, "foobar", true); + setSize(200, 200); + setLayout(new BorderLayout()); + Panel p = new Panel(); + p.setLayout(new FlowLayout(FlowLayout.CENTER)); + Button okButton; + okButton = new Button("dismiss"); + p.add(okButton); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("Calling setVisible(false)"); + setVisible(false); + } + }); + Button newButton; + p.add(newButton = new Button("dismiss-with-dispose")); + newButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("Calling setVisible(false) + dispose()"); + setVisible(false); + dispose(); + } + }); + add("South", p); + pack(); + } +} + +class MyFrame extends Frame implements ActionListener { + public MyFrame() { + super(); + setSize(600, 400); + setTitle("HideDialogTest"); + setLayout(new BorderLayout()); + Panel toolbar = new Panel(); + toolbar.setLayout(new FlowLayout(FlowLayout.LEFT)); + Button testButton = new Button("test"); + testButton.addActionListener(this); + toolbar.add(testButton); + add("North", toolbar); + } + + public void actionPerformed(ActionEvent e) { + String s = e.getActionCommand(); + if (s.equals("test")) { + System.out.println("Begin test"); + MyDialog d = new MyDialog(this); + d.setVisible(true); + System.out.println("End test"); + } + } + + public void paint(Graphics g) { + for (int i = 0; i < 10; i++) { + g.setColor(Color.red); + g.fillRect(0, 0, 2000, 2000); + g.setColor(Color.blue); + g.fillRect(0, 0, 2000, 2000); + } + } +} diff --git a/test/jdk/java/awt/Dialog/ModalDialogTest.java b/test/jdk/java/awt/Dialog/ModalDialogTest.java new file mode 100644 index 0000000000000..aceacc9209a80 --- /dev/null +++ b/test/jdk/java/awt/Dialog/ModalDialogTest.java @@ -0,0 +1,147 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +/* + * @test + * @bug 4078176 + * @summary Test to verify Modal dialogs don't act modal if addNotify() + * is called before setModal(true). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ModalDialogTest + */ + +public class ModalDialogTest implements ActionListener { + public boolean modal = true; + Button closeBtn = new Button("Close me"); + Button createBtn = new Button("Create Dialog"); + Button createNewBtn = new Button("Create Modal Dialog"); + Button lastBtn = new Button("Show Last Dialog"); + Dialog dialog; + Dialog newDialog; + Frame testFrame; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Use 'Modal' checkbox to select which dialog you're + going to create - modal or non-modal. + (this checkbox affects only new created dialog but + not existing one) + 2. Use 'Create Dialog' button to create a dialog. + If you have selected 'Modal' checkbox then dialog has to + be created modal - you can make sure of that clicking + on any other control (i.e. 'Modal' checkbox) - they + should not work. + 3. Use 'Show Last Dialog' button to bring up last + created dialog - to make sure that if you show/hide + modal dialog several times it stays modal. + 4. On the appearing dialog there are two buttons: + 'Close Me' which closes the dialog, + and 'Create Modal Dialog' which creates one more + MODAL dialog just to make sure that + in situation with two modal dialogs all is fine. + 5. If created modal dialogs are really modal + (which means that they blocks the calling app) + then test is PASSED, otherwise it's FAILED." + """; + ModalDialogTest test = new ModalDialogTest(); + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(test.initialize()) + .build() + .awaitAndCheck(); + } + + public Frame initialize() { + testFrame = new Frame("Parent Frame"); + Frame frame = new Frame("Modal Dialog test"); + Panel panel = new Panel(); + panel.setLayout(new BorderLayout()); + + createBtn.addActionListener(this); + createNewBtn.addActionListener(this); + closeBtn.addActionListener(this); + lastBtn.addActionListener(this); + panel.add("Center", createBtn); + panel.add("South", lastBtn); + Checkbox cb = new Checkbox("Modal", modal); + cb.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + modal = ((Checkbox) e.getSource()).getState(); + } + }); + panel.add("North", cb); + panel.setSize(200, 100); + + frame.add(panel); + frame.pack(); + return frame; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == createBtn) { + if (dialog != null) { + dialog.dispose(); + } + dialog = new Dialog(testFrame, "Modal Dialog"); + dialog.add("North", closeBtn); + dialog.add("South", createNewBtn); + createBtn.setEnabled(false); + dialog.pack(); + dialog.setModal(modal); + dialog.setVisible(true); + } else if (e.getSource() == closeBtn && dialog != null) { + createBtn.setEnabled(true); + dialog.setVisible(false); + } else if (e.getSource() == lastBtn && dialog != null) { + dialog.setVisible(true); + } else if (e.getSource() == createNewBtn && newDialog == null) { + newDialog = new Dialog(testFrame, "New Modal Dialog"); + Button clsBtn = new Button("Close Me"); + clsBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + newDialog.dispose(); + newDialog = null; + } + }); + newDialog.add("North", clsBtn); + newDialog.pack(); + newDialog.setModal(true); + newDialog.setVisible(true); + } + } +} diff --git a/test/jdk/java/awt/Dialog/NestedDialogTest.java b/test/jdk/java/awt/Dialog/NestedDialogTest.java new file mode 100644 index 0000000000000..28fb1bc919e6a --- /dev/null +++ b/test/jdk/java/awt/Dialog/NestedDialogTest.java @@ -0,0 +1,312 @@ +/* + * 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.Choice; +import java.awt.Dialog; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Vector; +import java.util.Enumeration; + +/* + * @test + * @bug 4110094 4178930 4178390 + * @summary Test: Rewrite of Win modal dialogs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NestedDialogTest + */ + +public class NestedDialogTest { + private static Vector windows = new Vector(); + static String instructions = """ + To solve various race conditions, windows modal dialogs were rewritten. This + test exercises various modal dialog boundary conditions and checks that + previous fixes to modality are incorporated in the rewrite. + + Check the following: + - No IllegalMonitorStateException is thrown when a dialog closes + + - Open multiple nested dialogs and verify that all other windows + are disabled when modal dialog is active. + + - Check that the proper window is activated when a modal dialog closes. + + - Close nested dialogs out of order (e.g. close dialog1 before dialog2) + and verify that this works and no deadlock occurs. + + - Check that all other windows are disabled when a FileDialog is open. + + - Check that the proper window is activated when a FileDialog closes. + + - Verify that the active window nevers switches to another application + when closing dialogs, even temporarily. + + - Check that choosing Hide always sucessfully hides a dialog. You should + try this multiple times to catch any race conditions. + + - Check that the scrollbar on the Choice component in the dialog works, as opposed + to just using drag-scrolling or the cursor keys + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("NestedDialogTest") + .instructions(instructions) + .testTimeOut(5) + .rows((int) instructions.lines().count() + 2) + .columns(35) + .testUI(NestedDialogTest::createGUI) + .build() + .awaitAndCheck(); + } + + public static Frame createGUI() { + Frame frame1 = new NestedDialogTestFrame("frame0"); + Frame frame2 = new NestedDialogTestFrame("frame1"); + frame2.setLocation(100, 100); + return frame1; + } + + public static void addWindow(Window window) { + // System.out.println("Pushing window " + window); + windows.removeElement(window); + windows.addElement(window); + } + + public static void removeWindow(Window window) { + // System.out.println("Popping window " + window); + windows.removeElement(window); + } + + public static Window getWindow(int index) { + return (Window) windows.elementAt(index); + } + + public static Enumeration enumWindows() { + return windows.elements(); + } + + public static int getWindowIndex(Window win) { + return windows.indexOf(win); + } +} + +class NestedDialogTestFrame extends Frame { + NestedDialogTestFrame(String name) { + super(name); + setSize(200, 200); + show(); + + setLayout(new FlowLayout()); + Button btnDlg = new Button("Dialog..."); + add(btnDlg); + Button btnFileDlg = new Button("FileDialog..."); + add(btnFileDlg); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + System.exit(0); + } + }); + + btnDlg.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + Dialog d1 = new SimpleDialog(NestedDialogTestFrame.this, null, true); + System.out.println("Returned from showing dialog: " + d1); + } + } + ); + + btnFileDlg.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + FileDialog dlg = new FileDialog(NestedDialogTestFrame.this); + dlg.show(); + } + } + ); + + validate(); + } + + public void show() { + if (!isVisible()) { + NestedDialogTest.addWindow(this); + } + super.show(); + } + + public void dispose() { + NestedDialogTest.removeWindow(this); + super.dispose(); + } +} + +class SimpleDialog extends Dialog { + Button btnNested; + Button btnFileDlg; + Button btnShow; + Button btnHide; + Button btnDispose; + Button btnExit; + List listWins; + Dialog dlgPrev; + + public SimpleDialog(Frame frame, Dialog prev, boolean isModal) { + super(frame, "", isModal); + + dlgPrev = prev; + + addWindowListener(new WindowAdapter() { + public void windowActivated(WindowEvent ev) { + populateListWin(); + } + }); + + setTitle(getName()); + + Panel panelNorth = new Panel(); + panelNorth.setLayout(new GridLayout(1, 1)); + listWins = new List(); + panelNorth.add(listWins); + + Panel panelSouth = new Panel(); + panelSouth.setLayout(new FlowLayout()); + btnNested = new Button("Dialog..."); + panelSouth.add(btnNested); + btnFileDlg = new Button("FileDialog..."); + panelSouth.add(btnFileDlg); + btnShow = new Button("Show"); + panelSouth.add(btnShow); + btnHide = new Button("Hide"); + panelSouth.add(btnHide); + btnDispose = new Button("Dispose"); + panelSouth.add(btnDispose); + + Choice cbox = new Choice(); + cbox.add("Test1"); + cbox.add("Test2"); + cbox.add("Test3"); + cbox.add("Test4"); + cbox.add("Test5"); + cbox.add("Test6"); + cbox.add("Test7"); + cbox.add("Test8"); + cbox.add("Test9"); + cbox.add("Test10"); + cbox.add("Test11"); + panelSouth.add(cbox); + + validate(); + + add("Center", panelNorth); + add("South", panelSouth); + + btnNested.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Dialog dlg = new SimpleDialog((Frame) getParent(), SimpleDialog.this, true); + System.out.println("Returned from showing dialog: " + dlg); + } + }); + + btnFileDlg.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FileDialog dlg = new FileDialog((Frame) getParent()); + dlg.show(); + } + }); + + btnHide.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Window wnd = getSelectedWindow(); + System.out.println(wnd); + wnd.hide(); + } + }); + + btnShow.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + getSelectedWindow().show(); + } + }); + + btnDispose.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + getSelectedWindow().dispose(); + populateListWin(); + } + }); + + pack(); + setSize(getSize().width, getSize().height * 2); + if (dlgPrev != null) { + Point pt = dlgPrev.getLocation(); + setLocation(pt.x + 30, pt.y + 50); + } + show(); + } + + private Window getSelectedWindow() { + Window window; + int index = listWins.getSelectedIndex(); + + window = NestedDialogTest.getWindow(index); + return window; + } + + private void populateListWin() { + Enumeration enumWindows = NestedDialogTest.enumWindows(); + + listWins.removeAll(); + while (enumWindows.hasMoreElements()) { + Window win = (Window) enumWindows.nextElement(); + listWins.add(win.getName()); + } + listWins.select(NestedDialogTest.getWindowIndex(this)); + } + + public void show() { + if (!isVisible()) { + NestedDialogTest.addWindow(this); + } + super.show(); + } + + public void dispose() { + NestedDialogTest.removeWindow(this); + super.dispose(); + } +} diff --git a/test/jdk/java/awt/Dialog/ShownModalDialogSerializationTest.java b/test/jdk/java/awt/Dialog/ShownModalDialogSerializationTest.java new file mode 100644 index 0000000000000..b57dd2cf8f238 --- /dev/null +++ b/test/jdk/java/awt/Dialog/ShownModalDialogSerializationTest.java @@ -0,0 +1,92 @@ +/* + * 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 + * 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.TextArea; +import java.io.File; +import java.io.FileOutputStream; +import java.io.ObjectOutputStream; + +/* + * @test + * @bug 4739757 + * @summary REGRESSION: Modal Dialog is not serializable after showing + * @key headful + * @run main ShownModalDialogSerializationTest + */ + +public class ShownModalDialogSerializationTest { + static volatile Frame frame; + static volatile Frame outputFrame; + static volatile Dialog dialog; + + public static void main(String[] args) throws Exception { + + EventQueue.invokeLater(ShownModalDialogSerializationTest::createTestUI); + + while (dialog == null || !dialog.isShowing()) { + Thread.sleep(500); + } + File file = new File("dialog.ser"); + FileOutputStream fos = new FileOutputStream(file); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(dialog); + oos.flush(); + file.delete(); + + EventQueue.invokeAndWait(ShownModalDialogSerializationTest::deleteTestUI); + } + + static void deleteTestUI() { + if (dialog != null) { + dialog.setVisible(false); + dialog.dispose(); + } + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + if (outputFrame != null) { + outputFrame.setVisible(false); + outputFrame.dispose(); + } + } + + private static void createTestUI() { + outputFrame = new Frame("ShownModalDialogSerializationTest"); + TextArea output = new TextArea(40, 50); + outputFrame.add(output); + + frame = new Frame("invisible dialog owner"); + dialog = new Dialog(frame, "Dialog for Close", true); + dialog.add(new Label("Close This Dialog")); + outputFrame.setSize(200, 200); + outputFrame.setVisible(true); + dialog.pack(); + dialog.setVisible(true); + } +} diff --git a/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java b/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java new file mode 100644 index 0000000000000..82ca54871848e --- /dev/null +++ b/test/jdk/java/awt/EventQueue/PushPopDeadlock/PushPopDeadlock.java @@ -0,0 +1,97 @@ +/* + * 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 4212687 + * @summary Verifies that calling EventQueue.push() and EventQueue.pop() + * does not deadlock. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PushPopDeadlock + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Robot; +import java.awt.Toolkit; + +public class PushPopDeadlock { + static int counter = 0; + static Robot robot; + static Frame f; + static Label l; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + String INSTRUCTIONS = """ + Click rapidly in the Frame labeled 'Click Here!'. + The number in the Frame should continue to increase. If the number + stops increasing (remains at a constant value), the test fails. + """; + + PassFailJFrame pfJFrame = PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(PushPopDeadlock::createUI) + .build(); + PushPopDeadlock.test(); + pfJFrame.awaitAndCheck(); + } + + public static Frame createUI() { + f = new Frame("Click Here!"); + l = new Label("Counter: " + counter); + f.add(l); + f.setSize(200, 200); + return f; + } + + public static void test() { + EventQueue q = new EventQueue() { + public void push(EventQueue queue) { + super.push(queue); + pop(); + } + }; + EventQueue q2 = new EventQueue(); + + Toolkit.getDefaultToolkit().getSystemEventQueue().push(q); + + new Thread(() -> { + while (true) { + robot.delay(500); + l.setText("Counter: " + ++counter); + q.push(q2); + try { + Thread.currentThread().sleep(500); + } catch (InterruptedException e) { + return; + } + } + }).start(); + } +} diff --git a/test/jdk/java/awt/FileDialog/DoubleActionCloseX.java b/test/jdk/java/awt/FileDialog/DoubleActionCloseX.java new file mode 100644 index 0000000000000..5d3feaa42ed4b --- /dev/null +++ b/test/jdk/java/awt/FileDialog/DoubleActionCloseX.java @@ -0,0 +1,75 @@ +/* + * 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 + * 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.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6227750 + * @summary Tests that FileDialog can be closed by clicking the 'close' (X) button + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DoubleActionCloseX + */ + +public class DoubleActionCloseX { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + NOTE: On Linux and Mac, there is no 'close'(X) button + when file dialog is visible, press Pass. + + Click the 'Open File Dialog' button to open FileDialog. + A file dialog will appear on the screen. + Click on the 'close'(X) button. + The dialog should be closed. + If not, the test failed, press Fail otherwise press Pass. + """; + + PassFailJFrame.builder() + .title("DoubleActionCloseX Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(DoubleActionCloseX::createUI) + .build() + .awaitAndCheck(); + } + public static Frame createUI() { + Frame f = new Frame("DoubleActionCloseX Test"); + Button b = new Button("Open File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/DoubleActionESC.java b/test/jdk/java/awt/FileDialog/DoubleActionESC.java new file mode 100644 index 0000000000000..748c3aeb5e446 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/DoubleActionESC.java @@ -0,0 +1,122 @@ +/* + * 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 + * 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.Dimension; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.concurrent.CountDownLatch; + +/* + * @test + * @bug 5097243 + * @summary Tests that FileDialog can be closed by ESC any time + * @key headful + * @run main DoubleActionESC + */ + +public class DoubleActionESC { + private static Frame f; + private static Button showBtn; + private static FileDialog fd; + private static Robot robot; + private static volatile Point p; + private static volatile Dimension d; + private static volatile CountDownLatch latch; + private static final int REPEAT_COUNT = 2; + + public static void main(String[] args) throws Exception { + latch = new CountDownLatch(1); + + robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + p = showBtn.getLocationOnScreen(); + d = showBtn.getSize(); + }); + + for (int i = 0; i < REPEAT_COUNT; ++i) { + Thread thread = new Thread(() -> { + robot.mouseMove(p.x + d.width / 2, p.y + d.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + }); + thread.start(); + robot.delay(3000); + + Thread thread1 = new Thread(() -> { + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.waitForIdle(); + }); + thread1.start(); + robot.delay(3000); + } + + latch.await(); + if (fd.isVisible()) { + throw new RuntimeException("File Dialog is not closed"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + f = new Frame("DoubleActionESC Test"); + showBtn = new Button("Show File Dialog"); + fd = new FileDialog(f); + showBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == showBtn) { + fd.setSize(200, 200); + fd.setLocation(200, 200); + fd.setVisible(true); + latch.countDown(); + } + } + }); + f.add(showBtn); + f.setSize(300, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + } +} diff --git a/test/jdk/java/awt/FileDialog/KeyboardInteractionTest.java b/test/jdk/java/awt/FileDialog/KeyboardInteractionTest.java new file mode 100644 index 0000000000000..f919a8a338af8 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/KeyboardInteractionTest.java @@ -0,0 +1,87 @@ +/* + * 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 + * 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.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6259434 + * @summary PIT: Choice in FileDialog is not responding to keyboard interactions, XToolkit + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual KeyboardInteractionTest + */ + +public class KeyboardInteractionTest { + public static void main(String[] args) throws Exception { + System.setProperty("sun.awt.disableGtkFileDialogs", "true"); + String INSTRUCTIONS = """ + 1) Click on 'Show File Dialog' button to bring up the FileDialog window. + A file dialog will come up. + 2) You will see a text field 'Enter full path or filename'. + Right next to it, you will see a button. + Transfer the focus on this button using 'TAB'. + Make sure that the popup choice is not shown. + 3) Press 'ESC'. If file dialog isn't disposed, then the test failed. + 4) Again, click on 'Show File Dialog' to bring up the file dialog. + A file dialog will come up. + 5) You will see a text field 'Enter full path or filename'. + Right next to it, you will see a button. + Click on this button. The popup choice will appear. + 6) Look at the popup choice. Change the current item in the popup + choice by the arrow keys. + If the text in the 'Enter full path or filename' text field isn't + changed, then the test failed. + 7) The test passed. + """; + + PassFailJFrame.builder() + .title("KeyboardInteractionTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(KeyboardInteractionTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("KeyboardInteractionTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/PathChoiceDisposeTest.java b/test/jdk/java/awt/FileDialog/PathChoiceDisposeTest.java new file mode 100644 index 0000000000000..267b2a807cff8 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/PathChoiceDisposeTest.java @@ -0,0 +1,76 @@ +/* + * 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 + * 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.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6240084 + * @summary Test that disposing unfurled list by the pressing ESC + * in FileDialog is working properly on XToolkit + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PathChoiceDisposeTest + */ + +public class PathChoiceDisposeTest { + public static void main(String[] args) throws Exception { + System.setProperty("sun.awt.disableGtkFileDialogs", "true"); + String INSTRUCTIONS = """ + 1) Click on 'Show File Dialog' button to bring up the FileDialog window. + 2) Open the directory selection choice by clicking button next to + 'Enter Path or Folder Name'. A drop-down will appear. + 3) Press 'ESC'. + 4) If you see that the dialog gets disposed and the popup + still remains on the screen, the test failed, otherwise passed. + """; + + PassFailJFrame.builder() + .title("PathChoiceDisposeTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PathChoiceDisposeTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("PathChoiceDisposeTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/PathChoiceWorkArrowsTest.java b/test/jdk/java/awt/FileDialog/PathChoiceWorkArrowsTest.java new file mode 100644 index 0000000000000..17aee8abb800a --- /dev/null +++ b/test/jdk/java/awt/FileDialog/PathChoiceWorkArrowsTest.java @@ -0,0 +1,86 @@ +/* + * 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 + * 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.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6240074 + * @summary Test that file drop-down field in FileDialog is working properly on XToolkit + * @requires (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PathChoiceWorkArrowsTest + */ + +public class PathChoiceWorkArrowsTest { + public static void main(String[] args) throws Exception { + System.setProperty("sun.awt.disableGtkFileDialogs", "true"); + String INSTRUCTIONS = """ + This is only XAWT test. + + 1) Click on 'Show File Dialog' to bring up the FileDialog window. + A file dialog would come up. + 2) Click on the button next to 'Enter folder name' field. + A drop-down will appear. After this, there are 2 scenarios. + 3) Press the down arrow one by one. You will see a '/' being + appended as soon as the current entry is removed. + Keep pressing till the last entry is reached. Now the drop-down + will stop responding to arrow keys. If yes, the test failed. + 4) Press the up arrow. The cursor will directly go to the last + entry ('/') and navigation will stop there. You will see 2 + entries being selected at the same time. + If yes, the test failed. + """; + + PassFailJFrame.builder() + .title("PathChoiceWorkArrowsTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PathChoiceWorkArrowsTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("PathChoiceWorkArrowsTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setSize(200, 200); + fd.setLocation(200, 200); + fd.setVisible(true); + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/SavedDirInitTest.java b/test/jdk/java/awt/FileDialog/SavedDirInitTest.java new file mode 100644 index 0000000000000..7a3b33f55fe16 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/SavedDirInitTest.java @@ -0,0 +1,79 @@ +/* + * 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 + * 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.FileDialog; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6260650 + * @summary FileDialog.getDirectory() does not return null when file dialog is cancelled + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SavedDirInitTest + */ + +public class SavedDirInitTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on 'Show File Dialog' button to bring up the FileDialog window. + 1) A file dialog will come up. + 2) Press 'Cancel' button to cancel the file dialog. + 3) The result (passed or failed) will be shown in the message window below. + """; + + PassFailJFrame.builder() + .title("SavedDirInitTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(SavedDirInitTest::createUI) + .logArea(2) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("SavedDirInitTest Test"); + Button b = new Button("Show File Dialog"); + FileDialog fd = new FileDialog(f); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + fd.setVisible(true); + if (fd.getDirectory() == null && fd.getFile() == null) { + PassFailJFrame.log("TEST PASSED"); + } else { + PassFailJFrame.log("TEST FAILED. dir = " + fd.getDirectory() + + " , file = " + fd.getFile()); + } + } + }); + f.add(b); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/FileDialog/TestFileDialogDupJNIRef.java b/test/jdk/java/awt/FileDialog/TestFileDialogDupJNIRef.java new file mode 100644 index 0000000000000..56b6c49214488 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/TestFileDialogDupJNIRef.java @@ -0,0 +1,82 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Dialog; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4906972 + * @summary Tests using of JNI reference to peer object. + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestFileDialogDupJNIRef + */ + +public class TestFileDialogDupJNIRef { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This is a crash test. + After test started you will see 'Test Frame' with one button. + 1. Click the button to open FileDialog. + 2. Go to the dialog and choose any directory with some files in it.. + 3. Click on any file to highlight it. + 4. Click on the file again to rename. + 5. Leave the file in edit mode and click Open button + + If there was no crash the test passed, Press Pass. + """; + + PassFailJFrame.builder() + .title("TestFileDialogDupJNIRef Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(TestFileDialogDupJNIRef::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame frame = new Frame("TestFileDialogDupJNIRef Test Frame"); + Button open = new Button("Open File Dialog"); + + open.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FileDialog fd = new FileDialog(frame); + fd.setVisible(true); + } + }); + + frame.setLayout(new FlowLayout()); + frame.add(open); + frame.setSize(250, 70); + return frame; + } +} diff --git a/test/jdk/java/awt/Focus/ActivateFocusTest.java b/test/jdk/java/awt/Focus/ActivateFocusTest.java new file mode 100644 index 0000000000000..09f5bbb172ca8 --- /dev/null +++ b/test/jdk/java/awt/Focus/ActivateFocusTest.java @@ -0,0 +1,148 @@ +/* + * 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 4369903 + * @summary Focus on window activation does not work correctly + * @key headful + * @run main ActivateFocusTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Toolkit; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class ActivateFocusTest { + + public static void main(final String[] args) { + ActivateFocusTest app = new ActivateFocusTest(); + app.doTest(); + } + + public void doTest() { + ActivateFocus[] af = new ActivateFocus[2]; + boolean testFailed = false; + Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize(); + for (int i = 0; i < 2; i++) { + af[i] = new ActivateFocus(i); + af[i].setLocation(i * 160 + scrSize.width / 2, scrSize.height / 2); + af[i].setVisible(true); + } + try { + Thread.sleep(5000); + } catch (InterruptedException ie) { + throw new RuntimeException("TEST FAILED - thread was interrupted"); + } + for (int i = 0; i < 2; i++) { + testFailed = (af[i].lw.focusCounter > 1); + } + if (testFailed) { + throw new RuntimeException("TEST FAILED - focus is gained more than one time"); + } else { + System.out.println("TEST PASSED"); + } + } + + } + +class ActivateFocus extends Frame { + + public LightWeight lw = null; + int num; + + public String toString() { + return ("Window " + num); + } + + public ActivateFocus(int i) { + setTitle("Window " + i); + lw = new LightWeight(i); + num=i; + addWindowListener(new WindowAdapter() { + public void windowActivated(WindowEvent e) { + if(lw != null) { + lw.requestFocus(); + } + } + }); + add(lw); + pack(); + } + + // A very simple lightweight component + class LightWeight extends Component implements FocusListener { + + boolean focused = false; + int num; + public int focusCounter = 0; + + public LightWeight(int num) { + this.num = num; + addFocusListener(this); + } + + public void paint(Graphics g) { + Dimension size = getSize(); + int w = size.width; + int h = size.height; + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + g.setColor(Color.black); + g.drawOval(0, 0, w-1, h-1); + if (focused) { + g.drawLine(w/2, 0, w/2, h); + g.drawLine(0, h/2, w, h/2); + } + + } + + public Dimension getPreferredSize() { + return new Dimension(150, 150); + } + + public void focusGained(FocusEvent e) { + focused = true; + focusCounter++; + System.out.println("focusGained on " + e.getComponent()); + repaint(); + } + + public void focusLost(FocusEvent e) { + focused = false; + System.out.println("focusLost on " + e.getComponent()); + repaint(); + } + + public String toString() { + return ("Component " + num); + } + } +} diff --git a/test/jdk/java/awt/Focus/AltTabEventsTest.java b/test/jdk/java/awt/Focus/AltTabEventsTest.java new file mode 100644 index 0000000000000..15d679ce7295a --- /dev/null +++ b/test/jdk/java/awt/Focus/AltTabEventsTest.java @@ -0,0 +1,151 @@ +/* + * 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 + * 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 4524015 + * @summary Tests that when user switches between windows using Alt-tab then the appropriate events are generated + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual AltTabEventsTest + */ + +import java.awt.Button; +import java.awt.Choice; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class AltTabEventsTest { + + private static final String INSTRUCTIONS = """ + This test verifies that when user switches between windows using Alt-tab + key combination then appropriate window events are generated. Also, when + user interacts with Menu bar, Popup menu, Choice then no excessive window + event is generated. + + After test started you will see Frame('Test for 4524015')-F1 with some + components and Frame('Another frame')-F2 with no components. + 1. Make F1 active by clicking on it. + 2. Press Alt-tab. + In the messqge dialog area you should see that + WINDOW_DEACTIVATED, WINDOW_LOST_FOCUS event were generated. + If you switched to F2 then also WINDOW_ACTIVATED, WINDOW_GAINED_FOCUS + were generated. + If no events were generated the test FAILED. + Repeat the 2) with different circumstances. + + 3. Make F1 active by clicking on it. + 4. Click on Menu bar/Button 'popup'/Choice and select some item from + the list shown. If any of the window events appeared in the output then + the test FAILED. + + else the test PASSED."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("AltTabEventsTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(35) + .testUI(Test::new) + .logArea() + .build() + .awaitAndCheck(); + } + +} + + +class Test extends Frame { + PopupMenu pop; + Frame f; + + void println(String messageIn) { + PassFailJFrame.log(messageIn); + } + + public Test() { + super("Test for 4524015"); + WindowAdapter wa = new WindowAdapter() { + public void windowActivated(WindowEvent e) { + println(e.toString()); + } + public void windowDeactivated(WindowEvent e) { + println(e.toString()); + } + public void windowGainedFocus(WindowEvent e) { + println(e.toString()); + } + public void windowLostFocus(WindowEvent e) { + println(e.toString()); + } + }; + addWindowListener(wa); + addWindowFocusListener(wa); + + f = new Frame("Another frame"); + f.addWindowListener(wa); + f.addWindowFocusListener(wa); + f.setBounds(800, 300, 300, 100); + f.setVisible(true); + + setLayout(new FlowLayout()); + Button b = new Button("popup"); + add(b); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + pop.show((Component)e.getSource(), 10, 10); + } + }); + Choice cho = new Choice(); + add(cho); + cho.addItem("1"); + cho.addItem("2"); + cho.addItem("3"); + + MenuBar bar = new MenuBar(); + Menu menu = new Menu("menu"); + MenuItem item = new MenuItem("first"); + menu.add(item); + item = new MenuItem("second"); + menu.add(item); + bar.add(menu); + setMenuBar(bar); + + pop = new PopupMenu(); + pop.add("1"); + pop.add("@"); + add(pop); + setSize(300, 100); + } +} + diff --git a/test/jdk/java/awt/Focus/CanvasPanelFocusOnClickTest.java b/test/jdk/java/awt/Focus/CanvasPanelFocusOnClickTest.java new file mode 100644 index 0000000000000..8df21ba0f0a67 --- /dev/null +++ b/test/jdk/java/awt/Focus/CanvasPanelFocusOnClickTest.java @@ -0,0 +1,212 @@ +/* + * 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 4041703 4096228 4032657 4066152 4149866 4025223 + * @summary Ensures that an Panel/Canvas without heavyweight children + receives focus on mouse click + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CanvasPanelFocusOnClickTest + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +public class CanvasPanelFocusOnClickTest { + + private static final String INSTRUCTIONS = """ + + Click on the red Canvas. Verify that it has focus by key pressing. + Click on the yellow Panel. Verify that it has focus by key pressing. + Click on the blue heavyweight Panel (NOT ON THE BUTTON!). + Verify that it doesn't have focus by key pressing. + If two empty containers are able to the get focus by a mouse click + and the container with heavyweight children are unable to get + the focus by a mouse click which can be verified through messages in message dialog + the test passes."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("CanvasPanelFocusOnClickTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(CanvasPanelFocusOnClickTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Canvas canvas = new Canvas();; + Panel emptyPanel = new Panel(); + Panel panel = new Panel(); + Button buttonInPanel = new Button("BUTTON ON PANEL"); + + Frame frame = new Frame("CanvasPanelFocusOnClickTest Frame"); + frame.setLayout(new GridLayout(3, 1)); + canvas.setBackground(Color.red); + canvas.setName("RED CANVAS"); + canvas.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + canvas.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + frame.add(canvas); + + emptyPanel.setBackground(Color.yellow); + emptyPanel.setName("YELLOW PANEL"); + emptyPanel.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + emptyPanel.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + frame.add(emptyPanel); + + panel.setBackground(Color.blue); + panel.setName("BLUE PANEL"); + buttonInPanel.setName("BUTTON ON PANEL"); + buttonInPanel.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + buttonInPanel.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + panel.add(buttonInPanel); + panel.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + println(e.toString()); + } + public void focusLost(FocusEvent e) { + println(e.toString()); + } + }); + panel.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + printKey(e); + } + + public void keyTyped(KeyEvent e) { + printKey(e); + } + + public void keyReleased(KeyEvent e) { + printKey(e); + } + }); + frame.add(panel); + + frame.setSize(200, 200); + + return frame; + + } + + static void printKey(KeyEvent e) { + String typeStr; + switch(e.getID()) { + case KeyEvent.KEY_PRESSED: + typeStr = "KEY_PRESSED"; + break; + case KeyEvent.KEY_RELEASED: + typeStr = "KEY_RELEASED"; + break; + case KeyEvent.KEY_TYPED: + typeStr = "KEY_TYPED"; + break; + default: + typeStr = "unknown type"; + } + + Object source = e.getSource(); + if (source instanceof Component) { + typeStr += " on " + ((Component)source).getName(); + } else { + typeStr += " on " + source; + } + + println(typeStr); + } + + static void println(String messageIn) { + PassFailJFrame.log(messageIn); + } +} diff --git a/test/jdk/java/awt/Focus/ComponentLostFocusTest.java b/test/jdk/java/awt/Focus/ComponentLostFocusTest.java new file mode 100644 index 0000000000000..6af8322b2cd82 --- /dev/null +++ b/test/jdk/java/awt/Focus/ComponentLostFocusTest.java @@ -0,0 +1,154 @@ +/* + * 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 + * 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 4982943 + * @key headful + * @summary focus lost in text fields or text areas, unable to enter characters from keyboard + * @run main ComponentLostFocusTest + */ + +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +public class ComponentLostFocusTest { + + static Frame frame; + static TextField tf; + static Robot r; + static Dialog dialog = null; + static volatile boolean passed; + static volatile Point loc; + static volatile int width; + static volatile int top; + + private static void createTestUI() { + + dialog = new Dialog(frame, "Dialog", true); + + frame = new Frame("ComponentLostFocusTest Frame"); + frame.setLayout(new FlowLayout()); + frame.addWindowFocusListener(new WindowAdapter() { + public void windowGainedFocus(WindowEvent e) { + System.out.println("Frame gained focus: "+e); + } + }); + tf = new TextField("Text Field"); + frame.add(tf); + frame.setSize(400,300); + frame.setVisible(true); + frame.setLocationRelativeTo(null); + frame.validate(); + } + + public static void doTest() { + System.out.println("dialog.setVisible.... "); + new Thread(new Runnable() { + public void run() { + dialog.setVisible(true); + } + }).start(); + + // The bug is that this construction leads to the redundant xRequestFocus + // By the way, the requestFocusInWindow() works fine before the fix + System.out.println("requesting.... "); + frame.requestFocus(); + + r.delay(1000); + + // Returning the focus to the initial frame will work correctly after the fix + System.out.println("disposing.... "); + dialog.dispose(); + + r.delay(1000); + + // We want to track the GAIN_FOCUS from this time + tf.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + System.out.println("TextField gained focus: " + e); + passed = true; + } + }); + + } + + private static void doRequestFocusToTextField() { + // do activation using press title + r.mouseMove(loc.x + width / 2, loc.y + top / 2); + r.waitForIdle(); + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.waitForIdle(); + + // request focus to the text field + tf.requestFocus(); + } + + public static final void main(String args[]) throws Exception { + r = new Robot(); + r.setAutoDelay(100); + + EventQueue.invokeAndWait(() -> createTestUI()); + r.waitForIdle(); + r.delay(1000); + try { + EventQueue.invokeAndWait(() -> { + doTest(); + loc = frame.getLocationOnScreen(); + width = frame.getWidth(); + top = frame.getInsets().top; + }); + doRequestFocusToTextField(); + + System.out.println("Focused window: " + + KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getFocusedWindow()); + System.out.println("Focus owner: " + + KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getFocusOwner()); + + if (!passed) { + throw new RuntimeException("TextField got no focus! Test failed."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} + diff --git a/test/jdk/java/awt/Focus/ConsumedKeyEventTest.java b/test/jdk/java/awt/Focus/ConsumedKeyEventTest.java new file mode 100644 index 0000000000000..dda82adeec587 --- /dev/null +++ b/test/jdk/java/awt/Focus/ConsumedKeyEventTest.java @@ -0,0 +1,197 @@ +/* + * 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 + * 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 4700276 + * @summary Peers process KeyEvents before KeyEventPostProcessors + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ConsumedKeyEventTest +*/ + +import java.awt.Canvas; +import java.awt.Component; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.KeyboardFocusManager; +import java.awt.KeyEventPostProcessor; +import java.awt.event.KeyEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class ConsumedKeyEventTest implements KeyEventPostProcessor { + + private static final String INSTRUCTIONS = """ + This is a Windows-only test. + When the test starts, you will see a Frame with two components in it, + components look like colored rectangles, one of them is lightweight, one is heavyweight. + Do the following: + 1. Click the mouse on the left component. + If it isn't yellow after the click (that means it doesn't have focus), the test fails. + 2. Press and release ALT key. + In the output window, the text should appear stating that those key events were consumed. + If no output appears, the test fails. + 3. Press space bar. If system menu drops down, the test fails. + 4. Click the right rectangle. + It should become red after the click. If it doesn't, it means that it didn't get the focus, and the test fails. + 5. Repeat steps 2. and 3. + 6. If the test didn't fail on any of the previous steps, the test passes."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ConsumedKeyEventTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(35) + .testUI(ConsumedKeyEventTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + KeyboardFocusManager.getCurrentKeyboardFocusManager(). + addKeyEventPostProcessor((e) -> { + System.out.println("postProcessor(" + e + ")"); + // consumes all ALT-events + if (e.getKeyCode() == KeyEvent.VK_ALT) { + println("consumed " + e); + e.consume(); + return true; + } + return false; + }); + FocusRequestor requestor = new FocusRequestor(); + Frame frame = new Frame("Main Frame"); + frame.setLayout(new FlowLayout()); + + Canvas canvas = new CustomCanvas(); + canvas.addMouseListener(requestor); + frame.add(canvas); + canvas.requestFocus(); + + Component lwComp = new LWComponent(); + lwComp.addMouseListener(requestor); + frame.add(lwComp); + + frame.pack(); + + return frame; + } + + public boolean postProcessKeyEvent(KeyEvent e) { + System.out.println("postProcessor(" + e + ")"); + // consumes all ALT-events + if (e.getKeyCode() == KeyEvent.VK_ALT) { + println("consumed " + e); + e.consume(); + return true; + } + return false; + } + + static void println(String messageIn) { + PassFailJFrame.log(messageIn); + } +}// class ConsumedKeyEventTest + +class CustomCanvas extends Canvas { + CustomCanvas() { + super(); + setName("HWComponent"); + setSize(100, 100); + addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent fe) { + repaint(); + } + + public void focusLost(FocusEvent fe) { + repaint(); + } + }); + } + + public void paint(Graphics g) { + if (isFocusOwner()) { + g.setColor(Color.YELLOW); + } else { + g.setColor(Color.GREEN); + } + g.fillRect(0, 0, 100, 100); + } + +} + +class LWComponent extends Component { + LWComponent() { + super(); + setName("LWComponent"); + addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent fe) { + repaint(); + } + + public void focusLost(FocusEvent fe) { + repaint(); + } + }); + } + + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } + + public void paint(Graphics g) { + if (isFocusOwner()) { + g.setColor(Color.RED); + } else { + g.setColor(Color.BLACK); + } + g.fillRect(0, 0, 100, 100); + } + +} + +class FocusRequestor extends MouseAdapter { + static int counter = 0; + public void mouseClicked(MouseEvent me) { + System.out.println("mouseClicked on " + me.getComponent().getName()); + } + public void mousePressed(MouseEvent me) { + System.out.println("mousePressed on " + me.getComponent().getName()); + me.getComponent().requestFocus(); + } + public void mouseReleased(MouseEvent me) { + System.out.println("mouseReleased on " + me.getComponent().getName()); + } +} + diff --git a/test/jdk/java/awt/Focus/DeiconifyTest.java b/test/jdk/java/awt/Focus/DeiconifyTest.java new file mode 100644 index 0000000000000..e87b13b8e65fa --- /dev/null +++ b/test/jdk/java/awt/Focus/DeiconifyTest.java @@ -0,0 +1,79 @@ +/* + * 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 + * 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 4380809 + * @summary Focus disappears after deiconifying frame + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DeiconifyTest +*/ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +public class DeiconifyTest { + + private static final String INSTRUCTIONS = """ + 1. Activate frame \"Main frame\" + be sure that button has focus + 2. Minimize frame and then restore it. + If the button has focus then test passed, else failed"""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DeiconifyTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(DeiconifyTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("Main frame"); + Button button = new Button("button"); + button.addFocusListener(new FocusListener() { + public void focusGained(FocusEvent fe) { + println("focus gained"); + } + public void focusLost(FocusEvent fe) { + println("focus lost"); + } + }); + frame.add(button); + frame.setSize(300, 100); + + return frame; + } + + static void println(String messageIn) { + PassFailJFrame.log(messageIn); + } +} + diff --git a/test/jdk/java/awt/Focus/EmptyWindowKeyTest.java b/test/jdk/java/awt/Focus/EmptyWindowKeyTest.java new file mode 100644 index 0000000000000..bbdf8ce4f383b --- /dev/null +++ b/test/jdk/java/awt/Focus/EmptyWindowKeyTest.java @@ -0,0 +1,87 @@ +/* + * 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 4464723 + * @summary Tests simple KeyAdapter / KeyListener on an empty, focusable window + * @key headful + * @run main EmptyWindowKeyTest +*/ + +import java.awt.AWTEvent; +import java.awt.Frame; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.Robot; + +public class EmptyWindowKeyTest { + + static volatile boolean passed1, passed2; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + MainFrame mainFrame = new MainFrame(); + mainFrame.setSize(50,50); + mainFrame.addKeyListener(new KeyboardTracker()); + robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + robot.waitForIdle(); + robot.delay(1000); + if (!passed1 || !passed2) { + throw new RuntimeException("KeyPress/keyRelease not seen," + + "passed1 " + passed1 + " passed2 " + passed2); + } + } + + static public class KeyboardTracker extends KeyAdapter { + public KeyboardTracker() { } + public void keyTyped(KeyEvent e) {} + + public void keyPressed(KeyEvent e) { + if (e.getKeyText(e.getKeyCode()).equals("A")) { + passed1 = true; + } + } + public void keyReleased(KeyEvent e) { + if (e.getKeyText(e.getKeyCode()).equals("A")) { + passed2 = true; + } + } + } + + static public class MainFrame extends Frame { + + public MainFrame() { + super(); + enableEvents(AWTEvent.KEY_EVENT_MASK); + setVisible(true); + } + + } + +} + diff --git a/test/jdk/java/awt/Focus/FocusKeepTest.java b/test/jdk/java/awt/Focus/FocusKeepTest.java new file mode 100644 index 0000000000000..0adc463f5d9ae --- /dev/null +++ b/test/jdk/java/awt/Focus/FocusKeepTest.java @@ -0,0 +1,103 @@ +/* + * 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 4128659 + * @summary Tests whether a focus request will work on a focus lost event. + * @key headful + * @run main FocusKeepTest + */ + +import java.awt.BorderLayout; +import java.awt.KeyboardFocusManager; +import java.awt.Robot; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import javax.swing.JFrame; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +public class FocusKeepTest { + + static JFrame frame; + static JTextField tf; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + SwingUtilities.invokeAndWait(() -> createTestUI()); + robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() instanceof JTextField tf1) { + if (!tf1.getText().equals("TextField 1")) { + throw new RuntimeException("Focus on wrong textfield"); + } + } else { + throw new RuntimeException("Focus not on correct component"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createTestUI() { + frame = new JFrame("FocusKeepTest"); + tf = new JTextField("TextField 1"); + tf.addFocusListener(new MyFocusAdapter("TextField 1")); + frame.add(tf, BorderLayout.NORTH); + + tf = new JTextField("TextField 2"); + tf.addFocusListener(new MyFocusAdapter("TextField 2")); + frame.add(tf, BorderLayout.SOUTH); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + static class MyFocusAdapter extends FocusAdapter { + private String myName; + + public MyFocusAdapter (String name) { + myName = name; + } + + public void focusLost (FocusEvent e) { + if (myName.equals ("TextField 1")) { + e.getComponent().requestFocus (); + } + } + + public void focusGained (FocusEvent e) { + } + } +} diff --git a/test/jdk/java/awt/Focus/FocusPolicyTest.java b/test/jdk/java/awt/Focus/FocusPolicyTest.java new file mode 100644 index 0000000000000..3ec362acaf085 --- /dev/null +++ b/test/jdk/java/awt/Focus/FocusPolicyTest.java @@ -0,0 +1,177 @@ +/* + * 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 4897459 + * @key headful + * @summary The key does not switches focus in the internal frames in Swing apps. + * @run main FocusPolicyTest + */ + +import java.awt.Container; +import java.awt.Component; +import java.awt.DefaultFocusTraversalPolicy; +import java.awt.Dialog; +import java.awt.FocusTraversalPolicy; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Toolkit; +import java.awt.Window; +import javax.swing.JDialog; +import javax.swing.JInternalFrame; +import javax.swing.JFrame; +import javax.swing.JWindow; +import javax.swing.LayoutFocusTraversalPolicy; + +public class FocusPolicyTest { + static int stageNum; + static FocusTraversalPolicy customPolicy = new CustomPolicy(); + final static Class awtDefaultPolicy = DefaultFocusTraversalPolicy.class; + final static Class swingDefaultPolicy = LayoutFocusTraversalPolicy.class; + + public static void main(String[] args) { + final boolean isXawt = "sun.awt.X11.XToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName()); + + System.err.println("isXawt = " + isXawt); + + // 1. Check default policy + if (KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getDefaultFocusTraversalPolicy().getClass() != awtDefaultPolicy) { + throw new RuntimeException("Error: stage 1: default policy is not DefaultFocusTraversalPolicy"); + } + + // 2. Check AWT top-levels policies + stageNum = 2; + checkAWTPoliciesFor(awtDefaultPolicy); + + // 3. Check Swing top-levels policies + stageNum = 3; + checkSwingPoliciesFor(swingDefaultPolicy); + + // 4. Check default policy if not XToolkit + if (!isXawt) { + if (KeyboardFocusManager.getCurrentKeyboardFocusManager(). + getDefaultFocusTraversalPolicy().getClass() != swingDefaultPolicy) { + throw new RuntimeException("Error: stage 4: default policy is not LayoutFocusTraversalPolicy"); + } + } + + // 5. Check AWT top-levels policies + // this is a bug in XAWT we should change the test as soon as + // we will be able to fix this bug. + stageNum = 5; + Class defaultPolicy = swingDefaultPolicy; + if (isXawt) { + defaultPolicy = awtDefaultPolicy; + } + checkAWTPoliciesFor(defaultPolicy); + + // Set custom policy as default + KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalPolicy(customPolicy); + + // 6. Check AWT top-levels policies for custom + stageNum = 6; + checkAWTPoliciesFor(customPolicy.getClass()); + + // 7. Check Swing top-levels policies for custom + stageNum = 7; + checkSwingPoliciesFor(customPolicy.getClass()); + + return; + } + + public static void checkAWTPoliciesFor(Class expectedPolicyClass) { + Window[] tlvs = new Window[7]; + + tlvs[0] = new Frame(""); + tlvs[1] = new Frame("", tlvs[0].getGraphicsConfiguration()); + tlvs[2] = new Window((Frame)tlvs[0]); + tlvs[3] = new Dialog((Frame)tlvs[0], "", false); + tlvs[4] = new Dialog((Frame)tlvs[0], "", false, tlvs[0].getGraphicsConfiguration()); + tlvs[5] = new Dialog((Dialog)tlvs[3], "", false); + tlvs[6] = new Dialog((Dialog)tlvs[3], "", false, tlvs[0].getGraphicsConfiguration()); + + for (int i = 0; i < 7; i++) { + Class policyClass = tlvs[i].getFocusTraversalPolicy().getClass(); + if (policyClass != expectedPolicyClass) { + throw new RuntimeException("Error: stage " + stageNum + ": " + + tlvs[i].getClass().getName() + + "'s policy is " + policyClass.getName() + + " but not " + expectedPolicyClass.getName()); + } + } + } + + public static void checkSwingPoliciesFor(Class expectedPolicyClass) { + Container[] tlvs = new Container[12]; + + tlvs[0] = new JFrame(); + tlvs[1] = new JFrame(tlvs[0].getGraphicsConfiguration()); + tlvs[2] = new JFrame(""); + tlvs[3] = new JFrame("", tlvs[0].getGraphicsConfiguration()); + tlvs[4] = new JWindow((Frame)tlvs[0]); + tlvs[5] = new JWindow((Window)tlvs[4]); + tlvs[6] = new JWindow((Window)tlvs[4], tlvs[0].getGraphicsConfiguration()); + tlvs[7] = new JDialog((Frame)tlvs[0], "", false); + tlvs[8] = new JDialog((Frame)tlvs[0], "", false, tlvs[0].getGraphicsConfiguration()); + tlvs[9] = new JDialog((Dialog)tlvs[7], "", false); + tlvs[10] = new JDialog((Dialog)tlvs[7], "", false, tlvs[0].getGraphicsConfiguration()); + tlvs[11] = new JInternalFrame("", false, false, false, false); + + for (int i = 0; i < tlvs.length; i++) { + Class policyClass = tlvs[i].getFocusTraversalPolicy().getClass(); + if (policyClass != expectedPolicyClass) { + throw new RuntimeException("Error: stage " + stageNum + + ": " + tlvs[i].getClass().getName() + + "'s policy is " + policyClass.getName() + " but not " + + expectedPolicyClass.getName()); + } + } + } + + // Dummy policy. + static class CustomPolicy extends FocusTraversalPolicy { + public Component getComponentAfter(Container focusCycleRoot, + Component aComponent) { + return null; + } + + public Component getComponentBefore(Container focusCycleRoot, + Component aComponent) { + return null; + } + + public Component getFirstComponent(Container focusCycleRoot) { + return null; + } + + public Component getLastComponent(Container focusCycleRoot) { + return null; + } + + public Component getDefaultComponent(Container focusCycleRoot) { + return null; + } + } +} diff --git a/test/jdk/java/awt/Focus/HiddenTraversalTest.java b/test/jdk/java/awt/Focus/HiddenTraversalTest.java new file mode 100644 index 0000000000000..59e7f477945e5 --- /dev/null +++ b/test/jdk/java/awt/Focus/HiddenTraversalTest.java @@ -0,0 +1,72 @@ +/* + * 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 4157017 + * @summary Checks whether focus can be traversed when component not visible + within parent container. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual HiddenTraversalTest +*/ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; + +public class HiddenTraversalTest { + + private static final String INSTRUCTIONS = """ + Examine the Frame. If six buttons are visible, resize the frame + so that only four are visible. If fewer than six buttons are + visible, do nothing.\n + Now, repeatedly press the tab key. Focus should cycle through the + visible and invisible buttons. If after six presses of the tab + button 'Button 0' has focus, the test passes. If focus is instead + stuck at 'Button 3', the test fails."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("HiddenTraversalTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HiddenTraversalTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame f = new Frame("Focus test"); + Panel p = new Panel(new FlowLayout()); + for (int i = 0; i < 6; i++) { + p.add(new Button("Button " + i)); + } + f.add(p); + f.setSize(200, 100); + return f; + } + +} + diff --git a/test/jdk/java/awt/Focus/InactiveFocusRace.java b/test/jdk/java/awt/Focus/InactiveFocusRace.java new file mode 100644 index 0000000000000..efca2dbf53a13 --- /dev/null +++ b/test/jdk/java/awt/Focus/InactiveFocusRace.java @@ -0,0 +1,188 @@ +/* + * 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 + * 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 4697451 + * @summary Test that there is no race between focus component in inactive window and window activation + * @key headful + * @run main InactiveFocusRace +*/ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.InputEvent; + +public class InactiveFocusRace { + + static Frame activeFrame, inactiveFrame; + Button activeButton, inactiveButton1, inactiveButton2; + Semaphore sema; + final static int TIMEOUT = 10000; + + public static void main(String[] args) throws Exception { + try { + InactiveFocusRace test = new InactiveFocusRace(); + test.init(); + test.start(); + } finally { + if (activeFrame != null) { + activeFrame.dispose(); + } + if (inactiveFrame != null) { + inactiveFrame.dispose(); + } + } + } + + public void init() { + activeButton = new Button("active button"); + inactiveButton1 = new Button("inactive button1"); + inactiveButton2 = new Button("inactive button2"); + activeFrame = new Frame("Active frame"); + inactiveFrame = new Frame("Inactive frame"); + inactiveFrame.setLayout(new FlowLayout()); + activeFrame.add(activeButton); + inactiveFrame.add(inactiveButton1); + inactiveFrame.add(inactiveButton2); + activeFrame.pack(); + activeFrame.setLocation(300, 10); + inactiveFrame.pack(); + inactiveFrame.setLocation(300, 300); + sema = new Semaphore(); + + inactiveButton1.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + System.err.println("Button 1 got focus"); + } + }); + inactiveButton2.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + System.err.println("Button2 got focus"); + sema.raise(); + } + }); + activeFrame.addWindowListener(new WindowAdapter() { + public void windowActivated(WindowEvent e) { + System.err.println("Window activated"); + sema.raise(); + } + }); + } + + public void start() { + Robot robot = null; + try { + robot = new Robot(); + } catch (Exception e) { + throw new RuntimeException("Unable to create robot"); + } + + inactiveFrame.setVisible(true); + activeFrame.setVisible(true); + + // Wait for active frame to become active + try { + sema.doWait(TIMEOUT); + } catch (InterruptedException ie) { + throw new RuntimeException("Wait was interrupted"); + } + if (!sema.getState()) { + throw new RuntimeException("Frame doesn't become active on show"); + } + sema.setState(false); + + // press on second button in inactive frame + Point loc = inactiveButton2.getLocationOnScreen(); + robot.mouseMove(loc.x+5, loc.y+5); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + // after all second button should be focus owner. + try { + sema.doWait(TIMEOUT); + } catch (InterruptedException ie) { + throw new RuntimeException("Wait was interrupted"); + } + if (!sema.getState()) { + throw new RuntimeException("Button2 didn't become focus owner"); + } + Toolkit.getDefaultToolkit().sync(); + robot.waitForIdle(); + if (!(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == inactiveButton2)) { + throw new RuntimeException("Button2 should be the focus owner after all"); + } + + } + +} + +class Semaphore { + boolean state = false; + int waiting = 0; + public Semaphore() { + } + public void doWait() throws InterruptedException { + synchronized(this) { + if (state) return; + waiting++; + wait(); + waiting--; + } + } + public void doWait(int timeout) throws InterruptedException { + synchronized(this) { + if (state) return; + waiting++; + wait(timeout); + waiting--; + } + } + public void raise() { + synchronized(this) { + state = true; + if (waiting > 0) { + notifyAll(); + } + } + } + public boolean getState() { + synchronized(this) { + return state; + } + } + public void setState(boolean newState) { + synchronized(this) { + state = newState; + } + } +} diff --git a/test/jdk/java/awt/Focus/InitialPrintDlgFocusTest.java b/test/jdk/java/awt/Focus/InitialPrintDlgFocusTest.java new file mode 100644 index 0000000000000..cc9d0c0371182 --- /dev/null +++ b/test/jdk/java/awt/Focus/InitialPrintDlgFocusTest.java @@ -0,0 +1,107 @@ +/* + * 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 + * 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 4688591 + * @summary Tab key hangs in Native Print Dialog on win32 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InitialPrintDlgFocusTest + */ + +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.JobAttributes; +import java.awt.PageAttributes; +import java.awt.PrintJob; +import java.awt.Toolkit; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JFrame; + +public class InitialPrintDlgFocusTest { + + private static final String INSTRUCTIONS = """ + After the tests starts you will see a frame titled "PrintTest". + Press the "Print" button and the print dialog should appear. + If you are able to transfer focus between components of the Print dialog + using the TAB key, then the test passes else the test fails. + + Note: close the Print dialog before clicking on "Pass" or "Fail" buttons."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("InitialPrintDlgFocusTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(InitialPrintDlgFocusTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + return new PrintTest(); + + } +} + +class PrintTest extends JFrame implements ActionListener { + + JButton b; + JobAttributes jbattrib; + Toolkit tk ; + PageAttributes pgattrib; + + public PrintTest() { + setTitle("PrintTest"); + setSize(500, 400); + + b = new JButton("Print"); + jbattrib = new JobAttributes(); + tk = Toolkit.getDefaultToolkit(); + pgattrib = new PageAttributes(); + getContentPane().setLayout(new FlowLayout()); + getContentPane().add(b); + + b.addActionListener(this); + + } + + public void actionPerformed(ActionEvent ae) { + if(ae.getSource()==b) + jbattrib.setDialog(JobAttributes.DialogType.NATIVE); + + PrintJob pjob = tk.getPrintJob(this, "Printing Test", + jbattrib, pgattrib); + + } +} + diff --git a/test/jdk/java/awt/Focus/KeyStrokeTest.java b/test/jdk/java/awt/Focus/KeyStrokeTest.java new file mode 100644 index 0000000000000..7c462ce8f22d6 --- /dev/null +++ b/test/jdk/java/awt/Focus/KeyStrokeTest.java @@ -0,0 +1,118 @@ +/* + * 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 4845868 + * @summary REGRESSION: First keystroke after JDialog is closed is lost + * @key headful + * @run main KeyStrokeTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +public class KeyStrokeTest { + static boolean keyTyped; + static Frame frame; + + public static void main(String[] args) throws Exception { + try { + KeyStrokeTest test = new KeyStrokeTest(); + test.doTest(); + } finally { + if (frame != null) { + frame.dispose(); + } + } + } + + private static void doTest() throws Exception { + final Object monitor = new Object(); + frame = new Frame(); + TextField textField = new TextField() { + public void transferFocus() { + System.err.println("transferFocus()"); + final Dialog dialog = new Dialog(frame, true); + Button btn = new Button("Close It"); + btn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.err.println("action performed"); + dialog.setVisible(false); + } + }); + dialog.add(btn); + dialog.setSize(200, 200); + dialog.setVisible(true); + } + }; + + textField.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + System.err.println(e); + if (e.getKeyChar() == 'a') { + keyTyped = true; + } + + synchronized (monitor) { + monitor.notifyAll(); + } + } + }); + frame.add(textField); + frame.setSize(400, 400); + frame.setVisible(true); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + + robot.delay(1000); + robot.keyPress(KeyEvent.VK_SPACE); + robot.keyRelease(KeyEvent.VK_SPACE); + + robot.delay(1000); + synchronized (monitor) { + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + monitor.wait(3000); + } + + if (!keyTyped) { + throw new RuntimeException("TEST FAILED"); + } + + System.out.println("Test passed"); + } + +} diff --git a/test/jdk/java/awt/Focus/LightweightPopupTest.java b/test/jdk/java/awt/Focus/LightweightPopupTest.java new file mode 100644 index 0000000000000..bc3dd0d038b9b --- /dev/null +++ b/test/jdk/java/awt/Focus/LightweightPopupTest.java @@ -0,0 +1,87 @@ +/* + * 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 4472032 + * @summary Switching between lightweight menus by horizontal arrow key works incorrect + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LightweightPopupTest +*/ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; + +public class LightweightPopupTest { + + private static final String INSTRUCTIONS = """ + When the test starts, you will see a frame titled + 'Lightweight Popup Test', which contains a button + (titled 'JButton') and two menus ('Menu 1' and 'Menu 2'). + Make sure that both menus, when expanded, fit entirely + into the frame. Now take the following steps: + 1. Click on 'JButton' to focus it. + 2. Click 'Menu 1' to expand it. + 3. Press right arrow to select 'Menu 2'. + Now check where the focus is. If it is on 'JButton' + (you can press space bar to see if it is there), then + the test failed. If 'JButton' is not focused, then + the test passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("LightweightPopupTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(LightweightPopupTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + + JFrame frame = new JFrame("Lightweight Popup Test"); + JButton button = new JButton("JButton"); + JMenuBar menuBar = new JMenuBar(); + JMenu menu1 = new JMenu("Menu 1"); + menu1.add(new JMenuItem("Menu Item 1")); + menu1.add(new JMenuItem("Menu Item 2")); + menuBar.add(menu1); + JMenu menu2 = new JMenu("Menu 2"); + menu2.add(new JMenuItem("Menu Item 3")); + menu2.add(new JMenuItem("Menu Item 4")); + menuBar.add(menu2); + + frame.add(button); + frame.setJMenuBar(menuBar); + frame.setSize(300, 200); + return frame; + } + +} + diff --git a/test/jdk/java/awt/Focus/MinimizeNonfocusableWindowTest.java b/test/jdk/java/awt/Focus/MinimizeNonfocusableWindowTest.java new file mode 100644 index 0000000000000..3114850dc7af3 --- /dev/null +++ b/test/jdk/java/awt/Focus/MinimizeNonfocusableWindowTest.java @@ -0,0 +1,76 @@ +/* + * 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 + * 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 6399659 + * @summary When minimizing non-focusable window focus shouldn't jump out of the focused window. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MinimizeNonfocusableWindowTest +*/ + +import java.awt.Frame; +import java.awt.Window; +import java.util.List; + +public class MinimizeNonfocusableWindowTest { + + private static final String INSTRUCTIONS = """ + + You should see three frames: Frame-1, Frame-2 and Unfocusable. + + 1. Click Frame-1 to make it focused window, then click Frame-2. + Minimize Unfocusable frame with the mouse. If Frame-2 is still + the focused window continue testing, otherwise press FAIL. + + 2. Restore Unfocusable frame to normal state. Try to resize by dragging + its edge with left mouse button. It should be resizable. If not press + FAIL. Try the same with right mouse button. It shouldn't resize. + If it does, press FAIL, otherwise press PASS."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MinimizeNonfocusableWindowTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .testUI(MinimizeNonfocusableWindowTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static List createTestUI() { + Frame frame1 = new Frame("Frame-1"); + Frame frame2 = new Frame("Frame-2"); + Frame frame3 = new Frame("Unfocusable"); + frame1.setBounds(100, 0, 200, 100); + frame2.setBounds(100, 150, 200, 100); + frame3.setBounds(100, 300, 200, 100); + + frame3.setFocusableWindowState(false); + + return List.of(frame1, frame2, frame3); + } +} + diff --git a/test/jdk/java/awt/Focus/ProxiedWindowHideTest.java b/test/jdk/java/awt/Focus/ProxiedWindowHideTest.java new file mode 100644 index 0000000000000..a99ec4f0a0d90 --- /dev/null +++ b/test/jdk/java/awt/Focus/ProxiedWindowHideTest.java @@ -0,0 +1,77 @@ +/* + * 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 4396407 + * @summary Tests that after a proxied window is hidden, focus is being restored correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ProxiedWindowHideTest +*/ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Container; +import javax.swing.Box; +import javax.swing.JComboBox; +import javax.swing.JFrame; + +public class ProxiedWindowHideTest { + + private static final String INSTRUCTIONS = """ + You will see a JFrame. + Click on JComboBox, list will expand then select any item in it. + After selection, list should collapse. + Click on Button('Push'). + If you are able to make it focused by mouse click, + (black rectangle will appear around it) the test is PASSED, + otherwise the test is FAILED."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ProxiedWindowHideTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ProxiedWindowHideTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame frame = new JFrame("ProxiedWindowHideTest frame"); + String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" }; + JComboBox cb = new JComboBox(petStrings); + + cb.setLightWeightPopupEnabled(false); + Container parent = Box.createVerticalBox(); + parent.add(new Button("Push")); + parent.add(cb); + frame.add(parent, BorderLayout.CENTER); + frame.pack(); + return frame; + } + +} + diff --git a/test/jdk/java/awt/Focus/RequestInInactiveFrame.java b/test/jdk/java/awt/Focus/RequestInInactiveFrame.java new file mode 100644 index 0000000000000..3297fe17501a1 --- /dev/null +++ b/test/jdk/java/awt/Focus/RequestInInactiveFrame.java @@ -0,0 +1,106 @@ +/* + * 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 + * 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 6458497 + * @summary check focus requests in inactive frames + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RequestInInactiveFrame + */ + +import java.util.ArrayList; + +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.Window; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class RequestInInactiveFrame { + + private static final String INSTRUCTIONS = """ + After the tests starts you will see two frames: \"test frame\" and \"opposite frame\" + activate the former by click on its title + Focus should be on \"press me\" button (if it's not, the test fails) + press on \"press me\" button and activate \"opposite frame\" + wait for several seconds. + Focus should either remain on button in the \"opposite frame\" + or goes to \"focus target\" button (in this case \"test frame\" should be activated + if it's not, the test failed. + Activate \"test frame\" one more time, press on \"press me\" button and switch focus + to some native window. Wait for several seconds, + If you see focus border around + \"focus target\" and \"test frame\" is not active then the test failed. + if focus transfered to that button and the frame is activated, or if there is no focus + in java - tests passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("RequestInInactiveFrame Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(RequestInInactiveFrame::createTestUI) + .build() + .awaitAndCheck(); + } + + private static ArrayList createTestUI() { + JFrame frame = new JFrame("test frame"); + final JButton btn2 = new JButton("focus target"); + JButton btn1 = new JButton("press me"); + btn1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("waiting..."); + try { + Thread.sleep(3000); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + System.out.println("requesting focus"); + btn2.requestFocus(); + } + }); + frame.setLayout(new FlowLayout()); + frame.add(btn1); + frame.add(btn2); + frame.pack(); + frame.setLocation(200, 100); + + JFrame frame2 = new JFrame("opposite frame"); + JButton btn3 = new JButton("just a button"); + frame2.add(btn3); + frame2.pack(); + frame2.setLocation(200, 200); + + ArrayList list = new ArrayList<>(); + list.add(frame); + list.add(frame2); + return list; + } + +} diff --git a/test/jdk/java/awt/Focus/WindowDisposeFocusTest.java b/test/jdk/java/awt/Focus/WindowDisposeFocusTest.java new file mode 100644 index 0000000000000..5e2e49147e54d --- /dev/null +++ b/test/jdk/java/awt/Focus/WindowDisposeFocusTest.java @@ -0,0 +1,98 @@ +/* + * 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 4257071 4228379 + * @summary Ensures that focus lost is delivered to a lightweight component + in a disposed window + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WindowDisposeFocusTest +*/ + +import java.awt.Window; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class WindowDisposeFocusTest { + + private static final String INSTRUCTIONS = """ + Click on "Second" + Click on close box + When dialog pops up, "Second" should no longer have focus."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("WindowDisposeFocusTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(WindowDisposeFocusTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Window createTestUI() { + return JFCFocusBug2.test(new String[]{}); + } +} + +class JFCFocusBug2 extends JPanel { + + static public Window test(String[] args) { + final JFrame frame = new JFrame("WindowDisposeFrame"); + frame.setSize(100, 100); + frame.setVisible(true); + + final JFCFocusBug2 bug = new JFCFocusBug2(); + final JDialog dialog = new JDialog(frame, false); + dialog.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + dialog.dispose(); + JDialog dialog2 = new JDialog(frame, false); + dialog2.setContentPane(bug); + dialog2.pack(); + dialog2.setVisible(true); + } + }); + dialog.setContentPane(bug); + dialog.pack(); + dialog.setVisible(true); + return frame; + } + + public JFCFocusBug2() { + _first = new JButton("First"); + _second = new JButton("Second"); + add(_first); + add(_second); + } + + private JButton _first; + private JButton _second; +} diff --git a/test/jdk/java/awt/Focus/bug6435715.java b/test/jdk/java/awt/Focus/bug6435715.java new file mode 100644 index 0000000000000..f13f731169ff7 --- /dev/null +++ b/test/jdk/java/awt/Focus/bug6435715.java @@ -0,0 +1,91 @@ +/* + * 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 + * 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 6435715 + * @summary JButton stops receiving the focusGained event and eventually focus is lost altogether + * @modules java.desktop/sun.awt + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug6435715 + */ + +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class bug6435715 { + + private static final String INSTRUCTIONS = """ + 1. after test started you will see frame with three buttons. Notice that focus is on Button2. + 2. Click on Button 3. Focus goes to Button3. + 3. Click on Button1 and quickly switch to another window. Via either alt/tab or + clicking another window with the mouse. + 4. After a few seconds, come back to the frame. Notice that focus is around Button2 + 5. Click at Button3. If focus remains at Button2 test failed, if focus is on Button3 - test passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug6435715 Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 5) + .columns(35) + .testUI(bug6435715::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame fr = new JFrame("FocusIssue"); + sun.awt.SunToolkit.setLWRequestStatus(fr, true); + + JPanel panel = new JPanel(); + final JButton b1 = new JButton("Button 1"); + final JButton b2 = new JButton("Button 2"); + final JButton b3 = new JButton("Button 3"); + + panel.add(b1); + panel.add(b2); + panel.add(b3); + + b1.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent event) { + synchronized (this) { + try { + wait(1000); + } catch (Exception ex) { + ex.printStackTrace(); + } + b2.requestFocus(); + } + } + }); + fr.getContentPane().add(panel); + fr.pack(); + return fr; + } + +} diff --git a/test/jdk/java/awt/Frame/AddRemoveMenuBarTest_5.java b/test/jdk/java/awt/Frame/AddRemoveMenuBarTest_5.java new file mode 100644 index 0000000000000..f5f1018c26282 --- /dev/null +++ b/test/jdk/java/awt/Frame/AddRemoveMenuBarTest_5.java @@ -0,0 +1,120 @@ +/* + * 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.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Robot; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @key headful + * @bug 4159883 + * @summary Adding/Removing a menu causes frame to unexpected small size + * @requires (os.family == "linux" | os.family == "windows") + */ + +public class AddRemoveMenuBarTest_5 { + + static Frame frame; + static MenuBar menu; + static Button btnAdd, btnRemove; + static Dimension oldSize; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + try { + EventQueue.invokeAndWait(AddRemoveMenuBarTest_5::initAndShowGui); + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + oldSize = frame.getSize(); + changeMenubar(true); + }); + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + checkSize(); + changeMenubar(false); + }); + robot.waitForIdle(); + robot.delay(500); + + EventQueue.invokeAndWait(AddRemoveMenuBarTest_5::checkSize); + } finally { + EventQueue.invokeAndWait(frame::dispose); + } + } + + public static void initAndShowGui() { + frame = new Frame(); + frame.setLocationRelativeTo(null); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + System.out.println("Frame size:" + frame.getSize().toString()); + System.out.println("Button size:" + btnAdd.getSize().toString()); + } + }); + frame.add("West", btnAdd = new Button("TRY:ADD")); + frame.add("East", btnRemove = new Button("TRY:REMOVE")); + + + btnAdd.addActionListener((e) -> changeMenubar(true)); + btnRemove.addActionListener((e) -> changeMenubar(false)); + frame.setSize(500, 100); + frame.setVisible(true); + } + + private static void changeMenubar(boolean enable) { + if (enable) { + menu = new MenuBar(); + menu.add(new Menu("BAAAAAAAAAAAAAAA")); + menu.add(new Menu("BZZZZZZZZZZZZZZZ")); + menu.add(new Menu("BXXXXXXXXXXXXXXX")); + } else { + menu = null; + } + frame.setMenuBar(menu); + frame.invalidate(); + frame.validate(); + + System.out.println("Frame size:" + frame.getSize().toString()); + System.out.println("Button size:" + btnAdd.getSize().toString()); + } + + private static void checkSize() { + Dimension newSize = frame.getSize(); + if (!oldSize.equals(newSize)) { + throw new RuntimeException("Frame size changed: old %s new %s" + .formatted(oldSize, newSize)); + } + } +} diff --git a/test/jdk/java/awt/Frame/DefaultFrameIconTest.java b/test/jdk/java/awt/Frame/DefaultFrameIconTest.java new file mode 100644 index 0000000000000..f8b48c6df2ca5 --- /dev/null +++ b/test/jdk/java/awt/Frame/DefaultFrameIconTest.java @@ -0,0 +1,67 @@ +/* + * 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. + */ + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4240766 8259023 + * @summary Frame Icon is wrong - should be Coffee Cup or Duke image icon + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultFrameIconTest +*/ + +public class DefaultFrameIconTest { + + private static final String INSTRUCTIONS = """ + You should see a dialog and a frame. + If both have Coffee Cup or Duke image icon in the upper left corner, + the test passes, otherwise it fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DefaultFrameIconTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(DefaultFrameIconTest::createAndShowUI) + .positionTestUIRightRow() + .build() + .awaitAndCheck(); + } + + private static List createAndShowUI() { + Frame testFrame = new Frame("Frame DefaultFrameIconTest"); + Dialog testDialog = new Dialog(testFrame, "Dialog DefaultFrameIconTest"); + + testDialog.setSize(250, 100); + + testFrame.setSize(250, 100); + return List.of(testFrame, testDialog); + } +} diff --git a/test/jdk/java/awt/Frame/DefaultLocationTest.java b/test/jdk/java/awt/Frame/DefaultLocationTest.java new file mode 100644 index 0000000000000..dfeb5e93e3c89 --- /dev/null +++ b/test/jdk/java/awt/Frame/DefaultLocationTest.java @@ -0,0 +1,66 @@ +/* + * 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.EventQueue; +import java.awt.Frame; +import java.awt.Label; + +/* + * @test + * @bug 4085599 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Test default location for frame + * @run main/manual DefaultLocationTest + */ + +public class DefaultLocationTest { + private static Frame f; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + A small frame containing the label 'Hello World' should + appear in the upper left hand corner of the screen. The + exact location is dependent upon the window manager. + + On Linux and Mac machines, the default location for frame + is below the taskbar or close to top-left corner. + + Upon test completion, click Pass or Fail appropriately."""; + + PassFailJFrame passFailJFrame = new PassFailJFrame("DefaultLocationTest " + + " Instructions", INSTRUCTIONS, 5, 10, 40); + EventQueue.invokeAndWait(DefaultLocationTest::createAndShowUI); + passFailJFrame.awaitAndCheck(); + } + + private static void createAndShowUI() { + f = new Frame("DefaultLocation"); + f.add("Center", new Label("Hello World")); + f.pack(); + PassFailJFrame.addTestWindow(f); + PassFailJFrame.positionTestWindow( + null, PassFailJFrame.Position.HORIZONTAL); + f.setVisible(true); + } +} diff --git a/test/jdk/java/awt/Frame/DeiconifyClipTest.java b/test/jdk/java/awt/Frame/DeiconifyClipTest.java new file mode 100644 index 0000000000000..c650355f3ec73 --- /dev/null +++ b/test/jdk/java/awt/Frame/DeiconifyClipTest.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + +/* + * DeiconifyClipTest.java + * + * summary: + * + * What happens is that we call AwtWindow::UpdateInsets when + * processing WM_NCCALCSIZE delivered on programmatic deiconification. + * At this point IsIconic returns false (so UpdateInsets proceeds), + * but the rect sizes still seems to be those weird of the iconic + * state. Based on them we compute insets with top = left = 0 (and + * bottom and right that are completely bogus) and pass them to + * PaintUpdateRgn which results in incorrect clip origin. Immediately + * after that we do UpdateInsets again during WM_SIZE processing and + * get real values. + */ + +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; + +/* + * @test + * @bug 4792958 + * @summary Incorrect clip region after programmatic restore + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DeiconifyClipTest +*/ + +public class DeiconifyClipTest { + private static final String INSTRUCTIONS = """ + This test creates a frame that is automatically iconified/deiconified + in a cycle. + + The test FAILS if after deiconfication the frame has a greyed-out area + in the lower-right corner. + If the frame contents is drawn completely - the test PASSES. + + Press PASS or FAIL button accordingly. + """; + + static TestFrame testFrame; + static volatile boolean shouldContinue = true; + + public static void main(String[] args) throws Exception { + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("DeiconifyClipTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(DeiconifyClipTest::createAndShowUI) + .build(); + try { + runThread(); + } finally { + passFailJFrame.awaitAndCheck(); + shouldContinue = false; + } + } + + private static void runThread() { + new Thread(() -> { + for (int i = 0; i < 1000 && shouldContinue; ++i) { + try { + Thread.sleep(3000); + SwingUtilities.invokeAndWait(() -> { + if ((testFrame.getExtendedState() & Frame.ICONIFIED) + != 0) { + testFrame.setExtendedState(Frame.NORMAL); + } else { + testFrame.setState(Frame.ICONIFIED); + } + }); + } catch (Exception ignored) { + } + } + }).start(); + } + + static Frame createAndShowUI() { + testFrame = new TestFrame(); + testFrame.getContentPane().setLayout(new BoxLayout(testFrame.getContentPane(), + BoxLayout.Y_AXIS)); + testFrame.getContentPane().setBackground(Color.yellow); + testFrame.setSize(300, 300); + return testFrame; + } + + static class TestFrame extends JFrame { + public TestFrame() { + super("DeiconifyClipTest"); + } + + // make it more visible if the clip is wrong. + public void paint(Graphics g) { + Insets b = getInsets(); + Dimension d = getSize(); + + int x = b.left; + int y = b.top; + int w = d.width - x - b.right; + int h = d.height - y - b.bottom; + + g.setColor(Color.white); + g.fillRect(0, 0, d.width, d.height); + + g.setColor(Color.green); + g.drawRect(x, y, w-1, h-1); + g.drawLine(x, y, x+w, y+h); + g.drawLine(x, y+h, x+w, y); + } + } +} diff --git a/test/jdk/java/awt/Frame/DisabledParentOfToplevel.java b/test/jdk/java/awt/Frame/DisabledParentOfToplevel.java new file mode 100644 index 0000000000000..878809749d35a --- /dev/null +++ b/test/jdk/java/awt/Frame/DisabledParentOfToplevel.java @@ -0,0 +1,99 @@ +/* + * 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 + * 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.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 5062118 + * @key headful + * @summary Disabling of a parent should not disable Window. + * @run main DisabledParentOfToplevel + */ + +public class DisabledParentOfToplevel { + private static Button okBtn; + private static Window ww; + private static Frame parentFrame; + private static volatile Point p; + private static volatile Dimension d; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + p = okBtn.getLocationOnScreen(); + d = okBtn.getSize(); + }); + robot.mouseMove(p.x + d.width / 2, p.x + d.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(500); + if (ww.isVisible()) { + throw new RuntimeException("Window is visible but should be hidden: failure."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (parentFrame != null) { + parentFrame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + parentFrame = new Frame("parentFrame"); + parentFrame.setSize(100, 100); + parentFrame.setEnabled(false); + ww = new Window(parentFrame); + ww.setLayout(new BorderLayout()); + okBtn = new Button("Click to Close Me"); + ww.add(okBtn); + ww.setSize(250, 250); + ww.setLocation(110, 110); + okBtn.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + System.out.println("Pressed: close"); + ww.setVisible(false); + } + }); + parentFrame.setVisible(true); + ww.setVisible(true); + okBtn.requestFocus(); + } +} diff --git a/test/jdk/java/awt/Frame/DisposeTest.java b/test/jdk/java/awt/Frame/DisposeTest.java new file mode 100644 index 0000000000000..08c0def638e69 --- /dev/null +++ b/test/jdk/java/awt/Frame/DisposeTest.java @@ -0,0 +1,112 @@ +/* + * 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 javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/* + * @test + * @key headful + * @bug 4127271 + * @summary Tests that disposing of a Frame with MenuBar removes all traces + * of the Frame from the screen. + */ + +public class DisposeTest { + private static Frame backgroundFrame; + private static Frame testedFrame; + + private static final Rectangle backgroundFrameBounds = + new Rectangle(100, 100, 200, 200); + private static final Rectangle testedFrameBounds = + new Rectangle(150, 150, 100, 100); + + private static Robot robot; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + try { + EventQueue.invokeAndWait(DisposeTest::initAndShowGui); + robot.waitForIdle(); + robot.delay(500); + EventQueue.invokeAndWait(testedFrame::dispose); + robot.waitForIdle(); + robot.delay(500); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + backgroundFrame.dispose(); + testedFrame.dispose(); + }); + } + } + + private static void test() { + BufferedImage bi = robot.createScreenCapture(backgroundFrameBounds); + int redPix = Color.RED.getRGB(); + + for (int x = 0; x < bi.getWidth(); x++) { + for (int y = 0; y < bi.getHeight(); y++) { + if (bi.getRGB(x, y) != redPix) { + try { + ImageIO.write(bi, "png", + new File("failure.png")); + } catch (IOException ignored) {} + throw new RuntimeException("Test failed"); + } + } + } + } + + private static void initAndShowGui() { + backgroundFrame = new Frame("DisposeTest background"); + backgroundFrame.setUndecorated(true); + backgroundFrame.setBackground(Color.RED); + backgroundFrame.setBounds(backgroundFrameBounds); + backgroundFrame.setVisible(true); + + testedFrame = new UglyFrame(); + } + + static class UglyFrame extends Frame { + public UglyFrame() { + super("DisposeTest"); + MenuBar mb = new MenuBar(); + Menu m = new Menu("menu"); + mb.add(m); + setMenuBar(mb); + setBounds(testedFrameBounds); + setVisible(true); + } + } +} + diff --git a/test/jdk/java/awt/Frame/EmptyFrameTest.java b/test/jdk/java/awt/Frame/EmptyFrameTest.java new file mode 100644 index 0000000000000..7a324a3cbc994 --- /dev/null +++ b/test/jdk/java/awt/Frame/EmptyFrameTest.java @@ -0,0 +1,99 @@ +/* + * 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. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +/* + * @test + * @bug 4237529 + * @key headful + * @summary Test repainting of an empty frame + * @run main EmptyFrameTest + */ + +public class EmptyFrameTest { + private static Frame f; + private static Robot robot; + private static volatile Point p; + private static volatile Dimension d; + private static final int TOLERANCE = 5; + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + robot.delay(1000); + f.setSize(50, 50); + robot.delay(500); + EventQueue.invokeAndWait(() -> { + p = f.getLocation(); + d = f.getSize(); + }); + Rectangle rect = new Rectangle(p, d); + BufferedImage img = robot.createScreenCapture(rect); + if (chkImgBackgroundColor(img)) { + try { + ImageIO.write(img, "png", new File("Frame.png")); + } catch (IOException ignored) {} + throw new RuntimeException("Frame doesn't repaint itself on resize"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + f = new Frame("EmptyFrameTest"); + f.setUndecorated(true); + f.setBackground(Color.RED); + f.setVisible(true); + } + + private static boolean chkImgBackgroundColor(BufferedImage img) { + for (int x = 1; x < img.getWidth() - 1; ++x) { + for (int y = 1; y < img.getHeight() - 1; ++y) { + Color c = new Color(img.getRGB(x, y)); + if ((c.getRed() - Color.RED.getRed()) > TOLERANCE) { + return true; + } + } + } + return false; + } +} diff --git a/test/jdk/java/awt/Frame/FocusTest.java b/test/jdk/java/awt/Frame/FocusTest.java new file mode 100644 index 0000000000000..14d194789fe94 --- /dev/null +++ b/test/jdk/java/awt/Frame/FocusTest.java @@ -0,0 +1,151 @@ +/* + * 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. + */ + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Window; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @bug 4140293 + * @summary Tests that focus is returned to the correct Component when a Frame + * is reactivated. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FocusTest + */ + +public class FocusTest { + private static final String INSTRUCTIONS = """ + Click on the bottom rectangle. Move the mouse slightly. + A focus rectangle should appear around the bottom rectangle. + + Now, deactivate the window and then reactivate it. + (You would click on the caption bar of another window, + and then on the caption bar of the FocusTest Frame.) + + If the focus rectangle appears again, the test passes. + If it does not appear, or appears around the top rectangle, + the test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FocusTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(6) + .testUI(FocusTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Window createAndShowUI() { + Frame frame = new Frame("FocusTest"); + + frame.add(new FocusTestPanel()); + frame.setSize(400, 400); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + frame.dispose(); + } + }); + + frame.validate(); + return frame; + } + + private static class FocusTestPanel extends Panel { + PassiveClient pc1 = new PassiveClient("pc1"); + PassiveClient pc2 = new PassiveClient("pc2"); + + public FocusTestPanel() { + super(); + setLayout(new GridLayout(2, 1, 10, 10)); + add(pc1); + add(pc2); + } + } + + private static class PassiveClient extends Canvas implements FocusListener { + boolean haveFocus = false; + final String name; + + PassiveClient(String name) { + super(); + this.name = name; + setSize(400, 100); + setBackground(Color.cyan); + setVisible(true); + setEnabled(true); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + requestFocus(); + } + }); + addFocusListener(this); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + Dimension size = getSize(); + g.fillRect(0, 0, size.width, size.height); + if (haveFocus) { + g.setColor(Color.black); + g.drawRect(0, 0, size.width - 1, size.height - 1); + g.drawRect(1, 1, size.width - 3, size.height - 3); + } + g.setColor(getForeground()); + } + + public void focusGained(FocusEvent event) { + haveFocus = true; + paint(getGraphics()); + PassFailJFrame.log("<<<< %s Got focus!! %s>>>>".formatted(this, event)); + } + + public void focusLost(FocusEvent event) { + haveFocus = false; + paint(getGraphics()); + PassFailJFrame.log("<<<< %s Lost focus!! %s>>>>".formatted(this, event)); + } + + @Override + public String toString() { + return "PassiveClient " + name; + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameLayoutTest.java b/test/jdk/java/awt/Frame/FrameLayoutTest.java new file mode 100644 index 0000000000000..d6e994beaace0 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameLayoutTest.java @@ -0,0 +1,70 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; + +/* + * @test + * @bug 4173503 + * @library /java/awt/regtesthelpers + * @requires (os.family == "windows") + * @build PassFailJFrame + * @summary Tests that frame layout is performed when frame is maximized from taskbar + * @run main/manual FrameLayoutTest + */ + +public class FrameLayoutTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Right-click on the taskbar button for this test. In the menu appeared, + choose Maximize. The frame will be maximized. Check if buttons inside + the frame are laid out properly, i.e. they occupy the frame entirely. + + If so, test passes. If buttons occupy small rectangle in the top left + corner, test fails."""; + + PassFailJFrame.builder() + .title("Frame's Layout Test Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(FrameLayoutTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Maximize Test"); + f.add(new Button("North"), BorderLayout.NORTH); + f.add(new Button("South"), BorderLayout.SOUTH); + f.add(new Button("East"), BorderLayout.EAST); + f.add(new Button("West"), BorderLayout.WEST); + f.add(new Button("Cent"), BorderLayout.CENTER); + f.pack(); + f.setState(Frame.ICONIFIED); + return f; + } +} diff --git a/test/jdk/java/awt/Frame/FrameMenuPackTest.java b/test/jdk/java/awt/Frame/FrameMenuPackTest.java new file mode 100644 index 0000000000000..c034acd8eb710 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameMenuPackTest.java @@ -0,0 +1,99 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.ScrollPane; +import java.awt.Window; +import java.util.List; + +/* + * @test + * @bug 4084766 + * @summary Test for bug(s): 4084766 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameMenuPackTest + */ + +public class FrameMenuPackTest { + private static final String INSTRUCTIONS = """ + Check that both frames that appear are properly packed with + the scrollpane visible. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("FrameMenuPackTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameMenuPackTest::createAndShowUI) + .positionTestUIRightRow() + .build() + .awaitAndCheck(); + } + + private static List createAndShowUI() { + // Frame without menu, packs correctly + PackedFrame f1 = new PackedFrame(false); + f1.pack(); + + // Frame with menu, doesn't pack right + PackedFrame f2 = new PackedFrame(true); + f2.pack(); + + return List.of(f1, f2); + } + + private static class PackedFrame extends Frame { + public PackedFrame(boolean withMenu) { + super("PackedFrame"); + + MenuBar menubar; + Menu fileMenu; + MenuItem foo; + ScrollPane sp; + + sp = new ScrollPane(); + sp.add(new Label("Label in ScrollPane")); + System.out.println(sp.getMinimumSize()); + + this.setLayout(new BorderLayout()); + this.add(sp, "Center"); + this.add(new Label("Label in Frame"), "South"); + + if (withMenu) { + menubar = new MenuBar(); + fileMenu = new Menu("File"); + foo = new MenuItem("foo"); + fileMenu.add(foo); + menubar.add(fileMenu); + this.setMenuBar(menubar); + } + } + } +} diff --git a/test/jdk/java/awt/Frame/FramePaintTest.java b/test/jdk/java/awt/Frame/FramePaintTest.java new file mode 100644 index 0000000000000..aaa14893b34a2 --- /dev/null +++ b/test/jdk/java/awt/Frame/FramePaintTest.java @@ -0,0 +1,91 @@ +/* + * 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.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; + +/* + * @test + * @bug 4023385 + * @summary resizing a frame causes too many repaints + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FramePaintTest +*/ + +public class FramePaintTest { + private static final String INSTRUCTIONS = """ + You should see a Frame titled "Repaint Test", filled with colored blocks. + + Resize the frame several times, both inward as well as outward. + + The blocks should move to fill the window without any flashes or + glitches which ensures that repaint is not done excessively + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FramePaintTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(ResizeLW::new) + .build() + .awaitAndCheck(); + } + + static class ResizeLW extends Frame { + + public ResizeLW() { + super("Repaint Test"); + setBackground(Color.red); + setLayout(new FlowLayout()); + setSize(300, 300); + + for (int i = 0; i < 10; i++) { + add(new ColorComp(Color.blue)); + add(new ColorComp(Color.green)); + } + } + + private static class ColorComp extends Component { + public ColorComp(Color c) { + super(); + setBackground(c); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + } + + public Dimension getPreferredSize() { + return new Dimension(50, 50); + } + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizableTest.java b/test/jdk/java/awt/Frame/FrameResizableTest.java new file mode 100644 index 0000000000000..4734ce71670aa --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizableTest.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.Label; +import java.awt.Panel; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 1231233 + * @summary Tests whether the resizable property of a Frame is + * respected after it is set. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizableTest + */ + +public class FrameResizableTest { + private static final String INSTRUCTIONS = """ + There is a frame with two buttons and a label. The label + reads 'true' or 'false' to indicate whether the frame can be + resized or not. + + When the first button, 'Set Resizable', is + clicked, you should be able to resize the frame. + When the second button, 'UnSet Resizable', is clicked, you should + not be able to resize the frame. + + A frame is resized in a way which depends upon the window manager (WM) running. + You may resize the frame by dragging the corner resize handles or the borders, + or you may use the title bar's resize menu items and buttons. + + Upon test completion, click Pass or Fail appropriately. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizableTest Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(FrameResizable::new) + .build() + .awaitAndCheck(); + } + + private static class FrameResizable extends Frame { + Label label; + Button buttonResizable; + Button buttonNotResizable; + + public FrameResizable() { + super("FrameResizable"); + setResizable(false); + Panel panel = new Panel(); + + add("North", panel); + ActionListener actionListener = (e) -> { + if (e.getSource() == buttonResizable) { + setResizable(true); + } else if (e.getSource() == buttonNotResizable) { + setResizable(false); + } + label.setText("Resizable: " + isResizable()); + }; + + panel.add(buttonResizable = new Button("Set Resizable")); + panel.add(buttonNotResizable = new Button("UnSet Resizable")); + panel.add(label = new Label("Resizable: " + isResizable())); + buttonResizable.addActionListener(actionListener); + buttonNotResizable.addActionListener(actionListener); + + setSize(400, 200); + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_3.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_3.java new file mode 100644 index 0000000000000..6fdef005775a5 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_3.java @@ -0,0 +1,86 @@ +/* + * 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 + * 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.GridLayout; +import java.awt.Window; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4097207 + * @summary setSize() on a Frame does not resize its content + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_3 +*/ + +public class FrameResizeTest_3 { + + private static final String INSTRUCTIONS = """ + 1. You would see a frame titled 'TestFrame' with 2 buttons + named 'setSize(500,500)' and 'setSize(400,400)' + 2. Click any button and you would see the frame resized + 3. If the buttons get resized along with the frame + (ie., to fit the frame), press Pass else press Fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizeTest_3 Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(6) + .testUI(FrameResizeTest_3::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Window createTestUI() { + Frame frame = new Frame("TestFrame"); + frame.setLayout(new GridLayout(2, 1)); + + Button butSize500 = new Button("setSize(500,500)"); + Button butSize400 = new Button("setSize(400,400)"); + + ActionListener actionListener = e -> { + if (e.getSource() instanceof Button) { + if (e.getSource() == butSize500) { + frame.setSize(500, 500); + PassFailJFrame.log("New bounds: " + frame.getBounds()); + } else if (e.getSource() == butSize400) { + frame.setSize(400, 400); + PassFailJFrame.log("New bounds: " + frame.getBounds()); + } + } + }; + butSize500.addActionListener(actionListener); + butSize400.addActionListener(actionListener); + frame.add(butSize500); + frame.add(butSize400); + + frame.setSize(270, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_4.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_4.java new file mode 100644 index 0000000000000..4428feee3355b --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_4.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +/* Note that although this test makes use of Swing classes, like JFrame and */ +/* JButton, it is really an AWT test, because it tests mechanism of sending */ +/* paint events. */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import java.awt.BorderLayout; + +/* + * @test + * @bug 4174831 + * @summary Tests that frame do not flicker on diagonal resize + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_4 + */ + +public class FrameResizeTest_4 { + private static final String INSTRUCTIONS = """ + Try enlarging the frame diagonally. + If buttons inside frame excessively repaint themselves and flicker + while you enlarge frame, the test fails. + Otherwise, it passes. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizeTest_4 Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameResizeTest_4::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + JFrame f = new JFrame("FrameResizeTest_4 Flickering Frame"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(new JButton("West"), BorderLayout.WEST); + panel.add(new JButton("East"), BorderLayout.EAST); + panel.add(new JButton("North"), BorderLayout.NORTH); + panel.add(new JButton("South"), BorderLayout.SOUTH); + panel.add(new JButton("Center"), BorderLayout.CENTER); + f.setContentPane(panel); + + f.pack(); + f.setBounds(100, 50, 300, 200); + + return f; + } +} diff --git a/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_5.java b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_5.java new file mode 100644 index 0000000000000..5a43b755a5212 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameResizeTest/FrameResizeTest_5.java @@ -0,0 +1,105 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; + +/* + * @test + * @summary Test to make sure non-resizable Frames can be resized with the + * setSize() method. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameResizeTest_5 +*/ + +public class FrameResizeTest_5 { + private static final String INSTRUCTIONS = """ + This tests the programmatic resizability of non-resizable Frames. + Even when a Frame is set to be non-resizable, it should still be + programmatically resizable using the setSize() method. + + Initially the Frame will be resizable. Try using the "Smaller" + and "Larger" buttons to verify that the Frame resizes correctly. + Then, click the "Toggle" button to make the Frame non-resizable. + Again, verify that clicking the "Larger" and "Smaller" buttons + causes the Frame to get larger and smaller. If the Frame does + not change size, or does not re-layout correctly, the test fails. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameResizeTest_5 Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(6) + .testUI(TestFrame::new) + .build() + .awaitAndCheck(); + } + + private static class TestFrame extends Frame { + Button bLarger, bSmaller, bCheck, bToggle; + + public TestFrame() { + super("Frame Resize Test"); + setSize(200, 200); + bLarger = new Button("Larger"); + bLarger.addActionListener(e -> { + setSize(400, 400); + validate(); + }); + bSmaller = new Button("Smaller"); + bSmaller.addActionListener(e -> { + setSize(200, 100); + validate(); + }); + bCheck = new Button("Resizable?"); + bCheck.addActionListener(e -> { + if (isResizable()) { + PassFailJFrame.log("Frame is resizable"); + setResizable(true); + } else { + PassFailJFrame.log("Frame is not resizable"); + setResizable(false); + } + }); + bToggle = new Button("Toggle"); + bToggle.addActionListener(e -> { + if (isResizable()) { + PassFailJFrame.log("Frame is now not resizable"); + setResizable(false); + } else { + PassFailJFrame.log("Frame is now resizable"); + setResizable(true); + } + }); + setLayout(new GridLayout(4, 1)); + add(bSmaller); + add(bLarger); + add(bCheck); + add(bToggle); + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameSetCursorTest.java b/test/jdk/java/awt/Frame/FrameSetCursorTest.java new file mode 100644 index 0000000000000..98968a8fbab82 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameSetCursorTest.java @@ -0,0 +1,100 @@ +/* + * 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.BorderLayout; +import java.awt.Button; +import java.awt.Cursor; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.ActionListener; +import java.lang.Exception; +import java.lang.InterruptedException; +import java.lang.Object; +import java.lang.String; +import java.lang.Thread; + +/* + * @test + * @bug 4097226 + * @summary Frame.setCursor() sometimes doesn't update the cursor until user moves the mouse + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual FrameSetCursorTest + */ + +public class FrameSetCursorTest { + private static final String INSTRUCTIONS = """ + 1. Keep the instruction dialog and TestFrame side by side so that + you can read the instructions while doing the test + 2. Click on the 'Start Busy' button on the frame titled 'TestFrame' + and DO NOT MOVE THE MOUSE ANYWHERE till you complete the steps below + 3. The cursor on the TestFrame changes to busy cursor + 4. If you don't see the busy cursor press 'Fail' after + the `done sleeping` message + 5. If the busy cursor is seen, after 5 seconds the message + 'done sleeping' is displayed in the message window + 6. Check for the cursor type after the display of 'done sleeping' + 7. If the cursor on the TestFrame has changed back to default cursor + (without you touching or moving the mouse), then press 'Pass' + else if the frame still shows the busy cursor press 'Fail' + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("FrameSetCursorTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameSetCursorTest::createAndShowUI) + .logArea(5) + .build() + .awaitAndCheck(); + + } + + static Frame createAndShowUI() { + Frame frame = new Frame("TestFrame"); + Panel panel = new Panel(); + Button busyButton = new Button("Start Busy"); + + ActionListener actionListener = event -> { + Object source = event.getSource(); + if (source == busyButton) { + frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) {} + PassFailJFrame.log("done sleeping"); + frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + }; + + busyButton.addActionListener(actionListener); + panel.setLayout(new BorderLayout()); + panel.add("North", busyButton); + + frame.add(panel); + frame.pack(); + frame.setSize(200, 200); + return frame; + } +} \ No newline at end of file diff --git a/test/jdk/java/awt/Frame/FrameSetMinimumSizeTest.java b/test/jdk/java/awt/Frame/FrameSetMinimumSizeTest.java new file mode 100644 index 0000000000000..929a36e773e40 --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameSetMinimumSizeTest.java @@ -0,0 +1,91 @@ +/* + * 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 + * 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.EventQueue; +import java.awt.Frame; + +/* + * @test + * @bug 4320050 + * @key headful + * @summary Minimum size for java.awt.Frame is not being enforced. + * @run main FrameSetMinimumSizeTest + */ + +public class FrameSetMinimumSizeTest { + private static Frame f; + private static volatile boolean passed; + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(() -> { + try { + createAndShowUI(); + + f.setSize(200, 200); + passed = verifyFrameSize(new Dimension(300, 300)); + isFrameSizeOk(passed); + + f.setSize(200, 400); + passed = verifyFrameSize(new Dimension(300, 400)); + isFrameSizeOk(passed); + + f.setSize(400, 200); + passed = verifyFrameSize(new Dimension(400, 300)); + isFrameSizeOk(passed); + + f.setMinimumSize(null); + + f.setSize(200, 200); + passed = verifyFrameSize(new Dimension(200, 200)); + isFrameSizeOk(passed); + } finally { + if (f != null) { + f.dispose(); + } + } + }); + } + + private static void createAndShowUI() { + f = new Frame("Minimum Size Test"); + f.setSize(300, 300); + f.setMinimumSize(new Dimension(300, 300)); + f.setVisible(true); + } + + private static boolean verifyFrameSize(Dimension expected) { + + if (f.getSize().width != expected.width || f.getSize().height != expected.height) { + return false; + } + return true; + } + + private static void isFrameSizeOk(boolean passed) { + if (!passed) { + throw new RuntimeException("Frame's setMinimumSize not honoured for the" + + " frame size: " + f.getSize()); + } + } +} diff --git a/test/jdk/java/awt/Frame/FrameVisualTest.java b/test/jdk/java/awt/Frame/FrameVisualTest.java new file mode 100644 index 0000000000000..767eb0a18965c --- /dev/null +++ b/test/jdk/java/awt/Frame/FrameVisualTest.java @@ -0,0 +1,117 @@ +/* + * 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 + * 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.EventQueue; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; + +/* + * @test + * @bug 4328588 + * @key headful + * @summary Non-default visual on top-level Frame should work + * @run main FrameVisualTest + */ + +public class FrameVisualTest { + private static GraphicsConfiguration[] gcs; + private static volatile Frame[] frames; + private static volatile int index; + + private static Frame f; + private static Robot robot; + private static volatile Point p; + private static volatile Dimension d; + private static final int TOLERANCE = 5; + + public static void main(String[] args) throws Exception { + gcs = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getConfigurations(); + robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + createAndShowUI(); + }); + robot.delay(1000); + System.out.println("frames.length: "+frames.length); + for (index = 0; index < frames.length; index++) { + EventQueue.invokeAndWait(() -> { + p = frames[index].getLocation(); + d = frames[index].getSize(); + }); + Rectangle rect = new Rectangle(p, d); + BufferedImage img = robot.createScreenCapture(rect); + if (chkImgBackgroundColor(img)) { + try { + ImageIO.write(img, "png", new File("Frame_" + index + ".png")); + } catch (IOException ignored) {} + throw new RuntimeException("Frame visual test failed with non-white background color"); + } + } + } finally { + for (index = 0; index < frames.length; index++) { + EventQueue.invokeAndWait(() -> { + if (frames[index] != null) { + frames[index].dispose(); + } + }); + } + } + } + + private static void createAndShowUI() { + frames = new Frame[gcs.length]; + for (int i = 0; i < frames.length; i++) { + frames[i] = new Frame("Frame w/ gc " + i, gcs[i]); + frames[i].setSize(100, 100); + frames[i].setUndecorated(true); + frames[i].setBackground(Color.WHITE); + frames[i].setVisible(true); + } + } + + private static boolean chkImgBackgroundColor(BufferedImage img) { + + // scan for mid-line and if it is non-white color then return true. + for (int x = 1; x < img.getWidth() - 1; ++x) { + Color c = new Color(img.getRGB(x, img.getHeight() / 2)); + if ((c.getRed() - Color.WHITE.getRed()) > TOLERANCE && + (c.getGreen() - Color.WHITE.getGreen()) > TOLERANCE && + (c.getBlue() - Color.WHITE.getBlue()) > TOLERANCE) { + return true; + } + } + return false; + } +} + diff --git a/test/jdk/java/awt/Frame/I18NTitle.java b/test/jdk/java/awt/Frame/I18NTitle.java new file mode 100644 index 0000000000000..7127fc3dfbf6b --- /dev/null +++ b/test/jdk/java/awt/Frame/I18NTitle.java @@ -0,0 +1,65 @@ +/* + * 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 + * 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.Frame; +import java.awt.Label; +import java.awt.Window; + +/* + * @test + * @bug 6269884 4929291 + * @summary Tests that title which contains mix of non-English characters is displayed correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual I18NTitle + */ + +public class I18NTitle { + private static final String INSTRUCTIONS = """ + You will see a frame with some title (S. Chinese, Cyrillic and German). + Please check if non-English characters are visible and compare + the visible title with the same string shown in the label + (it should not look worse than the label). + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("I18NTitle Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .testUI(I18NTitle::createAndShowGUI) + .build() + .awaitAndCheck(); + } + + private static Window createAndShowGUI() { + String s = "\u4e2d\u6587\u6d4b\u8bd5 \u0420\u0443\u0441\u0441\u043a\u0438\u0439 Zur\u00FCck"; + Frame frame = new Frame(s); + frame.setLayout(new BorderLayout()); + Label l = new Label(s); + frame.add(l); + frame.setSize(400, 100); + return frame; + } +} diff --git a/test/jdk/java/awt/Frame/IMStatusBar.java b/test/jdk/java/awt/Frame/IMStatusBar.java new file mode 100644 index 0000000000000..7e882d01c707c --- /dev/null +++ b/test/jdk/java/awt/Frame/IMStatusBar.java @@ -0,0 +1,70 @@ +/* + * 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.BorderLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.TextField; + +/* + * @test + * @bug 4113040 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Checks that IMStatusBar does not affect Frame layout + * @run main/manual/othervm -Duser.language=ja -Duser.country=JP IMStatusBar + */ + +public class IMStatusBar { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + If the window appears the right size, but then resizes so that the + status field overlaps the bottom label, press Fail; otherwise press Pass. + """; + + PassFailJFrame.builder() + .title("IMStatusBar Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(IMStatusBar::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame(); + Panel centerPanel = new Panel(); + f.setSize(200, 200); + f.setLayout(new BorderLayout()); + f.add(new Label("Top"), BorderLayout.NORTH); + f.add(centerPanel, BorderLayout.CENTER); + f.add(new Label("Bottom"), BorderLayout.SOUTH); + centerPanel.setLayout(new BorderLayout()); + centerPanel.add(new TextField("Middle"), BorderLayout.CENTER); + centerPanel.validate(); + return f; + } +} diff --git a/test/jdk/java/awt/Frame/InitialIconifiedTest.java b/test/jdk/java/awt/Frame/InitialIconifiedTest.java new file mode 100644 index 0000000000000..4a8d959566575 --- /dev/null +++ b/test/jdk/java/awt/Frame/InitialIconifiedTest.java @@ -0,0 +1,138 @@ +/* + * 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. + */ + +import javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/* + * @test + * @key headful + * @bug 4851435 + * @summary Frame is not shown initially iconified after pack + */ + +public class InitialIconifiedTest { + + private static Frame backgroundFrame; + private static Frame testedFrame; + + private static final Rectangle backgroundFrameBounds = + new Rectangle(100, 100, 200, 200); + private static final Rectangle testedFrameBounds = + new Rectangle(150, 150, 100, 100); + + private static Robot robot; + + private static final StringBuilder FAILURES = new StringBuilder(); + + public static void main(String[] args) throws Exception { + robot = new Robot(); + try { + EventQueue.invokeAndWait(InitialIconifiedTest::initAndShowBackground); + robot.waitForIdle(); + robot.delay(500); + + test(false); + test(true); + } finally { + EventQueue.invokeAndWait(() -> { + backgroundFrame.dispose(); + testedFrame.dispose(); + }); + } + + if (!FAILURES.isEmpty()) { + throw new RuntimeException(FAILURES.toString()); + } + } + + private static void test(boolean isUndecorated) throws Exception { + String prefix = isUndecorated ? "undecorated" : "decorated"; + + EventQueue.invokeAndWait(() -> initAndShowTestedFrame(isUndecorated)); + // On macos, we can observe the animation of the window from the initial + // NORMAL state to the ICONIFIED state, + // even if the window was created in the ICONIFIED state. + // The following delay is commented out to capture this animation + // robot.waitForIdle(); + // robot.delay(500); + if (!testIfIconified(prefix + "_no_extra_delay")) { + FAILURES.append("Case %s frame with no extra delay failed\n" + .formatted(isUndecorated ? "undecorated" : "decorated")); + } + + EventQueue.invokeAndWait(() -> initAndShowTestedFrame(isUndecorated)); + robot.waitForIdle(); + robot.delay(500); + if (!testIfIconified(prefix + "_with_extra_delay")) { + FAILURES.append("Case %s frame with extra delay failed\n" + .formatted(isUndecorated ? "undecorated" : "decorated")); + } + } + + private static void initAndShowBackground() { + backgroundFrame = new Frame("DisposeTest background"); + backgroundFrame.setUndecorated(true); + backgroundFrame.setBackground(Color.RED); + backgroundFrame.setBounds(backgroundFrameBounds); + backgroundFrame.setVisible(true); + } + + private static void initAndShowTestedFrame(boolean isUndecorated) { + if (testedFrame != null) { + testedFrame.dispose(); + } + testedFrame = new Frame("Should have started ICONIC"); + if (isUndecorated) { + testedFrame.setUndecorated(true); + } + testedFrame.setExtendedState(Frame.ICONIFIED); + testedFrame.setBounds(testedFrameBounds); + testedFrame.setVisible(true); + } + + private static boolean testIfIconified(String prefix) { + BufferedImage bi = robot.createScreenCapture(backgroundFrameBounds); + int redPix = Color.RED.getRGB(); + + for (int x = 0; x < bi.getWidth(); x++) { + for (int y = 0; y < bi.getHeight(); y++) { + if (bi.getRGB(x, y) != redPix) { + try { + ImageIO.write(bi, "png", + new File(prefix + "_failure.png")); + } catch (IOException ignored) {} + return false; + } + } + } + return true; + } +} diff --git a/test/jdk/java/awt/Frame/InsetCorrectionTest.java b/test/jdk/java/awt/Frame/InsetCorrectionTest.java new file mode 100644 index 0000000000000..1ea6f03b24b15 --- /dev/null +++ b/test/jdk/java/awt/Frame/InsetCorrectionTest.java @@ -0,0 +1,102 @@ +/* + * 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.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4091426 + * @key headful + * @summary Test inset correction when setVisible(true) BEFORE setSize(), setLocation() + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual InsetCorrectionTest + */ + +public class InsetCorrectionTest { + private static final String INSTRUCTIONS = """ + There is a frame of size 300x300 at location (100,100). + It has a menubar with one menu, 'File', but the frame + is otherwise empty. In particular, there should be no + part of the frame that is not shown in the background color. + Upon test completion, click Pass or Fail appropriately. + """; + + private static InsetCorrection testFrame; + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(() -> testFrame = new InsetCorrection()); + + try { + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("InsetCorrectionTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea(3) + .build(); + EventQueue.invokeAndWait(() -> + PassFailJFrame.log("frame location: " + testFrame.getBounds())); + passFailJFrame.awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(testFrame::dispose); + } + } + + static class InsetCorrection extends Frame + implements ActionListener { + MenuBar mb; + Menu file; + MenuItem cause_bug_b; + + public InsetCorrection() { + super("InsetCorrection"); + mb = new MenuBar(); + file = new Menu("File"); + mb.add(file); + cause_bug_b = new MenuItem("cause bug"); + file.add(cause_bug_b); + setMenuBar(mb); + cause_bug_b.addActionListener(this); + + // Making the frame visible before setSize and setLocation() + // are being called causes sometimes strange behaviour with + // JDK1.1.5G. The frame is then sometimes to large and the + // excess areas are drawn in black. This only happens + // sometimes. + setVisible(true); + setSize(300, 300); + setLocation(100, 100); + } + + public void actionPerformed(ActionEvent e) { + setVisible(false); + setVisible(true); + } + } +} diff --git a/test/jdk/java/awt/Frame/MenuBarOffsetTest.java b/test/jdk/java/awt/Frame/MenuBarOffsetTest.java new file mode 100644 index 0000000000000..65a4a8ef4bd59 --- /dev/null +++ b/test/jdk/java/awt/Frame/MenuBarOffsetTest.java @@ -0,0 +1,81 @@ +/* + * 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. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Menu; +import java.awt.MenuBar; + +/* + * @test + * @bug 4180577 + * @summary offset problems with menus in frames: (2 * 1) should be (2 * menuBarBorderSize) + * @requires (os.family == "linux" | os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarOffsetTest +*/ + +public class MenuBarOffsetTest { + private static final String INSTRUCTIONS = """ + If a menubar containing a menubar item labeled Test appears. + in a frame, and fits within the frame, press Pass, else press Fail. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MenuBarOffsetTest Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(FrameTest::new) + .build() + .awaitAndCheck(); + } + + private static class FrameTest extends Frame { + public FrameTest() { + super("MenuBarOffsetTest FrameTest"); + MenuBar m = new MenuBar(); + setMenuBar(m); + Menu test = m.add(new Menu("Test")); + test.add("1"); + test.add("2"); + setSize(100, 100); + } + + public void paint(Graphics g) { + setForeground(Color.red); + Insets i = getInsets(); + Dimension d = getSize(); + System.err.println(getBounds()); + System.err.println("" + i); + + g.drawRect(i.left, i.top, + d.width - i.left - i.right - 1, + d.height - i.top - i.bottom - 1); + } + } +} diff --git a/test/jdk/java/awt/Frame/MenuCrash.java b/test/jdk/java/awt/Frame/MenuCrash.java new file mode 100644 index 0000000000000..f68dd4ad00002 --- /dev/null +++ b/test/jdk/java/awt/Frame/MenuCrash.java @@ -0,0 +1,168 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.CheckboxMenuItem; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.List; + +/* + * @test + * @bug 4133279 + * @summary Clicking in menu in inactive frame crashes application + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuCrash + */ +public class MenuCrash { + + private static final String INSTRUCTIONS = """ + Two frames will appear, alternate between frames by clicking on the + menubar of the currently deactivated frame and verify no crash occurs. + + Try mousing around the menus and choosing various items to see the menu + item name reflected in the text field. Note that CheckBoxMenuItems do + not fire action events so the check menu item (Item 03) will not change + the field. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MenuCrash Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .testUI(MenuCrash::createAndShowUI) + .positionTestUIRightRow() + .build() + .awaitAndCheck(); + } + + + private static List createAndShowUI() { + Frame frame1 = new MenuFrame("Frame 1 MenuCrash"); + Frame frame2 = new MenuFrame("Frame 2 MenuCrash"); + + frame1.setSize(300, 200); + frame2.setSize(300, 200); + + frame1.validate(); + frame2.validate(); + + return List.of(frame1, frame2); + } + + static class MenuFrame extends Frame { + private final TextField field; + + MenuFrame(String name) { + super(name); + setLayout(new FlowLayout()); + + Button removeMenus = new Button("Remove Menus"); + removeMenus.addActionListener(ev -> remove(getMenuBar())); + + Button addMenus = new Button("Add Menus"); + addMenus.addActionListener(ev -> setupMenus()); + + add(removeMenus); + add(addMenus); + field = new TextField(20); + add(field); + + addWindowListener( + new WindowAdapter() { + public void windowActivated(WindowEvent e) { + setupMenus(); + } + } + ); + + addComponentListener( + new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + System.out.println(MenuFrame.this); + } + } + ); + + pack(); + } + + private void addMenuListeners() { + MenuBar menuBar = getMenuBar(); + + for (int nMenu = 0; nMenu < menuBar.getMenuCount(); nMenu++) { + Menu menu = menuBar.getMenu(nMenu); + for (int nMenuItem = 0; nMenuItem < menu.getItemCount(); nMenuItem++) { + MenuItem item = menu.getItem(nMenuItem); + item.addActionListener(ev -> field.setText(ev.getActionCommand())); + } + } + } + + private void setupMenus() { + MenuItem miSetLabel = new MenuItem("Item 01"); + MenuItem miSetEnabled = new MenuItem("Item 02"); + CheckboxMenuItem miSetState = new CheckboxMenuItem("Item 03"); + MenuItem miAdded = new MenuItem("Item 04 Added"); + + MenuBar menuBar = new MenuBar(); + Menu menu1 = new Menu("Menu 01"); + menu1.add(miSetLabel); + menu1.add(miSetEnabled); + menu1.add(miSetState); + menuBar.add(menu1); + setMenuBar(menuBar); + + // now that the peers are created, screw + // around with the menu items + miSetLabel.setLabel("Menu 01 - SetLabel"); + miSetEnabled.setEnabled(false); + miSetState.setState(true); + menu1.add(miAdded); + menu1.remove(miAdded); + menu1.addSeparator(); + menu1.add(miAdded); + + Menu menu2 = new Menu("Menu 02"); + menuBar.add(menu2); + menuBar.remove(menu2); + menuBar.add(menu2); + menu2.add(new MenuItem("Foo")); + menu1.setLabel("Menu Number 1"); + menu2.setLabel("Menu Number 2"); + + addMenuListeners(); + } + } +} diff --git a/test/jdk/java/awt/Frame/MinimumSizeTest.java b/test/jdk/java/awt/Frame/MinimumSizeTest.java new file mode 100644 index 0000000000000..cfff77be5f93c --- /dev/null +++ b/test/jdk/java/awt/Frame/MinimumSizeTest.java @@ -0,0 +1,114 @@ +/* + * 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 javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/* + * @test + * @key headful + * @bug 1256759 + * @summary Checks that Frames with a very small size don't cause Motif + * to generate VendorShells which consume the entire desktop. + */ + +public class MinimumSizeTest { + + private static final Color BG_COLOR = Color.RED; + private static Frame backgroundFrame; + private static Frame testedFrame; + + private static Robot robot; + private static final Point location = new Point(200, 200); + private static final Point[] testPointLocations = { + new Point(100, 200), + new Point(200, 100), + new Point(450, 210), + new Point(210, 350), + }; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + + try { + EventQueue.invokeAndWait(MinimumSizeTest::initAndShowGui); + robot.waitForIdle(); + robot.delay(500); + test(); + System.out.println("Test passed."); + } finally { + EventQueue.invokeAndWait(() -> { + backgroundFrame.dispose(); + testedFrame.dispose(); + }); + } + } + + private static void test() { + for (Point testLocation : testPointLocations) { + Color pixelColor = robot.getPixelColor(testLocation.x, testLocation.y); + + if (!pixelColor.equals(BG_COLOR)) { + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + BufferedImage screenCapture = robot.createScreenCapture(new Rectangle(screenSize)); + try { + ImageIO.write(screenCapture, "png", new File("failure.png")); + } catch (IOException ignored) {} + throw new RuntimeException("Pixel color does not match expected color %s at %s" + .formatted(pixelColor, testLocation)); + } + } + } + + private static void initAndShowGui() { + backgroundFrame = new Frame("MinimumSizeTest background"); + backgroundFrame.setUndecorated(true); + backgroundFrame.setBackground(BG_COLOR); + backgroundFrame.setBounds(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())); + backgroundFrame.setVisible(true); + + testedFrame = new MinimumSizeTestFrame(); + testedFrame.setVisible(true); + } + + private static class MinimumSizeTestFrame extends Frame { + public MinimumSizeTestFrame() { + super("MinimumSizeTest"); + setVisible(true); + setBackground(Color.BLUE); + setSize(0, 0); + setLocation(location); + } + } +} + diff --git a/test/jdk/java/awt/Frame/MultiScreenTest.java b/test/jdk/java/awt/Frame/MultiScreenTest.java new file mode 100644 index 0000000000000..845f601138b74 --- /dev/null +++ b/test/jdk/java/awt/Frame/MultiScreenTest.java @@ -0,0 +1,485 @@ +/* + * 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 + * 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.Canvas; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Label; +import java.awt.LayoutManager; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.TextField; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import java.awt.image.ColorModel; +import java.awt.image.MemoryImageSource; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JFrame; + +import jtreg.SkippedException; + +/* + * @test + * @bug 4312921 + * @key multimon + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @summary Tests that no garbage is painted on primary screen with DGA + * @run main/manual MultiScreenTest + */ + +public class MultiScreenTest { + static GraphicsEnvironment ge; + static GraphicsDevice[] gs; + + public static void main(String[] args) throws Exception { + ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + gs = ge.getScreenDevices(); + if (gs.length < 2) { + throw new SkippedException("You have only one monitor in your system - test passed"); + } + MultiScreenTest obj = new MultiScreenTest(); + String INSTRUCTIONS = + "This test is to be run only on multiscreen machine. " + + "You have " + gs.length + " monitors in your system.\n" + + "Actively drag the DitherTest frames on the secondary screen and " + + "if you see garbage appearing on your primary screen " + + "test failed otherwise it passed.";; + + PassFailJFrame.builder() + .title("MultiScreenTest Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(obj::init) + .build() + .awaitAndCheck(); + } + + public List init() { + List list = new ArrayList<>(); + for (int j = 0; j < gs.length; j++) { + GraphicsConfiguration[] gc = gs[j].getConfigurations(); + if (gc.length > 0) { + for (int i = 0; i < gc.length / 2; i++) { + JFrame f = new JFrame(gc[i]); //test JFrame( gc ) + GCCanvas c = new GCCanvas(gc[i]);//test canvas( gc ) + Rectangle gcBounds = gc[i].getBounds(); //test getBounds() + int xoffs = gcBounds.x; + int yoffs = gcBounds.y; + + f.getContentPane().add(c); + f.setTitle("Screen# " + Integer.toString(j) + ", GC#" + Integer.toString(i)); + f.setSize(300, 200); + f.setLocation(400 + xoffs, (i * 150) + yoffs);//test displaying in right location + list.add(f); + + Frame ditherfs = new Frame("DitherTest GC#" + Integer.toString(i), gc[i]); + ditherfs.setLayout(new BorderLayout()); //showDitherTest + DitherTest ditherTest = new DitherTest(gc[i]); + ditherfs.add("Center", ditherTest); + ditherfs.setBounds(300, 200, 300, 200); + ditherfs.setLocation(750 + xoffs, (i * 50) + yoffs); + ditherfs.pack(); + ditherfs.show(); + ditherTest.start(); + } + } + } + return list; + } +} + +class GCCanvas extends Canvas { + + GraphicsConfiguration gc; + Rectangle bounds; + Graphics g = this.getGraphics(); + Dimension size = getSize(); + + public GCCanvas(GraphicsConfiguration gc) { + super(gc); + this.gc = gc; + bounds = gc.getBounds(); + } + + public void paint( Graphics _g ) { + + Graphics2D g = (Graphics2D) _g; + + g.drawRect(0, 0, size.width-1, size.height-1); + g.setColor(Color.lightGray); + g.draw3DRect(1, 1, size.width-3, size.height-3, true); + + g.setColor(Color.red); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g.drawString("HELLO!", 110, 10); + + g.setColor(Color.blue); + g.drawString("ScreenSize="+Integer.toString(bounds.width)+"X"+ + Integer.toString(bounds.height), 10, 20); + g.setColor(Color.green); + g.drawString(gc.toString(), 10, 30); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + + g.setColor(Color.orange); + g.fillRect(40, 20, 50, 50); + + g.setColor(Color.red); + g.drawRect(100, 20, 30, 30); + + g.setColor(Color.gray); + g.drawLine(220, 20, 280, 40); + + g.setColor(Color.cyan); + g.fillArc(150, 30, 30, 30, 0, 200); + } + + public Dimension getPreferredSize(){ + return new Dimension(300, 200); + } +} + +class DitherCanvas extends Canvas { + Image img; + static String calcString = "Calculating..."; + + GraphicsConfiguration mGC; + + public DitherCanvas(GraphicsConfiguration gc) { + super(gc); + mGC = gc; + } + + public GraphicsConfiguration getGraphicsConfig() { + return mGC; + } + + public void paint(Graphics g) { + int w = getSize().width; + int h = getSize().height; + if (img == null) { + super.paint(g); + g.setColor(Color.black); + FontMetrics fm = g.getFontMetrics(); + int x = (w - fm.stringWidth(calcString)) / 2; + int y = h / 2; + g.drawString(calcString, x, y); + } else { + g.drawImage(img, 0, 0, w, h, this); + } + } + + public void update(Graphics g) { + paint(g); + } + + public Dimension getMinimumSize() { + return new Dimension(20, 20); + } + + public Dimension getPreferredSize() { + return new Dimension(200, 200); + } + + public Image getImage() { + return img; + } + + public void setImage(Image img) { + this.img = img; + paint(getGraphics()); + } +} + +class DitherTest extends Panel implements Runnable { + final static int NOOP = 0; + final static int RED = 1; + final static int GREEN = 2; + final static int BLUE = 3; + final static int ALPHA = 4; + final static int SATURATION = 5; + + Thread runner; + + DitherControls XControls; + DitherControls YControls; + DitherCanvas canvas; + + public DitherTest(GraphicsConfiguration gc) { + String xspec, yspec; + int xvals[] = new int[2]; + int yvals[] = new int[2]; + + xspec = "red"; + yspec = "blue"; + int xmethod = colormethod(xspec, xvals); + int ymethod = colormethod(yspec, yvals); + + setLayout(new BorderLayout()); + XControls = new DitherControls(this, xvals[0], xvals[1], + xmethod, false); + YControls = new DitherControls(this, yvals[0], yvals[1], + ymethod, true); + YControls.addRenderButton(); + add("North", XControls); + add("South", YControls); + add("Center", canvas = new DitherCanvas(gc)); + } + + public void start() { + runner = new Thread(this); + runner.start(); + } + + int colormethod(String s, int vals[]) { + int method = NOOP; + + if (s == null) { + s = ""; + } + + String lower = s.toLowerCase(); + int len = 0; + if (lower.startsWith("red")) { + method = RED; + lower = lower.substring(3); + } else if (lower.startsWith("green")) { + method = GREEN; + lower = lower.substring(5); + } else if (lower.startsWith("blue")) { + method = BLUE; + lower = lower.substring(4); + } else if (lower.startsWith("alpha")) { + method = ALPHA; + lower = lower.substring(4); + } else if (lower.startsWith("saturation")) { + method = SATURATION; + lower = lower.substring(10); + } + + if (method == NOOP) { + vals[0] = 0; + vals[1] = 0; + return method; + } + + int begval = 0; + int endval = 255; + + try { + int dash = lower.indexOf('-'); + if (dash < 0) { + begval = endval = Integer.parseInt(lower); + } else { + begval = Integer.parseInt(lower.substring(0, dash)); + endval = Integer.parseInt(lower.substring(dash + 1)); + } + } catch (Exception e) { + } + + if (begval < 0) { + begval = 0; + } + if (endval < 0) { + endval = 0; + } + if (begval > 255) { + begval = 255; + } + if (endval > 255) { + endval = 255; + } + + vals[0] = begval; + vals[1] = endval; + + return method; + } + + void applymethod(int c[], int method, int step, int total, int vals[]) { + if (method == NOOP) + return; + int val = ((total < 2) + ? vals[0] + : vals[0] + ((vals[1] - vals[0]) * step / (total - 1))); + switch (method) { + case RED: + c[0] = val; + break; + case GREEN: + c[1] = val; + break; + case BLUE: + c[2] = val; + break; + case ALPHA: + c[3] = val; + break; + case SATURATION: + int max = Math.max(Math.max(c[0], c[1]), c[2]); + int min = max * (255 - val) / 255; + if (c[0] == 0) { + c[0] = min; + } + if (c[1] == 0) { + c[1] = min; + } + if (c[2] == 0) { + c[2] = min; + } + break; + } + } + + public void run() { + canvas.setImage(null); // Wipe previous image + Image img = calculateImage(); + synchronized (this) { + if (img != null && runner == Thread.currentThread()) { + canvas.setImage(img); + } + } + } + + /** + * Calculates and returns the image. Halts the calculation and returns + * null if stopped during the calculation. + */ + Image calculateImage() { + Thread me = Thread.currentThread(); + + int width = canvas.getSize().width; + int height = canvas.getSize().height; + int xvals[] = new int[2]; + int yvals[] = new int[2]; + int xmethod = XControls.getParams(xvals); + int ymethod = YControls.getParams(yvals); + int pixels[] = new int[width * height]; + int c[] = new int[4]; + int index = 0; + + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + c[0] = c[1] = c[2] = 0; + c[3] = 255; + if (xmethod < ymethod) { + applymethod(c, xmethod, i, width, xvals); + applymethod(c, ymethod, j, height, yvals); + } else { + applymethod(c, ymethod, j, height, yvals); + applymethod(c, xmethod, i, width, xvals); + } + pixels[index++] = ((c[3] << 24) | + (c[0] << 16) | + (c[1] << 8) | + (c[2] << 0)); + + } + // Poll once per row to see if we've been told to stop. + if (runner != me) { + return null; + } + } + + return createImage(new MemoryImageSource(width, height, + ColorModel.getRGBdefault(), pixels, 0, width)); + } + + public String getInfo() { + return "An interactive demonstration of dithering."; + } + + public String[][] getParameterInfo() { + String[][] info = { + {"xaxis", "{RED, GREEN, BLUE, PINK, ORANGE, MAGENTA, CYAN, WHITE, YELLOW, GRAY, DARKGRAY}", + "The color of the Y axis. Default is RED."}, + {"yaxis", "{RED, GREEN, BLUE, PINK, ORANGE, MAGENTA, CYAN, WHITE, YELLOW, GRAY, DARKGRAY}", + "The color of the X axis. Default is BLUE."} + }; + return info; + } +} + +class DitherControls extends Panel implements ActionListener { + TextField start; + TextField end; + Button button; + Choice choice; + DitherTest dt; + + static LayoutManager dcLayout = new FlowLayout(FlowLayout.CENTER, 10, 5); + + public DitherControls(DitherTest app, int s, int e, int type, + boolean vertical) { + dt = app; + setLayout(dcLayout); + add(new Label(vertical ? "Vertical" : "Horizontal")); + add(choice = new Choice()); + choice.addItem("Noop"); + choice.addItem("Red"); + choice.addItem("Green"); + choice.addItem("Blue"); + choice.addItem("Alpha"); + choice.addItem("Saturation"); + choice.select(type); + add(start = new TextField(Integer.toString(s), 4)); + add(end = new TextField(Integer.toString(e), 4)); + } + + public void addRenderButton() { + add(button = new Button("New Image")); + button.addActionListener(this); + } + + public int getParams(int vals[]) { + vals[0] = Integer.parseInt(start.getText()); + vals[1] = Integer.parseInt(end.getText()); + return choice.getSelectedIndex(); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == button) { + dt.start(); + } + } +} diff --git a/test/jdk/java/awt/Frame/PackTwiceTest.java b/test/jdk/java/awt/Frame/PackTwiceTest.java new file mode 100644 index 0000000000000..63cd20612f0d3 --- /dev/null +++ b/test/jdk/java/awt/Frame/PackTwiceTest.java @@ -0,0 +1,67 @@ +/* + * 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.Frame; +import java.awt.TextField; + +/* + * @test + * @bug 4097744 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary packing a frame twice stops it resizing + * @run main/manual PackTwiceTest + */ + +public class PackTwiceTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. You would see a Frame titled 'TestFrame' + 2. The Frame displays a text as below: + 'I am a lengthy sentence...can you see me?' + 3. If you can see the full text without resizing the frame + using mouse, press 'Pass' else press 'Fail'."""; + + PassFailJFrame.builder() + .title("PackTwiceTest Instruction") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(PackTwiceTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("PackTwiceTest TestFrame"); + TextField tf = new TextField(); + f.add(tf, "Center"); + tf.setText("I am a short sentence"); + f.pack(); + f.pack(); + tf.setText("I am a lengthy sentence...can you see me?"); + f.pack(); + f.requestFocus(); + return f; + } +} diff --git a/test/jdk/java/awt/GradientPaint/JerkyGradient.java b/test/jdk/java/awt/GradientPaint/JerkyGradient.java new file mode 100644 index 0000000000000..dc33b9c185af8 --- /dev/null +++ b/test/jdk/java/awt/GradientPaint/JerkyGradient.java @@ -0,0 +1,125 @@ +/* + * 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 4221201 + * @summary Test where the gradient drawn should remain in sync with the + * rotating rectangle. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual JerkyGradient + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +public class JerkyGradient extends Panel implements Runnable { + protected static Shape mShape; + protected static Paint mPaint; + protected static double mTheta; + static Thread animatorThread; + static BufferedImage mImg; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Watch at least one full rotation of the rectangle. Check that + the gradient drawn remains in sync with the rotating + rectangle. If so, pass this test. Otherwise, fail this test. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(JerkyGradient::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Rotating Gradient Test"); + JerkyGradient jg = new JerkyGradient(); + f.add(jg); + f.setSize(200, 200); + return f; + } + + public JerkyGradient() { + mShape = new Rectangle2D.Double(60, 50, 80, 100); + mPaint = new GradientPaint(0, 0, Color.red, + 25, 25, Color.yellow, + true); + mImg = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); + + animatorThread = new Thread(this); + animatorThread.setPriority(Thread.MIN_PRIORITY); + animatorThread.start(); + } + + public synchronized void run() { + Thread me = Thread.currentThread(); + double increment = Math.PI / 36; + double twoPI = Math.PI * 2; + + while (animatorThread == me) { + mTheta = (mTheta + increment) % twoPI; + repaint(); + try { + wait(50); + } catch (InterruptedException ie) { + break; + } + } + } + + public void update(Graphics g) { + Graphics2D g2 = mImg.createGraphics(); + g2.setColor(getBackground()); + g2.fillRect(0, 0, 200, 200); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2.rotate(mTheta, 100, 100); + g2.setPaint(Color.black); + g2.drawLine(100, 30, 100, 55); + g2.setPaint(mPaint); + g2.fill(mShape); + g2.setPaint(Color.black); + g2.draw(mShape); + paint(g); + g2.dispose(); + } + + public void paint(Graphics g) { + g.drawImage(mImg, 0, 0, null); + } +} diff --git a/test/jdk/java/awt/GradientPaint/ShearTest.java b/test/jdk/java/awt/GradientPaint/ShearTest.java new file mode 100644 index 0000000000000..95a4e4a6dcd3e --- /dev/null +++ b/test/jdk/java/awt/GradientPaint/ShearTest.java @@ -0,0 +1,137 @@ +/* + * 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 4171820 + * @summary Checks that GradientPaint responds to shearing transforms correctly + * The gradients drawn should be parallel to the sides of the + * indicated anchor rectangle. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ShearTest + */ + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; + +public class ShearTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays 2 rows each containing 4 gradient fills. Each + gradient fill is labeled depending on whether the line or lines + of the gradient should be truly vertical, truly horizontal, or + some slanted diagonal direction. The test passes if the direction + of each gradient matches its label. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(ShearTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Shear Gradient Test"); + f.setLayout(new GridLayout(0, 1)); + f.add(getPanelSet(false), "North"); + f.add(getPanelSet(true), "Center"); + f.setSize(500, 300); + return f; + } + + public static Panel getPanelSet(boolean horizontal) { + String direven = horizontal ? "Slanted" : "Vertical"; + String dirodd = horizontal ? "Horizontal" : "Slanted"; + + Panel p = new Panel(); + p.setLayout(new GridLayout(0, 4)); + p.add(new ShearCanvas(direven, false, horizontal, false, true)); + p.add(new ShearCanvas(dirodd, false, horizontal, true, false)); + p.add(new ShearCanvas(direven, true, horizontal, false, true)); + p.add(new ShearCanvas(dirodd, true, horizontal, true, false)); + + return p; + } + + public static class ShearCanvas extends Canvas { + public static final int GRADW = 30; + + public static final Rectangle anchor = + new Rectangle(-GRADW / 2, -GRADW / 2, GRADW, GRADW); + + public static final Color faintblue = new Color(0f, 0f, 1.0f, 0.35f); + + private AffineTransform txform; + private GradientPaint grad; + private String label; + + public ShearCanvas(String label, + boolean cyclic, boolean horizontal, + boolean shearx, boolean sheary) { + txform = new AffineTransform(); + if (shearx) { + txform.shear(-.5, 0); + } + if (sheary) { + txform.shear(0, -.5); + } + int relx = horizontal ? 0 : GRADW / 2; + int rely = horizontal ? GRADW / 2 : 0; + grad = new GradientPaint(-relx, -rely, Color.green, + relx, rely, Color.yellow, cyclic); + this.label = label; + } + + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + + AffineTransform at = g2d.getTransform(); + g2d.translate(75, 75); + g2d.transform(txform); + g2d.setPaint(grad); + g2d.fill(g.getClip()); + g2d.setColor(faintblue); + g2d.fill(anchor); + g2d.setTransform(at); + + Dimension d = getSize(); + g2d.setColor(Color.black); + g2d.drawRect(0, 0, d.width - 1, d.height - 1); + g2d.drawString(label, 5, d.height - 5); + g2d.dispose(); + } + } +} diff --git a/test/jdk/java/awt/Graphics2D/BasicStrokeValidate.java b/test/jdk/java/awt/Graphics2D/BasicStrokeValidate.java new file mode 100644 index 0000000000000..251f14e5081bd --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/BasicStrokeValidate.java @@ -0,0 +1,87 @@ +/* + * 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 + * 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 4363534 + * @summary This test verifies that setting a non-thin-line BasicStroke + * on a Graphics2D obtained from a BufferedImage will correctly validate + * the pipelines for the line-widening pipeline even if that is the only + * non-default attribute on the graphics. + * + */ + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +public class BasicStrokeValidate { + + public static final int TESTW = 100; + public static final int TESTH = 100; + + public static void main(String[] args) { + BufferedImage bi1 = createImage(false); + BufferedImage bi2 = createImage(true); + compare(bi1, bi2); // images should differ + } + + static BufferedImage createImage(boolean dashed) { + BufferedImage bi = new BufferedImage(TESTW, TESTH, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bi.createGraphics(); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, TESTW, TESTH); + g2d.setColor(Color.black); + if (dashed) { + g2d.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, + BasicStroke.JOIN_MITER, 10.0f, + new float[] {2.5f, 3.5f}, + 0.0f)); + } + g2d.drawRect(10, 10, TESTW-20, TESTH-20); + g2d.setStroke(new BasicStroke(10f)); + g2d.drawRect(20, 20, TESTW-40, TESTH-40); + return bi; + } + + static void compare(BufferedImage i1, BufferedImage i2) { + boolean same = true; + int w = i1.getWidth(), h = i1.getHeight(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int p1 = i1.getRGB(x, y); + int p2 = i2.getRGB(x, y); + if (p1 != p2) { + same = false; + } + } + if (!same) { + break; + } + } + if (same) { + throw new RuntimeException("No difference"); + } + } +} diff --git a/test/jdk/java/awt/Graphics2D/DrawImageIAETest/DrawImageIAETest.java b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/DrawImageIAETest.java new file mode 100644 index 0000000000000..af7ebae72a6c8 --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/DrawImageIAETest.java @@ -0,0 +1,169 @@ +/* + * 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 4191004 + * @summary Tests that no IllegalArgumentException is thrown when calling + * drawImage with certain conditions + * @key headful + */ + +import java.awt.AlphaComposite; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.Toolkit; +import java.awt.geom.GeneralPath; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javax.swing.JFrame; +import javax.swing.JPanel; + +public class DrawImageIAETest extends Frame { + + static String filename = "/duke.gif"; + private volatile Image dimg; + private volatile BufferedImage bimg; + static volatile DrawImageIAETest app; + static volatile JFrame jframe; + static volatile boolean passed = true; + static volatile Exception exception = null; + static volatile CountDownLatch imageLatch = new CountDownLatch(1); + + DrawImageIAETest(String title) { + super(title); + } + + public static void main(final String[] args) throws Exception { + EventQueue.invokeAndWait(DrawImageIAETest:: createUI); + imageLatch.await(3, TimeUnit.MILLISECONDS); + try { + if (!passed) { + throw new RuntimeException("Test FAILED: exception caught:" + exception); + } + } finally { + if (jframe != null) { + EventQueue.invokeAndWait(jframe::dispose); + } + if (app != null) { + EventQueue.invokeAndWait(app::dispose); + } + } + } + + static void createUI() { + app = new DrawImageIAETest("DrawImageIAETest"); + app.setLayout (new BorderLayout()); + app.setSize(200,200); + app.setLocationRelativeTo(null); + app.setVisible(true); + + String file; + try { + String dir = System.getProperty("test.src", + System.getProperty("user.dir")); + file = dir + filename; + } catch (Exception e) { + file = "." + filename; + } + + Image textureAlphaSource = null; + MediaTracker tracker = new MediaTracker(app); + app.dimg = Toolkit.getDefaultToolkit().getImage(file); + tracker.addImage(app.dimg, 1); + try { + tracker.waitForAll(); + imageLatch.countDown(); + } catch (Exception e) { + System.err.println("Can't load images"); + } + + if (app.dimg == null) { + passed = false; + return; + } + + jframe = new JFrame("Test DrawImageIAETest"); + jframe.setSize(300, 300); + JPanel jpanel; + jframe.getContentPane().add("Center", jpanel = new JPanel() { + public void paint(Graphics _g) { + Graphics2D g = (Graphics2D)_g; + Dimension d = getSize(); + Graphics2D g2 = app.createGraphics2D(d.width, d.height); + app.drawDemo(d.width, d.height, g2); + g2.dispose(); + g.drawImage(app.bimg, 0, 0, app); + } + }); + jpanel.setSize(140, 140); + jframe.setVisible(true); + } + + public void drawDemo(int w, int h, Graphics2D g2) { + GeneralPath p1 = new GeneralPath(); + GeneralPath p2 = new GeneralPath(); + + int dukeX = 73; + int dukeY = 26; + + double x = 118; + double y = 17; + double ew = 50; + double eh = 48; + + p1.append(new Ellipse2D.Double(x, y, ew, eh), false); + p2.append(new Rectangle2D.Double(x+5, y+5, ew-10, eh-10),false); + + g2.setClip(p1); + g2.clip(p2); + try { + g2.drawImage(dimg, dukeX, dukeY, null); + } catch (IllegalArgumentException e) { + passed = false; + exception = e; + } + } + + public Graphics2D createGraphics2D(int w, int h) { + Graphics2D g2 = null; + if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) { + bimg = (BufferedImage) createImage(w, h); + } + g2 = bimg.createGraphics(); + g2.setBackground(getBackground()); + g2.clearRect(0, 0, w, h); + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); + return g2; + } +} diff --git a/test/jdk/java/awt/Graphics2D/DrawImageIAETest/duke.gif b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/duke.gif new file mode 100644 index 0000000000000..ed32e0ff79b05 Binary files /dev/null and b/test/jdk/java/awt/Graphics2D/DrawImageIAETest/duke.gif differ diff --git a/test/jdk/java/awt/Graphics2D/ImageRendering/ImageRendering.java b/test/jdk/java/awt/Graphics2D/ImageRendering/ImageRendering.java new file mode 100644 index 0000000000000..209a93e92d50f --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/ImageRendering/ImageRendering.java @@ -0,0 +1,66 @@ +/* + * 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 4203598 + * @summary This test verifies that an image with transparent background can be displayed + * correctly with the red background color given. + * The correct display should be the sleeping Duke on a red background. + * + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; + +public class ImageRendering { + + public static void main(String[] args) throws Exception { + + String imgName = "snooze.gif"; + File file = new File(System.getProperty("test.src", "."), imgName); + BufferedImage image = ImageIO.read(file); + int w = image.getWidth(); + int h = image.getHeight(); + BufferedImage dest = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = dest.createGraphics(); + g2d.drawImage(image, 0, 0, Color.red, null); + int redPixel = Color.red.getRGB(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int srcPixel = image.getRGB(x, y); + if ((srcPixel & 0x0ff000000) == 0) { + int destPix = dest.getRGB(x, y); + if (destPix != redPixel) { + throw new RuntimeException("Not red at x=" + x + + " y=" + y + + "pix = " + Integer.toHexString(destPix)); + } + } + } + } + } +} diff --git a/test/jdk/java/awt/Graphics2D/ImageRendering/snooze.gif b/test/jdk/java/awt/Graphics2D/ImageRendering/snooze.gif new file mode 100644 index 0000000000000..e357e316cdbef Binary files /dev/null and b/test/jdk/java/awt/Graphics2D/ImageRendering/snooze.gif differ diff --git a/test/jdk/java/awt/Graphics2D/ScaledThinLineTest.java b/test/jdk/java/awt/Graphics2D/ScaledThinLineTest.java new file mode 100644 index 0000000000000..fd4a5dd5e7ca9 --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/ScaledThinLineTest.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 4210466 4417756 + * @summary thin lines are not draw correctly under large scales + */ +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.image.BufferedImage; +import java.awt.geom.Ellipse2D; + +public class ScaledThinLineTest { + + public static void main(String[] args) { + ScaledThinLineTest c1 = new ScaledThinLineTest(200, 200); + ScaledThinLineTest c2 = new ScaledThinLineTest(1, 10000); + ScaledThinLineTest c3 = new ScaledThinLineTest(10000, 1); + ScaledThinLineTest c4 = new ScaledThinLineTest(0.01, 10000); + ScaledThinLineTest c5 = new ScaledThinLineTest(10000, 0.01); + compare(c1.bi, c2.bi); + compare(c2.bi, c3.bi); + compare(c3.bi, c4.bi); + compare(c4.bi, c5.bi); + } + + private final Shape shape; + private final double scaleX,scaleY; + private BufferedImage bi = null; + + public ScaledThinLineTest(double width, double height) { + shape = new Ellipse2D.Double(0.25*width, 0.25*height, width, height); + scaleX = 200/width; + scaleY = 200/height; + int iw = 300, ih = 300; + bi = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = bi.createGraphics(); + g2.setColor(Color.white); + g2.fillRect(0, 0, iw, ih); + g2.setColor(Color.black); + g2.scale(scaleX,scaleY); + g2.setStroke(new BasicStroke(0)); + g2.draw(shape); + } + + + static void compare(BufferedImage i1, BufferedImage i2) { + int w = i1.getWidth(), h = i1.getHeight(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int p1 = i1.getRGB(x, y); + int p2 = i2.getRGB(x, y); + if (p1 != p2) { + System.out.println("images differ at " + x + " " + y); + } + } + } + } +} diff --git a/test/jdk/java/awt/Graphics2D/TextPerf.java b/test/jdk/java/awt/Graphics2D/TextPerf.java new file mode 100644 index 0000000000000..d3e5cc2d4d0dd --- /dev/null +++ b/test/jdk/java/awt/Graphics2D/TextPerf.java @@ -0,0 +1,159 @@ +/* + * 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 4190429 + * @key headful + * @summary In this bug, text drawing performance should be reasonable. + * And should (per string) be consistent with the size of the + * rectangle in which the string is drawn, not the rectangle + * bounding the whole window. + */ + +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class TextPerf extends Canvas { + + static volatile CountDownLatch paintLatch = new CountDownLatch(1); + static volatile long paintTime = 5000; // causes test fail if it is not updated. + static volatile Frame frame; + + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(TextPerf::createUI); + paintLatch.await(5, TimeUnit.SECONDS); + if (paintTime > 2000) { + throw new RuntimeException("Paint time is " + paintTime + "ms"); + } + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + + static void createUI() { + frame = new Frame("TextPerf"); + frame.setLayout(new BorderLayout()); + TextPerf tp = new TextPerf(); + frame.add(tp, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } + + public Dimension getPreferredSize() { + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + return new Dimension(d.width - 50, d.height - 50); + } + + static Font[] fonts = { + new Font(Font.SERIF, Font.PLAIN, 10), + new Font(Font.SANS_SERIF, Font.PLAIN, 10), + new Font(Font.MONOSPACED, Font.ITALIC, 10), + new Font(Font.SERIF, Font.PLAIN, 14), + new Font(Font.SERIF, Font.BOLD, 12), + }; + + public void paint(Graphics g1) { + + Graphics2D g = (Graphics2D)g1; + String text = "Hello,_Wgjpqy!"; + Toolkit.getDefaultToolkit().sync(); + long startTime = System.currentTimeMillis(); + FontMetrics[] cachedMetrics = new FontMetrics[fonts.length]; + Dimension size = getSize(); + int prim = 0; + int spaceWidth = 5; + Color cols[] = { Color.red, Color.blue, Color.yellow, + Color.green, Color.pink, Color.orange} ; + + for (int y = 20; y < size.height; y += 20) { + int i = 0; + for (int x = 0; x < size.width; i++) { + Font font = fonts[i % fonts.length]; + FontMetrics metrics = cachedMetrics[i % fonts.length]; + if (metrics == null) { + metrics = g.getFontMetrics(font); + cachedMetrics[i % fonts.length] = metrics; + } + + g.setFont(font); + g.setColor(cols[i % cols.length]); + switch (prim++) { + case 0: g.drawString(text, x, y); + break; + case 1: g.drawBytes(text.getBytes(), 0, text.length(), x, y); + break; + case 2: char[] chs= new char[text.length()]; + text.getChars(0,text.length(), chs, 0); + g.drawChars(chs, 0, text.length(), x, y); + break; + case 3: GlyphVector gv = font.createGlyphVector( + g.getFontRenderContext(), text); + g.drawGlyphVector(gv, (float)x, (float)y); + default: prim = 0; + } + + x += metrics.stringWidth(text) + spaceWidth; + } + } + + // Draw some transformed text to verify correct bounds calculated + AffineTransform at = AffineTransform.getTranslateInstance(50, 50); + at.scale(7.0,7.0); + at.rotate(1.0); + g.transform(at); + g.setColor(Color.black); + Font font = new Font(Font.SERIF, Font.PLAIN, 20); + RenderingHints hints = new RenderingHints(null); + hints.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHints(hints); + g.setFont(font); + FontMetrics metrics = g.getFontMetrics(font); + g.drawString("Java", 5,5); + + Toolkit.getDefaultToolkit().sync(); + long endTime = System.currentTimeMillis(); + paintTime = endTime - startTime; + String msg = "repainted in " + paintTime + " milliseconds"; + System.out.println(msg); + System.out.flush(); + + paintLatch.countDown(); + } +} diff --git a/test/jdk/java/awt/GraphicsEnvironment/DefaultScreenDeviceTest.java b/test/jdk/java/awt/GraphicsEnvironment/DefaultScreenDeviceTest.java new file mode 100644 index 0000000000000..b59460322daf6 --- /dev/null +++ b/test/jdk/java/awt/GraphicsEnvironment/DefaultScreenDeviceTest.java @@ -0,0 +1,116 @@ +/* + * 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.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Label; +import java.awt.Rectangle; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.List; + +/* + * @test + * @bug 4473671 + * @summary Test to verify GraphicsEnvironment.getDefaultScreenDevice always + * returning first screen + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultScreenDeviceTest + */ + +public class DefaultScreenDeviceTest { + private static Frame testFrame; + + public static void main(String[] args) throws Exception { + GraphicsEnvironment ge = GraphicsEnvironment. + getLocalGraphicsEnvironment(); + GraphicsDevice[] gds = ge.getScreenDevices(); + if (gds.length < 2) { + System.out.println("Test requires at least 2 displays"); + return; + } + + String INSTRUCTIONS = """ + 1. The test is for systems which allows primary display + selection in multiscreen systems. + Set the system primary screen to be the rightmost + (i.e. the right screen in two screen configuration) + This can be done by going to OS Display Settings + selecting the screen and checking the 'Use this device + as primary monitor' checkbox. + 2. When done, click on 'Frame on Primary Screen' button and + see where the frame will pop up + 3. If Primary Frame pops up on the primary display, + the test passed, otherwise it failed + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static List initialize() { + Frame frame = new Frame("Default screen device test"); + GraphicsConfiguration gc = + GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); + + testFrame = new Frame("Primary screen frame", gc); + frame.setLayout(new BorderLayout()); + frame.setSize(200, 200); + + Button b = new Button("Frame on Primary Screen"); + b.addActionListener(e -> { + if (testFrame != null) { + testFrame.setVisible(false); + testFrame.dispose(); + } + + testFrame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e1) { + testFrame.setVisible(false); + testFrame.dispose(); + } + }); + testFrame.add(new Label("This frame should be on the primary screen")); + testFrame.setBackground(Color.red); + testFrame.pack(); + Rectangle rect = gc.getBounds(); + testFrame.setLocation(rect.x + 100, rect.y + 100); + testFrame.setVisible(true); + }); + frame.add(b); + return List.of(testFrame, frame); + } +} diff --git a/test/jdk/java/awt/Label/ContainerValidateTest.java b/test/jdk/java/awt/Label/ContainerValidateTest.java new file mode 100644 index 0000000000000..e379ebb606b8c --- /dev/null +++ b/test/jdk/java/awt/Label/ContainerValidateTest.java @@ -0,0 +1,169 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/* + * @test + * @key headful + * @bug 4247913 + * @summary Tests that Label repaints after call Container.validate() + * @run main ContainerValidateTest + */ + +public class ContainerValidateTest extends Frame implements MouseListener { + private static Robot robot; + private static Panel currentPanel; + private static Button currentBtn; + private static Panel updatedPanel; + private static Label updatedLabel; + private static TextField updatedTxtField; + private static Button updatedBtn; + + private static volatile Rectangle btnBounds; + + Panel pnl1 = new Panel(); + Panel pnl2 = new Panel(); + Label lbl1 = new Label("Label 1"); + Label lbl2 = new Label("Label 2"); + TextField txt1 = new TextField("field1", 20); + TextField txt2 = new TextField("field2", 20); + Button btn1 = new Button("Swap 1"); + Button btn2 = new Button("Swap 2"); + + public static void main(String[] args) throws Exception { + robot = new Robot(); + + ContainerValidateTest containerValidate = new ContainerValidateTest(); + EventQueue.invokeAndWait(containerValidate::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + containerValidate.testUI(); + } + + private void createAndShowUI() { + this.setTitle("ContainerValidateTest Test"); + pnl1.add(lbl1); + pnl1.add(txt1); + pnl1.add(btn1); + + pnl2.add(lbl2); + pnl2.add(txt2); + pnl2.add(btn2); + + btn1.addMouseListener(this); + btn2.addMouseListener(this); + + this.add(pnl1, BorderLayout.CENTER); + pack(); + setLocationRelativeTo(null); + setVisible(true); + } + + private void testUI() throws Exception { + EventQueue.invokeAndWait(() -> btnBounds + = new Rectangle(btn1.getLocationOnScreen().x, + btn1.getLocationOnScreen().y, + btn1.getWidth(), + btn1.getHeight())); + for (int i= 1; i < 4 ; i++) { + EventQueue.invokeAndWait(() -> { + currentPanel = (Panel) this.getComponent(0); + currentBtn = (Button) currentPanel.getComponent(2); + }); + + robot.mouseMove(btnBounds.x + (int) btnBounds.getWidth() / 2, + btnBounds.y + (int) btnBounds.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + //large delay set for completion of UI validate() + robot.delay(500); + + EventQueue.invokeAndWait(() -> { + updatedPanel = (Panel) this.getComponent(0); + updatedLabel = (Label) updatedPanel.getComponent(0); + updatedTxtField = (TextField) updatedPanel.getComponent(1); + updatedBtn = (Button) updatedPanel.getComponent(2); + }); + testPanelComponents(currentBtn.getLabel()); + } + } + + private void testPanelComponents(String btnLabel) { + if (btnLabel.equals("Swap 1")) { + if (!(updatedLabel.getText().equals(lbl2.getText()) + && updatedTxtField.getText().equals(txt2.getText()) + && updatedBtn.getLabel().equals(btn2.getLabel()))) { + throw new RuntimeException("Test Failed!! Labels not repainted" + + " after Container.validate()"); + } + } else { + if (!(updatedLabel.getText().equals(lbl1.getText()) + && updatedTxtField.getText().equals(txt1.getText()) + && updatedBtn.getLabel().equals(btn1.getLabel()))) { + throw new RuntimeException("Test Failed!! Labels not repainted" + + " after Container.validate()"); + } + } + } + + @Override + public void mousePressed(MouseEvent evt) { + if (evt.getComponent() instanceof Button btn) { + if (btn.equals(btn1)) { + remove(pnl1); + add(pnl2, BorderLayout.CENTER); + } else { + remove(pnl2); + add(pnl1, BorderLayout.CENTER); + } + invalidate(); + validate(); + } + } + + @Override + public void mouseReleased(MouseEvent e) {} + + @Override + public void mouseEntered(MouseEvent e) {} + + @Override + public void mouseExited(MouseEvent e) {} + + @Override + public void mouseClicked(MouseEvent e) {} +} diff --git a/test/jdk/java/awt/LightweightComponent/LWParentMovedTest/LWParentMovedTest.java b/test/jdk/java/awt/LightweightComponent/LWParentMovedTest/LWParentMovedTest.java new file mode 100644 index 0000000000000..d46af3a0d5e51 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/LWParentMovedTest/LWParentMovedTest.java @@ -0,0 +1,99 @@ +/* + * 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 4147246 + * @summary Simple check for peer != null in Component.componentMoved + * @key headful + * @run main LWParentMovedTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; + +public class LWParentMovedTest { + static CMTFrame f; + + // test will throw an exception and fail if lwc is null + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> f = new CMTFrame()); + } finally { + if (f != null) { + EventQueue.invokeAndWait(() -> f.dispose()); + } + } + } +} + +class CMTFrame extends Frame { + Container lwc; + Button button; + + public CMTFrame() { + super("Moving LWC Test"); + setLayout(new FlowLayout()); + lwc = new LWSquare(Color.blue, 100, 100); + button = new Button(); + lwc.add(button); + add(lwc); + + setSize(400, 300); + setVisible(true); + + // queue up a bunch of COMPONENT_MOVED events + for (int i = 0; i < 1000; i++) { + lwc.setLocation(i, i); + } + + // remove heavyweight from lightweight container + lwc.remove(button); + } +} + +// +// Lightweight container +// +class LWSquare extends Container { + int width; + int height; + + public LWSquare(Color color, int w, int h) { + setBackground(color); + setLayout(new FlowLayout()); + width = w; + height = h; + setName("LWSquare-" + color.toString()); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, 1000, 1000); + } +} diff --git a/test/jdk/java/awt/LightweightComponent/LightWeightTabFocus/LightWeightTabFocus.java b/test/jdk/java/awt/LightweightComponent/LightWeightTabFocus/LightWeightTabFocus.java new file mode 100644 index 0000000000000..05889580fd423 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/LightWeightTabFocus/LightWeightTabFocus.java @@ -0,0 +1,154 @@ +/* + * 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 4095214 + * @summary Test change of focus on lightweights using the tab key + * @key headful + * @run main LightWeightTabFocus + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +public class LightWeightTabFocus { + private static Frame f; + private static LightweightButton btn1; + private static Button btn2; + private static Robot robot; + private static volatile Point point; + private static Point loc; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> createUI()); + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + loc = f.getLocation(); + point = btn2.getLocation(); + }); + robot.mouseMove(loc.x + point.x, loc.y + point.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + // First TAB + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + if (!btn1.hasFocus()) { + new RuntimeException("First tab failed"); + } + // Second TAB + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + if (!btn2.hasFocus()) { + new RuntimeException("Second tab failed"); + } + // First SHIFT+TAB + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_TAB); + robot.delay(100); + robot.keyRelease(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_SHIFT); + if (!btn1.hasFocus()) { + new RuntimeException("First shift+tab failed"); + } + // Second SHIFT+TAB + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_TAB); + robot.delay(100); + robot.keyRelease(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_SHIFT); + if (!btn2.hasFocus()) { + new RuntimeException("Second shift+tab failed"); + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static Frame createUI() { + f = new Frame("TAB Focus Change on LW Test"); + f.setLayout(new FlowLayout()); + btn1 = new LightweightButton(); + f.add(btn1); + btn2 = new Button("Click Me To start"); + f.add(btn2); + f.pack(); + f.setVisible(true); + return f; + } +} + +class LightweightButton extends Component implements FocusListener { + boolean focus; + LightweightButton() { + focus = false; + addFocusListener(this); + } + + public Dimension getPreferredSize() + { + return new Dimension(100, 100); + } + + public void focusGained(FocusEvent e) { + focus = true; + repaint(); + } + + public void focusLost(FocusEvent e) { + focus = false; + repaint(); + } + + public void paint(Graphics g) { + if (focus) { + g.drawString("Has Focus", 10, 20); + } else { + g.drawString("Not Focused", 10, 20); + } + } + + public boolean isFocusable() { + return true; + } +} diff --git a/test/jdk/java/awt/LightweightComponent/LightweightFontTest/LightweightFontTest.java b/test/jdk/java/awt/LightweightComponent/LightweightFontTest/LightweightFontTest.java new file mode 100644 index 0000000000000..4fd90656d6124 --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/LightweightFontTest/LightweightFontTest.java @@ -0,0 +1,182 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4077709 4153989 + * @summary Lightweight component font settable test + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LightweightFontTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.Graphics; + + +public class LightweightFontTest { + static Font desiredFont = null; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + [ There are 7 steps to this test ] + 1. The 2 bordered labels (Emacs vs. vi) should be in a LARGE font + (approximately 1/2 inch tall) + 2. The labels should not overlap. + 3. Each button should be large enough to contain the entire label. + 4. The labels should have red backgrounds + 5. The text in the left label should be blue and the right yellow + 6. Resize the window to make it much smaller and larger + 7. The buttons should never overlap, and they should be large + enough to contain the entire label. + (although the button may disappear if there is not enough + room in the window)" + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(LightweightFontTest::createUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Lightweight Font Test"); + f.setLayout(new FlowLayout()); + + desiredFont = new Font(Font.DIALOG, Font.PLAIN, 36); + Component component; + component = new BorderedLabel("Emacs or vi?"); + component.setFont(desiredFont); + component.setBackground(Color.red); + component.setForeground(Color.blue); + f.add(component); + component = new BorderedLabel("Vi or Emacs???"); + component.setFont(desiredFont); + component.setBackground(Color.red); + component.setForeground(Color.yellow); + f.add(component); + f.pack(); + return f; + } +} + +/** + * Lightweight component + */ +class BorderedLabel extends Component { + boolean superIsButton; + String labelString; + + BorderedLabel(String labelString) { + this.labelString = labelString; + + Component thisComponent = this; + superIsButton = (thisComponent instanceof Button); + if(superIsButton) { + ((Button)thisComponent).setLabel(labelString); + } + } + + public Dimension getMinimumSize() { + Dimension minSize = new Dimension(); + + if (superIsButton) { + minSize = super.getMinimumSize(); + } else { + + Graphics g = getGraphics(); + verifyFont(g); + FontMetrics metrics = g.getFontMetrics(); + + minSize.width = metrics.stringWidth(labelString) + 14; + minSize.height = metrics.getMaxAscent() + metrics.getMaxDescent() + 9; + + g.dispose(); + } + return minSize; + } + + public Dimension getPreferredSize() { + Dimension prefSize = new Dimension(); + if (superIsButton) { + prefSize = super.getPreferredSize(); + } else { + prefSize = getMinimumSize(); + } + return prefSize; + } + + public void paint(Graphics g) { + verifyFont(g); + super.paint(g); + if (superIsButton) { + return; + } + Dimension size = getSize(); + Color oldColor = g.getColor(); + + // draw border + g.setColor(getBackground()); + g.fill3DRect(0, 0, size.width, size.height, false); + g.fill3DRect(3, 3, size.width - 6, size.height - 6, true); + + // draw text + FontMetrics metrics = g.getFontMetrics(); + int centerX = size.width / 2; + int centerY = size.height / 2; + int textX = centerX - (metrics.stringWidth(labelString) / 2); + int textY = centerY + ((metrics.getMaxAscent() + + metrics.getMaxDescent()) / 2); + g.setColor(getForeground()); + g.drawString(labelString, textX, textY); + + g.setColor(oldColor); + } + + /** + * Verifies that the font is correct and prints a warning + * message and/or throws a RuntimeException if it is not. + */ + private void verifyFont(Graphics g) { + Font desiredFont = LightweightFontTest.desiredFont; + Font actualFont = g.getFont(); + if (!actualFont.equals(desiredFont)) { + PassFailJFrame.log("AWT BUG: FONT INFORMATION LOST!"); + PassFailJFrame.log(" Desired font: " + desiredFont); + PassFailJFrame.log(" Actual font: " + actualFont); + PassFailJFrame.forceFail(); + } + } +} diff --git a/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java b/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java new file mode 100644 index 0000000000000..fbbc2ae61854a --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/MultipleAddNotifyTest/MultipleAddNotifyTest.java @@ -0,0 +1,118 @@ +/* + * 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 4058400 + * @summary Tests that calling addNotify on a lightweight component more than + * once does not break event dispatching for that component. + * @key headful + * @run main MultipleAddNotifyTest + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class MultipleAddNotifyTest { + static volatile boolean passFlag; + static volatile int posX; + static volatile int posY; + static Frame f; + static LightComponent l; + + public static void main(String[] args) throws Exception { + Robot r; + try { + r = new Robot(); + r.setAutoWaitForIdle(true); + passFlag = false; + + EventQueue.invokeAndWait(() -> { + f = new Frame("Multiple addNotify Test"); + l = new LightComponent(); + f.setLayout(new FlowLayout()); + l.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + System.out.println("Mouse Clicked"); + passFlag = true; + } + }); + f.add(l); + f.addNotify(); + f.addNotify(); + + if (!l.isVisible()) { + throw new RuntimeException("Test failed. LW Component " + + "not visible."); + } + f.setSize(200, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + }); + r.waitForIdle(); + r.delay(1000); + + EventQueue.invokeAndWait(() -> { + posX = f.getX() + l.getWidth() + (l.getWidth() / 2); + posY = f.getY() + l.getHeight(); + }); + + r.mouseMove(posX, posY); + r.delay(500); + + r.mousePress(InputEvent.BUTTON1_DOWN_MASK); + r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + r.delay(500); + + if (!passFlag) { + throw new RuntimeException("Test failed. MouseClicked event " + + "not working properly."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} + +class LightComponent extends Component { + public void paint(Graphics g) { + setSize(100, 100); + Dimension d = getSize(); + g.setColor(Color.red); + g.fillRect(0, 0, d.width, d.height); + } +} diff --git a/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java b/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java new file mode 100644 index 0000000000000..beae4b4d9042e --- /dev/null +++ b/test/jdk/java/awt/LightweightComponent/PopupTest/PopupTest.java @@ -0,0 +1,93 @@ +/* + * 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 4476083 + * @summary Disabled components do not receive MouseEvent in Popups + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PopupTest + */ + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Frame; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +public class PopupTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + PopupMenus should disappear when a disabled component is + clicked. + + Step 1. Pop down the popup menu by clicking on it. + Step 2. Click on the disabled component to make the menu + disappear. + + If the menu disappears when the disabled component is clicked, + the test passes, otherwise, the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(PopupTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Disabled Component in Popup Test"); + f.setLayout(new BorderLayout()); + + JButton b = new JButton("step 1: press me to display menu"); + b.addActionListener(e -> { + JPopupMenu m = new JPopupMenu(); + m.add(new JMenuItem("item 1")); + m.add(new JMenuItem("item 2")); + m.add(new JMenuItem("item 3")); + m.add(new JMenuItem("item 4")); + m.add(new JMenuItem("item 5")); + m.add(new JMenuItem("item 6")); + m.show((Component) e.getSource(), 0, 10); + }); + + JLabel disabled = new JLabel("step 2: click me. the menu should be " + + "dismissed"); + disabled.setEnabled(false); + + JLabel enabled = new JLabel("step 3: there is no step 3"); + + f.add(BorderLayout.NORTH, b); + f.add(BorderLayout.CENTER, disabled); + f.add(BorderLayout.SOUTH, enabled); + f.setSize(300, 200); + return f; + } +} diff --git a/test/jdk/java/awt/List/ActionEventWhenHitEnterTest.java b/test/jdk/java/awt/List/ActionEventWhenHitEnterTest.java new file mode 100644 index 0000000000000..232189ef53bbb --- /dev/null +++ b/test/jdk/java/awt/List/ActionEventWhenHitEnterTest.java @@ -0,0 +1,126 @@ +/* + * 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 + * 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 4234245 + * @summary the actionEvent is not invoked when hit enter on list. + * @key headful + * @run main ActionEventWhenHitEnterTest + */ + +import java.awt.BorderLayout; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.IllegalComponentStateException; +import java.awt.List; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; + +public class ActionEventWhenHitEnterTest + implements ActionListener, ItemListener { + + volatile boolean passed1; + volatile boolean passed2; + volatile Point pt; + List list; + Frame frame; + + public static void main(final String[] args) throws Exception { + ActionEventWhenHitEnterTest app = new ActionEventWhenHitEnterTest(); + app.doTest(); + } + + public ActionEventWhenHitEnterTest() { + list = new List(7); + for (int i = 0; i < 10; i++) { + list.add("Item " + i); + } + list.addItemListener(this); + list.addActionListener(this); + } + + public void actionPerformed(ActionEvent ae) { + passed1 = true; + System.out.println("--> Action event invoked: " + ae.getSource()); + } + + public void itemStateChanged(ItemEvent ie) { + passed2 = true; + System.out.println("--> Item state changed:" + ie.getSource()); + } + + public void doTest() throws Exception { + EventQueue.invokeAndWait(() -> { + frame = new Frame("ActionEventWhenHitEnterTest"); + frame.add(list); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + try { + Robot robot = new Robot(); + robot.setAutoDelay(100); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + pt = list.getLocationOnScreen(); + }); + + if (pt.x != 0 || pt.y != 0) { + robot.mouseMove(pt.x + list.getWidth() / 2, + pt.y + list.getHeight() / 2); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + } + + robot.waitForIdle(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + if (!passed1 || !passed2) { + throw new RuntimeException("ActionEventWhenHitEnterTest FAILED"); + } + System.out.println("Test PASSED"); + + } + +} diff --git a/test/jdk/java/awt/List/DisabledListIsGreyTest.java b/test/jdk/java/awt/List/DisabledListIsGreyTest.java new file mode 100644 index 0000000000000..ec1a257066606 --- /dev/null +++ b/test/jdk/java/awt/List/DisabledListIsGreyTest.java @@ -0,0 +1,75 @@ +/* + * 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 + * 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 6354810 + * @summary Items in the list are not grayed out when disabled, XToolkit + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DisabledListIsGreyTest +*/ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; + +public class DisabledListIsGreyTest { + + private static final String INSTRUCTIONS = """ + 1) After the test started you will see two lists. + 2) One of them is enabled, and the second is disabled. + 3) Check that the items of the disabled list are grayed. + 4) If so, the test passed. Otherwise, failed."""; + + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("DisabledListIsGreyTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(DisabledListIsGreyTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("DisabledListIsGreyTest Frame"); + frame.setLayout(new FlowLayout()); + + List list1 = new List(3); + List list2 = new List(3); + for (int i = 0; i < 5; i++) { + list1.addItem("Item " + i); + list2.addItem("Item " + i); + } + frame.add(list1); + + list2.setEnabled(false); + frame.add(list2); + frame.pack(); + return frame; + } + +} diff --git a/test/jdk/java/awt/List/HandlingKeyEventIfMousePressedTest.java b/test/jdk/java/awt/List/HandlingKeyEventIfMousePressedTest.java new file mode 100644 index 0000000000000..e5c89c1df126d --- /dev/null +++ b/test/jdk/java/awt/List/HandlingKeyEventIfMousePressedTest.java @@ -0,0 +1,167 @@ +/* + * 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 + * 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 6293432 + * @summary Key events ('SPACE', 'UP', 'DOWN') aren't blocked + * if mouse is kept in 'PRESSED' state for List + * @key headful + * @run main HandlingKeyEventIfMousePressedTest + */ + +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.InputEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; + +public class HandlingKeyEventIfMousePressedTest { + + static Frame frame; + static List list; + static volatile Point loc; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> createUI()); + robot.waitForIdle(); + robot.delay(1000); + EventQueue.invokeAndWait(() -> { + loc = list.getLocationOnScreen(); + }); + robot.mouseMove(loc.x + 10, loc.y + 10); + robot.waitForIdle(); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + + // key pressing when the mouse is kept in the 'pressed' state + robot.keyPress(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_DOWN); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + + int selectedIndex = list.getSelectedIndex(); + if (selectedIndex != 0) { + throw new RuntimeException("Test failed: list.getCurrentItem = " + selectedIndex); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createUI() { + frame = new Frame("HandlingKeyEventIfMousePressedTest"); + list = new List(10, false); + + list.add("111"); + list.add("222"); + list.add("333"); + list.add("444"); + frame.add(list); + + addListeners(); + + frame.setLayout(new FlowLayout()); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + // added in order to have more information in failed case + private static void addListeners() { + + list.addMouseMotionListener( + new MouseMotionAdapter() { + @Override + public void mouseDragged(MouseEvent me) { + System.out.println(me); + } + + @Override + public void mouseMoved(MouseEvent me) { + System.out.println(me); + } + }); + + list.addMouseListener( + new MouseAdapter(){ + public void mousePressed(MouseEvent me) { + System.out.println(me); + } + public void mouseClicked(MouseEvent me) { + System.out.println(me); + } + public void mouseEntered(MouseEvent me) { + System.out.println(me); + } + public void mouseExited(MouseEvent me) { + System.out.println(me); + } + public void mouseReleased(MouseEvent me) { + System.out.println(me); + } + }); + + list.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent ae) { + System.out.println(ae); + } + }); + + list.addItemListener( + new ItemListener() { + public void itemStateChanged(ItemEvent ie) { + System.out.println(ie); + } + }); + + list.addFocusListener( + new FocusAdapter() { + public void focusGained(FocusEvent fe) { + System.out.println(fe); + } + public void focusLost(FocusEvent fe) { + System.out.println(fe); + } + }); + } +} diff --git a/test/jdk/java/awt/List/HorizScrollWorkTest.java b/test/jdk/java/awt/List/HorizScrollWorkTest.java new file mode 100644 index 0000000000000..6de1de1a4f264 --- /dev/null +++ b/test/jdk/java/awt/List/HorizScrollWorkTest.java @@ -0,0 +1,69 @@ +/* + * 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 + * 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 6355467 + * @summary Horizontal scroll bar thumb of a List does not stay at the end + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "linux") + * @run main/manual HorizScrollWorkTest +*/ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; + +public class HorizScrollWorkTest { + + private static final String INSTRUCTIONS = """ + This is a linux only test. + Drag and drop the horizontal scroll bar thumb at the right end. + If the thumb does not stay at the right end, then the test failed. Otherwise passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("HorizScrollWorkTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HorizScrollWorkTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("HorizScrollWorkTest Frame"); + List list = new List(4); + + frame.setLayout (new FlowLayout()); + + list.add("veryyyyyyyyyyyyyyyyyyyyyyyyyy longgggggggggggggggggggggg stringggggggggggggggggggggg"); + + frame.add(list); + frame.pack(); + + return frame; + } +} diff --git a/test/jdk/java/awt/List/HorizScrollbarEraseTest.java b/test/jdk/java/awt/List/HorizScrollbarEraseTest.java new file mode 100644 index 0000000000000..2601a7ed0b2e6 --- /dev/null +++ b/test/jdk/java/awt/List/HorizScrollbarEraseTest.java @@ -0,0 +1,139 @@ +/* + * 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 4895367 + * @summary List scrolling w/ down arrow keys obscures horizontal scrollbar + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "linux") + * @run main/manual HorizScrollbarEraseTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class HorizScrollbarEraseTest { + + private static final String INSTRUCTIONS = """ + This is a Unix-only test. + Do the four mini-tests below. + If the horizontal scrollbar is ever erased by a rectangle + of the background color, the test FAILS. + If the horizontal scrollbars remain painted, test passes."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("HorizScrollbarEraseTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(HorizScrollbarEraseTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("HorizScrollbarEraseTest"); + Panel borderPanel = new Panel(); + borderPanel.setLayout(new BorderLayout()); + Button focusedButton = new Button("Focus starts here"); + borderPanel.add(focusedButton, BorderLayout.NORTH); + + Panel gridPanel = new Panel(); + gridPanel.setLayout(new GridLayout(0, 4)); + borderPanel.add(gridPanel, BorderLayout.CENTER); + + InstructionList il1 = new InstructionList("Tab to Item 2, then \n" + + "press the down" + + "arrow key to scroll down"); + il1.list.select(2); + il1.list.makeVisible(0); + gridPanel.add(il1); + + InstructionList il2 = new InstructionList("Tab to the next List,\n" + + "then press the down\n" + + "arrow key to select\n" + + "the last item."); + il2.list.select(3); + il2.list.makeVisible(0); + gridPanel.add(il2); + + InstructionList il3 = new InstructionList("Click the button to\n" + + "programmatically\n" + + "select item 3 (not showing)"); + Button selectBtn = new Button("Click Me"); + final List selectList = il3.list; + selectBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + selectList.select(3); + } + }); + il3.add(selectBtn, BorderLayout.CENTER); + gridPanel.add(il3); + + InstructionList il4 = new InstructionList("Click the button to\nprogrammatically\ndeselect item 3\n(not showing)"); + Button deselectBtn = new Button("Click Me"); + final List deselectList = il4.list; + deselectBtn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + deselectList.deselect(3); + } + }); + il4.add(deselectBtn, BorderLayout.CENTER); + il4.list.select(3); + il4.list.makeVisible(0); + gridPanel.add(il4); + + frame.add(borderPanel); + frame.pack(); + return frame; + + } +} + +class InstructionList extends Panel { + TextArea ta; + public List list; + + public InstructionList(String instructions) { + super(); + setLayout(new BorderLayout()); + ta = new TextArea(instructions, 6, 25, TextArea.SCROLLBARS_NONE); + ta.setFocusable(false); + list = new List(); + for (int i = 0; i < 5; i++) { + list.add("Item " + i + ", a long, long, long, long item"); + } + add(ta, BorderLayout.NORTH); + add(list, BorderLayout.SOUTH); + } +} diff --git a/test/jdk/java/awt/List/ListActionEventTest.java b/test/jdk/java/awt/List/ListActionEventTest.java new file mode 100644 index 0000000000000..2cd8d04a39dc6 --- /dev/null +++ b/test/jdk/java/awt/List/ListActionEventTest.java @@ -0,0 +1,93 @@ +/* + * 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 + * 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 4089604 + * @summary Enter key doesn't fire List actionPerformed as specified + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListActionEventTest +*/ + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.List; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class ListActionEventTest { + + private static final String INSTRUCTIONS = """ + A frame will be shown. + 1. Click any item in the list (say item 1) in the frame + 2. A message 'ItemSelected' is displayed on the message window. + 3. Press the return key on the selected item. + 4. If the text 'ActionPerformed' is displayed on the message window, + then press PASS else press FAIL."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ListActionEventTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ListActionEventTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("ListActionEventTest frame"); + + Panel pnl1 = new Panel(); + frame.add(pnl1); + pnl1.setLayout(new BorderLayout()); + + List list = new List(); + for (int i = 0; i < 5; i++) { + list.addItem("Item " + i); + } + pnl1.add(list); + + list.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + PassFailJFrame.log("ActionPerformed"); + } + }); + + list.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent ev) { + PassFailJFrame.log("ItemSelected"); + } + }); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/List/ListAddPerfTest.java b/test/jdk/java/awt/List/ListAddPerfTest.java new file mode 100644 index 0000000000000..7ff3eaf882c62 --- /dev/null +++ b/test/jdk/java/awt/List/ListAddPerfTest.java @@ -0,0 +1,99 @@ +/* + * 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 4117288 + * @summary JDKversion1.2beta3-J List's add() method is much slower. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListAddPerfTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.List; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ListAddPerfTest { + + static Button button; + static List list; + + private static final String INSTRUCTIONS = """ + It is used to check the performance of List add operation. + + Instructions: + Click on the Remove All button. + The list should be cleared. + The button is now named "Add Items". + Click on the "Add Items" button. + 800 items should be added to the list. + Notice not only how fast or slow this is, but also how + 'smooth' it goes as well i.e. without any flashing. + + Press pass if the list performance is acceptable."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ListAddPerfTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ListAddPerfTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("ListAddPerfTest"); + frame.setLayout(new BorderLayout()); + + button = new Button("Remove All"); + button.addActionListener((ActionEvent e) -> { + if (list.getItemCount() > 0) { + list.removeAll(); + list.invalidate(); + button.setLabel("Add Items"); + } else { + for (int i = 0; i < 800; i ++) { + list.add("My number is " + i); + } + button.setLabel("Remove All"); + } + }); + + list = new List(15); + for (int i = 0; i < 800; i ++) { + list.add("My number is " + i); + } + + frame.add("North", button); + frame.add("South", list); + + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/List/ListFrameResizeTest.java b/test/jdk/java/awt/List/ListFrameResizeTest.java new file mode 100644 index 0000000000000..4655e817da581 --- /dev/null +++ b/test/jdk/java/awt/List/ListFrameResizeTest.java @@ -0,0 +1,104 @@ +/* + * 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 + * 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 4085379 + * @summary List component not properly "resized" with GridBagLayout + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ListFrameResizeTest + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.List; + +public class ListFrameResizeTest { + + private static final String INSTRUCTIONS = """ + This test is for windows only. + + 1. A Frame will appear with a List + (the List occupies the whole Frame) + 2. Minimize the Frame, the Frame is now in the Task Bar (ie.,iconified) + 3. Right click (right mouse button) the icon in the task bar + and click on the 'maximize' menuitem to maximize the Frame + 4. If you notice the List has not been resized + (ie.,if it partly occupies the Frame), then press FAIL else press PASS"."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ListFrameResizeTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ListFrameResizeTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + wintest client = new wintest("ListFrameResizeTest Frame"); + client.resize(500, 300); + client.setBackground(Color.blue); + return client; + } + +} + +class wintest extends Frame { + private List msg; + + public wintest(String title) { + super(title); + msg = new List(); + for (int i = 0; i < 100; i++) { + msg.add("" + i); + } + + GridBagLayout gridbag = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + + setLayout(gridbag); + + constraints.fill = GridBagConstraints.BOTH; + + constraints.anchor = GridBagConstraints.CENTER; + constraints.insets = new Insets(10, 10, 10, 10); + constraints.ipadx = 0; + constraints.ipady = 0; + constraints.weightx = 1; + constraints.weighty = 1; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = GridBagConstraints.REMAINDER; + constraints.gridheight = GridBagConstraints.REMAINDER; + gridbag.setConstraints(msg, constraints); + add(msg); + } +} diff --git a/test/jdk/java/awt/List/MouseDraggedOriginatedByScrollBarTest.java b/test/jdk/java/awt/List/MouseDraggedOriginatedByScrollBarTest.java new file mode 100644 index 0000000000000..600d38fe39380 --- /dev/null +++ b/test/jdk/java/awt/List/MouseDraggedOriginatedByScrollBarTest.java @@ -0,0 +1,103 @@ +/* + * 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 + * 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 6240151 + * @summary XToolkit: Dragging the List scrollbar initiates DnD + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseDraggedOriginatedByScrollBarTest +*/ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class MouseDraggedOriginatedByScrollBarTest { + + private static final String INSTRUCTIONS = """ + 1) Click and drag the scrollbar of the list. + 2) Keep dragging till the mouse pointer goes out the scrollbar. + 3) The test failed if you see messages about events. The test passed if you don't."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MouseDraggedOriginatedByScrollBarTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MouseDraggedOriginatedByScrollBarTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame(); + List list = new List(4, false); + + list.add("000"); + list.add("111"); + list.add("222"); + list.add("333"); + list.add("444"); + list.add("555"); + list.add("666"); + list.add("777"); + list.add("888"); + list.add("999"); + + frame.add(list); + + list.addMouseMotionListener( + new MouseMotionAdapter(){ + @Override + public void mouseDragged(MouseEvent me){ + PassFailJFrame.log(me.toString()); + } + }); + + list.addMouseListener( + new MouseAdapter() { + public void mousePressed(MouseEvent me) { + PassFailJFrame.log(me.toString()); + } + + public void mouseReleased(MouseEvent me) { + PassFailJFrame.log(me.toString()); + } + + public void mouseClicked(MouseEvent me){ + PassFailJFrame.log(me.toString()); + } + }); + + frame.setLayout(new FlowLayout()); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/List/MultiSelectionListCrashTest.java b/test/jdk/java/awt/List/MultiSelectionListCrashTest.java new file mode 100644 index 0000000000000..b408a12ebbac7 --- /dev/null +++ b/test/jdk/java/awt/List/MultiSelectionListCrashTest.java @@ -0,0 +1,89 @@ +/* + * 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 4201967 + * @summary tests that a multiselection list doesn't causes crash when FileDialog is invoked + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiSelectionListCrashTest + */ + +import java.awt.Button; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class MultiSelectionListCrashTest { + + private static final String INSTRUCTIONS = """ + Press "Invoke dialog" button to invoke a FileDialog. + When it appears close it by pressing cancel button. + If all remaining frames are enabled and + page fault didn't occur the test passed. Otherwise the test failed. + + Try to invoke a FileDialog several times to verify that the bug doesn't exist."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MultiSelectionListCrashTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MultiSelectionListCrashTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + + Frame frame = new Frame("MultiSelectionListCrashTest frame"); + Button button = new Button("Invoke dialog"); + button.addActionListener(new FileDialogInvoker(frame)); + List list = new List(4, true); + list.add("Item1"); + list.add("Item2"); + frame.setLayout(new FlowLayout()); + frame.add(button); + frame.add(list); + frame.setSize(200, 200); + return frame; + } +} + +class FileDialogInvoker implements ActionListener { + FileDialog fileDialog; + + public FileDialogInvoker(Frame frame) { + fileDialog = new FileDialog(frame); + } + + public void actionPerformed(ActionEvent e) { + fileDialog.setVisible(true); + } + +} diff --git a/test/jdk/java/awt/List/MultiSelectionListHorizScrollbar.java b/test/jdk/java/awt/List/MultiSelectionListHorizScrollbar.java new file mode 100644 index 0000000000000..289cd0c2daca9 --- /dev/null +++ b/test/jdk/java/awt/List/MultiSelectionListHorizScrollbar.java @@ -0,0 +1,80 @@ +/* + * 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 4102881 + * @summary Ensure multiple selection Lists have horizontal scrollbars when necessary + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiSelectionListHorizScrollbar +*/ + +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; + +public class MultiSelectionListHorizScrollbar { + + private static final String INSTRUCTIONS = """ + Resize the frame so that the lists are not wide enough + to fully display the lines of text they contain. + Once the lists are in this state, press pass + if both lists display an horizontal scrollbar. Otherwise press fail."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MultiSelectionListHorizScrollbar Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MultiSelectionListHorizScrollbar::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("MultiSelectionListHorizScrollbar Frame"); + List singleList = new List(3); + List multiList = new List(3, true); + + frame.setLayout(new GridLayout(1, 2)); + frame.add(singleList); + frame.add(multiList); + + singleList.addItem("This is the 1st item in the list! Does it scroll horizontally??"); + singleList.addItem("This is the 2nd item in the list! Does it scroll horizontally??"); + singleList.addItem("This is the 4th item in the list! Does it scroll horizontally??"); + singleList.addItem("This is the 5th item in the list! Does it scroll horizontally??"); + singleList.addItem("This is the 6th item in the list! Does it scroll horizontally??"); + + multiList.addItem("This is the 1st item in the list! Does it scroll horizontally??"); + multiList.addItem("This is the 2nd item in the list! Does it scroll horizontally??"); + multiList.addItem("This is the 4th item in the list! Does it scroll horizontally??"); + multiList.addItem("This is the 5th item in the list! Does it scroll horizontally??"); + multiList.addItem("This is the 6th item in the list! Does it scroll horizontally??"); + + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/List/RepaintAfterResize.java b/test/jdk/java/awt/List/RepaintAfterResize.java new file mode 100644 index 0000000000000..12bb584aefa88 --- /dev/null +++ b/test/jdk/java/awt/List/RepaintAfterResize.java @@ -0,0 +1,73 @@ +/* + * 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 + * 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 6308295 + * @summary XAWTduplicate list item is displayed + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RepaintAfterResize +*/ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; + +public class RepaintAfterResize { + + private static final String INSTRUCTIONS = """ + 1) A Frame appears with a list + 2) Resize somehow the frame using mouse + 3) Move down the vertical scrollbar of the list + 4) If you see that two selected items are displayed then the test failed. + Otherwise, the test passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("RepaintAfterResize Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(RepaintAfterResize::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("RepaintAfterResize Frame"); + List list = new List(4, false); + + frame.setLayout (new FlowLayout ()); + list.setBounds(100, 100, 100, 100); + for (int i = 0 ; i < 7 ; i++) { + list.add(" " + i); + } + frame.add(list); + list.select(3); + + frame.setSize(100, 100); + return frame; + + } +} diff --git a/test/jdk/java/awt/List/ScrollbarPositionTest.java b/test/jdk/java/awt/List/ScrollbarPositionTest.java new file mode 100644 index 0000000000000..25167fa56278c --- /dev/null +++ b/test/jdk/java/awt/List/ScrollbarPositionTest.java @@ -0,0 +1,104 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4024943 + * @summary Test for position of List scrollbar when it is added + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollbarPositionTest + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ScrollbarPositionTest { + static int item = 0; + static List list; + static Button addButton, delButton; + + private static final String INSTRUCTIONS = """ + Click on the "Add List Item" button many times + until the vertical scrollbar appears. + Verify that the displayed vertical scrollbar does not take the space + that was occupied by buttons before the scrollbar is shown."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ScrollbarPositionTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ScrollbarPositionTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Panel pan; + + Frame frame = new Frame("ScrollbarPositionTest Frame"); + frame.setLayout(new GridLayout(1, 2)); + list = new List(); + frame.add(list); + frame.add(pan = new Panel()); + pan.setLayout(new GridLayout(4, 1)); + + MyListener listener = new MyListener(); + addButton = new Button("Add List Item"); + addButton.addActionListener(listener); + pan.add(addButton); + + delButton = new Button("Delete List Item"); + delButton.addActionListener(listener); + pan.add(delButton); + + frame.pack(); + return frame; + } + + static class MyListener implements ActionListener { + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == addButton) { + String s = "item"; + for (int i = 0; i <= item; i++) { + s = s +" "+Integer.toString(i); + } + item++; + list.addItem(s); + } else if (evt.getSource() == delButton) { + int i; + if ((i = list.countItems()) > 0) { + list.delItem(i - 1); + --item; + } + } + } + } +} diff --git a/test/jdk/java/awt/List/ScrollbarPresenceTest.java b/test/jdk/java/awt/List/ScrollbarPresenceTest.java new file mode 100644 index 0000000000000..d82cbb80060ca --- /dev/null +++ b/test/jdk/java/awt/List/ScrollbarPresenceTest.java @@ -0,0 +1,71 @@ +/* + * 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 + * 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 6336384 + * @summary ScrollBar does not show up correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ScrollbarPresenceTest +*/ + +import java.awt.Font; +import java.awt.Frame; +import java.awt.List; + +public class ScrollbarPresenceTest { + + private static final String INSTRUCTIONS = """ + You will see a list, + If a vertical scrollbar appears on the list and the list is big enough + to show all items then the test failed else the test passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ScrollbarPresenceTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ScrollbarPresenceTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame("ScrollbarPresenceTest Frame"); + List list = new List(); + + for (int i = 0; i < 6; i++) { + list.addItem("Row " + i); + } + + list.setFont(new Font("MonoSpaced", Font.PLAIN, 12)); + list.setBounds(30, 30, 128, 104); + frame.add(list); + + frame.pack(); + return frame; + } + +} diff --git a/test/jdk/java/awt/List/SelectedItemVisibilityTest.java b/test/jdk/java/awt/List/SelectedItemVisibilityTest.java new file mode 100644 index 0000000000000..53364e937713c --- /dev/null +++ b/test/jdk/java/awt/List/SelectedItemVisibilityTest.java @@ -0,0 +1,128 @@ +/* + * 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 + * 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 4676536 + * @summary REGRESSION: makeVisible() method of List Component does not perform + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SelectedItemVisibilityTest + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.List; + +public class SelectedItemVisibilityTest { + + static List list1, list2; + static int visibleItem = 4; + static int selectedItems[] = {6, 7, 8}; + static String selectedItemsStr = ""; + + static { + for (int i = 0 ; i < selectedItems.length ; i++) { + selectedItemsStr += ""+selectedItems[i]+" "; + } + } + + private static final String INSTRUCTIONS = + "You should see two lists.\n" + + "\n" + + "list1: \n" + + "\t1. the first visible item should be " + visibleItem + + "\n\t2. the selected item should be " + selectedItems[0] + + "\n" + + "list2:\n" + + "\t1. the first visible item should be " + visibleItem + + "\n\t2. the selected items should be " + selectedItemsStr + + "\n" + + "\nIf it is so the test passed else failed."; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("SelectedItemVisibilityTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(SelectedItemVisibilityTest::createTestUI) + .logArea() + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + + Frame frame = new Frame("SelectedItemVisibilityTest Frame"); + frame.setLayout(new FlowLayout()); + + // list1 + list1 = new List(4); + for (int i = 0; i < 20; i++) { + list1.add(""+i); + } + list1.makeVisible(visibleItem); + list1.select(selectedItems[0]); + frame.add(new Label("list1:")); + frame.add(list1); + + // list2 + list2 = new List(4); + list2.setMultipleMode(true); + for (int i = 0; i < 20; i++) { + list2.add(""+i); + } + list2.makeVisible(visibleItem); + for (int i = 0 ; i < selectedItems.length ; i++) { + list2.select(selectedItems[i]); + } + frame.add(new Label("list2:")); + frame.add(list2); + frame.setSize(200, 200); + + // common output + String s; + int sel[]; + + PassFailJFrame.log("list1: "); + PassFailJFrame.log("\tgetVisibleIndex="+list1.getVisibleIndex()); + sel = list1.getSelectedIndexes(); + s = "\tgetSelectedIndexes="; + for (int i = 0 ; i < sel.length ; i++) { + s += "" + sel[i] + " "; + } + PassFailJFrame.log(s); + + PassFailJFrame.log("list2: "); + PassFailJFrame.log("\tgetVisibleIndex="+list2.getVisibleIndex()); + sel = list2.getSelectedIndexes(); + s = "\tgetSelectedIndexes="; + for (int i = 0 ; i < sel.length ; i++) { + s += "" + sel[i] + " "; + } + PassFailJFrame.log(s); + return frame; + } +} diff --git a/test/jdk/java/awt/List/SetForegroundTest.java b/test/jdk/java/awt/List/SetForegroundTest.java new file mode 100644 index 0000000000000..7b22e5385088b --- /dev/null +++ b/test/jdk/java/awt/List/SetForegroundTest.java @@ -0,0 +1,109 @@ +/* + * 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 + * 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 6246467 + * @summary Tests that list works correctly if user specified foreground colors on XToolkit/Motif + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetForegroundTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.List; +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.ScrollPane; + +public class SetForegroundTest { + + private static final String INSTRUCTIONS = """ + To make sure, that for each component + (Button, Checkbox, Label, List, TextArea, TextField, Choice) + in the frame, + the title exist and the color of the title is red. + If not, the test failed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("SetForegroundTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(SetForegroundTest::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame frame = new Frame(); + ScrollPane sp = new ScrollPane() { + public Dimension getPreferredSize() { + return new Dimension(180, 180); + } + }; + Panel p = new Panel(); + Component childs[] = new Component[] {new Button("button"), + new Checkbox("checkbox"), + new Label("label"), + new List(3, false), + new TextArea("text area"), + new TextField("text field"), + new Choice()}; + + p.setLayout (new FlowLayout ()); + + sp.add(p); + + sp.validate(); + + frame.add(sp); + for (int i = 0; i < childs.length; i++){ + childs[i].setForeground(Color.red); + } + + for (int i = 0; i < childs.length; i++) { + p.add(childs[i]); + if (childs[i] instanceof List) { + ((List)childs[i]).add("list1"); + ((List)childs[i]).add("list2"); + } else if (childs[i] instanceof Choice) { + ((Choice)childs[i]).add("choice1"); + ((Choice)childs[i]).add("choice2"); + } + } + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/MenuBar/CellsResize.java b/test/jdk/java/awt/MenuBar/CellsResize.java new file mode 100644 index 0000000000000..64777095fa8d5 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/CellsResize.java @@ -0,0 +1,159 @@ +/* + * 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 + * 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 6502052 + * @summary Menu cells must resize if font changes (XToolkit) + * @requires os.family == "linux" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CellsResize + */ + +import java.awt.Button; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuComponent; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.Toolkit; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class CellsResize { + private static Frame frame; + private static MenuBar menuBar; + private static PopupMenu popupMenu; + private static Menu barSubMenu; + private static Menu popupSubMenu; + private static boolean fontMultiplied = false; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Open all nested menus in menu bar. + 2. Click on "popup-menu" button to show popup-menus. + 3. Open all nested menus in popup-menu. + 4. Click on "big-font" button (to make all menus have a + bigger font). + 5. Open all nested menus again (as described in 1, 2, 3). + 6. If all menu items use a bigger font now and their labels fit + into menu-item size, press "pass", otherwise press "fail". + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(CellsResize::createUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + public static Frame createUI () { + if (!checkToolkit()) { + new RuntimeException("Toolkit check failed."); + } + frame = new Frame("MenuBar Cell Resize Test"); + + popupMenu = new PopupMenu(); + popupMenu.add(createMenu(false)); + + frame.add(popupMenu); + + menuBar = new MenuBar(); + menuBar.add(createMenu(true)); + + frame.setMenuBar(menuBar); + + Button bp = new Button("popup-menu"); + bp.addMouseListener(new MouseAdapter() { + public void mouseReleased(MouseEvent e) { + popupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + }); + + Button bf = new Button("big-font"); + bf.addMouseListener(new MouseAdapter() { + public void mouseReleased(MouseEvent e) { + bigFont(); + } + }); + + Panel panel = new Panel(); + panel.setLayout(new GridLayout(2, 1)); + panel.add(bp); + panel.add(bf); + + frame.add(panel); + frame.setSize(300, 300); + return frame; + } + + static boolean checkToolkit() { + String toolkitName = Toolkit.getDefaultToolkit().getClass().getName(); + return toolkitName.equals("sun.awt.X11.XToolkit"); + } + + static Menu createMenu(boolean bar) { + Menu menu1 = new Menu("Menu-1"); + Menu menu11 = new Menu("Menu-11"); + menu1.add(menu11); + if (bar) { + barSubMenu = menu11; + } else { + popupSubMenu = menu11; + } + menu11.add(new MenuItem("MenuItem")); + return menu1; + } + + static void bigFont() { + if (fontMultiplied) { + return; + } else { + fontMultiplied = true; + } + + multiplyFont(barSubMenu, 7); + multiplyFont(popupSubMenu, 7); + + // NOTE: if previous two are moved below following + // two, they get their font multiplied twice. + + multiplyFont(menuBar, 5); + multiplyFont(popupMenu, 5); + } + + static void multiplyFont(MenuComponent comp, int times) { + Font font = comp.getFont(); + float size = font.getSize() * times; + comp.setFont(font.deriveFont(size)); + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarAddRemoveTest/MenuBarAddRemoveTest.java b/test/jdk/java/awt/MenuBar/MenuBarAddRemoveTest/MenuBarAddRemoveTest.java new file mode 100644 index 0000000000000..fdb9f06e98c76 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarAddRemoveTest/MenuBarAddRemoveTest.java @@ -0,0 +1,72 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4028130 4112308 + * @summary Test for location of Frame/MenuBar when MenuBar is re-added + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarAddRemoveTest + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; + +public class MenuBarAddRemoveTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click the left mouse button on the "Re-Add MenuBar" + button several times. + 3. The Frame/MenuBar may repaint or flash, but the location + of its upper left corner should never change. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarAddRemoveTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Re-Add MenuBar Test Frame"); + Button b = new Button("Re-Add MenuBar"); + b.addActionListener(e -> f.setMenuBar(createMenuBar())); + f.setMenuBar(createMenuBar()); + f.add(b); + f.pack(); + return f; + } + + private static MenuBar createMenuBar() { + MenuBar bar = new MenuBar(); + bar.add(new Menu("foo")); + return bar; + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarOnDisabledFrame/MenuBarOnDisabledFrame.java b/test/jdk/java/awt/MenuBar/MenuBarOnDisabledFrame/MenuBarOnDisabledFrame.java new file mode 100644 index 0000000000000..06f5d96c19e32 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarOnDisabledFrame/MenuBarOnDisabledFrame.java @@ -0,0 +1,77 @@ +/* + * 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 + * 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 6185057 + * @summary Disabling a frame does not disable the menus on the frame, on + * solaris/linux + * @requires os.family != "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarOnDisabledFrame + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class MenuBarOnDisabledFrame { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Check if MenuBar is disabled on 'Disabled frame' + Press pass if menu bar is disabled, fail otherwise + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarOnDisabledFrame::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Disabled frame"); + MenuBar mb = new MenuBar(); + Menu m1 = new Menu("Disabled Menu 1"); + Menu m2 = new Menu("Disabled Menu 2"); + MenuItem m11 = new MenuItem("MenuItem 1.1"); + MenuItem m21 = new MenuItem("MenuItem 2.1"); + Button b = new Button("Disabled button"); + + m1.add(m11); + m2.add(m21); + mb.add(m1); + mb.add(m2); + f.setMenuBar(mb); + f.add(b); + f.setEnabled(false); + f.setSize(300, 300); + return f; + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarRemoveMenu/MenuBarRemoveMenuTest.java b/test/jdk/java/awt/MenuBar/MenuBarRemoveMenu/MenuBarRemoveMenuTest.java new file mode 100644 index 0000000000000..098065d1361f6 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarRemoveMenu/MenuBarRemoveMenuTest.java @@ -0,0 +1,93 @@ +/* + * 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 4275848 + * @summary Tests that MenuBar is painted correctly after its submenu is removed + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarRemoveMenuTest + */ + +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class MenuBarRemoveMenuTest implements ActionListener { + private static MenuBar menubar; + private static Button removeButton; + private static Button addButton; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Press "Remove menu" button. If you see that both menus + disappeared, the test failed. Otherwise try to add and remove + menu several times to verify that the test passed. Every time + you press "Remove menu" button only one menu should go away. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarRemoveMenuTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame(); + menubar = new MenuBar(); + removeButton = new Button("Remove menu"); + addButton = new Button("Add menu"); + removeButton.addActionListener(new MenuBarRemoveMenuTest()); + addButton.addActionListener(new MenuBarRemoveMenuTest()); + addButton.setEnabled(false); + menubar.add(new Menu("menu")); + menubar.add(new Menu("menu")); + frame.setMenuBar(menubar); + frame.setLayout(new GridLayout(1, 2)); + frame.add(removeButton); + frame.add(addButton); + frame.pack(); + return frame; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == removeButton) { + menubar.remove(0); + removeButton.setEnabled(false); + addButton.setEnabled(true); + } else { + menubar.add(new Menu("menu")); + removeButton.setEnabled(true); + addButton.setEnabled(false); + } + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuBarVisuals/MenuBarVisuals.java b/test/jdk/java/awt/MenuBar/MenuBarVisuals/MenuBarVisuals.java new file mode 100644 index 0000000000000..7663dd0d99be1 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuBarVisuals/MenuBarVisuals.java @@ -0,0 +1,88 @@ +/* + * 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 + * 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 6180416 + * @summary Tests MenuBar and drop down menu visuals + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuBarVisuals + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.event.KeyEvent; + +public class MenuBarVisuals { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Look at the MenuBar and traverse the menus using mouse and + keyboard. Then check if following is showing correctly: + 1. Mnemonic label Ctrl+A is NOT drawn for Menu 1/Submenu 1.1 + 2. Mnemonic label Ctrl+B is drawn for + Menu 1/Submenu 1.1/Item 1.1.1 + 3. Mnemonic label Ctrl+C is drawn for Menu1/Item 1.2 + Press PASS if Menu is drawing correctly, FAIL otherwise. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MenuBarVisuals::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("MenuBar Visuals Test"); + MenuBar mb = new MenuBar(); + Menu menu1 = new Menu("Menu 1"); + Menu submenu11 = new Menu("Submenu 1.1"); + MenuItem item111 = new MenuItem("Item 1.1.1"); + MenuItem item112 = new MenuItem("Item 1.1.2"); + MenuItem item12 = new MenuItem("Item 1.2"); + Menu menu2 = new Menu("Menu 2"); + MenuItem item21 = new MenuItem("Item 2.1"); + MenuItem item22 = new MenuItem("Item 2.2"); + item111.setShortcut(new MenuShortcut(KeyEvent.VK_B, false)); + submenu11.add(item111); + submenu11.add(item112); + submenu11.setShortcut(new MenuShortcut(KeyEvent.VK_A, false)); + menu1.add(submenu11); + item12.setShortcut(new MenuShortcut(KeyEvent.VK_C, false)); + menu1.add(item12); + mb.add(menu1); + menu2.add(item21); + menu2.add(item22); + mb.add(menu2); + f.setMenuBar(mb); + f.setSize(300, 300); + return f; + } +} diff --git a/test/jdk/java/awt/MenuBar/MenuNPE/MenuNPE.java b/test/jdk/java/awt/MenuBar/MenuNPE/MenuNPE.java new file mode 100644 index 0000000000000..a7a3a3480118e --- /dev/null +++ b/test/jdk/java/awt/MenuBar/MenuNPE/MenuNPE.java @@ -0,0 +1,63 @@ +/* + * 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 + * 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 5005194 + * @summary Frame.remove(getMenuBar()) throws NPE if the frame doesn't + * have a menu bar + * @key headful + * @run main MenuNPE + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class MenuNPE { + private static Frame frame; + public static void main(String[] args) throws Exception { + try { + frame = new Frame("Menu NPE"); + MenuBar menuBar = new MenuBar(); + Menu menu1 = new Menu("Menu 01"); + MenuItem menuLabel = new MenuItem("Item 01"); + menu1.add(menuLabel); + menuBar.add(menu1); + frame.setMenuBar(menuBar); + frame.setSize(200, 200); + frame.setVisible(true); + frame.validate(); + frame.remove(frame.getMenuBar()); + frame.remove(frame.getMenuBar()); + System.out.println("Test passed."); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (frame != null) { + frame.dispose(); + } + } + } +} diff --git a/test/jdk/java/awt/MenuBar/SetHelpMenuTest/SetHelpMenuTest.java b/test/jdk/java/awt/MenuBar/SetHelpMenuTest/SetHelpMenuTest.java new file mode 100644 index 0000000000000..fcfc3e80ed34c --- /dev/null +++ b/test/jdk/java/awt/MenuBar/SetHelpMenuTest/SetHelpMenuTest.java @@ -0,0 +1,88 @@ +/* + * 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 4275843 + * @summary MenuBar doesn't display all of its Menus correctly on Windows + * @requires os.family == "windows" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetHelpMenuTest + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +public class SetHelpMenuTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + An empty frame should be visible. When focused, the MenuBar + should have 5 menus ("one", "two", "three", "Help 2", + "four"). If so, then the test passed. Otherwise, the test + failed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(SetHelpMenuTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("Help MenuBar Test"); + f.setSize(100, 100); + + MenuBar mb = new MenuBar(); + Menu h1, h2; + + f.setMenuBar(mb); + mb.add(createMenu("one", false)); + mb.add(createMenu("two", false)); + mb.add(createMenu("three", true)); + + mb.add(h1 = createMenu("Help 1", false)); // h1 is HelpMenu + mb.setHelpMenu(h1); + + mb.add(h2 = createMenu("Help 2", false)); // h2 replaced h1 + mb.setHelpMenu(h2); + + mb.add(createMenu("four", false)); + + return f; + } + + private static Menu createMenu(String name, boolean tearOff) { + Menu m = new Menu(name, tearOff); + m.add(new MenuItem(name + " item 1")); + m.add(new MenuItem(name + " item 2")); + m.add(new MenuItem(name + " item 3")); + return m; + } +} diff --git a/test/jdk/java/awt/MenuBar/SetMBarWhenHidden/SetMBarWhenHidden.java b/test/jdk/java/awt/MenuBar/SetMBarWhenHidden/SetMBarWhenHidden.java new file mode 100644 index 0000000000000..67eefe4894248 --- /dev/null +++ b/test/jdk/java/awt/MenuBar/SetMBarWhenHidden/SetMBarWhenHidden.java @@ -0,0 +1,92 @@ +/* + * 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 4105881 + * @summary Sets the menu bar while frame window is hidden, then shows + frame again + * @key headful + * @run main SetMBarWhenHidden + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Rectangle; + +// test case for 4105881: FRAME.SETSIZE() DOESN'T WORK FOR SOME SOLARIS WITH +// JDK115+CASES ON +public class SetMBarWhenHidden { + private static Frame f; + private static Rectangle startBounds; + private static Rectangle endBounds; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> { + f = new Frame("Set MenuBar When Hidden Test"); + Menu file; + Menu edit; + MenuBar menubar = new MenuBar(); + file = new Menu("File"); + menubar.add(file); + edit = new Menu("Edit"); + menubar.add(edit); + edit.setEnabled(false); + f.setMenuBar(menubar); + f.setSize(200, 200); + startBounds = f.getBounds(); + System.out.println("About to call setVisible(false)"); + f.setVisible(false); + System.out.println("About to call setSize(500, 500)"); + f.setSize(500, 500); + // create a new menubar and add + MenuBar menubar1 = new MenuBar(); + menubar1.add(file); + menubar1.add(edit); + System.out.println("About to call setMenuBar"); + f.setMenuBar(menubar1); + System.out.println("About to call setVisible(true)"); + f.setVisible(true); + endBounds = f.getBounds(); + }); + if (startBounds.getHeight() > endBounds.getHeight() && + startBounds.getWidth() > endBounds.getWidth()) { + throw new RuntimeException("Test failed. Frame size didn't " + + "change.\nStart: " + startBounds + "\n" + + "End: " + endBounds); + } else { + System.out.println("Test passed.\nStart: " + startBounds + + "\nEnd: " + endBounds); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/MenuItem/GiantFontTest.java b/test/jdk/java/awt/MenuItem/GiantFontTest.java new file mode 100644 index 0000000000000..f1e352373bb1e --- /dev/null +++ b/test/jdk/java/awt/MenuItem/GiantFontTest.java @@ -0,0 +1,83 @@ +/* + * 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 + * 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.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +/* + * @test + * @bug 4700350 + * @requires os.family != "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests menu item font is big + * @run main/manual GiantFontTest + */ + +public class GiantFontTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + A frame with one menu will appear. + On Linux, the menu's (present on menu bar) font should + be quite large (48 point). + If not, test fails. + + On Windows, the menu's (present on menu bar) font + should be normal size. + If the menu text is clipped by the title bar, or is painted over + the title bar or client area, the test fails. + + On both Windows and Linux, the menu items in the popup + menu should be large. + + If so, test passes."""; + + PassFailJFrame.builder() + .title("GiantFontTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(GiantFontTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Font giantFont = new Font("Dialog", Font.PLAIN, 48); + Frame f = new Frame("GiantFontTest"); + MenuBar mb = new MenuBar(); + Menu m = new Menu("My font is too big!"); + m.setFont(giantFont); + for (int i = 0; i < 5; i++) { + m.add(new MenuItem("Some MenuItems")); + } + mb.add(m); + f.setMenuBar(mb); + f.setSize(450, 400); + return f; + } +} diff --git a/test/jdk/java/awt/MenuItem/LotsOfMenuItemsTest.java b/test/jdk/java/awt/MenuItem/LotsOfMenuItemsTest.java new file mode 100644 index 0000000000000..7a528205d27d8 --- /dev/null +++ b/test/jdk/java/awt/MenuItem/LotsOfMenuItemsTest.java @@ -0,0 +1,117 @@ +/* + * 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.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +/* + * @test + * @bug 4175790 + * @requires os.family == "windows" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Win32: Running out of command ids for menu items + * @run main/manual LotsOfMenuItemsTest + */ + +public class LotsOfMenuItemsTest extends ComponentAdapter { + private static final int NUM_WINDOWS = 400; + private static TestFrame firstFrame; + + public static void main(String[] args) throws Exception { + LotsOfMenuItemsTest obj = new LotsOfMenuItemsTest(); + String INSTRUCTIONS = """ + This test creates lots of frames with menu bars. + When it's done you will see two frames. + Try to select menu items from each of them. + + If everything seems to work - test passed. + Click "Pass" button in the test harness window. + + If test crashes on you - test failed."""; + + PassFailJFrame.builder() + .title("LotsOfMenuItemsTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(obj::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private Frame createAndShowUI() { + firstFrame = new TestFrame("First frame"); + firstFrame.addComponentListener(this); + return firstFrame; + } + + @Override + public void componentShown(ComponentEvent e) { + final int x = firstFrame.getX(); + final int y = firstFrame.getY() + firstFrame.getHeight() + 8; + TestFrame testFrame; + for (int i = 1; i < NUM_WINDOWS - 1; ++i) { + testFrame = new TestFrame("Running(" + i + ")...", x, y); + testFrame.setVisible(false); + testFrame.dispose(); + } + testFrame = new TestFrame("Last Frame", x, y); + PassFailJFrame.addTestWindow(testFrame); + } + + private static class TestFrame extends Frame { + static int n = 0; + + public TestFrame(String title) { + this(title, 0, 0, false); + } + + public TestFrame(String s, int x, int y) { + this(s, x, y, true); + } + + private TestFrame(String title, int x, int y, boolean visible) { + super(title); + MenuBar mb = new MenuBar(); + for (int i = 0; i < 10; ++i) { + Menu m = new Menu("Menu_" + (i + 1)); + for (int j = 0; j < 20; ++j) { + MenuItem mi = new MenuItem("Menu item " + ++n); + m.add(mi); + } + mb.add(m); + } + setMenuBar(mb); + setLocation(x, y); + setSize(450, 150); + if (visible) { + setVisible(true); + } + } + } +} diff --git a/test/jdk/java/awt/MenuItem/MenuSetFontTest.java b/test/jdk/java/awt/MenuItem/MenuSetFontTest.java new file mode 100644 index 0000000000000..9a4e8f8583905 --- /dev/null +++ b/test/jdk/java/awt/MenuItem/MenuSetFontTest.java @@ -0,0 +1,72 @@ +/* + * 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 + * 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.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +/* + * @test + * @bug 4066657 8009454 + * @requires os.family != "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests that setting a font on the Menu with MenuItem takes effect. + * @run main/manual MenuSetFontTest + */ + +public class MenuSetFontTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Look at the menu in the upper left corner of the 'SetFont Test' frame. + Click on the "File" menu. You will see "menu item" item. + Press Pass if menu item is displayed using bold and large font, + otherwise press Fail. + If you do not see menu at all, press Fail."""; + + PassFailJFrame.builder() + .title("MenuSetFontTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(MenuSetFontTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("SetFont Test"); + MenuBar menuBar = new MenuBar(); + Menu menu = new Menu("File"); + MenuItem item = new MenuItem("menu item"); + menu.add(item); + menuBar.add(menu); + menuBar.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24)); + frame.setMenuBar(menuBar); + frame.setSize(300, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/MenuItem/NullOrEmptyStringLabelTest.java b/test/jdk/java/awt/MenuItem/NullOrEmptyStringLabelTest.java new file mode 100644 index 0000000000000..79d4c02ad246a --- /dev/null +++ b/test/jdk/java/awt/MenuItem/NullOrEmptyStringLabelTest.java @@ -0,0 +1,105 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4251036 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary MenuItem setLabel(null/"") behaves differently under Win32 and Solaris + * @run main/manual NullOrEmptyStringLabelTest + */ + +public class NullOrEmptyStringLabelTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + The bug is reproducible under Win32 and Solaris. + Setting 'null' and "" as a label of menu item + should set blank label on all platforms according to the specification. + But under Solaris setting "" as a label of menu item used to + cause some garbage to be set as label. + Under Win32 setting 'null' as a label used to result in + throwing NullPointerException. + + If you see any of these things happen test fails otherwise + it passes."""; + + PassFailJFrame.builder() + .title("NullOrEmptyStringLabelTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(NullOrEmptyStringLabelTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("Null Or Empty String Label Test"); + Menu menu = new Menu("Menu"); + MenuItem mi = new MenuItem("Item"); + MenuBar mb = new MenuBar(); + Button button1 = new Button("Set MenuItem label to 'null'"); + Button button2 = new Button("Set MenuItem label to \"\""); + Button button3 = new Button("Set MenuItem label to 'text'"); + button1.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + System.out.println("Setting MenuItem label to null"); + mi.setLabel(null); + } + }); + button2.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + System.out.println("Setting MenuItem label to \"\""); + mi.setLabel(""); + } + }); + button3.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ev) { + System.out.println("Setting MenuItem label to 'text'"); + mi.setLabel("text"); + } + }); + menu.add(mi); + mb.add(menu); + frame.add(button1, BorderLayout.NORTH); + frame.add(button2, BorderLayout.CENTER); + frame.add(button3, BorderLayout.SOUTH); + frame.setMenuBar(mb); + frame.setSize(200, 135); + return frame; + } +} diff --git a/test/jdk/java/awt/MenuItem/UnicodeMenuItemTest.java b/test/jdk/java/awt/MenuItem/UnicodeMenuItemTest.java new file mode 100644 index 0000000000000..b73fe954af6db --- /dev/null +++ b/test/jdk/java/awt/MenuItem/UnicodeMenuItemTest.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; + +/* + * @test + * @bug 4099695 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary menu items with Unicode labels treated as separators + * @run main/manual UnicodeMenuItemTest + */ + +public class UnicodeMenuItemTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on the "Menu" on the top-left corner of frame. + + The menu should have four entries: + 1) a row of five unicode characters: \u00c4\u00cb\u00cf\u00d6\u00dc + 2) a menu separator + 3) a unicode character: \u012d + 4) a unicode character: \u022d + + If the menu items look like the list above, the test passes. + It is okay if the unicode characters look like empty boxes + or something - as long as they are not separators. + + If either of the last two menu items show up as separators, + the test FAILS. + + Press 'Pass' if above instructions hold good else press 'Fail'."""; + + PassFailJFrame.builder() + .title("UnicodeMenuItemTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testUI(UnicodeMenuItemTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + private static Frame createAndShowUI() { + Frame frame = new Frame("Unicode MenuItem Test"); + MenuBar mb = new MenuBar(); + Menu m = new Menu("Menu"); + + MenuItem mi1 = new MenuItem("\u00c4\u00cb\u00cf\u00d6\u00dc"); + m.add(mi1); + + MenuItem separator = new MenuItem("-"); + m.add(separator); + + MenuItem mi2 = new MenuItem("\u012d"); + m.add(mi2); + + MenuItem mi3 = new MenuItem("\u022d"); + m.add(mi3); + + mb.add(m); + + frame.setMenuBar(mb); + frame.setSize(450, 150); + return frame; + } +} diff --git a/test/jdk/java/awt/MenuShortcut/ActionCommandTest.java b/test/jdk/java/awt/MenuShortcut/ActionCommandTest.java new file mode 100644 index 0000000000000..bd1648cdc2cd3 --- /dev/null +++ b/test/jdk/java/awt/MenuShortcut/ActionCommandTest.java @@ -0,0 +1,111 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4079449 + * @key headful + * @summary MenuItem objects return null if they are activated by shortcut + */ + +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import static java.awt.event.KeyEvent.VK_CONTROL; +import static java.awt.event.KeyEvent.VK_META; + +public class ActionCommandTest implements ActionListener { + + static volatile Frame frame; + static volatile boolean event = false; + static volatile boolean failed = false; + static final String ITEMTEXT = "Testitem"; + + static void createUI() { + frame = new Frame("ActionCommand Menu Shortcut Test"); + MenuBar mb = new MenuBar(); + Menu m = new Menu("Test"); + MenuItem mi = new MenuItem(ITEMTEXT, new MenuShortcut(KeyEvent.VK_T)); + mi.addActionListener(new ActionCommandTest()); + m.add(mi); + mb.add(m); + frame.setMenuBar(mb); + frame.setBounds(50, 400, 200, 200); + frame.setVisible(true); + } + + public static void main(String[] args ) throws Exception { + + EventQueue.invokeAndWait(ActionCommandTest::createUI); + try { + Robot robot = new Robot(); + + robot.waitForIdle(); + robot.delay(2000); + + // Ensure window has focus + Point p = frame.getLocationOnScreen(); + robot.mouseMove(p.x + frame.getWidth() / 2, p.y + frame.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(2000); + + // invoke short cut. + robot.keyPress(KeyEvent.VK_T); + robot.delay(50); + robot.keyRelease(KeyEvent.VK_T); + robot.waitForIdle(); + robot.delay(2000); + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + if (failed) { + throw new RuntimeException("No actioncommand"); + } + } + + // Since no ActionCommand is set, this should be the menuitem's label. + public void actionPerformed(ActionEvent e) { + event = true; + String s = e.getActionCommand(); + if (s == null || !s.equals(ITEMTEXT)) { + failed = true; + } + } + +} diff --git a/test/jdk/java/awt/MenuShortcut/CheckMenuShortcut.java b/test/jdk/java/awt/MenuShortcut/CheckMenuShortcut.java new file mode 100644 index 0000000000000..cebb42f1b55e3 --- /dev/null +++ b/test/jdk/java/awt/MenuShortcut/CheckMenuShortcut.java @@ -0,0 +1,139 @@ +/* + * 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 4167811 + * @summary tests that shortcuts work for Checkbox menu items + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CheckMenuShortcut +*/ + +import java.awt.CheckboxMenuItem; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Insets; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; + +public class CheckMenuShortcut implements ActionListener, ItemListener { + + static final String INSTRUCTIONS = """ + A window that contains a text area will be displayed. + The window will have a menu labeled 'Window Menu'. Click on the menu to see its items. + + The two menu items should have shortcuts which in order are : Ctrl-A, Ctrl-I. + On macOS these will be Command-A, Command-I. + + If the second item only has the label 'checkbox item' and no shortcut + ie none of Ctrl-I or Ctrl-i, or Command-I or Command-i on macOS painted on it, the test FAILS. + + The same second item - labeled 'checkbox item' is in fact a Checkbox menu item. + The menu item should NOT be checked (eg no tick mark). + + Dismiss the menu by clicking inside the window, do not select any of menu items. + After that press Ctrl-i, (Command-i on macOS). + + After that click on the menu again. If the second menu item 'checkbox item' is now + checked, the test PASSES, if it is not checked, the test FAILS. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("CheckboxMenuItem Shortcut Test Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .logArea() + .testUI(CheckMenuShortcut::createUI) + .build() + .awaitAndCheck(); + } + + + static Frame createUI() { + + MenuBar mainMenu; + Menu menu; + MenuItem action; + CheckboxMenuItem item; + TextArea pane; + + boolean isMac = System.getProperty("os.name").startsWith("Mac"); + String ctrlA = (isMac) ? "Command-A" : "Ctrl-A"; + String ctrlI = (isMac) ? "Command-I" : "Ctrl-I"; + + CheckMenuShortcut cms = new CheckMenuShortcut(); + Frame frame = new Frame("CheckMenuShortcut"); + + mainMenu = new MenuBar(); + menu = new Menu("Window Menu"); + + action = new MenuItem("action"); + action.setShortcut(new MenuShortcut(KeyEvent.VK_A, false)); + action.addActionListener(cms); + action.setActionCommand("action"); + menu.add(action); + + item = new CheckboxMenuItem("checkbox item", false); + item.setShortcut(new MenuShortcut(KeyEvent.VK_I,false)); + item.addItemListener(cms); + item.addActionListener(cms); + menu.add(item); + + mainMenu.add(menu); + + frame.setMenuBar(mainMenu); + + pane = new TextArea(ctrlA + " -- action menu test\n", 10, 40, TextArea.SCROLLBARS_VERTICAL_ONLY); + Dimension mySize = frame.getSize(); + Insets myIns = frame.getInsets(); + pane.setBounds(new Rectangle(mySize.width - myIns.left - myIns.right, + mySize.height - myIns.top - myIns.bottom)); + pane.setLocation(myIns.left,myIns.top); + frame.add(pane); + + pane.append(ctrlI + " -- item menu test\n"); + + frame.pack(); + return frame; + } + + public void itemStateChanged(ItemEvent evt) { + PassFailJFrame.log("Got item: " + evt.getItem() + "\n"); + } + + public void actionPerformed(ActionEvent evt) { + PassFailJFrame.log("Got action: " + evt.getActionCommand() + "\n"); + } +} diff --git a/test/jdk/java/awt/MenuShortcut/FunctionKeyShortcut.java b/test/jdk/java/awt/MenuShortcut/FunctionKeyShortcut.java new file mode 100644 index 0000000000000..960de08bd2d5a --- /dev/null +++ b/test/jdk/java/awt/MenuShortcut/FunctionKeyShortcut.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4034665 + * @key headful + * @summary Function keys should work correctly as shortcuts + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import static java.awt.event.KeyEvent.VK_CONTROL; +import static java.awt.event.KeyEvent.VK_META; + +public class FunctionKeyShortcut implements ActionListener { + + static volatile Frame frame; + static volatile boolean event = false; + static volatile boolean failed = false; + + static final boolean isMac = System.getProperty("os.name").contains("OS X"); + + static void createUI() { + frame = new Frame("Function Key Menu Shortcut Test"); + MenuBar mb = new MenuBar(); + Menu m = new Menu("Test"); + MenuItem mi1 = new MenuItem("Function key 1", new MenuShortcut(KeyEvent.VK_F1)); + MenuItem mi2 = new MenuItem("Function key 2", new MenuShortcut(KeyEvent.VK_F2)); + MenuItem mi3 = new MenuItem("Function key 3", new MenuShortcut(KeyEvent.VK_F3)); + MenuItem mi4 = new MenuItem("Function key 4", new MenuShortcut(KeyEvent.VK_F4)); + MenuItem mi5 = new MenuItem("Function key 5", new MenuShortcut(KeyEvent.VK_F5)); + MenuItem mi6 = new MenuItem("Function key 6", new MenuShortcut(KeyEvent.VK_F6)); + MenuItem mi7 = new MenuItem("Function key 7", new MenuShortcut(KeyEvent.VK_F7)); + MenuItem mi8 = new MenuItem("Function key 8", new MenuShortcut(KeyEvent.VK_F8)); + MenuItem mi9 = new MenuItem("Function key 8", new MenuShortcut(KeyEvent.VK_F9)); + + FunctionKeyShortcut fks = new FunctionKeyShortcut(); + mi1.addActionListener(fks); + mi2.addActionListener(fks); + mi3.addActionListener(fks); + mi4.addActionListener(fks); + mi5.addActionListener(fks); + mi6.addActionListener(fks); + mi7.addActionListener(fks); + mi8.addActionListener(fks); + mi9.addActionListener(fks); + + m.add(mi1); + m.add(mi2); + m.add(mi3); + m.add(mi4); + m.add(mi5); + m.add(mi6); + m.add(mi7); + m.add(mi8); + m.add(mi9); + + mb.add(m); + frame.setMenuBar(mb); + frame.setBounds(50,400,200,200); + frame.setVisible(true); + } + + public static void main(String[] args ) throws Exception { + + EventQueue.invokeAndWait(FunctionKeyShortcut::createUI); + try { + Robot robot = new Robot(); + + robot.waitForIdle(); + robot.delay(2000); + + // Ensure window has focus + Point p = frame.getLocationOnScreen(); + robot.mouseMove(p.x + frame.getWidth() / 2, p.y + frame.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(2000); + + int mod = (isMac) ? KeyEvent.VK_META : KeyEvent.VK_CONTROL; + robot.keyPress(mod); + robot.keyPress(KeyEvent.VK_F1); + robot.delay(50); + robot.keyRelease(KeyEvent.VK_F1); + robot.keyRelease(mod); + robot.waitForIdle(); + robot.delay(2000); + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + if (!event || failed) { + throw new RuntimeException("No actioncommand"); + } + } + + public void actionPerformed(ActionEvent e) { + System.out.println("Got " + e); + String s = e.getActionCommand(); + event = true; + if (s == null || !s.equals("Function key 1")) { + failed = true; + } + } + +} diff --git a/test/jdk/java/awt/MenuShortcut/MenuItemShortcutReplaceTest.java b/test/jdk/java/awt/MenuShortcut/MenuItemShortcutReplaceTest.java new file mode 100644 index 0000000000000..fe59d1a02ef34 --- /dev/null +++ b/test/jdk/java/awt/MenuShortcut/MenuItemShortcutReplaceTest.java @@ -0,0 +1,111 @@ +/* + * 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 4080225 + * @summary A replaced menu shortcut does not draw in the menu. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MenuItemShortcutReplaceTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; + +/* + * Manual test because visual verification of the shortcut being painted is required. + */ + +public class MenuItemShortcutReplaceTest implements ActionListener { + + static boolean isMac = System.getProperty("os.name").startsWith("Mac"); + static String shortcut = (isMac) ? "Cmd" : "Ctrl"; + static String instructions = + "1. On the frame 'MenuItem Shortcut Replace Test' click on the Menu 'Click here'.\n" + + " You will see a MenuItem 'MenuItem1' with the shortcut key displayed as" + + " '" + shortcut + "+M'.\n" + + "2. Click the 'Change Shortcutkey' button.\n" + + "3. Now click on the Menu again to see the MenuItem.\n" + + "4. If the shortcut key displayed near the MenuItem is changed to " + + "'" + shortcut + "+C', press 'Pass' else press 'Fail'"; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("MenuItem Shortcut Replace Test Instructions") + .instructions(instructions) + .columns(60) + .logArea() + .testUI(MenuItemShortcutReplaceTest::createUI) + .build() + .awaitAndCheck(); + + } + + static volatile Button change; + static volatile MenuItem mi; + static volatile MenuShortcut ms; + + static Frame createUI() { + Frame frame = new Frame("MenuItem Shortcut Replace Test"); + MenuBar mb = new MenuBar(); + change = new Button("Change ShortcutKey"); + Panel p = new Panel(); + p.add(change); + MenuItemShortcutReplaceTest misrt = new MenuItemShortcutReplaceTest(); + change.addActionListener(misrt); + Menu m = new Menu("Click here"); + mb.add(m); + mi = new MenuItem("MenuItem1"); + m.add(mi); + mi.addActionListener(misrt); + frame.setMenuBar(mb); + //Set the shortcut key for the menuitem + ms = new MenuShortcut(KeyEvent.VK_M); + mi.setShortcut(ms); + frame.add(p, BorderLayout.SOUTH); + frame.setSize(300, 300); + return frame; + } + + public void actionPerformed(ActionEvent e) { + //change the shortcut key + if (e.getSource() == change) { + ms = new MenuShortcut(KeyEvent.VK_C); + mi.setShortcut(ms); + PassFailJFrame.log("Shortcut key set to "+shortcut+"C"); + } + if (e.getSource() == mi) { + PassFailJFrame.log("MenuItem Selected"); + } + } +} diff --git a/test/jdk/java/awt/Mouse/DoubleClickTest.java b/test/jdk/java/awt/Mouse/DoubleClickTest.java new file mode 100644 index 0000000000000..037332a40981e --- /dev/null +++ b/test/jdk/java/awt/Mouse/DoubleClickTest.java @@ -0,0 +1,88 @@ +/* + * 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.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Event; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.TextArea; + +/* + * @test + * @bug 4092370 + * @summary Test to verify double click + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DoubleClickTest + */ + +public class DoubleClickTest { + static TextArea ta = new TextArea("", 10, 40); + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Double click on the red area. + 2. Verify that the event reports click_count > 1 on + Double-Click. If click_count shows only 1 for every + Double-Clicks then test FAILS, else test PASS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static Frame initialize() { + Frame frame = new Frame("Double-click Test"); + frame.setLayout(new BorderLayout()); + frame.add("East", new MyPanel(ta)); + frame.add("West", ta); + frame.setSize(200, 200); + return frame; + } +} + +class MyPanel extends Panel { + TextArea ta; + + MyPanel(TextArea ta) { + this.ta = ta; + setBackground(Color.red); + } + + public Dimension getPreferredSize() { + return new Dimension(50, 50); + } + + + public boolean mouseDown(Event event, int x, int y) { + ta.append("event click count= " + event.clickCount + "\n"); + return false; + } +} diff --git a/test/jdk/java/awt/Mouse/MouseClickCount.java b/test/jdk/java/awt/Mouse/MouseClickCount.java new file mode 100644 index 0000000000000..a63d24fcb9ffb --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseClickCount.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +import java.awt.Frame; +import java.awt.TextArea; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4199397 + * @summary Test to mouse click count + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseClickCount + */ + +public class MouseClickCount { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Clicking on Frame panel quickly will produce clickCount larger than 1 + in the TextArea the count is printed for each mouse click + 2. Verify that a left-button click followed by a right button click quickly + will not generate 1, 2, i.e. it's not considered a double clicking. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static Frame initialize() { + Frame f = new Frame("Mouse Click Count Test"); + final TextArea ta = new TextArea(); + f.add("South", ta); + f.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (e.getClickCount() == 1) ta.append("\n1"); + else ta.append(", " + e.getClickCount()); + } + }); + f.setSize(300, 500); + return f; + } +} diff --git a/test/jdk/java/awt/Mouse/MouseDragEnterExitTest.java b/test/jdk/java/awt/Mouse/MouseDragEnterExitTest.java new file mode 100644 index 0000000000000..b2d8e446ffc36 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseDragEnterExitTest.java @@ -0,0 +1,117 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; + +/* + * @test + * @bug 4141361 + * @summary Test to Ensures that mouse enter / exit is delivered to a new + * frame or component during a drag + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseDragEnterExitTest + */ + +public class MouseDragEnterExitTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on the blue frame, drag to the white frame, and back + You should get enter/exit messages for the frames when dragging + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(MouseEvents.initialize()) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class MouseEvents extends Frame { + static int WITH_WIDGET = 0; + + public MouseEvents(int mode) { + super("Mouse Drag Enter/Exit Test"); + setSize(300, 300); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + PassFailJFrame.log("Frame MOUSE_ENTERED" + ": " + " " + + e.getX() + " " + e.getY()); + } + + @Override + public void mouseExited(MouseEvent e) { + PassFailJFrame.log("Frame MOUSE_EXITED" + ": " + " " + + e.getX() + " " + e.getY()); + } + }); + + if (mode == WITH_WIDGET) { + setLayout(new BorderLayout()); + add("Center", new SimplePanel()); + } + } + + public static List initialize() { + MouseEvents m = new MouseEvents(MouseEvents.WITH_WIDGET); + m.setLocation(500, 300); + MouseEvents t = new MouseEvents(MouseEvents.WITH_WIDGET + 1); + t.setLocation(200, 200); + return List.of(m, t); + } +} + +class SimplePanel extends Panel { + public SimplePanel() { + super(); + setName("Test Panel"); + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + PassFailJFrame.log("Panel MOUSE_ENTERED" + ": " + " " + + e.getX() + " " + e.getY()); + } + + @Override + public void mouseExited(MouseEvent e) { + PassFailJFrame.log("Panel MOUSE_EXITED" + ": " + " " + + e.getX() + " " + e.getY()); + } + }); + setSize(100, 100); + setBackground(Color.blue); + } +} + diff --git a/test/jdk/java/awt/Mouse/MouseDragTest.java b/test/jdk/java/awt/Mouse/MouseDragTest.java new file mode 100644 index 0000000000000..cb8be7b0de25b --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseDragTest.java @@ -0,0 +1,215 @@ +/* + * 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. + */ + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseMotionListener; + +/* + * @test + * @bug 4035189 + * @summary Test to verify that Drag events go to wrong component + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseDragTest + */ + +class HeavySquare extends Canvas { + private final Color colorNormal; + private boolean gotADragEvent; + + public HeavySquare(Color color) { + colorNormal = color; + setBackground(colorNormal); + new MouseChecker(this); + addMouseMotionListener(new DragAdapter()); + addMouseListener(new PressReleaseAdapter()); + } + + class DragAdapter extends MouseMotionAdapter { + public void mouseDragged(MouseEvent ev) { + if (gotADragEvent) + return; + + Point mousePt = ev.getPoint(); + Dimension csize = getSize(); + boolean inBounds = + (mousePt.x >= 0 && mousePt.x <= csize.width && + mousePt.y >= 0 && mousePt.y <= csize.height); + if (!inBounds) { + setBackground(Color.green); + } + gotADragEvent = true; + } + } + + class PressReleaseAdapter extends MouseAdapter { + public void mousePressed(MouseEvent ev) { + gotADragEvent = false; + } + + public void mouseReleased(MouseEvent ev) { + setBackground(colorNormal); + } + } + + public Dimension preferredSize() { + return new Dimension(50, 50); + } +} + +class MouseFrame extends Frame { + public MouseFrame() { + super("MouseDragTest"); + new MouseChecker(this); + setLayout(new FlowLayout()); + add(new HeavySquare(Color.red)); + add(new HeavySquare(Color.blue)); + setBounds(new Rectangle(20, 20, 400, 300)); + } +} + +public class MouseDragTest { + static Frame TestFrame; + + public MouseDragTest() { + TestFrame = new MouseFrame(); + } + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. A frame with two boxes will appear. Click and drag _very_ quickly + off one of the components. You will know you were quick enough + when the component you dragged off of turns green + 2. Repeat this several times on both boxes, ensuring you get them + to turn green. The components should revert to their original + color when you release the mouse + 3. The test FAILS if the component doesn't revert to original + color, else PASS. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(new MouseFrame()) + .build() + .awaitAndCheck(); + } +} + +class MouseChecker implements MouseListener, MouseMotionListener { + private boolean isPressed = false; + private MouseEvent evPrev = null; + private MouseEvent evPrevPrev = null; + + public MouseChecker(Component comp) { + comp.addMouseListener(this); + comp.addMouseMotionListener(this); + } + + private void recordEv(MouseEvent ev) { + evPrevPrev = evPrev; + evPrev = ev; + } + + private synchronized void failure(String str) { + PassFailJFrame.forceFail("Test Failed : "+str); + } + + public void mouseClicked(MouseEvent ev) { + if (!(evPrev.getID() == MouseEvent.MOUSE_RELEASED && + evPrevPrev.getID() == MouseEvent.MOUSE_PRESSED)) { + failure("Got mouse click without press/release preceding."); + } + recordEv(ev); + } + + public void mousePressed(MouseEvent ev) { + recordEv(ev); + if (isPressed) { + failure("Got two mouse presses without a release."); + } + isPressed = true; + } + + public void mouseReleased(MouseEvent ev) { + recordEv(ev); + if (!isPressed) { + failure("Got mouse release without being pressed."); + } + isPressed = false; + } + + public void mouseEntered(MouseEvent ev) { + recordEv(ev); + Point mousePt = ev.getPoint(); + Component comp = (Component) ev.getSource(); + Dimension size = comp.getSize(); + boolean inBounds = + (mousePt.x >= 0 && mousePt.x <= size.width && + mousePt.y >= 0 && mousePt.y <= size.height); + + if (!inBounds) { + failure("Got mouse entered, but mouse not inside component."); + } + } + + public void mouseExited(MouseEvent ev) { + recordEv(ev); + Point mousePt = ev.getPoint(); + Component comp = (Component) ev.getSource(); + if (comp instanceof Frame) { + return; + } + Dimension size = comp.getSize(); + boolean isOnChild = (comp != comp.getComponentAt(mousePt)); + boolean inBounds = + (mousePt.x >= 0 && mousePt.x <= size.width && + mousePt.y >= 0 && mousePt.y <= size.height); + if (!isOnChild && inBounds) { + failure("Got mouse exit, but mouse still inside component."); + } + } + + public void mouseDragged(MouseEvent ev) { + recordEv(ev); + if (!isPressed) { + failure("Got drag without a press first."); + } + } + + public void mouseMoved(MouseEvent ev) { + recordEv(ev); + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest.java new file mode 100644 index 0000000000000..059ad5c054829 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest.java @@ -0,0 +1,174 @@ +/* + * 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. + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Robot; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/* + * @test + * @bug 4050138 + * @key headful + * @summary Test to verify Lightweight components don't get + * enter/exit during drags + * @run main MouseEnterExitTest + */ + +class LWSquare extends Container { + int width; + int height; + + public LWSquare(Color color, int w, int h) { + setBackground(color); + setLayout(new FlowLayout()); + width = w; + height = h; + addMouseListener(new EnterExitAdapter(this)); + setName("LWSquare-" + color.toString()); + } + + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getSize().width, getSize().height); + super.paint(g); + } + + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + + public Cursor getCursor() { + return new Cursor(Cursor.CROSSHAIR_CURSOR); + } +} + +class MouseFrame extends Frame { + public LWSquare lw; + + public MouseFrame() { + super("MouseEnterExitTest"); + setLayout(new FlowLayout()); + + lw = new LWSquare(Color.red, 75, 75); + add(lw); + setBounds(50, 50, 300, 200); + setVisible(true); + System.out.println(getInsets()); + + addMouseListener(new EnterExitAdapter(this)); + addWindowListener( + new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + dispose(); + } + } + ); + addKeyListener( + new KeyAdapter() { + public void keyPressed(KeyEvent ev) { + MouseEnterExitTest.getFrame().setTitle("MouseEnterExitTest"); + } + } + ); + } +} + + +public class MouseEnterExitTest { + static MouseFrame testFrame; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> testFrame = new MouseFrame()); + if (testFrame.lw.getBackground() != Color.red) { + throw new RuntimeException("Initial Background color not matching"); + } + robot.waitForIdle(); + robot.delay(100); + EventQueue.invokeAndWait(() -> robot.mouseMove( + testFrame.getLocationOnScreen().x + testFrame.getSize().width / 2, + testFrame.getLocationOnScreen().y + testFrame.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(100); + + if (testFrame.lw.getBackground() != Color.green) { + throw new RuntimeException("Initial Background color not matching"); + } + EventQueue.invokeAndWait(() -> robot.mouseMove( + testFrame.getLocationOnScreen().x + testFrame.getSize().width * 2, + testFrame.getLocationOnScreen().y + testFrame.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(100); + + if (testFrame.lw.getBackground() != Color.red) { + throw new RuntimeException("Initial Background color not matching"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (testFrame != null) { + testFrame.dispose(); + } + }); + } + } + + public static Frame getFrame() { + return testFrame; + } +} + +class EnterExitAdapter extends MouseAdapter { + Component compToColor; + Color colorNormal; + + EnterExitAdapter(Component comp) { + compToColor = comp; + colorNormal = comp.getBackground(); + } + + public void mouseEntered(MouseEvent ev) { + compToColor.setBackground(Color.green); + compToColor.repaint(); + } + + public void mouseExited(MouseEvent ev) { + compToColor.setBackground(colorNormal); + compToColor.repaint(); + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest2.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest2.java new file mode 100644 index 0000000000000..e09ac333447f6 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest2.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/* + * @test + * @bug 4150851 + * @summary Tests enter and exit events when a lightweight component is on a border + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseEnterExitTest2 + */ + +public class MouseEnterExitTest2 { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Verify that white component turns black whenever mouse enters the frame, + except when it enters the red rectangle. + 2. When the mouse enters the red part of the frame the component should stay white. + """; + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(EntryExitTest.initialize()) + .build() + .awaitAndCheck(); + } +} + +class EntryExitTest extends Component { + boolean inWin; + + public Dimension getPreferredSize() { + return new Dimension(200, 150); + } + + public void paint(Graphics g) { + Color c1, c2; + String s; + if (inWin) { + c1 = Color.black; + c2 = Color.white; + s = "IN"; + } else { + c2 = Color.black; + c1 = Color.white; + s = "OUT"; + } + g.setColor(c1); + Rectangle r = getBounds(); + g.fillRect(0, 0, r.width, r.height); + g.setColor(c2); + g.drawString(s, r.width / 2, r.height / 2); + } + + public static Frame initialize() { + EntryExitTest test = new EntryExitTest(); + MouseListener frameEnterExitListener = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + test.inWin = true; + test.repaint(); + } + + public void mouseExited(MouseEvent e) { + test.inWin = false; + test.repaint(); + } + }; + + Frame f = new Frame("Mouse Modifier Test"); + + f.add(test); + Component jc = new Component() { + public Dimension getPreferredSize() { + return new Dimension(100, 50); + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.red); + g.fillRect(0, 0, d.width, d.height); + } + }; + final Container cont = new Container() { + public Dimension getPreferredSize() { + return new Dimension(100, 100); + } + }; + cont.setLayout(new GridLayout(2, 1)); + cont.add(jc); + jc.addMouseListener(new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + //System.out.println("Component entered"); + } + public void mouseExited(MouseEvent e) { + //System.out.println("Component exited"); + } + }); + + f.add(cont, BorderLayout.NORTH); + f.addMouseListener(frameEnterExitListener); + f.pack(); + return f; + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest3.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest3.java new file mode 100644 index 0000000000000..d5096d7acf022 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest3.java @@ -0,0 +1,84 @@ +/* + * 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.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import javax.swing.JButton; + +/* + * @test + * @bug 4431868 + * @summary Tests that hw container doesn't receive mouse enter/exit events when mouse + * is moved between its lw and hw children + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MouseEnterExitTest3 + */ + +public class MouseEnterExitTest3 { + static final Button button = new Button("Button"); + static final JButton jbutton = new JButton("JButton"); + static final Frame frame = new Frame("Mouse Enter/Exit Test"); + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Move the mouse between Button and JButton + 2. Verify that the frame doesn't receive enter/exit events + (Enter/exit events are dumped to the area below) + 4. If you see enter/exit events dumped the test fails + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .logArea(4) + .build() + .awaitAndCheck(); + } + + final static MouseListener listener = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + PassFailJFrame.log(e.toString()); + } + + public void mouseExited(MouseEvent e) { + PassFailJFrame.log(e.toString()); + } + }; + + public static Frame initialize() { + frame.setLayout(new GridLayout(2, 1)); + frame.add(button); + frame.add(jbutton); + frame.addMouseListener(listener); + frame.pack(); + return frame; + } +} diff --git a/test/jdk/java/awt/Mouse/MouseEnterExitTest4.java b/test/jdk/java/awt/Mouse/MouseEnterExitTest4.java new file mode 100644 index 0000000000000..2ee3993ae4ede --- /dev/null +++ b/test/jdk/java/awt/Mouse/MouseEnterExitTest4.java @@ -0,0 +1,96 @@ +/* + * 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.Button; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/* + * @test + * @bug 4431868 + * @key headful + * @summary Tests that window totally obscured by its child doesn't receive + * enter/exit events when located over another frame + * @run main MouseEnterExitTest4 + */ + +public class MouseEnterExitTest4 { + static Button button = new Button("Button"); + static Frame frame = new Frame("Mouse Enter/Exit test"); + static Window window = new Window(frame); + static MouseListener listener = new MouseAdapter() { + public void mouseEntered(MouseEvent e) { + throw new RuntimeException("Test failed due to Mouse Enter event"); + } + + public void mouseExited(MouseEvent e) { + throw new RuntimeException("Test failed due to Mouse Exit event"); + } + }; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + + robot.setAutoDelay(100); + try { + EventQueue.invokeAndWait(() -> { + button.setBackground(Color.red); + window.add(button); + frame.setBounds(100, 100, 300, 300); + window.setBounds(200, 200, 100, 100); + window.addMouseListener(listener); + window.setVisible(true); + frame.setVisible(true); + }); + robot.waitForIdle(); + robot.delay(200); + EventQueue.invokeAndWait(() -> robot.mouseMove( + frame.getLocationOnScreen().x + frame.getSize().width / 2, + frame.getLocationOnScreen().y + frame.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(200); + EventQueue.invokeAndWait(() -> robot.mouseMove( + window.getLocationOnScreen().x + window.getSize().width * 2, + window.getLocationOnScreen().y + window.getSize().height / 2)); + robot.waitForIdle(); + robot.delay(500); + System.out.println("Test Passed"); + + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + if (window != null) { + window.dispose(); + } + }); + } + } +} diff --git a/test/jdk/java/awt/Mouse/MousePressedTest.java b/test/jdk/java/awt/Mouse/MousePressedTest.java new file mode 100644 index 0000000000000..721d69bd5ddf0 --- /dev/null +++ b/test/jdk/java/awt/Mouse/MousePressedTest.java @@ -0,0 +1,87 @@ +/* + * 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. + */ + +import java.awt.Container; +import java.awt.GridLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; + +/* + * @test + * @bug 4268759 + * @summary Tests whether clicking on the edge of a lightweight button + * causes sticking behavior + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MousePressedTest + */ + +public class MousePressedTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. Click and hold on the very bottom border (2-pixel-wide border) of the + JButton. Then drag the mouse straight down out of the JButton and + into the JRadioButton, and release the mouse button + 2. If the component remains highlighted as if the mouse button is still + down, the test fails + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + public static JFrame initialize() { + JFrame f = new JFrame("JButton Test"); + JPanel p = new JPanel(); + p.setLayout(new GridLayout(2, 2)); + JButton b = new JButton("JButton"); + p.add(b); + JCheckBox cb = new JCheckBox("JCheckBox"); + p.add(cb); + JRadioButton rb = new JRadioButton("JRadioButton"); + p.add(rb); + p.add(new JToggleButton("JToggleButton")); + + JScrollPane j = new JScrollPane(p, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + + Container c = f.getContentPane(); + c.setLayout(new GridLayout(1, 1)); + c.add(j); + f.pack(); + return f; + } +} diff --git a/test/jdk/java/awt/MouseInfo/ContainerResizeMousePositionTest.java b/test/jdk/java/awt/MouseInfo/ContainerResizeMousePositionTest.java new file mode 100644 index 0000000000000..72aa4d93a5c71 --- /dev/null +++ b/test/jdk/java/awt/MouseInfo/ContainerResizeMousePositionTest.java @@ -0,0 +1,141 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/* + * @test + * @key headful + * @bug 4009555 + * @summary Unit test for a new method in Container class: getMousePosition(boolean) + * while Container resized. + */ + +public class ContainerResizeMousePositionTest { + private static Frame frame; + private static Button button; + private static Robot robot; + private static volatile Point frameLocation; + private static volatile Point newLoc; + private static boolean testSucceeded = false; + + private static final CountDownLatch eventCaught = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + EventQueue.invokeAndWait(() -> createAndShowUI()); + robot.waitForIdle(); + robot.delay(1000); + testUI(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("Testing getMousePosition() after resize"); + button = new Button("Button"); + frame.setLayout(new BorderLayout()); + frame.add(button); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void testUI() throws Exception { + EventQueue.invokeAndWait(() -> { + frameLocation = frame.getLocationOnScreen(); + newLoc = new Point(frame.getWidth() + 10, frame.getHeight() + 10); + }); + + robot.mouseMove(frameLocation.x + newLoc.x, frameLocation.y + newLoc.y); + EventQueue.invokeAndWait(() -> { + button.addComponentListener(new ResizeAdapter()); + frame.setSize(frame.getWidth() * 2, frame.getHeight() * 2); + frame.validate(); + }); + robot.waitForIdle(); + robot.delay(500); + + if (!eventCaught.await(2, TimeUnit.SECONDS)) { + throw new RuntimeException("componentResized Event isn't" + + " received within a timeout"); + } + + if (!testSucceeded) { + throw new RuntimeException("Container.getMousePosition(boolean)" + + " returned incorrect result while Container resized"); + } + } + + static class ResizeAdapter extends ComponentAdapter { + int testStageCounter = 0; + @Override + public void componentResized(ComponentEvent e) { + Point pTrue = frame.getMousePosition(true); + if (frame.getMousePosition(false) == null) { + testStageCounter++; + System.out.println(""" + TEST STAGE 1 PASSED: + Container.getMousePosition(false) + returned NULL over Child Component + during resize. + """); + } + if (pTrue != null) { + testStageCounter++; + System.out.println(""" + TEST STAGE 2 PASSED: + Container.getMousePosition(true) + returned NON-NULL over Child Component + during resize. + """); + } + if (pTrue != null && pTrue.x == newLoc.x && pTrue.y == newLoc.y) { + testStageCounter++; + System.out.println(""" + TEST STAGE 3 PASSED: + Container.getMousePosition(true) + returned correct result over Child Component + during resize. + """); + } + testSucceeded = testStageCounter == 3; + eventCaught.countDown(); + } + } +} diff --git a/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java b/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java new file mode 100644 index 0000000000000..da8de3947be9a --- /dev/null +++ b/test/jdk/java/awt/Panel/PanelRepaint/PanelRepaint.java @@ -0,0 +1,455 @@ +/* + * 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 4148078 + * @summary Repainting problems in scrolled panel + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual PanelRepaint + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Scrollbar; +import java.awt.TextField; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +public class PanelRepaint extends Panel implements FocusListener { + static ScrollPanel sPanel; + static Panel panel; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Using scrollbars or tab keys to scroll the panel and + the panel is messy sometimes, e.g. one row bumps into + another. If all components painted correctly, the test passes. + Otherwise, the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(PanelRepaint::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("Panel Repaint Test"); + f.setLayout(new FlowLayout()); + f.setSize(620, 288); + PanelRepaint pr = new PanelRepaint(); + + panel = new Panel(); + panel.setLayout(null); + panel.setSize(500, 500); + sPanel = new ScrollPanel(panel); + + Button btn = new Button("Open"); + pr.addComp(btn); + btn.setBounds(400, 10, 60, 20); + btn.setActionCommand("OPEN"); + + Button btn1 = new Button("Close"); + pr.addComp(btn1); + btn1.setBounds(400, 50, 60, 20); + btn1.setActionCommand("CLOSE"); + + TextField t1 = new TextField("1"); + pr.addComp(t1); + t1.setBounds(10, 10, 100, 20); + TextField t2 = new TextField("2"); + pr.addComp(t2); + t2.setBounds(10, 50, 100, 20); + TextField t3 = new TextField("3"); + pr.addComp(t3); + t3.setBounds(10, 90, 100, 20); + TextField t4 = new TextField("4"); + pr.addComp(t4); + t4.setBounds(10, 130, 100, 20); + TextField t5 = new TextField("5"); + pr.addComp(t5); + t5.setBounds(10, 170, 100, 20); + TextField t6 = new TextField("6"); + pr.addComp(t6); + t6.setBounds(10, 210, 100, 20); + TextField t7 = new TextField("7"); + pr.addComp(t7); + t7.setBounds(10, 250, 100, 20); + TextField t8 = new TextField("8"); + pr.addComp(t8); + t8.setBounds(10, 290, 100, 20); + TextField t9 = new TextField("9"); + pr.addComp(t9); + t9.setBounds(10, 330, 100, 20); + + TextField t11 = new TextField("1"); + pr.addComp(t11); + t11.setBounds(120, 10, 100, 20); + TextField t12 = new TextField("2"); + pr.addComp(t12); + t12.setBounds(120, 50, 100, 20); + TextField t13 = new TextField("3"); + pr.addComp(t13); + t13.setBounds(120, 90, 100, 20); + TextField t14 = new TextField("4"); + pr.addComp(t14); + t14.setBounds(120, 130, 100, 20); + TextField t15 = new TextField("5"); + pr.addComp(t15); + t15.setBounds(120, 170, 100, 20); + TextField t16 = new TextField("6"); + pr.addComp(t16); + t16.setBounds(120, 210, 100, 20); + TextField t17 = new TextField("7"); + pr.addComp(t17); + t17.setBounds(120, 250, 100, 20); + TextField t18 = new TextField("8"); + pr.addComp(t18); + t18.setBounds(120, 290, 100, 20); + TextField t19 = new TextField("9"); + pr.addComp(t19); + t19.setBounds(120, 330, 100, 20); + + + TextField t21 = new TextField("1"); + pr.addComp(t21); + t21.setBounds(240, 10, 100, 20); + TextField t22 = new TextField("2"); + pr.addComp(t22); + t22.setBounds(240, 50, 100, 20); + TextField t23 = new TextField("3"); + pr.addComp(t23); + t23.setBounds(240, 90, 100, 20); + TextField t24 = new TextField("4"); + pr.addComp(t24); + t24.setBounds(240, 130, 100, 20); + TextField t25 = new TextField("5"); + pr.addComp(t25); + t25.setBounds(240, 170, 100, 20); + TextField t26 = new TextField("6"); + pr.addComp(t26); + t26.setBounds(240, 210, 100, 20); + TextField t27 = new TextField("7"); + pr.addComp(t27); + t27.setBounds(240, 250, 100, 20); + TextField t28 = new TextField("8"); + pr.addComp(t28); + t28.setBounds(240, 290, 100, 20); + TextField t29 = new TextField("9"); + pr.addComp(t29); + t29.setBounds(240, 330, 100, 20); + + pr.add(sPanel); + f.add(pr); + sPanel.setBounds(100, 100, 500, 250); + sPanel.doLayout(); + return f; + } + + public void addComp(Component c) { + panel.add(c); + c.addFocusListener(this); + } + + public void focusGained(FocusEvent e) { + sPanel.showComponent(e.getComponent()); + } + + public void focusLost(FocusEvent e) { + } +} + +class ScrollPanel extends Panel implements AdjustmentListener { + /** + * Constructor + */ + public ScrollPanel(Component c) { + setLayout(null); + setBackground(Color.lightGray); + add(hScroll = new Scrollbar(Scrollbar.HORIZONTAL)); + add(vScroll = new Scrollbar(Scrollbar.VERTICAL)); + add(square = new Panel()); + square.setBackground(Color.lightGray); + add(c); + } + + /** + * Scroll up/down/left/right to show the component specified + * + * @param comp is the component to be shown + */ + public void showComponent(Component comp) { + Component view = getComponent(3); + Rectangle viewRect = view.getBounds(); + Rectangle scrollRect = getBounds(); + Rectangle rect = comp.getBounds(); + while (comp != null) { + Component parent = comp.getParent(); + if (parent == null || parent == view) { + break; + } + Point p = parent.getLocation(); + rect.x += p.x; + rect.y += p.y; + comp = parent; + } + + int i = viewRect.y + rect.y; + int j = (viewRect.y + rect.y + rect.height + ScrollPanel.H_HEIGHT) + - (scrollRect.height); + + if (i < 0) { + vertUpdate(i); + } else if (j > 0) { + vertUpdate(j); + } + + i = viewRect.x + rect.x; + j = (viewRect.x + rect.x + rect.width + (V_WIDTH * 2)) - (scrollRect.width); + + if (i < 0) { + horzUpdate(i); + } else if (j > 0) { + horzUpdate(j); + } + } + + /** + * Returns the panel component of ScrollPanel + * + * @return the panel component of ScrollPanel + */ + public Component getScrolled() { + return getComponent(3); + } + + /** + * updates the scroll panel vertically with value i passed + * + * @param i the value to be updated with + */ + public void vertUpdate(int i) { + update(true, vScroll.getValue() + i); + } + + /** + * updates the scroll panel horizontally with value i passed + * + * @param i the value to be updated with + */ + public void horzUpdate(int i) { + update(false, hScroll.getValue() + i); + } + + /** + * updates the scroll panel vertically if bVert is true else horizontally + * + * @param n is the value + */ + public void update(boolean bVert, int n) { + if (n < 0) n = 0; + if (bVert) { + if (n > max.height) { + n = max.height; + } + if (offset.y != n) { + offset.y = n; + vScroll.setValue(n); + } + } else { + if (n > max.width) { + n = max.width; + } + if (offset.x != n) { + offset.x = n; + hScroll.setValue(n); + } + } + getScrolled().setLocation(-offset.x, -offset.y); + } + + /** + * Implementation of AdjustmentListener + */ + public void adjustmentValueChanged(AdjustmentEvent e) { + boolean bVert = e.getSource() == vScroll; + update(bVert, e.getValue()); + } + + /** + * Reimplementation of Component Methods + */ + public void addNotify() { + super.addNotify(); + vScroll.addAdjustmentListener(this); + hScroll.addAdjustmentListener(this); + } + + public void removeNotify() { + super.removeNotify(); + vScroll.removeAdjustmentListener(this); + hScroll.removeAdjustmentListener(this); + } + + public void setBounds(int x, int y, int w, int h) { + super.setBounds(x, y, w, h); + doLayout(); + } + + public void doLayout() { + Component c = getScrolled(); + Dimension d = c.getSize(); + if (d.width == 0 || d.height == 0) { + d = c.getPreferredSize(); + } + vert = 0; + horz = 0; + Dimension m = getSize(); + if (d.height > m.height || isScroll(true, m.height - horz, 0, d.height)) { + vert = V_WIDTH; + } + if (d.width + vert > m.width || isScroll(false, m.width - vert, 0, d.width)) { + horz = H_HEIGHT; + } + if (d.height + horz > m.height || isScroll(true, m.height - horz, 0, d.height)) { + vert = V_WIDTH; + } + if (d.width + vert > m.width || isScroll(false, m.width - vert, 0, d.width)) { + horz = H_HEIGHT; + } + if (horz != 0) { + if (m.width <= 0) { + m.width = 1; + } + hScroll.setBounds(0, m.height - H_HEIGHT, m.width - vert, H_HEIGHT); + hScroll.setValues(offset.x, m.width - vert, 0, d.width); + int i = d.width / 10; + if (i < 2) { + i = 2; + } + hScroll.setBlockIncrement(i); + i = d.width / 50; + if (i < 1) { + i = 1; + } + hScroll.setUnitIncrement(i); + max.width = d.width; + hScroll.setVisible(true); + } else { + offset.x = 0; + } + if (vert != 0) { + if (m.height <= 0) { + m.height = 1; + } + vScroll.setBounds(m.width - V_WIDTH, 0, V_WIDTH, m.height - horz); + vScroll.setValues(offset.y, m.height - horz, 0, d.height); + int i = d.height / 10; + if (i < 2) i = 2; + vScroll.setBlockIncrement(i); + i = d.height / 50; + if (i < 1) i = 1; + vScroll.setUnitIncrement(i); + max.height = d.height; + vScroll.setVisible(true); + } else { + offset.y = 0; + } + if (horz != 0 && vert != 0) { + square.setBounds(m.width - V_WIDTH, m.height - H_HEIGHT, V_WIDTH, H_HEIGHT); + square.setVisible(true); + } else { + square.setVisible(false); + } + c.setBounds(-offset.x, -offset.y, d.width, d.height); + c.repaint(); + updateScroll(true, offset.y); + updateScroll(false, offset.x); + } + + public Dimension getPreferredSize() { + return getScrolled().getPreferredSize(); + } + + public Dimension getMinimumSize() { + return getScrolled().getMinimumSize(); + } + + boolean isScroll(boolean bVert, int visible, int min, int max) { + int tot = max - min; + int net = tot - visible; + if (net <= 0) { + return false; + } + return true; + } + + void updateScroll(boolean bVert, int n) { + Component c = getScrolled(); + Dimension d = c.getSize(); + Dimension m = getSize(); + m.width -= vert; + m.height -= horz; + if (bVert) { + if (n >= 0 && d.height > m.height) { + if (n + m.height > d.height) + n = d.height - m.height; + } else + n = 0; + update(true, n); + } else { + if (n >= 0 && d.width > m.width) { + if (n + m.width > d.width) + n = d.width - m.width; + } else + n = 0; + update(false, n); + } + } + + static Scrollbar hScroll; + static Scrollbar vScroll; + static int vert = 0; + static int horz = 0; + + static Point offset = new Point(); + static Dimension max = new Dimension(); + // ScrollTimer timer; + static Component square; + final static int V_WIDTH = 17; + final static int H_HEIGHT = 17; +} diff --git a/test/jdk/java/awt/PopupMenu/ActivePopupCrashTest.java b/test/jdk/java/awt/PopupMenu/ActivePopupCrashTest.java new file mode 100644 index 0000000000000..51c12964e6295 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/ActivePopupCrashTest.java @@ -0,0 +1,220 @@ +/* + * 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. + */ + +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.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Robot; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import java.util.Hashtable; + +/* + * @test + * @bug 4214550 + * @summary Tests that there is no seg fault on repeatedly showing + * PopupMenu by right-clicking Label, Panel or Button + * @key headful + * @run main ActivePopupCrashTest + */ + +public class ActivePopupCrashTest { + private static Frame f; + private static Label l; + private static Button b; + private static Panel p; + + private static volatile Point labelCenter; + private static volatile Point buttonCenter; + private static volatile Point panelCenter; + + public static void main(String[] args) throws Exception { + final int REPEAT_COUNT = 5; + try { + Robot robot = new Robot(); + robot.setAutoDelay(50); + EventQueue.invokeAndWait(ActivePopupCrashTest::createAndShowUI); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + labelCenter = getCenterPoint(l); + buttonCenter = getCenterPoint(b); + panelCenter = getCenterPoint(p); + }); + + for (int i = 0; i < REPEAT_COUNT; i++) { + robot.mouseMove(labelCenter.x, labelCenter.y); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(buttonCenter.x, buttonCenter.y); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(panelCenter.x, panelCenter.y); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + } + + // To close the popup, otherwise test fails on windows with timeout error + robot.mouseMove(panelCenter.x - 5, panelCenter.y - 5); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private static Point getCenterPoint(Component component) { + Point p = component.getLocationOnScreen(); + Dimension size = component.getSize(); + return new Point(p.x + size.width / 2, p.y + size.height / 2); + } + + public static void createAndShowUI() { + f = new Frame("ActivePopupCrashTest Test"); + MenuItem item = new MenuItem("file-1"); + item.addActionListener(ActivePopupCrashTest::logActionEvent); + Menu m = new Menu("file"); + m.add(item); + item = new MenuItem("file-2"); + m.add(item); + MenuBar mb = new MenuBar(); + mb.add(m); + + f.setMenuBar(mb); + f.setSize(200, 200); + f.setLayout(new BorderLayout()); + + l = new Label("label"); + addPopup(l, "label"); + f.add(l, BorderLayout.NORTH); + + p = new Panel(); + addPopup(p, "panel"); + f.add(p, BorderLayout.CENTER); + + b = new Button("button"); + addPopup(b, "button"); + f.add(b, BorderLayout.SOUTH); + + f.setSize(400, 300); + f.setLocationRelativeTo(null); + f.setVisible(true); + } + + static void addPopup(Component c, String name) { + PopupMenu pm = new PopupMenu(); + MenuItem mi = new MenuItem(name + "-1"); + mi.addActionListener(ActivePopupCrashTest::logActionEvent); + pm.add(mi); + + mi = new MenuItem(name + "-2"); + pm.add(mi); + + setHash(c, pm); + c.add(pm); + c.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + mouseAction("mouseClicked", e); + } + + @Override + public void mousePressed(MouseEvent e) { + mouseAction("mousePressed", e); + } + + @Override + public void mouseReleased(MouseEvent e) { + mouseAction("mouseReleased", e); + } + }); + } + + static void logActionEvent(ActionEvent e) { + System.out.println("actionPerformed, event=" + e + ", mod=" + getMods(e)); + System.out.println("command=" + e.getActionCommand()); + System.out.println("param=" + e.paramString()); + System.out.println("source=" + e.getSource()); + } + + static String getMods(ActionEvent e) { return getMods(e.getModifiers()); } + + static String getMods(MouseEvent e) { return getMods(e.getModifiers()); } + + static String getMods(int mods) { + String modstr = ""; + if ((mods & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK) { + modstr += (" SHIFT"); + } else if ((mods & ActionEvent.ALT_MASK) == ActionEvent.ALT_MASK) { + modstr += (" ALT"); + } else if ((mods & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) { + modstr += (" CTRL"); + } else if ((mods & ActionEvent.META_MASK) == ActionEvent.META_MASK) { + modstr += (" META"); + } + return modstr; + } + + static void mouseAction(String which, MouseEvent e) { + Component c = e.getComponent(); + System.out.println(which + " e = " + e + " , mods = " + getMods(e) + + " , component = " + c); + if (e.isPopupTrigger()) { + System.out.println("isPopup"); + PopupMenu pm = getHash(c); + pm.show(c, c.getWidth() / 2, c.getHeight() / 2); + } + } + + static Hashtable popupTable = new Hashtable<>(); + + static void setHash(Component c, PopupMenu p) { + popupTable.put(c, p); + } + + static PopupMenu getHash(Component c) { + return popupTable.get(c); + } + +} diff --git a/test/jdk/java/awt/PopupMenu/KeyTraversalCrash.java b/test/jdk/java/awt/PopupMenu/KeyTraversalCrash.java new file mode 100644 index 0000000000000..4d1d4e8f8319b --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/KeyTraversalCrash.java @@ -0,0 +1,126 @@ +/* + * 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 + * 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.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuItem; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Robot; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 5021183 + * @summary Tests Key Traversal doesn't crash PopupMenu + * @key headful + * @run main KeyTraversalCrash + */ + +public class KeyTraversalCrash { + private static Frame f; + private static Label label; + + private static volatile Point loc; + private static volatile Dimension dim; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(100); + EventQueue.invokeAndWait(KeyTraversalCrash::createAndShowUI); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + loc = label.getLocationOnScreen(); + dim = label.getSize(); + }); + + robot.mouseMove(loc.x + 20, loc.y + 20); + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(loc.x + 25, loc.y + 25); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + robot.keyPress(KeyEvent.VK_LEFT); + robot.keyRelease(KeyEvent.VK_LEFT); + + robot.keyPress(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_DOWN); + + // To close the popup, otherwise test fails on windows with timeout error + robot.mouseMove(loc.x + dim.width - 20, loc.y + dim.height - 20); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + f = new Frame("KeyTraversalCrash Test"); + final PopupMenu popup = new PopupMenu(); + for (int i = 0; i < 10; i++) { + Menu menu = new Menu("Menu " + i); + for(int j = 0; j < 10; j++) { + MenuItem menuItem = new MenuItem("MenuItem " + j); + menu.add(menuItem); + } + popup.add(menu); + } + label = new Label("Label"); + f.add(label); + f.add(popup); + label.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent me) { + if (me.isPopupTrigger()) { + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent me) { + if (me.isPopupTrigger()) { + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + }); + f.setSize(200, 200); + f.setLocationRelativeTo(null); + f.setVisible(true); + } +} diff --git a/test/jdk/java/awt/PopupMenu/MultiplePopupMenusTest.java b/test/jdk/java/awt/PopupMenu/MultiplePopupMenusTest.java new file mode 100644 index 0000000000000..f939186ca072e --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/MultiplePopupMenusTest.java @@ -0,0 +1,86 @@ +/* + * 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.AWTEvent; +import java.awt.Button; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4186663 4265525 + * @summary Tests that multiple PopupMenus cannot appear at the same time + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultiplePopupMenusTest + */ + +public class MultiplePopupMenusTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click the right mouse button on the button + If multiple popups appear at the same time the + test fails else passes. + """; + + PassFailJFrame.builder() + .title("MultiplePopupMenusTest Instruction") + .instructions(INSTRUCTIONS) + .columns(30) + .testUI(MultiplePopupMenusTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame fr = new Frame("MultiplePopupMenusTest Test"); + TestButton button = new TestButton("button"); + fr.add(button); + fr.setSize(200, 200); + return fr; + } + + static class TestButton extends Button { + public TestButton(String title) { + super(title); + enableEvents(AWTEvent.MOUSE_EVENT_MASK); + } + + @Override + public void processMouseEvent(MouseEvent e) { + if (e.isPopupTrigger()) { + for (int i = 0; i < 10; i++) { + PopupMenu pm = new PopupMenu("Popup " + i); + pm.add(new MenuItem("item 1")); + pm.add(new MenuItem("item 2")); + add(pm); + pm.show(this, e.getX() + i * 5, e.getY() + i * 5); + } + } + super.processMouseEvent(e); + } + } +} diff --git a/test/jdk/java/awt/PopupMenu/PeripheryOfScreen.java b/test/jdk/java/awt/PopupMenu/PeripheryOfScreen.java new file mode 100644 index 0000000000000..b169a7d9692a7 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PeripheryOfScreen.java @@ -0,0 +1,82 @@ +/* + * 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 + * 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.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 6267162 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Popup Menu gets hidden below the screen when opened near the periphery + * of the screen, XToolkit Test if popup menu window is adjusted on screen + * when trying to show outside + * @run main/manual PeripheryOfScreen + */ + +public class PeripheryOfScreen { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Click on the button to show popup menu in the center of + frame. Move frame beyond the edge of screen and click on + button to show the popup menu and see if popup menu is + adjusted to the edge. + + Press Pass if popup menu behaves as per instruction, otherwise + press Fail. + """; + + PassFailJFrame.builder() + .title("PeripheryOfScreen Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PeripheryOfScreen::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI () { + Frame f = new Frame("PeripheryOfScreen Test frame"); + Button b = new Button("Click to show popup menu"); + PopupMenu pm = new PopupMenu("Test menu"); + MenuItem i = new MenuItem("Click me"); + pm.add(i); + b.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + pm.show(f, 100, 100); + } + }); + f.add(b); + f.add(pm); + f.setSize(300, 200); + f.toFront(); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupLeadingSeparatorTest.java b/test/jdk/java/awt/PopupMenu/PopupLeadingSeparatorTest.java new file mode 100644 index 0000000000000..c9274e9b807f0 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupLeadingSeparatorTest.java @@ -0,0 +1,81 @@ +/* + * 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. + */ + +import java.awt.Component; +import java.awt.Frame; +import java.awt.Font; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4169155 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Popup menus get a leading separator on Motif system + * @run main/manual PopupLeadingSeparatorTest + */ + +public class PopupLeadingSeparatorTest { + public static void main(String[] args) throws Exception { + PopupLeadingSeparatorTest obj = new PopupLeadingSeparatorTest(); + String INSTRUCTIONS = """ + Press mouse button on the frame. Popup menu without leading + separator should appear. + If a PopupMenu behaves same, press Pass, else press Fail. + """; + + PassFailJFrame.builder() + .title("PopupLeadingSeparatorTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupLeadingSeparatorTest Test"); + PopupMenu popupMenu = new PopupMenu("Popup Menu Title"); + popupMenu.add(new MenuItem("Item1")); + PopupMenu cascadeMenu = new PopupMenu("Multifont menu"); + cascadeMenu.add(new MenuItem("Item1")); + MenuItem item2 = new MenuItem("Item2"); + item2.setFont(new Font("Serif", Font.BOLD, 36)); + cascadeMenu.add(item2); + + popupMenu.add(cascadeMenu); + f.add(popupMenu); + f.setSize(300, 150); + f.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent evt) { + popupMenu.show((Component) evt.getSource(), evt.getX(), evt.getY()); + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupMenuCrash.java b/test/jdk/java/awt/PopupMenu/PopupMenuCrash.java new file mode 100644 index 0000000000000..7ba738b21d276 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupMenuCrash.java @@ -0,0 +1,106 @@ +/* + * 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. + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4281273 + * @summary PopupMenu crashed in Java. Simplified testcase. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "windows") + * @run main/manual PopupMenuCrash + */ + +public class PopupMenuCrash { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This tests a windows specific problem. + When you see a frame titled "PopupMenuCrash Test", right-click on it + several times for a few seconds. Then wait about 10 seconds before the + PopupMenus start to appear. Then dispose them one by one by clicking on them. + When PopupMenus do not appear anymore, press Pass. + In case of a failure, you'll see a crash. + """; + + PassFailJFrame.builder() + .title("PopupMenuCrash Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(PopupMenuCrash::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + final Frame f = new Frame("PopupMenuCrash Test"); + f.setLayout(new FlowLayout()); + f.add(new Label("Press right mouse button inside this frame.")); + f.add(new Label("A pop-up menu should appear.")); + f.addMouseListener(new MouseAdapter() { + PopupMenu popup; + boolean firstPress = true; + + @Override + public void mousePressed(MouseEvent evt) { + if (firstPress) { + firstPress = false; + try { + Thread.sleep(10000); + } catch (InterruptedException ignored) { + } + } + + if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0) { + popup = new PopupMenu("Popup Menu Title"); + MenuItem mi = new MenuItem("MenuItem"); + popup.add(mi); + f.add(popup); + popup.show(evt.getComponent(), evt.getX(), evt.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent evt) { + if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0) { + if (popup != null) { + f.remove(popup); + popup = null; + } + } + } + }); + + f.setSize(400, 350); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupMenuShowTest.java b/test/jdk/java/awt/PopupMenu/PopupMenuShowTest.java new file mode 100644 index 0000000000000..b36fdc5d19c22 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupMenuShowTest.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4168006 4196790 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Popup menu test fails on x86/Solaris 2.6 combination. + * @run main/manual PopupMenuShowTest + */ + +public class PopupMenuShowTest { + public static void main(String[] args) throws Exception { + PopupMenuShowTest obj = new PopupMenuShowTest(); + String INSTRUCTIONS = """ + Press the right mouse button in the PopupTest window. + If a PopupMenu appears, press Pass, else press Fail. + """; + + PassFailJFrame.builder() + .title("PopupMenuShowTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupMenuShowTest Test"); + f.setLayout(new FlowLayout()); + f.add(new Label("Press right mouse button inside this frame.")); + f.add(new Label("A pop-up menu should appear.")); + PopupMenu popupMenu = new PopupMenu("Popup Menu Title"); + MenuItem mi = new MenuItem("Menu Item"); + popupMenu.add(mi); + f.add(popupMenu); + f.setSize(400, 350); + f.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + popupMenu.show(e.getComponent(), e.getX(), e.getY()); + } + }); + return f; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupMenuWithMenuBar.java b/test/jdk/java/awt/PopupMenu/PopupMenuWithMenuBar.java new file mode 100644 index 0000000000000..1a927e29f84bc --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupMenuWithMenuBar.java @@ -0,0 +1,103 @@ +/* + * 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.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4038140 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Test for functionality of PopupMenuWithMenuBar + * @run main/manual PopupMenuWithMenuBar + */ + +public class PopupMenuWithMenuBar { + public static void main(String[] args) throws Exception { + PopupMenuWithMenuBar obj = new PopupMenuWithMenuBar(); + String INSTRUCTIONS = """ + There was a bug that prevented the popup menu from appearing properly + (if even at all) for a frame window when there is also a menu bar. + + Right click inside the frame window to display the popup window. If + the popup menu appears normally, then the test is successful and the + bug has been fixed."""; + + PassFailJFrame.builder() + .title("PopupMenuWithMenuBar Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupMenuWithMenuBar Test"); + f.setBounds(10, 10, 300, 250); + MenuBar menuBar = new MenuBar(); + Menu fileMenu = createFileMenu(); + menuBar.add(fileMenu); + f.setMenuBar(menuBar); + PopupMenu popupMenu = createPopupMenu(); + f.add(popupMenu); + f.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + popupMenu.show(f, e.getX(), e.getY()); + } + }); + return f; + } + + private Menu createFileMenu() { + String[] menu1Labels = new String[] + {"Save As", "Save As", "Quit"}; + MenuItem menuItem; + Menu returnMenu = new Menu("File"); + for (int menu1Index = 0; menu1Index < menu1Labels.length; menu1Index++) { + menuItem = new MenuItem(menu1Labels[menu1Index]); + returnMenu.add(menuItem); + } + return returnMenu; + } + + private PopupMenu createPopupMenu() { + String[] popupLabels = new String[] + {"Popup 1", "Popup 2", "Quit"}; + MenuItem menuItem; + PopupMenu returnMenu = new PopupMenu("Popups"); + for (int popupIndex = 0; popupIndex < popupLabels.length; popupIndex++) { + menuItem = new MenuItem(popupLabels[popupIndex]); + returnMenu.add(menuItem); + } + return returnMenu; + } +} diff --git a/test/jdk/java/awt/PopupMenu/PopupOnButton.java b/test/jdk/java/awt/PopupMenu/PopupOnButton.java new file mode 100644 index 0000000000000..581714bf76a85 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/PopupOnButton.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.PopupMenu; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4181790 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests a popup menu on a button. + * @run main/manual PopupOnButton + */ + +public class PopupOnButton { + public static void main(String[] args) throws Exception { + PopupOnButton obj = new PopupOnButton(); + String INSTRUCTIONS = """ + Right-click on the button. + Popup Menu should appear and behave fine. + If a PopupMenu appears, press Pass, else press Fail. + """; + + PassFailJFrame.builder() + .title("PopupOnButton Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(obj::createUI) + .build() + .awaitAndCheck(); + } + + private Frame createUI() { + Frame f = new Frame("PopupOnButton Test"); + Button b = new Button("button with popup menu"); + PopupMenu m = new PopupMenu("popup"); + m.add(new MenuItem("item1")); + m.add(new MenuItem("item2")); + m.add(new MenuItem("item3")); + b.add(m); + b.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + m.show((Component) e.getSource(), e.getX(), e.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + m.show((Component) e.getSource(), e.getX(), e.getY()); + } + } + }); + + f.add(b); + f.setSize(200, 150); + return f; + } + } diff --git a/test/jdk/java/awt/PopupMenu/StressTest.java b/test/jdk/java/awt/PopupMenu/StressTest.java new file mode 100644 index 0000000000000..221aa252aa0b7 --- /dev/null +++ b/test/jdk/java/awt/PopupMenu/StressTest.java @@ -0,0 +1,142 @@ +/* + * 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.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Robot; + +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/* + * @test + * @bug 4083400 + * @key headful + * @summary Tests that excessive popping up and down does not crash or + * throw an exception. + * @run main StressTest + */ + +public class StressTest { + private static Frame fr; + private static PopupTestPanel panel; + + private static volatile Point panelCenter; + + public static void main(String[] args) throws Exception { + final int REPEAT_COUNT = 5; + try { + Robot robot = new Robot(); + robot.setAutoDelay(50); + EventQueue.invokeAndWait(StressTest::createAndShowUI); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point loc = panel.getLocationOnScreen(); + Dimension dim = panel.getSize(); + panelCenter = new Point(loc.x + dim.width / 2, loc.y + dim.height / 2); + }); + + for (int i = 0; i < REPEAT_COUNT; i++) { + robot.mouseMove(panelCenter.x + i * 2, panelCenter.y + i * 2); + + robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); + + robot.mouseMove(panelCenter.x - i * 2, panelCenter.y - i * 2); + + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (fr != null) { + fr.dispose(); + } + }); + } + } + + public static void createAndShowUI() { + fr = new Frame("PopupMenu Test"); + panel = new PopupTestPanel(); + fr.add(panel); + fr.setSize(300, 200); + fr.setVisible(true); + } + + static class PopupTestPanel extends Panel { + + static class Item extends MenuItem { + public Item(String text) { + super(text); + } + + public boolean isEnabled() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + return super.isEnabled(); + } + } + + final PopupMenu popup; + + public PopupTestPanel() { + popup = new PopupMenu(); + popup.add(new Item("Soap")); + popup.add(new Item("Sponge")); + popup.add(new Item("Flannel")); + popup.add(new Item("Mat")); + popup.add(new Item("Towel")); + add(popup); + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopup(e); + } + } + + private void showPopup(MouseEvent e) { + popup.show((Component) e.getSource(), e.getX(), e.getY()); + } + }); + } + } +} diff --git a/test/jdk/java/awt/PrintJob/PrintCompatibilityTest.java b/test/jdk/java/awt/PrintJob/PrintCompatibilityTest.java new file mode 100644 index 0000000000000..a2433f455470e --- /dev/null +++ b/test/jdk/java/awt/PrintJob/PrintCompatibilityTest.java @@ -0,0 +1,446 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.JobAttributes; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PageAttributes; +import java.awt.Panel; +import java.awt.PrintJob; +import java.awt.Scrollbar; +import java.awt.ScrollPane; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.JobAttributes.DialogType; +import java.awt.PageAttributes.OriginType; + +import java.util.Enumeration; +import java.util.Properties; + +/* + * @test + * @bug 4247583 + * @key printer + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Tests that the old Properties API still works + * @run main/manual PrintCompatibilityTest + */ + +public class PrintCompatibilityTest { + + public static void main(String[] args) throws Exception { + + String INSTRUCTIONS = """ + A frame window will appear. + Choose 'Print to Printer...' from the 'Print' menu. Make sure that you print + to a printer, not a file. Examine the output and verify that the frame and all + the components in it get printed properly. + + Known problems: + * The text in the second row of the menubar is not indented correctly. + + You can also use the 'Print to Screen...' command for a quick manual check that + printing works, but this is only for debugging purposes."""; + + PassFailJFrame.builder() + .title("PrintComponentTest Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(60) + .testTimeOut(10) + .testUI(new MainFrame()) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class MainFrame extends Frame { + private LWContainer lwc; + + public MainFrame() { + super("PrintCompatibilityTest"); + + setSize(800, 400); + setLayout(new FlowLayout()); + + // peered components + Button button = new Button("Button"); + button.setFont(new Font("Dialog", Font.PLAIN, 12)); + add(button); + add(new TestCanvas()); + Checkbox cbox = new Checkbox("Checkbox", true); + cbox.setFont(new Font("DialogInput", Font.PLAIN, 12)); + add(cbox); + Choice choice = new Choice(); + choice.add("Choice 1"); + choice.add("Choice Two"); + choice.setFont(new Font("Monospaced", Font.PLAIN, 12)); + add(choice); + Label label = new Label("Label"); + label.setFont(new Font("Serif", Font.PLAIN, 12)); + add(label); + List list = new List(); + list.add("List 1"); + list.add("List Two"); + list.setFont(new Font("SansSerif", Font.PLAIN, 12)); + add(list); + add(new Scrollbar(Scrollbar.VERTICAL) ); + add(new Scrollbar(Scrollbar.HORIZONTAL) ); + ScrollPane scrollpane = new ScrollPane(); + Button spButton = new Button("Button in a scrollpane"); + spButton.setFont(new Font("Monospaced", Font.PLAIN, 12)); + scrollpane.add(spButton); + add(scrollpane); + TextArea textarea = new TextArea("TextArea", 3, 30); + textarea.setFont(new Font("Dialog", Font.ITALIC, 10)); + add(textarea); + TextField textfield = new TextField("TextField"); + textfield.setFont(new Font("DialogInput", Font.ITALIC, 10)); + add(textfield); + + // nested components + Panel panel1 = new Panel(); + panel1.setLayout(new FlowLayout()); + panel1.setBackground(Color.red); + this.add(panel1); + + Button p1Button = new Button("level 2"); + p1Button.setFont(new Font("Monospaced", Font.ITALIC, 10)); + panel1.add(p1Button); + + Panel panel2 = new Panel(); + panel2.setLayout(new FlowLayout()); + panel2.setBackground(Color.green); + panel1.add(panel2); + + Button p2Button = new Button("level 3"); + p2Button.setFont(new Font("Serif", Font.ITALIC, 10)); + panel2.add(p2Button); + + + // lightweight components + LWButton lwbutton = new LWButton("LWbutton"); + lwbutton.setFont(new Font("SansSerif", Font.ITALIC, 10)); + add(lwbutton); + + lwc = new LWContainer("LWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainer"); + lwc.setFont(new Font("Monospaced", Font.ITALIC, 10)); + add(lwc); + Button lwcButton1 = new Button("HW Button 1"); + Button lwcButton2 = new Button("HW Button 2"); + LWButton lwcButton3 = new LWButton("LW Button"); + lwcButton1.setFont(new Font("Dialog", Font.BOLD, 14)); + lwcButton2.setFont(new Font("DialogInput", Font.BOLD, 14)); + lwcButton3.setFont(new Font("Monospaced", Font.BOLD, 14)); + lwc.add(lwcButton1); + lwc.add(lwcButton2); + lwc.add(lwcButton3); + + // overlapping components + add(new ZOrderPanel()); + + /////////////////////// + + Menu menu = new Menu("Print"); + Menu menu2 = new Menu("File"); + Menu menu3 = new Menu("Edit"); + Menu menu4 = new Menu("ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLong"); + menu2.setFont(new Font("SansSerif", Font.BOLD, 20)); + menu2.setEnabled(false); + menu3.setFont(new Font("Monospaced", Font.ITALIC, 18)); + menu3.setEnabled(false); + menu4.setEnabled(false); + MenuItem itemPrinter = new MenuItem("Print to Printer..."); + MenuItem itemScreen = new MenuItem("Print to Screen..."); + menu.add(itemPrinter); + menu.add(itemScreen); + MenuBar menuBar = new MenuBar(); + menuBar.add( menu ); + menuBar.add( menu2 ); + menuBar.add( menu3 ); + menuBar.add( menu4 ); + setMenuBar(menuBar); + + itemPrinter.addActionListener( new ActionPrint() ); + itemScreen.addActionListener( new ActionPrintToScreen() ); + setVisible(true); + } + + static void printProps(Properties props) + { + Enumeration propNames = props.propertyNames(); + while (propNames.hasMoreElements()) { + String propName = (String)propNames.nextElement(); + PassFailJFrame.log( propName + " = " + props.getProperty(propName)); + } + } + + class ActionPrint implements ActionListener { + private final int ITERATIONS = 1; + private Properties props = new Properties(); + + public void actionPerformed(ActionEvent ev) { + PassFailJFrame.log("About to show print dialog..."); + printProps(props); + PrintJob pj = getToolkit().getPrintJob( + MainFrame.this, "Print test!", props); + if (pj == null) { + return; + } + Dimension d = pj.getPageDimension(); + PassFailJFrame.log("About to print..."); + PassFailJFrame.log("Dimensions: " + d); + printProps(props); + + // For xor mode set, there is a printing issue with number of copies to be print. + // So, ITERATIONS are changed to 1 from 3. + // So, for now the XOR related code is commented out. + + //boolean xor = false; + + for (int i = 0; i < ITERATIONS; i++) { + Graphics g = pj.getGraphics(); + g.setColor(Color.red); + //if (xor) { + // g.setXORMode(Color.blue); + //} + g.translate(13, 13); + printAll(g); + g.dispose(); + //xor = (xor) ? false : true; + } + + // For xor mode set, LWC components don't get printed. + // So, for now the code is commented out and separate bug + // (JDK-8340495) is filed to handle it. + + // one more page so that we can test printing a lightweight + // at the top of the hierarchy (BugId 4212564) + //Graphics g = pj.getGraphics(); + //g.setColor(Color.red); + //g.translate(13, 13); + //lwc.printAll(g); + //g.dispose(); + // end 4212564 + + pj.end(); + } + } + + class ActionPrintToScreen implements ActionListener { + public void actionPerformed(ActionEvent ev) { + PrintFrame printFrame = new PrintFrame(MainFrame.this); + printFrame.show(); + Graphics g = printFrame.getGraphics(); + g.setColor(Color.red); + printAll(g); + g.dispose(); + } + } + + // Frame window that displays results of printing + // main window to a screen Graphics-- useful for + // quick testing of printing + class PrintFrame extends Frame + { + private Component printComponent; + public PrintFrame( Component c ) + { + super("Print to Screen"); + printComponent = c ; + addWindowListener( new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + setVisible(false); + dispose(); + } + } + ); + setSize(printComponent.getSize()); + setResizable(false); + } + + public void paint( Graphics g ) { + printComponent.printAll(g); + } + } + + class LWButton extends Component { + String label; + int width = 100; + int height = 30; + + public LWButton(String label) { + super(); + this.label = label; + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.orange); + g.setFont(getFont()); + g.fillRect(0, 0, d.width, d.height); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class LWContainer extends Container { + String label; + int width = 300; + int height = 100; + + public LWContainer(String label) { + super(); + this.label = label; + setLayout(new FlowLayout()); + } + + public void paint(Graphics g) { + super.paint(g); + Dimension d = getSize(); + g.setColor(Color.green); + g.setFont(getFont()); + g.drawLine(0, 0, d.width - 1, 0); + g.drawLine(d.width - 1, 0, d.width - 1, d.height - 1); + g.drawLine(d.width - 1, d.height - 1, 0, d.height - 1); + g.drawLine(0, d.height - 1, 0, 0); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class TestCanvas extends Canvas { + int width = 100; + int height = 100; + + public void paint(Graphics g) { + g.setColor(Color.blue); + g.fillRoundRect(10, 10, 50, 50, 15, 30); + g.setColor(Color.red); + g.fillOval(70, 70, 25, 25); + } + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + } + + class ZOrderPanel extends Panel + { + ZOrderPanel() + { + setLayout(null); + + Component first, second, third, fourth; + + setVisible(true); + // add first component + first = makeBox("Second", Color.blue, + new Font("Serif", Font.BOLD, 14), + -1); + // insert on top + second = makeBox("First", Color.yellow, + new Font("SansSerif", Font.BOLD, 14), + 0); + // put at the back + fourth = makeBox("Fourth", Color.red, + new Font("Monospaced", Font.BOLD, 14), + 2); + // insert in last position + third = makeBox("Third", Color.green, + new Font("Dialog", Font.PLAIN, 12), + 3); + // swap third and fourth to correct positions + remove(third); + add(third, 2); + // re-validate so third and fourth peers change position + validate(); + // now make things really interesting with a lightweight + // component at the top of the z-order, that should print + // _below_ the native guys to match the screen... + add(new LWButton("LWButton"), 0); + } + + public Dimension preferredSize() + { + return new Dimension(260, 80); + } + + public void layout() + { + int i, n; + Insets ins = getInsets(); + n = getComponentCount(); + for (i = n-1; i >= 0; i--) { + Component p = getComponent(i); + p.setBounds(ins.left + 40 * i, ins.top + 5 * i, 60, 60); + } + } + + public Component makeBox(String s, Color c, Font f, int index) + { + Label l = new Label(s); + l.setBackground(c); + l.setAlignment(Label.RIGHT); + l.setFont(f); + add(l, index); + validate(); + return l; + } + } +} diff --git a/test/jdk/java/awt/PrintJob/PrintComponentTest.java b/test/jdk/java/awt/PrintJob/PrintComponentTest.java new file mode 100644 index 0000000000000..d568c5a427977 --- /dev/null +++ b/test/jdk/java/awt/PrintJob/PrintComponentTest.java @@ -0,0 +1,486 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.JobAttributes; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.PageAttributes; +import java.awt.Panel; +import java.awt.PrintJob; +import java.awt.Scrollbar; +import java.awt.ScrollPane; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.JobAttributes.DialogType; +import java.awt.PageAttributes.OriginType; + + +/* + * @test + * @bug 4111262 4035285 4038900 4046147 4049680 4084038 4100004 4105875 + * @bug 4117502 4037486 4068433 4128031 4151161 4151707 4155884 4212564 + * @bug 4025626 4029565 4034365 4036068 4040622 4061890 4067405 4086256 + * @bug 4113827 4116722 4121984 4145350 4146510 4172659 4179886 4218471 + * @bug 4219657 4227128 4242308 4245917 4265746 + * @key printer + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Test printing of lightweight (and heavyweight) components + * @run main/manual PrintComponentTest + */ + +public class PrintComponentTest { + public static void main(String[] args) throws Exception { + + String INSTRUCTIONS = """ + A frame window will appear. + Choose 'Print to Printer...' from the 'Print' menu. Examine the output + and verify that the frame and all the components in it get printed properly. + + Print using both 'Portrait' and 'Landscape' orientation. + Verify that the paper dimensions printed to standard error + are exactly inverted. + (That is, if the output for 'Portrait' is + "Dimensions: java.awt.Dimension[width=612,height=792]" then the output + for 'Landscape' should be "Dimensions: java.awt.Dimension[width=792, height=612].) + + Now, attempt to print a second time. When the print dialog box appears, + however, cancel the print request. + Verify that _no_ output is sent to standard error. + + You should attempt to print with both the native and common print dialogs, + as well as with no dialog. + Note that on Linux the native and common print dialogs are identical. + + On Windows, the common print dialog communicates with the printer to + determine supported paper sizes and duplex capability. + Verify that these constraints are properly enforced in the common dialog + for the target printer. + + Known problems: + * The text in the second row of the menubar is not indented + correctly. + + You can also use the 'Print to Screen...' command for a quick manual + check that printing works, but this is only for debugging purposes."""; + + PassFailJFrame.builder() + .title("PrintComponentTest Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(60) + .testTimeOut(10) + .testUI(new MainFrame()) + .logArea(8) + .build() + .awaitAndCheck(); + } +} + +class MainFrame extends Frame { + private LWContainer lwc; + + public MainFrame() { + super("PrintComponentTest"); + + setSize(800, 400); + setLayout(new FlowLayout()); + + // peered components + Button button = new Button("Button"); + button.setFont(new Font("Dialog", Font.PLAIN, 12)); + add(button); + add(new TestCanvas()); + Checkbox cbox = new Checkbox("Checkbox", true); + cbox.setFont(new Font("DialogInput", Font.PLAIN, 12)); + add(cbox); + Choice choice = new Choice(); + choice.add("Choice 1"); + choice.add("Choice Two"); + choice.setFont(new Font("Monospaced", Font.PLAIN, 12)); + add(choice); + Label label = new Label("Label"); + label.setFont(new Font("Serif", Font.PLAIN, 12)); + add(label); + List list = new List(); + list.add("List 1"); + list.add("List Two"); + list.setFont(new Font("SansSerif", Font.PLAIN, 12)); + add(list); + add(new Scrollbar(Scrollbar.VERTICAL) ); + add(new Scrollbar(Scrollbar.HORIZONTAL) ); + ScrollPane scrollpane = new ScrollPane(); + Button spButton = new Button("Button in a scrollpane"); + spButton.setFont(new Font("Monospaced", Font.PLAIN, 12)); + scrollpane.add(spButton); + add(scrollpane); + TextArea textarea = new TextArea("TextArea", 3, 30); + textarea.setFont(new Font("Dialog", Font.ITALIC, 10)); + add(textarea); + TextField textfield = new TextField("TextField"); + textfield.setFont(new Font("DialogInput", Font.ITALIC, 10)); + add(textfield); + + // nested components + Panel panel1 = new Panel(); + panel1.setLayout(new FlowLayout()); + panel1.setBackground(Color.red); + this.add(panel1); + + Button p1Button = new Button("level 2"); + p1Button.setFont(new Font("Monospaced", Font.ITALIC, 10)); + panel1.add(p1Button); + + Panel panel2 = new Panel(); + panel2.setLayout(new FlowLayout()); + panel2.setBackground(Color.green); + panel1.add(panel2); + + Button p2Button = new Button("level 3"); + p2Button.setFont(new Font("Serif", Font.ITALIC, 10)); + panel2.add(p2Button); + + + // lightweight components + LWButton lwbutton = new LWButton("LWbutton"); + lwbutton.setFont(new Font("SansSerif", Font.ITALIC, 10)); + add(lwbutton); + + lwc = new LWContainer("LWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainerLWContainer"); + lwc.setFont(new Font("Monospaced", Font.ITALIC, 10)); + add(lwc); + Button lwcButton1 = new Button("HW Button 1"); + Button lwcButton2 = new Button("HW Button 2"); + LWButton lwcButton3 = new LWButton("LW Button"); + lwcButton1.setFont(new Font("Dialog", Font.BOLD, 14)); + lwcButton2.setFont(new Font("DialogInput", Font.BOLD, 14)); + lwcButton3.setFont(new Font("Monospaced", Font.BOLD, 14)); + lwc.add(lwcButton1); + lwc.add(lwcButton2); + lwc.add(lwcButton3); + + // overlapping components + add(new ZOrderPanel()); + + /////////////////////// + + Menu menu = new Menu("Print"); + Menu menu2 = new Menu("File"); + Menu menu3 = new Menu("Edit"); + Menu menu4 = new Menu("ReallyReallyReallyReallyReallyReallyReallyReally" + + "ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReally" + + "ReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLong"); + menu2.setFont(new Font("SansSerif", Font.BOLD, 20)); + menu2.setEnabled(false); + menu3.setFont(new Font("Monospaced", Font.ITALIC, 18)); + menu3.setEnabled(false); + menu4.setEnabled(false); + MenuItem itemJFC = + new MenuItem("Print to Printer with Cross-Platform Dialog..."); + itemJFC.setActionCommand("common"); + MenuItem itemNative = + new MenuItem("Print to Printer with Native Dialog..."); + itemNative.setActionCommand("native"); + MenuItem itemBackground = + new MenuItem("Print to Printer in Background"); + itemBackground.setActionCommand("none"); + MenuItem itemScreen = new MenuItem("Print to Screen..."); + menu.add(itemJFC); + menu.add(itemNative); + menu.add(itemBackground); + menu.add(itemScreen); + MenuBar menuBar = new MenuBar(); + menuBar.add( menu ); + menuBar.add( menu2 ); + menuBar.add( menu3 ); + menuBar.add( menu4 ); + setMenuBar(menuBar); + + ActionPrint actionPrint = new ActionPrint(); + + itemJFC.addActionListener( actionPrint ); + itemNative.addActionListener( actionPrint ); + itemBackground.addActionListener( actionPrint ); + itemScreen.addActionListener( new ActionPrintToScreen() ); + } + + class ActionPrint implements ActionListener { + private final int ITERATIONS = 1; + private PageAttributes pageAttributes = new PageAttributes(); + private JobAttributes jobAttributes = new JobAttributes(); + + public void actionPerformed(ActionEvent ev) { + DialogType dialog; + if (ev.getActionCommand().equals("common")) { + dialog = DialogType.COMMON; + } else if (ev.getActionCommand().equals("native")) { + dialog = DialogType.NATIVE; + } else { + dialog = DialogType.NONE; + } + jobAttributes.setDialog(dialog); + pageAttributes.setOrigin(OriginType.PRINTABLE); + System.err.println(jobAttributes); + System.err.println(pageAttributes); + + PassFailJFrame.log("About to show print dialog..."); + + PrintJob pj = getToolkit().getPrintJob( + MainFrame.this, "Print test!", jobAttributes, pageAttributes); + if (pj == null) { + return; + } + Dimension d = pj.getPageDimension(); + PassFailJFrame.log("About to print..."); + PassFailJFrame.log("Dimensions: " + d); + System.err.println(jobAttributes); + System.err.println(pageAttributes); + + // For xor mode set, there is a printing issue with number of copies to be print. + // So, ITERATIONS are changed to 1 from 3. + // So, for now the XOR related code is commented out. + + //boolean xor = false; + + for (int i = 0; i < ITERATIONS; i++) { + Graphics g = pj.getGraphics(); + g.setColor(Color.red); + //if (xor) { + // g.setXORMode(Color.blue); + //} + printAll(g); + g.dispose(); + //xor = (xor) ? false : true; + } + + // For xor mode set, LWC components don't get printed. + // So, for now the code is commented out and separate bug + // (JDK-8340495) is filed to handle it. + + // one more page so that we can test printing a lightweight + // at the top of the hierarchy (BugId 4212564) + //Graphics g = pj.getGraphics(); + //g.setColor(Color.red); + //lwc.printAll(g); + //g.dispose(); + // end 4212564 + + pj.end(); + } + } + + class ActionPrintToScreen implements ActionListener { + public void actionPerformed(ActionEvent ev) { + PrintFrame printFrame = new PrintFrame(MainFrame.this); + printFrame.show(); + Graphics g = printFrame.getGraphics(); + g.setColor(Color.red); + printAll(g); + g.dispose(); + } + } + + // Frame window that displays results of printing + // main window to a screen Graphics-- useful for + // quick testing of printing + class PrintFrame extends Frame + { + private Component printComponent; + public PrintFrame( Component c ) + { + super("Print to Screen"); + printComponent = c ; + addWindowListener( new WindowAdapter() { + public void windowClosing(WindowEvent ev) { + setVisible(false); + dispose(); + } + } + ); + setSize(printComponent.getSize()); + setResizable(false); + } + + public void paint( Graphics g ) { + printComponent.printAll(g); + } + } + + class LWButton extends Component { + String label; + int width = 100; + int height = 30; + + public LWButton(String label) { + super(); + this.label = label; + } + + public void paint(Graphics g) { + Dimension d = getSize(); + g.setColor(Color.orange); + g.setFont(getFont()); + g.fillRect(0, 0, d.width, d.height); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class LWContainer extends Container { + String label; + int width = 300; + int height = 100; + + public LWContainer(String label) { + super(); + this.label = label; + setLayout(new FlowLayout()); + } + + public void paint(Graphics g) { + super.paint(g); + Dimension d = getSize(); + g.setColor(Color.green); + g.setFont(getFont()); + g.drawLine(0, 0, d.width - 1, 0); + g.drawLine(d.width - 1, 0, d.width - 1, d.height - 1); + g.drawLine(d.width - 1, d.height - 1, 0, d.height - 1); + g.drawLine(0, d.height - 1, 0, 0); + g.setColor(Color.black); + int x = 5; + int y = (d.height - 5); + g.drawString(label, x, y); + } + + public Dimension getPreferredSize() + { + return new Dimension(width, height); + } + } + + class TestCanvas extends Canvas { + int width = 100; + int height = 100; + + public void paint(Graphics g) { + g.setColor(Color.blue); + g.fillRoundRect(10, 10, 50, 50, 15, 30); + g.setColor(Color.red); + g.fillOval(70, 70, 25, 25); + } + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + } + + class ZOrderPanel extends Panel + { + ZOrderPanel() + { + setLayout(null); + + Component first, second, third, fourth; + + setVisible(true); + // add first component + first = makeBox("Second", Color.blue, + new Font("Serif", Font.BOLD, 14), + -1); + // insert on top + second = makeBox("First", Color.yellow, + new Font("SansSerif", Font.BOLD, 14), + 0); + // put at the back + fourth = makeBox("Fourth", Color.red, + new Font("Monospaced", Font.BOLD, 14), + 2); + // insert in last position + third = makeBox("Third", Color.green, + new Font("Dialog", Font.PLAIN, 12), + 3); + // swap third and fourth to correct positions + remove(third); + add(third, 2); + // re-validate so third and fourth peers change position + validate(); + // now make things really interesting with a lightweight + // component at the top of the z-order, that should print + // _below_ the native guys to match the screen... + add(new LWButton("LWButton"), 0); + } + + public Dimension preferredSize() + { + return new Dimension(260, 80); + } + + public void layout() + { + int i, n; + Insets ins = getInsets(); + n = getComponentCount(); + for (i = n-1; i >= 0; i--) { + Component p = getComponent(i); + p.setBounds(ins.left + 40 * i, ins.top + 5 * i, 60, 60); + } + } + + public Component makeBox(String s, Color c, Font f, int index) + { + Label l = new Label(s); + l.setBackground(c); + l.setAlignment(Label.RIGHT); + l.setFont(f); + add(l, index); + validate(); + return l; + } + } +} diff --git a/test/jdk/java/awt/PrintJob/ScaledImagePrintingTest.java b/test/jdk/java/awt/PrintJob/ScaledImagePrintingTest.java new file mode 100644 index 0000000000000..838c9210e308b --- /dev/null +++ b/test/jdk/java/awt/PrintJob/ScaledImagePrintingTest.java @@ -0,0 +1,102 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.PrintJob; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/* + * @test + * @bug 4257962 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary tests that scaled images are printed at resolution greater than 72dpi + * @run main/manual ScaledImagePrintingTest + */ + +public class ScaledImagePrintingTest { + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Press 'Print' button from the test UI. + + The test will bring up a print dialog. Select a printer and proceed. + Verify that the output is a series of a horizontal lines in a + rectangular box in the center of the page. + + If output is as mentioned above, press Pass else Fail."""; + + PassFailJFrame.builder() + .title("ScaledImagePrintingTest Test Instructions") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(40) + .testTimeOut(5) + .testUI(ScaledImagePrintingTest::createUI) + .logArea(8) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("ResolutionTest"); + Button b = new Button("Print"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PrintJob pj = frame.getToolkit().getPrintJob(frame, "ResolutionTest", null); + PassFailJFrame.log("Printing code started."); + if (pj != null) { + Graphics g = pj.getGraphics(); + g.setColor(Color.black); + int w = 200; + int h = 200; + Image image = frame.createImage(w, h); + Graphics imageGraphics = image.getGraphics(); + Dimension d = pj.getPageDimension(); + imageGraphics.setColor(Color.black); + for (int i = 0; i < h; i += 20) { + imageGraphics.drawLine(0, i, w, i); + } + g.translate(d.width / 2, d.height / 2); + g.drawImage(image, -w / 8, -h / 8, w / 4, h / 4, frame); + g.setColor(Color.black); + g.drawRect(-w / 4, -h / 4, w / 2, h / 2); + imageGraphics.dispose(); + g.dispose(); + pj.end(); + } + PassFailJFrame.log("Printing code finished."); + } + }); + frame.add(b); + frame.setSize(50, 50); + return frame; + } +} diff --git a/test/jdk/java/awt/Robot/CreateScreenCapture.java b/test/jdk/java/awt/Robot/CreateScreenCapture.java new file mode 100644 index 0000000000000..8060240ba8f1d --- /dev/null +++ b/test/jdk/java/awt/Robot/CreateScreenCapture.java @@ -0,0 +1,81 @@ +/* + * 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 4292503 + * @summary OutOfMemoryError with lots of Robot.createScreenCapture + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "linux") + * @run main/manual CreateScreenCapture +*/ + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextArea; + +public class CreateScreenCapture { + + static TextArea messageText; + + private static final String INSTRUCTIONS = """ + This test is linux only! + Once you see these instructions, run 'top' program. + Watch for java process. + The memory size used by this process should stop growing after several steps. + Numbers of steps test is performing are displayed in output window. + After 5-7 steps the size taken by the process should become stable. + If this happens, then test passed otherwise test failed. + + Small oscillations of the memory size are, however, acceptable."""; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + PassFailJFrame passFail = new PassFailJFrame(INSTRUCTIONS); + Dialog dialog = new Dialog(new Frame(), "Instructions"); + messageText = new TextArea("", 5, 80, TextArea.SCROLLBARS_BOTH); + dialog.add(messageText); + PassFailJFrame.addTestWindow(dialog); + PassFailJFrame.positionTestWindow(dialog, PassFailJFrame.Position.HORIZONTAL); + dialog.setSize(200, 300); + dialog.setVisible(true); + Rectangle rect = new Rectangle(0, 0, 1000, 1000); + for (int i = 0; i < 100; i++) { + Image image = robot.createScreenCapture(rect); + image.flush(); + image = null; + robot.delay(200); + log("step #" + i); + } + passFail.awaitAndCheck(); + } + + private static void log(String messageIn) { + messageText.append(messageIn + "\n"); + } +} + diff --git a/test/jdk/java/awt/Robot/RobotScrollTest.java b/test/jdk/java/awt/Robot/RobotScrollTest.java new file mode 100644 index 0000000000000..20dc9f2f4ebd9 --- /dev/null +++ b/test/jdk/java/awt/Robot/RobotScrollTest.java @@ -0,0 +1,101 @@ +/* + * 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 4374578 + * @summary Test robot wheel scrolling of Text + * @requires (os.family == "Windows") | (os.family == "linux") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RobotScrollTest +*/ + +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.TextArea; + +public class RobotScrollTest { + + static TextArea ta; + static Robot robot; + + private static final String INSTRUCTIONS = """ + 0. DON'T TOUCH ANYTHING! + 1. This test is for Windows and Linux only. + 2. Just sit back, and watch the Robot move the mouse to the TextArea. + 3. Once the pointer is on the text area, the Robot will use the mouse wheel + to scroll the text. + If the text scrolled, press PASS, else, press FAIL."""; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + PassFailJFrame passFail = new PassFailJFrame(INSTRUCTIONS); + createTestUI(); + passFail.awaitAndCheck(); + } + + private static void createTestUI() { + Frame f = new Frame("RobotScrollTest"); + ta = new TextArea(); + for (int i = 0; i < 100; i++) { + ta.append(i + "\n"); + } + f.add(ta); + f.setLocation(0, 400); + f.pack(); + PassFailJFrame.addTestWindow(f); + PassFailJFrame.positionTestWindow(f, PassFailJFrame.Position.HORIZONTAL); + f.setVisible(true); + doTest(); + } + + private static void doTest() { + robot.waitForIdle(); + robot.delay(1000); + // get loc of TextArea + Point taAt = ta.getLocationOnScreen(); + // get bounds of button + Rectangle bounds = ta.getBounds(); + + // move mouse to middle of button + robot.mouseMove(taAt.x + bounds.width / 2, + taAt.y + bounds.height / 2); + + // rotate wheel a few times + for (int j = 1; j < 8; j++) { + for (int k = 0; k < 5; k++) { + robot.mouseWheel(j); + } + + for (int k = 0; k < 5; k++) { + robot.mouseWheel(-1 * j); + } + } + } + +} + diff --git a/test/jdk/java/awt/TextField/CaretPositionTest/CaretPositionTest.java b/test/jdk/java/awt/TextField/CaretPositionTest/CaretPositionTest.java new file mode 100644 index 0000000000000..56a39758874e6 --- /dev/null +++ b/test/jdk/java/awt/TextField/CaretPositionTest/CaretPositionTest.java @@ -0,0 +1,104 @@ +/* + * 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 + * 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.AWTException; +import java.awt.Button; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.InputEvent; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4038580 + * @key headful + * @requires os.family != "windows" + * @summary Caret position not accurate in presence of selected areas + * @run main CaretPositionTest + */ + +public class CaretPositionTest extends Frame { + private TextField text_field; + private Button caretpos_button; + private Point onScreen; + private Dimension size; + String text = "12 45 789"; + private static volatile int position = -1; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + CaretPositionTest test = new CaretPositionTest(); + EventQueue.invokeAndWait(test::setupGUI); + try { + test.test(); + if (position != 9) { + throw new RuntimeException("Caret position should be at the end of the string"); + } + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } + + public void setupGUI() { + setLocationRelativeTo(null); + setTitle("CaretPositionTest"); + setLayout(new FlowLayout()); + text_field = new TextField(text, 9); + caretpos_button=new Button("CaretPosition"); + add(text_field); + add(caretpos_button); + pack(); + setVisible(true); + toFront(); + } + + public void test() throws AWTException, InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + onScreen = text_field.getLocationOnScreen(); + size = text_field.getSize(); + }); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.delay(1000); + int y = onScreen.y + (size.height / 2); + robot.mouseMove(onScreen.x + (size.width / 2), y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseMove(onScreen.x + 3, y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int x = onScreen.x + 5; x < onScreen.x + size.width; x += 2) { + robot.mouseMove(x, y); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + EventQueue.invokeAndWait(() -> { + position = text_field.getCaretPosition(); + }); + } +} diff --git a/test/jdk/java/awt/TextField/GetTextTest/GetTextTest.java b/test/jdk/java/awt/TextField/GetTextTest/GetTextTest.java new file mode 100644 index 0000000000000..ab5aebc11d47a --- /dev/null +++ b/test/jdk/java/awt/TextField/GetTextTest/GetTextTest.java @@ -0,0 +1,104 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4100188 + * @key headful + * @summary Make sure that TextFields contain all of, + * and exactly, the text that was entered into them. + * @run main GetTextTest + */ + +import java.awt.AWTException; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; + +public class GetTextTest extends Frame implements ActionListener { + private final String s = "test string"; + private volatile String ac; + private TextField t; + private Point location; + private Dimension size; + + public void setupGUI() { + setLayout(new FlowLayout(FlowLayout.LEFT)); + + t = new TextField(s, 32); + add(new Label("Hit after text")); + add(t); + t.addActionListener(this); + setLocationRelativeTo(null); + pack(); + setVisible(true); + } + + public void actionPerformed(ActionEvent evt) { + ac = evt.getActionCommand(); + } + + public void performTest() throws AWTException, InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + location = t.getLocationOnScreen(); + size = t.getSize(); + }); + Robot robot = new Robot(); + robot.setAutoDelay(50); + robot.delay(1000); + robot.waitForIdle(); + robot.mouseMove(location.x + size.width - 3, location.y + (size.height / 2)); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(1000); + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + robot.delay(1000); + if (!s.equals(ac)) { + throw new RuntimeException("Action command should be the same as text field content"); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + GetTextTest test = new GetTextTest(); + EventQueue.invokeAndWait(test::setupGUI); + try { + test.performTest(); + } finally { + EventQueue.invokeAndWait(test::dispose); + } + } +} diff --git a/test/jdk/java/awt/TextField/SetBoundsTest/SetBoundsTest.java b/test/jdk/java/awt/TextField/SetBoundsTest/SetBoundsTest.java new file mode 100644 index 0000000000000..e37f69232ebec --- /dev/null +++ b/test/jdk/java/awt/TextField/SetBoundsTest/SetBoundsTest.java @@ -0,0 +1,123 @@ +/* + * 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 + * 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.Color; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.ScrollPane; +import java.awt.TextArea; +import java.awt.TextField; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 6198290 6277332 + * @summary TextField painting is broken when placed on a Scrollpane, XToolkit + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetBoundsTest + */ + +public class SetBoundsTest extends Frame { + + private static final String INSTRUCTIONS = """ + 1) Make active a frame with a scrollpane and a few components. + 2) Please, focus attention on the text components + placed on the upper half of the frame + 3) Make sure, that the scrollbar of the frame + have the same position during the test. + 4) Bring focus to TextField, try deleting the text. + If the text never gets erased, the test failed + 5) Bring focus to TextArea, try deleting the text. + If the text never gets erased, the test failed + 6) Please, focus attention on the text components + placed on the lower half of the frame + 7) Try input something into TextField. + If you can not input anything into TextField, the test failed + 8) Try input something into TextArea. + If you can not input anything into TextArea, the test failed + 9) The test passed + """; + + public SetBoundsTest() { + setTitle("SetBoundsTest Test Frame"); + setLayout(new GridLayout(2, 1)); + Panel hw = new Panel(); + ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + Container lw = new Container(); + fill(hw); + fill(lw); + + sp.add(hw); + add(sp); + add(lw); + + setSize(600, 600); + sp.setScrollPosition(20, 0); + + } + + private void fill(Container c) { + Button button = new Button("button"); + c.add(button); + button.setBackground(new Color(0xd3ceac)); + button.setForeground(new Color(0x000000)); + button.move(60, 80); + button.resize(400, 60); + button.show(true); + + TextField textfield = new TextField("textfield"); + c.add(textfield); + textfield.setBackground(new Color(0xd3ceac)); + textfield.setForeground(new Color(0x000000)); + textfield.move(60, 20); + textfield.resize(400, 40); + textfield.show(true); + + TextArea textarea = new TextArea("textarea"); + c.add(textarea); + textarea.setBackground(new Color(0xd3ceac)); + textarea.setForeground(new Color(0x000000)); + textarea.move(60, 80); + textarea.resize(400, 60); + textarea.show(true); + + c.setLayout (new FlowLayout()); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Set Bounds Test Instructions") + .instructions(INSTRUCTIONS) + .testUI(SetBoundsTest::new) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/TextField/SetEchoCharTest.java b/test/jdk/java/awt/TextField/SetEchoCharTest.java new file mode 100644 index 0000000000000..d5cb71975e6a7 --- /dev/null +++ b/test/jdk/java/awt/TextField/SetEchoCharTest.java @@ -0,0 +1,178 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Point; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import jdk.test.lib.Platform; + +/* + * @test + * @bug 4124697 + * @key headful + * @summary Make sure that after setting and then changing the echo + * character again, the TextField continues to function as expected. + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main SetEchoCharTest + */ + +public class SetEchoCharTest { + private static Frame frame; + private static Robot robot; + private static TextField tfPassword; + private static Button btn1; + private static Button btn2; + private static volatile Point btn1Loc; + private static volatile Point btn2Loc; + + private static final String CHANGE = "Change echo char"; + private static final String PRINT = "Print text"; + private static final String INITIAL_TEXT = "DefaultPwd"; + private static final String CHANGED_TEXT = "NewPwd"; + private static final char NEW_ECHO_CHAR = '*'; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.setAutoDelay(50); + + EventQueue.invokeAndWait(() -> createAndShowUI()); + robot.waitForIdle(); + robot.delay(1000); + + testEchoChar(); + robot.waitForIdle(); + robot.delay(200); + + testNewEchoChar(); + robot.waitForIdle(); + robot.delay(200); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("SetEchoCharTest"); + frame.setLayout(new FlowLayout()); + + Label label = new Label("Pwd:"); + tfPassword = new TextField(INITIAL_TEXT, 10); + tfPassword.setEchoChar('X'); + tfPassword.addActionListener((ActionListener) e -> { + if (e.getActionCommand().equals(CHANGED_TEXT)) { + //check the 2nd condition only if ActionEvent + //is triggered by changed text + if (!(tfPassword.getText().equals(CHANGED_TEXT) + && tfPassword.getEchoChar() == NEW_ECHO_CHAR)) { + throw new RuntimeException("Test Failed!!! TextField not working" + + " as expected after echo char change"); + } + } + }); + frame.add(label); + frame.add(tfPassword); + + btn1 = new Button(PRINT); + btn1.addActionListener(new BtnActionListener()); + frame.add(btn1); + + btn2 = new Button(CHANGE); + btn2.addActionListener(new BtnActionListener()); + frame.add(btn2); + frame.setSize(200,200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void testEchoChar() throws Exception { + EventQueue.invokeAndWait(() -> { + btn1Loc = btn1.getLocationOnScreen(); + btn2Loc = btn2.getLocationOnScreen(); + }); + + robot.mouseMove(btn1Loc.x + btn1.getWidth() / 2, + btn1Loc.y + btn1.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(1000); + + robot.mouseMove(btn2Loc.x + btn2.getWidth() / 2, + btn2Loc.y + btn2.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(1000); + } + + private static void testNewEchoChar() { + StringSelection stringSelection = new StringSelection(CHANGED_TEXT); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(stringSelection, stringSelection); + + int ctrlKey = Platform.isOSX() ? KeyEvent.VK_META : KeyEvent.VK_CONTROL; + robot.keyPress(ctrlKey); + robot.keyPress(KeyEvent.VK_V); + robot.keyRelease(KeyEvent.VK_V); + robot.keyRelease(ctrlKey); + + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + } + + private static class BtnActionListener implements ActionListener { + public void actionPerformed(ActionEvent evt) { + String ac = evt.getActionCommand(); + if (CHANGE.equals(ac)) { + tfPassword.setText(""); + tfPassword.setEchoChar(NEW_ECHO_CHAR); + tfPassword.requestFocus(); + } + if (PRINT.equals(ac)) { + if (!tfPassword.getText().equals(INITIAL_TEXT)) { + throw new RuntimeException("Test Failed!!!" + + " Initial text not as expected"); + } + } + } + } +} + diff --git a/test/jdk/java/awt/TextField/SetEchoCharTest3/SetEchoCharTest3.java b/test/jdk/java/awt/TextField/SetEchoCharTest3/SetEchoCharTest3.java new file mode 100644 index 0000000000000..10779365defc4 --- /dev/null +++ b/test/jdk/java/awt/TextField/SetEchoCharTest3/SetEchoCharTest3.java @@ -0,0 +1,65 @@ +/* + * 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 4222122 + * @summary TextField.setEchoCharacter() seems to be broken + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetEchoCharTest3 + */ + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.TextField; +import java.lang.reflect.InvocationTargetException; + +public class SetEchoCharTest3 extends Frame { + static String INSTRUCTIONS = """ + Type in the text field and "*" characters should echo. + If only one "*" echoes and then the system beeps after + the second character is typed, then press Fail, otherwise press Pass. + """; + public SetEchoCharTest3() { + setLayout(new FlowLayout()); + add(new Label("Enter text:")); + TextField tf = new TextField(15); + tf.setEchoChar('*'); + add(tf); + pack(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Set Echo Char Test 3") + .testUI(SetEchoCharTest3::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/TextField/SetEchoCharTest4/SetEchoCharTest4.java b/test/jdk/java/awt/TextField/SetEchoCharTest4/SetEchoCharTest4.java new file mode 100644 index 0000000000000..c1cc07afac0ad --- /dev/null +++ b/test/jdk/java/awt/TextField/SetEchoCharTest4/SetEchoCharTest4.java @@ -0,0 +1,101 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4226580 + * @summary TextField with echoChar add+remove+add seems to be broken + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetEchoCharTest4 + */ + +public class SetEchoCharTest4 extends Frame implements ActionListener { + TextField tf1 = new TextField(8); + TextField tf2 = new TextField(8); + TextField tf3 = new TextField(8); + Button b = new Button("Click Several Times"); + + static final String INSTRUCTIONS = """ + Type in the first text field and * characters will echo. + Type in the second text field and $ characters will echo. + Type in the third text field and # characters will echo. + + Hit the button several times. All characters should remain + the same and the test should not crash. + + Make sure the actual text matches what you typed in for each field. + Press Pass if everything's ok, otherwise Fail + """; + + public SetEchoCharTest4() { + setLayout(new FlowLayout()); + tf1.setEchoChar('*'); + tf2.setEchoChar('$'); + tf3.setEchoChar('#'); + addStuff(); + b.addActionListener(this); + setSize (200,200); + } + + private void addStuff() { + add(tf1); + add(tf2); + add(tf3); + add(b); + } + + public void actionPerformed(ActionEvent ae) { + PassFailJFrame.log("TextField1 = " + tf1.getText()); + PassFailJFrame.log("TextField2 = " + tf2.getText()); + PassFailJFrame.log("TextField3 = " + tf3.getText()); + PassFailJFrame.log("--------------"); + setVisible(false); + remove(tf1); + remove(tf2); + remove(tf3); + remove(b); + addStuff(); + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Set Echo Character Test") + .testUI(SetEchoCharTest4::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/TextField/SetEchoCharWordOpsTest.java b/test/jdk/java/awt/TextField/SetEchoCharWordOpsTest.java new file mode 100644 index 0000000000000..825c21fdc9639 --- /dev/null +++ b/test/jdk/java/awt/TextField/SetEchoCharWordOpsTest.java @@ -0,0 +1,90 @@ +/* + * 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 + * 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.FlowLayout; +import java.awt.Label; +import java.awt.TextField; +import javax.swing.JPanel; + +import jdk.test.lib.Platform; + +/* + * @test + * @bug 6191897 + * @summary Verifies that ctrl+left/right does not move word-by-word in a TextField + * with echo character set + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jdk.test.lib.Platform + * @run main/manual SetEchoCharWordOpsTest + */ + +public class SetEchoCharWordOpsTest { + + public static void main(String[] args) throws Exception { + String selectAllKey; + String moveKeys; + String selectKeys; + + if (Platform.isOSX()) { + selectAllKey = "Cmd + A"; + moveKeys = "Alt + Right/Left"; + selectKeys = "Shift + Alt + Right/Left"; + } else { + selectAllKey = "Ctrl + A"; + moveKeys = "Ctrl + Right/Left"; + selectKeys = "Shift + Ctrl + Right/Left"; + } + + String instructions = + "The password field (in the bottom panel) in this test contains" + + " a few words (3 words).\n" + + "Move the focus to the text field and press " + selectAllKey + ".\n" + + "Try moving the caret word-by-word with " + moveKeys + " or" + + " extending selection with " + selectKeys + "." + + " You should NOT be able to do that.\n\n" + + "If you are able to move the caret word-by-word press FAIL," + + " else press PASS."; + + PassFailJFrame.builder() + .title("SetEchoCharClipboard Instructions") + .instructions(instructions) + .rows((int) instructions.lines().count() + 3) + .columns(45) + .splitUIBottom(SetEchoCharWordOpsTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + + private static JPanel createAndShowUI() { + JPanel jPanel = new JPanel(); + TextField tf = new TextField("one two three", 15); + Label tfLabel = new Label("Password Field:"); + + jPanel.setLayout(new FlowLayout()); + tf.setEchoChar('*'); + jPanel.add(tfLabel); + jPanel.add(tf); + return jPanel; + } +} diff --git a/test/jdk/java/awt/TextField/SetPasswordTest/SetPasswordTest.java b/test/jdk/java/awt/TextField/SetPasswordTest/SetPasswordTest.java new file mode 100644 index 0000000000000..cff5e3750173e --- /dev/null +++ b/test/jdk/java/awt/TextField/SetPasswordTest/SetPasswordTest.java @@ -0,0 +1,105 @@ +/* + * 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 + * 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.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4084454 + * @summary Make sure that you can set the text in a "password mode" + * text field and that it echoes as the current echo char. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SetPasswordTest + */ + +public class SetPasswordTest extends Frame implements ActionListener { + private String setText = "Set text"; + private String getText = "Get text"; + private TextField tfPassword; + + static final String INSTRUCTIONS = """ + The purpose of this test is to ensure that when using a textField for + password entry that text set programmatically is not shown in the clear. + + We also test "pasting" text into the text field. + + 1. Press the "Set Text" button + Text should appear as '*' chars + - if the string "secret" appears then the test is failed. + 2. Use the mouse to select (highlight) all the text and press the DELETE key + 3. Use the system's copy/paste functionality to copy a text string from the + desktop or this window, and paste it into the text field. + 4. Text should appear in the text field as '*' chars + - if the string you pasted appears then the test is failed. + 5. press the "Get Text" button and the string you pasted + should be printed in the log area + - if it prints echo symbols instead the test is failed. + """; + + public SetPasswordTest() { + setLayout(new FlowLayout()); + tfPassword = new TextField("Initial text", 30); + tfPassword.setEchoChar('*'); + add(tfPassword); + + Button b1 = new Button(setText); + b1.addActionListener(this); + add(b1); + + Button b2 = new Button(getText); + b2.addActionListener(this); + add(b2); + pack(); + } + + public void actionPerformed(ActionEvent evt) { + String ac = evt.getActionCommand(); + if (setText.equals(ac)) { + tfPassword.setText("secret"); + } + + if (getText.equals(ac)) { + PassFailJFrame.log("Text: \"" + tfPassword.getText() + "\""); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Set Password Test") + .testUI(SetPasswordTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .logArea() + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/TextField/ZeroEchoCharTest/ZeroEchoCharTest.java b/test/jdk/java/awt/TextField/ZeroEchoCharTest/ZeroEchoCharTest.java new file mode 100644 index 0000000000000..274cec225729c --- /dev/null +++ b/test/jdk/java/awt/TextField/ZeroEchoCharTest/ZeroEchoCharTest.java @@ -0,0 +1,89 @@ +/* + * 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 + * 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.GridLayout; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.InvocationTargetException; + +/* + * @test + * @bug 4307281 + * @summary verify that after setting the echo char to 0 disguises are + * removed and user input is echoed to the screen unchanged. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ZeroEchoCharTest + */ + +public class ZeroEchoCharTest extends Frame implements ActionListener { + private final TextField textfield = new TextField(); + private final Button button1 = new Button("Set echo char to *"); + private final Button button2 = new Button("Set echo char to 0"); + static final String INSTRUCTIONS = """ + 1.Type in the text field. The user input must be echoed unchanged. + 2.Set echo char to '*' by pressing the corresponding button. + If all characters in the text field aren't immediately replaced + with '*', the test fails. + 3.Set echo char to 0 by pressing the corresponding button. + If disguises in the text field don't immediately revert to + the original characters, the test fails. + 4.Type in the text field. If the input is echoed unchanged, + the test passes. Otherwise, the test fails. + """; + + public ZeroEchoCharTest() { + button1.addActionListener(this); + button2.addActionListener(this); + + setLayout(new GridLayout(3, 1)); + + add(textfield); + add(button1); + add(button2); + pack(); + } + + public void actionPerformed(ActionEvent event) { + if (event.getSource() == button1) { + textfield.setEchoChar('*'); + } else if (event.getSource() == button2) { + textfield.setEchoChar((char)0); + } + } + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException { + PassFailJFrame.builder() + .title("Zero Echo Char Test") + .testUI(ZeroEchoCharTest::new) + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 1) + .columns(40) + .build() + .awaitAndCheck(); + } +} diff --git a/test/jdk/java/awt/TrayIcon/ClearPrevImageTest.java b/test/jdk/java/awt/TrayIcon/ClearPrevImageTest.java new file mode 100644 index 0000000000000..e97f645bec594 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/ClearPrevImageTest.java @@ -0,0 +1,126 @@ +/* + * 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 + * 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.AWTException; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6267936 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary Tests that the previous image in TrayIcon is cleared + * when a new image is set + * @run main/manual ClearPrevImageTest + */ + +public class ClearPrevImageTest { + private static SystemTray tray; + private static TrayIcon icon; + private static final String INSTRUCTIONS = """ + This test checks that the previous image in TrayIcon is cleared + when a new image is set. + + When the test starts, a RED square TrayIcon is added + to the SystemTray (also, called Taskbar Status Area in MS Windows, + Notification Area in, GNOME and System Tray in KDE). + + You should see it change into YELLOW after 5 seconds. + If you still see RED TrayIcon after 5 seconds, + press FAIL, otherwise press PASS + """; + + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + + PassFailJFrame passFailJFrame + = PassFailJFrame.builder() + .title("TrayIcon Change Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .build(); + + EventQueue.invokeAndWait(ClearPrevImageTest::createAndShowTrayIcon); + try { + changeTrayIcon(); + passFailJFrame.awaitAndCheck(); + } catch (Exception e) { + throw new RuntimeException("Test failed: ", e); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static void createAndShowTrayIcon() { + Image img1 = createIcon(Color.RED); + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(img1); + icon.setImageAutoSize(true); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + } + + private static void changeTrayIcon() { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Image img2 = createIcon(Color.YELLOW); + icon.setImage(img2); + } + + private static Image createIcon(Color color) { + BufferedImage image = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(color); + g.fillRect(0, 0, 16, 16); + g.dispose(); + return image; + } +} diff --git a/test/jdk/java/awt/TrayIcon/FocusLostAfterTrayTest.java b/test/jdk/java/awt/TrayIcon/FocusLostAfterTrayTest.java new file mode 100644 index 0000000000000..bd831ce94e4c0 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/FocusLostAfterTrayTest.java @@ -0,0 +1,136 @@ +/* + * 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 + * 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.AWTException; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.TextArea; +import java.awt.TrayIcon; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6269309 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary Tests that focus is transferred properly back + * to toplevel after clicking on a tray icon. + * @run main/manual FocusLostAfterTrayTest + */ + +public class FocusLostAfterTrayTest { + private static SystemTray tray; + private static TrayIcon icon; + + private static final String INSTRUCTIONS = """ + This test checks that focus is transferred properly back + to top-level after clicking on a tray icon. + + When the test starts, a Frame with a text area & a RED tray icon + are shown. If you don't see a tray icon please make sure that + the tray area (also called Taskbar Status Area on MS Windows + Notification Area on Gnome or System Tray on KDE) is visible. + + Click with a mouse inside a text area and make sure that it has + received input focus. Then click on the tray icon and then back + on the text area and verify that it has input focus again. Repeat + the last step several times. Ensure that the text area always + has the input focus and there are no "FOCUS LOST" event + for text area in the log section. + + If above is true, Press PASS, else FAIL. + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + + try { + PassFailJFrame.builder() + .title("FocusLostAfterTrayTest Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(FocusLostAfterTrayTest::createAndShowTrayIcon) + .logArea() + .build() + .awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static Frame createAndShowTrayIcon() { + Frame frame = new Frame("FocusLostAfterTrayTest"); + frame.setBounds(100, 300, 200, 200); + frame.setLayout(new BorderLayout()); + TextArea ta = new TextArea(); + ta.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + PassFailJFrame.log("FOCUS GAINED: " + + e.getComponent().getClass().toString()); + } + @Override + public void focusLost(FocusEvent e) { + PassFailJFrame.log("FOCUS LOST !! " + + e.getComponent().getClass().toString()); + } + }); + frame.add(ta); + + BufferedImage image = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(Color.RED); + g.fillRect(0, 0, 16, 16); + g.dispose(); + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(image); + icon.setImageAutoSize(true); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + return frame; + } +} diff --git a/test/jdk/java/awt/TrayIcon/MouseMoveTest.java b/test/jdk/java/awt/TrayIcon/MouseMoveTest.java new file mode 100644 index 0000000000000..51d80ff127b1b --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/MouseMoveTest.java @@ -0,0 +1,107 @@ +/* + * 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 + * 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.AWTException; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6267980 + * @summary PIT:Spurious MouseMoved events are triggered by Tray Icon + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @run main/manual MouseMoveTest + */ + +public class MouseMoveTest { + private static SystemTray tray; + private static TrayIcon icon; + private static final String INSTRUCTIONS = """ + 1) You will see a tray icon (white square) in notification area, + 2) Move mouse pointer to the icon and leave it somewhere inside the icon, + 3) Verify that MOUSE_MOVE events are NOT triggered after you have STOPPED + moving mouse. + 4) If events are still triggered Press FAIL else PASS. + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + + PassFailJFrame passFailJFrame + = PassFailJFrame.builder() + .title("TrayIcon Change Test Instructions") + .instructions(INSTRUCTIONS) + .columns(45) + .logArea() + .build(); + + try { + EventQueue.invokeAndWait(MouseMoveTest::createAndShowTrayIcon); + passFailJFrame.awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static void createAndShowTrayIcon() { + BufferedImage img = new BufferedImage(32, 32, + BufferedImage.TYPE_INT_ARGB); + Graphics g = img.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, 32, 32); + g.dispose(); + + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(img); + icon.setImageAutoSize(true); + + icon.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent me){ + PassFailJFrame.log(me.toString()); + } + }); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + } +} diff --git a/test/jdk/java/awt/TrayIcon/TrayIconKeySelectTest.java b/test/jdk/java/awt/TrayIcon/TrayIconKeySelectTest.java new file mode 100644 index 0000000000000..5e41f54638236 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/TrayIconKeySelectTest.java @@ -0,0 +1,122 @@ +/* + * 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 + * 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.AWTException; +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.image.BufferedImage; + +import jtreg.SkippedException; + +/* + * @test + * @bug 6267943 + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary Tests the possibility of selecting a tray icon with the keyboard. + * @run main/manual TrayIconKeySelectTest + */ + +public class TrayIconKeySelectTest { + private static SystemTray tray; + private static TrayIcon icon; + private static final String INSTRUCTIONS = """ + Tests that TrayIcon is selectable with the keyboard + When the test is started you will see three-color icon + in the system tray. + + 1. Bring the focus to the icon with TAB. Press ENTER key. + - One or more ActionEvent should be generated + (see the output area of the test) + + 2. Bring the focus again to the icon. Press SPACE key twice. + - One or more ActionEvent should be generated. + + 3. Bring the focus again to the icon. Click on the icon with + the LEFT mouse button twice. + - One or more ActionEvent should be generated. + + 4. Again bring the focus to the icon. Click on the icon with + the LEFT mouse button just once. + - NO ActionEvent should be generated. + + 5. Repeat the 4th step with other mouse buttons. + + If all the above are true press PASS, else FAIL + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + PassFailJFrame passFailJFrame; + try { + passFailJFrame + = PassFailJFrame.builder() + .title("TrayIconKeySelectTest Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .logArea() + .build(); + + EventQueue.invokeAndWait(TrayIconKeySelectTest::createAndShowTrayIcon); + passFailJFrame.awaitAndCheck(); + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + tray.remove(icon); + } + }); + } + } + + private static void createAndShowTrayIcon() { + BufferedImage im = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics gr = im.createGraphics(); + gr.setColor(Color.white); + gr.fillRect(0, 0, 16, 5); + gr.setColor(Color.blue); + gr.fillRect(0, 5, 16, 10); + gr.setColor(Color.red); + gr.fillRect(0, 10, 16, 16); + gr.dispose(); + + tray = SystemTray.getSystemTray(); + icon = new TrayIcon(im); + icon.setImageAutoSize(true); + icon.addActionListener(e -> PassFailJFrame.log(e.toString())); + + try { + tray.add(icon); + } catch (AWTException e) { + throw new RuntimeException("Error while adding" + + " icon to system tray", e); + } + } +} diff --git a/test/jdk/java/awt/TrayIcon/TrayIconTest.java b/test/jdk/java/awt/TrayIcon/TrayIconTest.java new file mode 100644 index 0000000000000..c78f6c134fed3 --- /dev/null +++ b/test/jdk/java/awt/TrayIcon/TrayIconTest.java @@ -0,0 +1,613 @@ +/* + * 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 + * 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.AWTException; +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Choice; +import java.awt.Color; +import java.awt.Container; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Label; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.TrayIcon; +import java.awt.TrayIcon.MessageType; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.util.HashMap; +import java.util.Map; +import jtreg.SkippedException; + +/* + * @test + * @key headful + * @bug 4310333 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame jtreg.SkippedException + * @summary A unit test for TrayIcon RFE + * @run main/manual TrayIconTest + */ + +public class TrayIconTest { + private static SystemTray tray; + static Frame frame = new Frame("TrayIcon Test"); + private static final String INSTRUCTIONS = """ + The test frame contains CheckboxGroup of tray icons. + A selected checkbox represents the TrayIcon (or null + TrayIcon) whose functionality is currently tested. + + If you are under Linux make sure your Application Panel has + System Tray (on Gnome it is called Notification Area). + + Perform all the cases (1-7) documented below. + + CASE 1: Testing ADD/REMOVE/PropertyChange functionality. + -------------------------------------------------------- + 1. Select null TrayIcon and pressAdd button: + - NullPointerException should be thrown. + 2. Select some of the valid TrayIcons and press Add button: + - The selected TrayIcon should appear in the SystemTray. + - PropertyChangeEvent should be fired (the property is + an array of TrayIcons added to the system tray). + 3. Press Add button again: + - IllegalArgumentException should be thrown. + - No PropertyChangeEvent should be fired. + 4. Press Remove button: + - The TrayIcon should disappear from the SystemTray. + - PropertyChangeEvent should be fired. + 5. Press Remove button again: + - It should have no effect. + - No PropertyChangeEvent should be fired. + 6. Add all the valid TrayIcons (by selecting everyone and pressing Add + button): + - All the TrayIcons should appear in the SystemTray. + - PropertyChangeEvent should be fired on each adding. + 7. Remove all the TrayIcons (again by selecting everyone and pressing + Remove): + - All the TrayIcons should disappear from the SystemTray. + - PropertyChangeEvent should be fired on each removing. + 8. Not for Windows! Remove the system tray (Notification Area) from + the desktop. Try to add some valid TrayIcon: + - AWTException should be thrown. + - No PropertyChangeEvent should be fired. + 9. Not for Windows! Add the system tray back to the desktop. Add all the + valid TrayIcons: + - All the TrayIcons should appear in the system tray. + - PropertyChangeEvent should be fired on each adding. + 11. Not for Windows! Remove the system tray from the desktop: + - All the TrayIcons should disappear. + - PropertyChangeEvent should be fired for each TrayIcon + removal. + - PropertyChangeEvent should be fired for SystemTray removal. + 12. Add the system tray and go to the next step. + - All the TrayIcons should appear again. + - PropertyChangeEvent should be fired for SystemTray addition. + - PropertyChangeEvent shouldn't be fired for TrayIcon removal. + + CASE 2: Testing RESIZE functionality. + ------------------------------------- + 1. Select some of the TrayIcons and add it. Then press resize button: + - The TrayIcon selected should be resized to fit the area it occupies. + 2. Press resize button again: + - The TrayIcon should be resized to the original size. + 3. Repeat the 1-2 steps for other TrayIcons: + - The TrayIcons should be resized appropriately. + + CASE 3: Testing EVENTS functionality + --------------------------------- + 1. Select some of the TrayIcons and add it. Select MouseEvent from the + group of checkboxes at the top-right of the test frame. + Click on the TrayIcon in the SystemTray: + - MOUSE_PRESSED MOUSE_RELEASED and MOUSE_CLICKED events should be + generated. + 2. Press mouse inside the TrayIcon dragging mouse and releasing it. + - Make sure that MOUSE_CLICKED event is not triggered. + 3. Click on the TrayIcon with different modification keys: + - there should be appropriate modifiers in the events. + 4. Keep clicking on the TrayIcon: + - there should be correct absolute coordinates in the events. + 5. Only for Windows! Focus the system tray using keyboard: + - press WIN key once to bring up the start menu then press ESC once to + close the menu the focus should be on the start button + - press TAB key for several times until you focus on the system + tray then use ARROW keys to move to the TrayIcon + - press ENTER or SPACE should trigger ACTION_PERFORMED message + make sure that mouse events are not triggered. + 6. Select MouseMotionEvent checkbox. Move mouse over the TrayIcon: + - MOUSE_MOVED event should be generated. It should contain + correct coordinates. + 7. Deselect both the checkboxes and then select AWTEventListener. + Click on the TrayIcon and then move mouse over it: + - Appropriate mouse events should be generated (catched by the + AWTEventListener). + 8. Deselect all the checkboxes and go to the following step. + + CASE 4: Testing DISPLAY MESSAGE functionality. + ---------------------------------------------- + 1. Select some of the TrayIcons and add it. Then press Display message + button: + - A balloon message should appear near the TrayIcon. + 2. After the message is displayed wait for some period: + - The message window should be closed automatically. + 3. Display the message again. Close it by pressing X in its top-right + corner: + - The message window should be closed immediately. + 4. Display the message again. Click inside it: + - The message should be closed an ACTION_PERFORMED event should be + generated with correct information and an Ok dialog should appear. + Close the dialog. + 5. Select a message type from the Type choice and display the message + again: + - It should contain an icon appropriate to the message type selected + or no icon if NONE is selected. + 6. Change the content of the Message and Caption text fields and + display the message: + - The message content should be changed in the accordance with the text + typed. + 7. Not for Windows! Type some too long or too short text for the Caption + and Message: + - The message should process the text correctly. The long text should + be cut. + 8. Not for Windows! Type null in the Message text field and display + the message: + - The message body should contain no text. + 9. Type null in the Caption text field and display the message: + - The message caption should contain no text. + 10. Type null in the both Message and Caption fields and display + the message: + - NullPointerException should be generated and no message should be + displayed. + 11. Try to hide the taskbar. Click Display message for several times. + Then restore the taskbar. Click on the TrayIcon: + - No message should appear. + Try to display the message once more: + - It should appear appropriately. + 12. Try to display the message for other TrayIcons: + - The messages should be displayed appropriately. + + CASE 5: Testing POPUP MENU functionality. + ----------------------------------------- + 1. Add some TrayIcon to the system tray. Press Set button in the + Popup menu test area. Trigger the popup menu for the TrayIcon with + the mouse: + - A popup menu should appear. Make sure it behaves properly. + - Make sure the 'duke.gif' image is animated while the popup menu is shown. + 2. Press Remove button for the popup menu and try to trigger it again: + - No popup menu should appear. + 3. Perform 1-2 steps for other TrayIcons: + - Make sure the popup menu behaves properly. + 4. Add more than one TrayIcons to the system tray. Press Set button in + the PopupMenu test area for some of the TrayIcon added. Trigger + the popup menu for this TrayIcon: + - A popup menu should appear properly. + 5. Try to set the popup menu to the same TrayIcon again: + - It should have no effect + 6. Try to set the popup menu for other TrayIcons you've added to the system + tray: + - for each one IllegalArgumentException should be thrown. + + CASE 6: Testing TOOLTIP functionality. + -------------------------------------- + 1. Type something in the Tooltip text field and press Set button. + Then move mouse cursor over the TrayIcon and wait for a second: + - A tooltip should appear containing the text typed. + 2. Show a tooltip again and keep your mouse over the TrayIcon for some period: + - The tooltip should disappear automatically. + 3. Show a tooltip again and leave the TrayIcon: + - The tooltip should disappear immediately. + 4. Type null in the Tooltip field and press set then move your + mouse to the SystemTray: + - The tooltip shouldn't appear. + 5. Type something too long in the Tooltip field and show the tooltip: + - The tooltip text should be cut. + + CASE 7: Testing ACTION functionality. + ------------------------------------- + 1. Add some TrayIcon to the system tray. Double click it with the left mouse + button: + - An ACTION_PERFORMED event should be generated. + 2. Double click the TrayIcon with the left mouse button several times: + - Several ACTION_PERFORMED events should be generated + - Make sure that the time-stamp of each event ('when' field) is increased. + + If all the above cases work as expected Press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + throw new SkippedException("Test not applicable as" + + " System Tray not supported"); + } + try { + PassFailJFrame.builder() + .title("TrayIconTest Instructions") + .instructions(INSTRUCTIONS) + .columns(50) + .rows(40) + .testUI(TrayIconTest::createAndShowUI) + .logArea(10) + .build() + .awaitAndCheck(); + + } finally { + EventQueue.invokeAndWait(() -> { + if (tray != null) { + //Remove any remaining tray icons before ending the test. + TrayIcon[] icons = tray.getTrayIcons(); + for (TrayIcon icon : icons) { + tray.remove(icon); + } + } + }); + } + } + + private static Frame createAndShowUI() { + final TrayIconControl ctrl = new TrayIconControl(); + frame.setLayout(new BorderLayout()); + frame.add(ctrl.cont, BorderLayout.CENTER); + frame.setBackground(Color.LIGHT_GRAY); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + ctrl.dispose(); + } + }); + + frame.pack(); + return frame; + } + + private static class TrayIconControl { + final String RED_ICON = "RED ICON"; + final String BLUE_ICON = "BLUE ICON"; + final String GREEN_ICON = "GREEN ICON"; + + CheckboxGroup cbg = new CheckboxGroup(); + Button addButton = new PackedButton(" Add "); + Button remButton = new PackedButton("Remove"); + Button resizeButton = new PackedButton("Resize"); + Button balloonButton = new PackedButton("Display message"); + Choice balloonChoice = new Choice(); + String[] balloonTypes = new String[] { "ERROR", "WARNING", "INFO", "NONE" }; + + TextField balloonText = new TextField( + "A TrayIcon can generate various MouseEvents and" + + " supports adding corresponding listeners to receive" + + " notification of these events. TrayIcon processes" + + " some of the events by itself. For example," + + " by default, when the right-mouse click", 70); + TextField balloonCaption = new TextField("TrayIcon", 70); + + MessageType[] typeArr = new MessageType[] { MessageType.ERROR, MessageType.WARNING, + MessageType.INFO, MessageType.NONE }; + Checkbox mouseListenerCbox = new Checkbox("MouseEvent"); + Checkbox motionListenerCbox = new Checkbox(" MouseMotionEvent"); + Checkbox awtListenerCbox = new Checkbox(" AWTEventListener"); + TextField tipText = new TextField("TrayIcon", 50); + Button tipButton = new PackedButton("Set"); + Button setPopupButton = new PackedButton("Set"); + Button remPopupButton = new PackedButton("Remove"); + + PopupMenu popupMenu = new PopupMenu(); + + Map resToObjMap = new HashMap<>(); + + Container cont = new Container(); + + TrayIconControl() { + Toolkit.getDefaultToolkit().addAWTEventListener(e -> { + if (e.getSource() instanceof TrayIcon && awtListenerCbox.getState()) { + PassFailJFrame.log(e.toString()); + } + }, MouseEvent.MOUSE_EVENT_MASK | MouseEvent.MOUSE_MOTION_EVENT_MASK | + ActionEvent.ACTION_EVENT_MASK); + + cont.setLayout(new GridLayout(4, 1)); + + Container raw1 = new Container(); + raw1.setLayout(new GridLayout(1, 4)); + cont.add(raw1); + + InsetsPanel cbgPanel = new InsetsPanel(); + cbgPanel.setLayout(new GridLayout(4, 1)); + Checkbox nullCbox = new Checkbox("null", cbg, true); + Checkbox redCbox = new Checkbox(RED_ICON, cbg, false); + Checkbox blueCbox = new Checkbox(BLUE_ICON, cbg, false); + Checkbox greenCbox = new Checkbox(GREEN_ICON, cbg, false); + cbgPanel.add(nullCbox); + cbgPanel.add(redCbox); + cbgPanel.add(blueCbox); + cbgPanel.add(greenCbox); + cbgPanel.addTo(raw1); + + InsetsPanel addremPanel = new InsetsPanel(); + addremPanel.setLayout(new BorderLayout()); + addremPanel.add(addButton.getParent(), BorderLayout.NORTH); + addremPanel.add(remButton.getParent(), BorderLayout.SOUTH); + addremPanel.addTo(raw1); + + InsetsPanel resizePanel = new InsetsPanel(); + resizePanel.add(resizeButton); + resizePanel.addTo(raw1); + + InsetsPanel lstPanel = new InsetsPanel(); + lstPanel.setLayout(new GridLayout(3, 1)); + lstPanel.add(mouseListenerCbox); + lstPanel.add(motionListenerCbox); + lstPanel.add(awtListenerCbox); + lstPanel.addTo(raw1); + + Container raw2 = new Container(); + raw2.setLayout(new BorderLayout()); + cont.add(raw2); + + InsetsPanel balloonPanel = new InsetsPanel(); + balloonPanel.setLayout(new BorderLayout()); + balloonPanel.add(balloonButton.getParent(), BorderLayout.NORTH); + Container bc = new Container(); + bc.setLayout(new FlowLayout()); + bc.add(new Label(" Type:")); + bc.add(balloonChoice); + balloonPanel.add(bc, BorderLayout.SOUTH); + balloonPanel.addTo(raw2, BorderLayout.WEST); + + InsetsPanel blnTextPanel = new InsetsPanel(); + blnTextPanel.setLayout(new GridLayout(2, 2)); + Container c1 = new Panel(); + c1.setLayout(new FlowLayout()); + blnTextPanel.add(c1); + c1.add(new Label("Message:")); + c1.add(balloonText); + + Container c2 = new Panel(); + c2.setLayout(new FlowLayout()); + blnTextPanel.add(c2); + c2.add(new Label("Caption:")); + c2.add(balloonCaption); + blnTextPanel.addTo(raw2, BorderLayout.CENTER); + + + Container raw3 = new Container(); + raw3.setLayout(new BorderLayout()); + cont.add(raw3); + + InsetsPanel popupPanel = new InsetsPanel(); + popupPanel.setLayout(new FlowLayout()); + popupPanel.add(new Label("Popup menu:")); + popupPanel.add(setPopupButton); + popupPanel.add(remPopupButton); + popupPanel.addTo(raw3); + + + Container raw4 = new Container(); + raw4.setLayout(new BorderLayout()); + cont.add(raw4); + + InsetsPanel tipPanel = new InsetsPanel(); + tipPanel.setLayout(new FlowLayout()); + tipPanel.add(new Label("Tooltip:")); + tipPanel.add(tipText); + tipPanel.add(tipButton); + tipPanel.addTo(raw4); + + addButton.addActionListener(e -> { + try { + tray.add(getCurIcon()); + } catch (NullPointerException npe) { + if (npe.getMessage() == null) { + PassFailJFrame.log("Probably wrong path to the images."); + throw npe; // if wrong images path was set + } + PassFailJFrame.log(npe.toString()); + } catch (IllegalArgumentException iae) { + PassFailJFrame.log(iae.toString()); + } catch (AWTException ise) { + PassFailJFrame.log(ise.toString()); + } + }); + remButton.addActionListener(e -> tray.remove(getCurIcon())); + + resizeButton.addActionListener( + e -> getCurIcon().setImageAutoSize(!getCurIcon().isImageAutoSize())); + + balloonButton.addActionListener(e -> { + String text = null, caption = null; + if (balloonText.getText().compareToIgnoreCase("null") != 0) { + text = balloonText.getText(); + } + if (balloonCaption.getText().compareToIgnoreCase("null") != 0) { + caption = balloonCaption.getText(); + } + try { + getCurIcon().displayMessage(caption, text, typeArr[balloonChoice.getSelectedIndex()]); + } catch (NullPointerException npe) { + PassFailJFrame.log(npe.toString()); + } + }); + + tipButton.addActionListener(e -> { + String tip = null; + if (tipText.getText().compareToIgnoreCase("null") != 0) { + tip = tipText.getText(); + } + getCurIcon().setToolTip(tip); + }); + + setPopupButton.addActionListener(e -> { + try { + getCurIcon().setPopupMenu(popupMenu); + } catch (IllegalArgumentException iae) { + PassFailJFrame.log(iae.toString()); + } + }); + + remPopupButton.addActionListener(e -> getCurIcon().setPopupMenu(null)); + for (String s: balloonTypes) { + balloonChoice.add(s); + } + + init(); + } + + void init() { + tray = SystemTray.getSystemTray(); + tray.addPropertyChangeListener("trayIcons", + e -> printPropertyChangeEvent(e)); + + tray.addPropertyChangeListener("systemTray", + e -> printPropertyChangeEvent(e)); + + configureTrayIcon(RED_ICON); + configureTrayIcon(BLUE_ICON); + configureTrayIcon(GREEN_ICON); + + for (String s: balloonTypes) { + popupMenu.add(new MenuItem(s)); + } + } + + void printPropertyChangeEvent(PropertyChangeEvent e) { + String name = e.getPropertyName(); + Object oldValue = e.getOldValue(); + Object newValue = e.getNewValue(); + + PassFailJFrame.log("PropertyChangeEvent[name=" + name + + ",oldValue=" + oldValue + ",newValue=" + newValue + "]"); + } + + void configureTrayIcon(String icon) { + Color color = Color.WHITE; + switch (icon) { + case "RED ICON" -> color = Color.RED; + case "BLUE ICON" -> color = Color.BLUE; + case "GREEN ICON" -> color = Color.GREEN; + } + Image image = createIcon(color); + TrayIcon trayIcon = new TrayIcon(image); + + trayIcon.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (mouseListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + public void mouseReleased(MouseEvent e) { + if (mouseListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + public void mouseClicked(MouseEvent e) { + if (mouseListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + }); + trayIcon.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent e) { + if (motionListenerCbox.getState()) + PassFailJFrame.log(e.toString()); + } + }); + trayIcon.addActionListener(e -> PassFailJFrame.log(e.toString())); + + resToObjMap.remove(icon); + resToObjMap.put(icon, trayIcon); + } + + String getCurImgName() { + return cbg.getSelectedCheckbox().getLabel(); + } + + TrayIcon getCurIcon() { + return resToObjMap.get(getCurImgName()); + } + + public void dispose() { + tray.remove(getCurIcon()); + } + + private static Image createIcon(Color color) { + BufferedImage image = new BufferedImage(16, 16, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setColor(color); + g.fillRect(0, 0, 16, 16); + g.dispose(); + return image; + } + + } + + private static class InsetsPanel extends Panel { + Container parent = new Container() { + public Insets getInsets() { + return new Insets(2, 2, 2, 2); + } + }; + + InsetsPanel() { + parent.setLayout(new BorderLayout()); + setBackground(new Color(240, 240, 240)); + } + + void addTo(Container c) { + parent.add(this); + c.add(parent); + } + + void addTo(Container c, String pos) { + parent.add(this); + c.add(parent, pos); + } + } + + private static class PackedButton extends Button { + Container parent = new Container(); + PackedButton(String l) { + super(l); + parent.setLayout(new FlowLayout()); + parent.add(this); + } + } +} + diff --git a/test/jdk/java/awt/Window/BadConfigure/BadConfigure.java b/test/jdk/java/awt/Window/BadConfigure/BadConfigure.java new file mode 100644 index 0000000000000..a25ab9b91b9a5 --- /dev/null +++ b/test/jdk/java/awt/Window/BadConfigure/BadConfigure.java @@ -0,0 +1,71 @@ +/* + * 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 + * 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 6261336 + * @summary Tests that Choice inside ScrollPane opens at the right location + * after resize + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BadConfigure +*/ + +import java.awt.BorderLayout; +import java.awt.Choice; +import java.awt.Frame; + +public class BadConfigure +{ + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + Please resize the BadConfigure window using the left border. + Now click on choice. Its popup will be opened. + Please verify that the popup is opened right under the choice. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + + private static Frame initialize() { + Frame f = new Frame("BadConfigure"); + f.setLayout(new BorderLayout()); + Choice ch = new Choice(); + f.add(ch, BorderLayout.NORTH); + ch.add("One"); + ch.add("One"); + ch.add("One"); + ch.add("One"); + ch.add("One"); + ch.add("One"); + f.setSize(200, 200); + f.validate(); + return f; + } +} diff --git a/test/jdk/java/awt/Window/InvalidFocusLostEventTest/InvalidFocusLostEventTest.java b/test/jdk/java/awt/Window/InvalidFocusLostEventTest/InvalidFocusLostEventTest.java new file mode 100644 index 0000000000000..569d59f146d38 --- /dev/null +++ b/test/jdk/java/awt/Window/InvalidFocusLostEventTest/InvalidFocusLostEventTest.java @@ -0,0 +1,101 @@ +/* + * 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 4397883 + * @summary Tests that non-focusable Window doesn't grab focus + * @key headful + * @run main InvalidFocusLostEventTest + */ + +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; + +public class InvalidFocusLostEventTest implements ActionListener { + private static Frame f; + private static Button b; + private static KeyboardFocusManager fm; + private static volatile Point bp; + private static volatile int width, height; + private static Robot robot; + + public static void main(String[] args) throws Exception { + try { + InvalidFocusLostEventTest test = new InvalidFocusLostEventTest(); + EventQueue.invokeAndWait(() -> test.createUI()); + runTest(); + // we should check focus after all events are processed, + // since focus transfers are asynchronous + robot.waitForIdle(); + if (fm.getFocusOwner() != b) { + throw new RuntimeException("Failed: focus was lost"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (f != null) { + f.dispose(); + } + }); + } + } + + private void createUI() { + f = new Frame("InvalidFocusLostEventTest"); + b = new Button("Press me"); + fm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + b.addActionListener(this); + f.add(b); + f.pack(); + f.setLocationRelativeTo(null); + f.setVisible(true); + } + + private static void runTest() throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + robot.setAutoWaitForIdle(true); + EventQueue.invokeAndWait(() -> { + bp = b.getLocationOnScreen(); + width = b.getWidth(); + height = b.getHeight(); + }); + robot.mouseMove(bp.x + width / 2, bp.y + height / 2 ); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } + + public void actionPerformed(ActionEvent ev) { + // pop up a non-focusable window + Window win = new Window(f); + win.setFocusableWindowState(false); + } +} diff --git a/test/jdk/java/awt/Window/LocationByPlatform/TestLocationByPlatform.java b/test/jdk/java/awt/Window/LocationByPlatform/TestLocationByPlatform.java new file mode 100644 index 0000000000000..33b9d048ef2a9 --- /dev/null +++ b/test/jdk/java/awt/Window/LocationByPlatform/TestLocationByPlatform.java @@ -0,0 +1,105 @@ +/* + * 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 + * 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 6318630 + * @summary Test that location by platform works + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestLocationByPlatform + */ + +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; + +public class TestLocationByPlatform { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + You should see two frames. One has locationByPlatform set, it + should be displayed somewhere on the screen most probably without + intersecting other Frames or stacked over normal frame with some + offset. Another has its location explicitly set to (0, 450). + Please verify that the frames are located correctly on the screen. + + Also verify that the picture inside of frames looks the same + and consists of red descending triangle occupying exactly the bottom + half of the frame. Make sure that there is a blue rectangle exactly + surrounding the client area of frame with no pixels between it and + the frame's decorations. Press Pass if this all is true, + otherwise press Fail. + """; + + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(13) + .columns(40) + .build(); + EventQueue.invokeAndWait(TestLocationByPlatform::createUI); + passFailJFrame.awaitAndCheck(); + } + private static void createUI() { + Frame frame = new Frame("Normal"); + frame.setLocation(0, 450); + Canvas c = new MyCanvas(); + frame.add(c, BorderLayout.CENTER); + frame.pack(); + PassFailJFrame.addTestWindow(frame); + frame.setVisible(true); + + frame = new Frame("Location by platform"); + frame.setLocationByPlatform(true); + c = new MyCanvas(); + frame.add(c, BorderLayout.CENTER); + frame.pack(); + PassFailJFrame.addTestWindow(frame); + frame.setVisible(true); + } + + static class MyCanvas extends Canvas { + @Override + public Dimension getPreferredSize() { + return new Dimension(400, 400); + } + + @Override + public void paint(Graphics g) { + g.setColor(Color.red); + for (int i = 399; i >= 0; i--) { + g.drawLine(400 - i - 1, 400 - i - 1, + 400 - i - 1, 399); + } + g.setColor(Color.blue); + g.drawLine(0, 0, 399, 0); + g.drawLine(0, 0, 0, 399); + g.drawLine(0, 399, 399, 399); + g.drawLine(399, 0, 399, 399); + } + } +} diff --git a/test/jdk/java/awt/Window/LocationByPlatformWithControls/TestLocationByPlatformWithControls.java b/test/jdk/java/awt/Window/LocationByPlatformWithControls/TestLocationByPlatformWithControls.java new file mode 100644 index 0000000000000..bfdba97e4eb99 --- /dev/null +++ b/test/jdk/java/awt/Window/LocationByPlatformWithControls/TestLocationByPlatformWithControls.java @@ -0,0 +1,270 @@ +/* + * 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 4102292 + * @summary Tests that location by platform works with other APIs + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestLocationByPlatformWithControls + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.Vector; + +public class TestLocationByPlatformWithControls extends Frame + implements ActionListener, ItemListener { + Panel northP; + Panel centerP; + Checkbox undecoratedCB; + Checkbox defaultLocationCB; + Checkbox visibleCB; + Checkbox iconifiedCB; + Checkbox maximizedCB; + Button createB; + Button packB; + Button moveB; + Button resizeB; + Button reshapeB; + Button disposeB; + Vector frames; + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test is to check that LocationByPlatform works with other + controls API. + 1) Create New Frame by clicking on "Create" Button in + "TestLocationByPlatformWithControls" window. + 2) Initially this Frame will not be visible, Click on checkbox + "LocationByPlatform" to set default platform location for the frame + and then click on checkbox "Visible" to see that Frame is displayed + at default offsets. + 3) Now you can play with different controls like Iconified, + Maximized, Pack, Move, Resize and Reshape to verify that these + controls work properly with the Frame. + 4) At the end dispose the Frame by clicking on "Dispose" button. + 5) Also we can do verify this for Undecorated Frame but for that we + need to follow same steps but in step 2 before we click on checkbox + "Visible", select "Undecorated" checkbox along with + "LocationByPlatform". + 6) If everything works properly test is passed, otherwise failed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(TestLocationByPlatformWithControls::new) + .logArea(4) + .build() + .awaitAndCheck(); + } + + public TestLocationByPlatformWithControls() { + northP = new Panel(); + centerP = new Panel(); + + undecoratedCB = new Checkbox("Undecorated"); + defaultLocationCB = new Checkbox("LocationByPlatform"); + visibleCB = new Checkbox("Visible"); + iconifiedCB = new Checkbox("Iconified"); + maximizedCB = new Checkbox("Maximized"); + + createB = new Button("Create"); + packB = new Button("Pack"); + moveB = new Button("Move"); + resizeB = new Button("Resize"); + reshapeB = new Button("Reshape"); + disposeB = new Button("Dispose"); + + frames = new Vector(10); + this.setTitle("TestLocationByPlatformWithControls"); + this.setLayout(new BorderLayout()); + this.add(northP, BorderLayout.NORTH); + + northP.add(new Label("New Frame")); + + createB.addActionListener(this); + northP.add(createB); + + centerP.setEnabled(false); + this.add(centerP, BorderLayout.CENTER); + + centerP.add(new Label("Last Frame")); + + centerP.add(defaultLocationCB); + defaultLocationCB.addItemListener(this); + + centerP.add(undecoratedCB); + undecoratedCB.addItemListener(this); + + centerP.add(iconifiedCB); + iconifiedCB.addItemListener(this); + + centerP.add(maximizedCB); + maximizedCB.addItemListener(this); + + centerP.add(visibleCB); + visibleCB.addItemListener(this); + + packB.addActionListener(this); + centerP.add(packB); + + moveB.addActionListener(this); + centerP.add(moveB); + + resizeB.addActionListener(this); + centerP.add(resizeB); + + reshapeB.addActionListener(this); + centerP.add(reshapeB); + + disposeB.addActionListener(this); + centerP.add(disposeB); + this.pack(); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == createB) { + Frame frame = new Frame(); + frame.setSize(200, 200); + frames.add(frame); + updateControls(frame); + Panel panel = new Panel(); + frame.add(panel); + panel.add(new Button ("Test Button")); + panel.add(new Button ("Test Button 1")); + panel.add(new Button ("Test Button 2")); + panel.add(new Button ("Test Button 3")); + centerP.setEnabled(true); + return; + } + + if (frames.isEmpty()) { + return; + } + + Frame last = (Frame)frames.lastElement(); + + if (e.getSource() == packB) { + last.pack(); + } else + if (e.getSource() == moveB) { + int x = (int)(Math.random() * 200); + int y = (int)(Math.random() * 200); + last.setLocation(x, y); + } else + if (e.getSource() == resizeB) { + int w = (int)(Math.random() * 200); + int h = (int)(Math.random() * 200); + last.setSize(w, h); + } else + if (e.getSource() == reshapeB) { + int x = (int)(Math.random() * 200); + int y = (int)(Math.random() * 200); + int w = (int)(Math.random() * 200); + int h = (int)(Math.random() * 200); + last.setBounds(x, y, w, h); + } else + if (e.getSource() == disposeB) { + last.dispose(); + frames.remove(frames.size() - 1); + if (frames.isEmpty()) { + updateControls(null); + centerP.setEnabled(false); + return; + } + last = (Frame)frames.lastElement(); + } + updateControls(last); + } + + public void updateControls(Frame f) { + undecoratedCB.setState(f != null ? + f.isUndecorated() : false); + defaultLocationCB.setState(f != null ? + f.isLocationByPlatform() : false); + visibleCB.setState(f != null ? + f.isVisible() : false); + iconifiedCB.setState(f != null ? + (f.getExtendedState() & Frame.ICONIFIED) != 0 : false); + maximizedCB.setState(f != null ? + (f.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 : false); + } + + public void itemStateChanged(ItemEvent e) { + Frame last = (Frame)frames.lastElement(); + try { + boolean state = e.getStateChange() == ItemEvent.SELECTED; + if (e.getSource() == visibleCB) { + last.setVisible(state); + } else + if (e.getSource() == defaultLocationCB) { + last.setLocationByPlatform(state); + } else + if (e.getSource() == undecoratedCB) { + last.setUndecorated(state); + } else + if (e.getSource() == iconifiedCB) { + if (state) { + last.setExtendedState(last.getExtendedState() | + Frame.ICONIFIED); + } else { + last.setExtendedState(last.getExtendedState() & + ~Frame.ICONIFIED); + } + } else + if (e.getSource() == maximizedCB) { + if (state) { + last.setExtendedState(last.getExtendedState() | + Frame.MAXIMIZED_BOTH); + } else { + last.setExtendedState(last.getExtendedState() & + ~Frame.MAXIMIZED_BOTH); + } + } + } catch (Throwable ex) { + PassFailJFrame.log(ex.getMessage()); + } finally { + updateControls(last); + } + } + + @Override + public void dispose() { + while (!frames.isEmpty()) { + Frame last = (Frame)frames.lastElement(); + last.dispose(); + frames.remove(frames.size() - 1); + } + } +} diff --git a/test/jdk/java/awt/Window/NoResizeEvent/NoResizeEvent.java b/test/jdk/java/awt/Window/NoResizeEvent/NoResizeEvent.java new file mode 100644 index 0000000000000..50b264acde970 --- /dev/null +++ b/test/jdk/java/awt/Window/NoResizeEvent/NoResizeEvent.java @@ -0,0 +1,82 @@ +/* + * 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 4942457 + * @key headful + * @summary Verifies that filtering of resize events on native level works. + * I.E.after Frame is shown no additional resize events are generated. + * @library /java/awt/patchlib ../../regtesthelpers + * @build java.desktop/java.awt.Helper + * @build Util + * @run main NoResizeEvent + */ + +import test.java.awt.regtesthelpers.Util; + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +public class NoResizeEvent { + //Mutter can send window insets too late, causing additional resize events. + private static final boolean IS_MUTTER = Util.getWMID() == Util.MUTTER_WM; + private static final int RESIZE_COUNT_LIMIT = IS_MUTTER ? 5 : 3; + private static Frame frame; + static int resize_count = 0; + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> createUI()); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + if (resize_count > RESIZE_COUNT_LIMIT) { + throw new RuntimeException("Resize event arrived: " + + resize_count + " times."); + } + } + } + + private static void createUI() { + frame = new Frame("NoResizeEvent"); + frame.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + System.out.println(e); + resize_count++; + } + }); + frame.setVisible(true); + + try { + Thread.sleep(3000); + } catch (InterruptedException ie) { + } + System.out.println("Resize count: " + resize_count); + } +} diff --git a/test/jdk/java/awt/Window/OwnedWindowShowTest/OwnedWindowShowTest.java b/test/jdk/java/awt/Window/OwnedWindowShowTest/OwnedWindowShowTest.java new file mode 100644 index 0000000000000..c8b5ad4a619d0 --- /dev/null +++ b/test/jdk/java/awt/Window/OwnedWindowShowTest/OwnedWindowShowTest.java @@ -0,0 +1,54 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4177156 + * @key headful + * @summary Tests that multiple level of window ownership doesn't cause + * NullPointerException when showing a Window + * @run main OwnedWindowShowTest + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Window; + +public class OwnedWindowShowTest { + public static void main(String[] args) throws Exception { + EventQueue.invokeAndWait(OwnedWindowShowTest::runTest); + } + + static void runTest() { + Frame parent = new Frame("OwnedWindowShowTest"); + try { + Window owner = new Window(parent); + Window window = new Window(owner); + // Showing a window with multiple levels of ownership + // should not throw NullPointerException + window.setVisible(true); + } finally { + parent.dispose(); + } + } +} diff --git a/test/jdk/java/awt/Window/ProxyCrash/PopupProxyCrash.java b/test/jdk/java/awt/Window/ProxyCrash/PopupProxyCrash.java new file mode 100644 index 0000000000000..b17b934a70280 --- /dev/null +++ b/test/jdk/java/awt/Window/ProxyCrash/PopupProxyCrash.java @@ -0,0 +1,192 @@ +/* + * 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 4381561 + * @key headful + * @summary Tests that when we show the popup window AWT doesn't crash due to + * the problems with focus proxy window code + * @run main PopupProxyCrash + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +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.Box; +import javax.swing.JComboBox; +import javax.swing.JTextField; +import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; + +public class PopupProxyCrash implements ActionListener { + private static JTextField jtf; + private static Button tf; + private static Panel panel; + private static Font[] fonts; + private static Robot robot; + + private static JComboBox cb; + + private static MyComboBoxUI comboBoxUI; + private static Frame frame; + private static int TEST_COUNT = 10; + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoDelay(100); + EventQueue.invokeAndWait(() -> createUI()); + robot.waitForIdle(); + runTest(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createUI() { + frame = new Frame("PopupProxyCrash"); + Font dialog = new Font("Dialog", Font.PLAIN, 12); + Font serif = new Font("Serif", Font.PLAIN, 12); + Font monospaced = new Font("Monospaced", Font.PLAIN, 12); + + fonts = new Font[] { dialog, serif, monospaced }; + + cb = new JComboBox(fonts); + + cb.setLightWeightPopupEnabled(false); + comboBoxUI = new MyComboBoxUI(); + cb.setUI(comboBoxUI); + jtf = new JTextField("JTextField"); + jtf.setFont(fonts[1]); + tf = new Button("TextField"); + tf.setFont(fonts[1]); + cb.addActionListener(new PopupProxyCrash()); + + panel = new Panel() { + public Dimension getPreferredSize() { + return new Dimension(100, 20); + } + public void paint(Graphics g) { + System.out.println("Painting with font " + getFont()); + g.setColor(Color.white); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.black); + g.setFont(getFont()); + g.drawString("LightWeight", 10, 10); + } + }; + panel.setFont(fonts[1]); + + Container parent = Box.createVerticalBox(); + parent.add(jtf); + parent.add(tf); + parent.add(panel); + parent.add(cb); + + frame.add(parent, BorderLayout.CENTER); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static Point getComboBoxLocation() throws Exception { + final Point[] result = new Point[1]; + + EventQueue.invokeAndWait(() -> { + Point point = cb.getLocationOnScreen(); + Dimension size = cb.getSize(); + + point.x += size.width / 2; + point.y += size.height / 2; + result[0] = point; + }); + return result[0]; + } + + private static Point getItemPointToClick(final int item) throws Exception { + final Point[] result = new Point[1]; + + EventQueue.invokeAndWait(() -> { + BasicComboPopup popup = (BasicComboPopup)comboBoxUI.getComboPopup(); + Point point = popup.getLocationOnScreen(); + Dimension size = popup.getSize(); + + int step = size.height / fonts.length; + point.x += size.width / 2; + point.y += step / 2 + step * item; + result[0] = point; + }); + return result[0]; + } + + static void runTest() throws Exception { + for (int i = 0; i < TEST_COUNT; i++) { + Point point = getComboBoxLocation(); + robot.mouseMove(point.x, point.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + + point = getItemPointToClick(i % fonts.length); + robot.mouseMove(point.x, point.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(500); + } + } + public void actionPerformed(ActionEvent ae) { + System.out.println("Font selected"); + Font font = fonts[((JComboBox)ae.getSource()).getSelectedIndex()]; + + tf.setFont(font); + jtf.setFont(font); + panel.setFont(font); + panel.repaint(); + } + + private static class MyComboBoxUI extends BasicComboBoxUI { + public ComboPopup getComboPopup() { + return popup; + } + } +} diff --git a/test/jdk/java/awt/Window/ResizeTest/ResizeTest.java b/test/jdk/java/awt/Window/ResizeTest/ResizeTest.java new file mode 100644 index 0000000000000..a9191f8bd0536 --- /dev/null +++ b/test/jdk/java/awt/Window/ResizeTest/ResizeTest.java @@ -0,0 +1,80 @@ +/* + * 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 4225955 + * @summary Tests that focus lost is delivered to a lightweight component + * in a disposed window + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ResizeTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; + +public class ResizeTest +{ + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1) Push button A to create modal dialog 2. + 2) Resize dialog 2, then click button B to hide it. + 3) Push button A again. Dialog B should be packed to its original + size. + 4) Push button B again to hide, and A to reshow. + Dialog B should still be the same size, then test is passed, + otherwise failed. + 5) Push button B to hide the modal dialog and then select pass/fail. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(ResizeTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("1"); + Dialog d = new Dialog(f, "2", true); + d.setLocationRelativeTo(null); + Button b2 = new Button("B"); + b2.addActionListener(e -> d.setVisible(false)); + d.setLayout(new BorderLayout()); + d.add(b2, BorderLayout.CENTER); + + Button b = new Button("A"); + f.add(b, BorderLayout.CENTER); + b.addActionListener(e -> { + d.pack(); + d.setVisible(true); + }); + f.pack(); + return f; + } +} diff --git a/test/jdk/java/awt/Window/ShowWindowTest/ShowWindowTest.java b/test/jdk/java/awt/Window/ShowWindowTest/ShowWindowTest.java new file mode 100644 index 0000000000000..4857929c94ebc --- /dev/null +++ b/test/jdk/java/awt/Window/ShowWindowTest/ShowWindowTest.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +/* + * @test + * @bug 4084997 + * @summary See if Window can be created without its size explicitly set + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ShowWindowTest + */ + +import java.awt.Button; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ShowWindowTest implements ActionListener +{ + private static Window window; + private static Button showButton; + private static Button hideButton; + + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1. You should see a Frame with a "Show" and a "Hide" button in it. + 2. Click on the "Show" button. A window with a "Hello World" Label + should appear + 3. If the window does not appear, the test failed, otherwise passed. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(ShowWindowTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("ShowWindowTest"); + frame.setLayout(new FlowLayout()); + frame.setSize(100,100); + frame.add(showButton = new Button("Show")); + frame.add(hideButton = new Button("Hide")); + + ActionListener handler = new ShowWindowTest(); + showButton.addActionListener(handler); + hideButton.addActionListener(handler); + + window = new Window(frame); + window.add("Center", new Label("Hello World")); + window.setLocationRelativeTo(null); + return frame; + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource() == showButton) { + window.pack(); + window.setVisible(true); + } else if (e.getSource() == hideButton) + window.setVisible(false); + } +} diff --git a/test/jdk/java/awt/Window/WindowToFrontTest/WindowToFrontTest.java b/test/jdk/java/awt/Window/WindowToFrontTest/WindowToFrontTest.java new file mode 100644 index 0000000000000..e6bbadcb546db --- /dev/null +++ b/test/jdk/java/awt/Window/WindowToFrontTest/WindowToFrontTest.java @@ -0,0 +1,83 @@ +/* + * 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 4488209 + * @summary JFrame toFront causes the entire frame to be repainted, causes UI + * to flash + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WindowToFrontTest + */ + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class WindowToFrontTest implements ActionListener { + static Frame frame; + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + 1) Click the "toFront" button, this causes the + "WindowToFrontTest" frame to move front and gets repainted + completely. + 2) Move "WindowToFrontTest" window and continue to click on "toFront + multiple times. If the "WindowToFrontTest" Frame content is not + drawn properly and continues to blink, test is failed + otherwise passed. + """; + + PassFailJFrame passFailJFrame = PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .rows(13) + .columns(40) + .build(); + EventQueue.invokeAndWait(() -> createUI()); + passFailJFrame.awaitAndCheck(); + } + + private static void createUI() { + frame = new Frame("WindowToFrontTest"); + frame.setLayout(new BorderLayout()); + frame.setSize(512, 512); + PassFailJFrame.addTestWindow(frame); + frame.setVisible(true); + + Frame buttonFrame = new Frame("Test Button"); + Button push = new Button("toFront"); + push.addActionListener(new WindowToFrontTest()); + buttonFrame.add(push); + buttonFrame.pack(); + PassFailJFrame.addTestWindow(buttonFrame); + buttonFrame.setVisible(true); + } + + public void actionPerformed(ActionEvent e) { + frame.toFront(); + } +} diff --git a/test/jdk/java/awt/Window/bug4189244.java b/test/jdk/java/awt/Window/bug4189244.java new file mode 100644 index 0000000000000..df37d2fce0e2b --- /dev/null +++ b/test/jdk/java/awt/Window/bug4189244.java @@ -0,0 +1,120 @@ +/* + * 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 + * 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 4189244 + * @summary Swing Popup menu is not being refreshed (cleared) under a Dialog + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @requires (os.family == "windows") + * @run main/manual bug4189244 +*/ + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; + +public class bug4189244 { + + private static final String INSTRUCTIONS = """ + This is Windows only test! + + Click right button on frame to show popup menu. + (menu should be placed inside frame otherwise bug is not reproducible) + click on any menu item (dialog will be shown). + close dialog. + if you see part of popupmenu, under dialog, before it is closed, + then test failed, else passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("bug4189244 Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(bug4189244::createTestUI) + .build() + .awaitAndCheck(); + } + + + private static JFrame createTestUI() { + RefreshBug panel = new RefreshBug(); + JFrame frame = new JFrame("Popup refresh bug"); + + frame.add(panel, BorderLayout.CENTER); + panel.init(); + frame.setSize(400, 400); + return frame; + } +} + +class RefreshBug extends JPanel implements ActionListener { + JPopupMenu _jPopupMenu = new JPopupMenu(); + + public void init() { + JMenuItem menuItem; + JButton jb = new JButton("Bring the popup here and select an item"); + + this.add(jb, BorderLayout.CENTER); + + for(int i = 1; i < 10; i++) { + menuItem = new JMenuItem("Item " + i); + menuItem.addActionListener(this); + _jPopupMenu.add(menuItem); + } + + MouseListener ml = new MouseAdapter() { + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + _jPopupMenu.show(e.getComponent(), + e.getX(), e.getY()); + } + } + }; + this.addMouseListener(ml); + + jb.addMouseListener(ml); + + } + + // An action is requested by the user + public void actionPerformed(java.awt.event.ActionEvent e) { + JOptionPane.showMessageDialog(this, + "Check if there is some popup left under me\n"+ + "if not, retry and let the popup appear where i am", + "WARNING", + JOptionPane.WARNING_MESSAGE); + + } +} diff --git a/test/jdk/java/awt/color/XAWTDifference/XAWTColors.jpg b/test/jdk/java/awt/color/XAWTDifference/XAWTColors.jpg new file mode 100644 index 0000000000000..f53b30407cc28 Binary files /dev/null and b/test/jdk/java/awt/color/XAWTDifference/XAWTColors.jpg differ diff --git a/test/jdk/java/awt/color/XAWTDifference/XAWTDifference.java b/test/jdk/java/awt/color/XAWTDifference/XAWTDifference.java new file mode 100644 index 0000000000000..ef0f5aacf4479 --- /dev/null +++ b/test/jdk/java/awt/color/XAWTDifference/XAWTDifference.java @@ -0,0 +1,223 @@ +/* + * 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 + * 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.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Label; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; + +/* + * @test + * @bug 5092883 6513478 7154025 + * @requires (os.family == "linux") + * @summary REGRESSION: SystemColor class gives back wrong values under Linux + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual XAWTDifference + */ + +public class XAWTDifference { + + private static final String INSTRUCTIONS = """ + You would see a frame with title "XAWTDifference Test Frame". + + Test Frame (1) + + a) It has three columns in it. The 1st one with ordinary components. + The 2nd one with disabled components. + The 3rd one with uneditable components (only text components + are there). Verify that the difference between different states + is visible. + + Standard Frame (2) + + b) You would also see a frame named StandardFrame (2) + with a lot of components in it. Actually this is just a jpg-image + in a frame. Verify that every component in the frame (1) looks + similar to the same component in (2). + + They might differ in colors and be darker or brighter but + the whole picture should be the same. + + c) Also check the color of the MenuBar Items in the MenuBar and + the PopupMenu assigned to TextArea. + As you can't compare the colors of menu items with the picture + so just look if the are adequate enough. + """; + private static final int HGAP = 20; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(XAWTDifference::createAndShowUI) + .positionTestUI(XAWTDifference::positionMultiTestUI) + .build() + .awaitAndCheck(); + } + + private static Panel addComponentsIntoPanel(boolean enabled, boolean editable) { + TextField tf = new TextField("TextField"); + TextArea ta = new TextArea("TextArea", 10, 10); + + Choice levelChooser = new Choice(); + levelChooser.add("Item #1"); + levelChooser.add("Item #2"); + + Button b = new Button("BUTTON"); + Label label = new Label("LABEL"); + java.awt.List list = new java.awt.List(4, false); + list.add("one"); + list.add("two"); + list.add("three"); + + Checkbox chb = new Checkbox(); + Scrollbar sb = new Scrollbar(Scrollbar.HORIZONTAL); + ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); + sp.add(new TextArea("this is a textarea in scrollpane")); + sp.setSize(200, 200); + Canvas canvas = new Canvas(); + canvas.setSize(100, 100); + + //add popup menu to Button + final PopupMenu pm = new PopupMenu(); + MenuItem i1 = new MenuItem("Item1"); + MenuItem i2 = new MenuItem("Item2"); + MenuItem i3 = new MenuItem("Item3"); + i3.setEnabled(false); + pm.add(i1); + pm.add(i2); + pm.add(i3); + canvas.add(pm); + + ta.add(pm); + ta.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (me.isPopupTrigger()) { + pm.show(me.getComponent(), me.getX(), me.getY()); + } + } + }); + + ArrayList componentList = new ArrayList<>(); + + componentList.add(tf); + componentList.add(ta); + if (editable){ + componentList.add(levelChooser); + componentList.add(b); + componentList.add(label); + componentList.add(list); + componentList.add(chb); + componentList.add(sb); + componentList.add(sp); + componentList.add(canvas); + } else { + tf.setEditable(false); + ta.setEditable(false); + } + + Panel panel = new Panel(); + panel.setLayout(new GridLayout(0, 1)); + for (Component c : componentList) { + if (!enabled) { + c.setEnabled(false); + } + panel.add(c); + } + return panel; + } + + private static List createAndShowUI() { + Frame testFrame = new Frame("XAWTDifference Test Frame"); + StandardFrame standardFrame = new StandardFrame("StandardFrame"); + standardFrame.pack(); + + testFrame.setLayout(new GridLayout(1, 3)); + testFrame.add(addComponentsIntoPanel(true, true)); + testFrame.add(addComponentsIntoPanel(false, true)); + testFrame.add(addComponentsIntoPanel(true, false)); + + MenuItem mi1 = new MenuItem("Item1"); + MenuItem mi2 = new MenuItem("Item2"); + MenuItem mi3 = new MenuItem("Disabled Item3"); + mi3.setEnabled(false); + + MenuBar mb = new MenuBar(); + Menu enabledMenu = new Menu("Enabled Menu"); + Menu disabledMenu = new Menu("Disabled Menu"); + disabledMenu.setEnabled(false); + mb.add(enabledMenu); + mb.add(disabledMenu); + enabledMenu.add(mi1); + enabledMenu.add(mi2); + enabledMenu.add(mi3); + + testFrame.setMenuBar(mb); + testFrame.setSize(standardFrame.getWidth(), standardFrame.getHeight()); + return List.of(testFrame, standardFrame); + } + + private static void positionMultiTestUI(List windows, + PassFailJFrame.InstructionUI instructionUI) { + int x = instructionUI.getLocation().x + instructionUI.getSize().width + HGAP; + for (Window w : windows) { + w.setLocation(x, instructionUI.getLocation().y); + x += w.getWidth() + HGAP; + } + } + + private static class StandardFrame extends Frame { + public StandardFrame(String name) { + super(name); + String testPath = System.getProperty("test.src", "."); + Panel panel = new Panel(); + panel.add(new JLabel(new ImageIcon(testPath + File.separator + "XAWTColors.jpg"))); + add(panel); + } + } +} diff --git a/test/jdk/java/awt/dnd/CustomDragCursorTest.java b/test/jdk/java/awt/dnd/CustomDragCursorTest.java new file mode 100644 index 0000000000000..9e5e006b31c81 --- /dev/null +++ b/test/jdk/java/awt/dnd/CustomDragCursorTest.java @@ -0,0 +1,289 @@ +/* + * 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.Button; +import java.awt.Component; +import java.awt.Cursor; +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.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/* + * @test + * @bug 4451328 + * @summary tests that a custom drag cursor is not changed + to the default drag cursor + * @key headful + * @run main CustomDragCursorTest + */ + +public class CustomDragCursorTest { + private static Frame frame; + private static final DragSourcePanel dragSourcePanel = new DragSourcePanel(); + private static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + + private static volatile Point srcPoint; + private static volatile Point dstPoint; + private static volatile boolean passed = true; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + EventQueue.invokeAndWait(CustomDragCursorTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point p = dragSourcePanel.getLocationOnScreen(); + Dimension d = dragSourcePanel.getSize(); + p.translate(d.width / 2, d.height / 2); + srcPoint = p; + + p = dropTargetPanel.getLocationOnScreen(); + d = dropTargetPanel.getSize(); + p.translate(d.width / 2, d.height / 2); + dstPoint = p; + }); + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(1000); + + if (!passed) { + throw new RuntimeException("Custom drag cursor changed to default."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("CustomDragCursorTest"); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourcePanel); + frame.add(dropTargetPanel); + frame.setLocationRelativeTo(null); + frame.setSize(300, 400); + frame.setVisible(true); + } + + public static void failed() { + passed = false; + } + + private static int sign(int n) { + return Integer.compare(n, 0); + } + + private static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + private final Cursor dragCursor = new Cursor(Cursor.HAND_CURSOR); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(dragCursor, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragExit(DragSourceEvent dse) { + if (!dragCursor.equals(dse.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragOver(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + if (!dragCursor.equals(dsde.getDragSourceContext().getCursor())) { + CustomDragCursorTest.failed(); + } + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + private static class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DragSourcePanel() { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton()); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + } + + private static class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) {} + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if(dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + add(comp); + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java new file mode 100644 index 0000000000000..700187f9dce49 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDAcceptanceTest.java @@ -0,0 +1,87 @@ +/* + * 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. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Panel; + +/* + * @test + * @bug 4166541 4225247 4297663 + * @summary Tests Basic DnD functionality + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDAcceptanceTest + */ + +public class DnDAcceptanceTest { + private static final String INSTRUCTIONS = """ + When test runs a Frame which contains a yellow button labeled + "Drag ME!" and a RED Panel will appear. + + Click on the button and drag to the red panel. + When the mouse enters the red panel + during the drag the panel should turn yellow. + + Release the mouse button, panel should turn red again and + a yellow button labeled Drag ME! should appear inside the panel. + You should be able to repeat this operation multiple times. + + If above is true press PASS, else press FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(38) + .testUI(DnDAcceptanceTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("DnDAcceptanceTest"); + Panel mainPanel; + Component dragSource, dropTarget; + + frame.setSize(400, 400); + frame.setLayout(new BorderLayout()); + + mainPanel = new Panel(); + mainPanel.setLayout(new BorderLayout()); + + mainPanel.setBackground(Color.BLACK); + + dropTarget = new DnDTarget(Color.RED, Color.YELLOW); + dragSource = new DnDSource("Drag ME!"); + + mainPanel.add(dragSource, "North"); + mainPanel.add(dropTarget, "Center"); + frame.add(mainPanel, BorderLayout.CENTER); + frame.setAlwaysOnTop(true); + return frame; + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java new file mode 100644 index 0000000000000..a35ccd9ee12cb --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDSource.java @@ -0,0 +1,155 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Container; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.MouseDragGestureRecognizer; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor df; + private transient int dropAction; + + DnDSource(String label) { + super(label); + Toolkit.getDefaultToolkit().createDragGestureRecognizer(MouseDragGestureRecognizer.class, + DragSource.getDefaultDragSource(), + this, DnDConstants.ACTION_COPY, this); + setBackground(Color.yellow); + setForeground(Color.blue); + df = new DataFlavor(DnDSource.class, "DnDSource"); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop); + } + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public void dragGestureChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dragGestureChanged"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + dsde.getDragSourceContext().setCursor(null); + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + dropAction = dsde.getDropAction(); + System.out.println("dropAction = " + dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] {df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + return df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException, IOException { + + Object copy = null; + + if (!df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + Container parent = getParent(); + switch (dropAction) { + case DnDConstants.ACTION_COPY: + try { + copy = this.clone(); + } catch (CloneNotSupportedException e) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + + oos.writeObject(this); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + try { + copy = ois.readObject(); + } catch (ClassNotFoundException cnfe) { + // do nothing + } + } + + parent.add(this); + return copy; + + case DnDConstants.ACTION_MOVE: + synchronized(this) { + if (parent != null) parent.remove(this); + } + return this; + + case DnDConstants.ACTION_LINK: + return this; + + default: + //throw new IOException("bad operation"); + return this; // workaround for: 4135456 getDropAction() always return 0 + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java new file mode 100644 index 0000000000000..d147741f0d225 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDAcceptanceTest/DnDTarget.java @@ -0,0 +1,115 @@ +/* + * 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. + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Panel; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.IOException; + +class DnDTarget extends Panel implements DropTargetListener { + Color bgColor; + Color htColor; + + DnDTarget(Color bgColor, Color htColor) { + super(); + this.bgColor = bgColor; + this.htColor = htColor; + setBackground(bgColor); + setDropTarget(new DropTarget(this, this)); + } + + public void dragEnter(DropTargetDragEvent e) { + System.err.println("[Target] dragEnter"); + e.acceptDrag(DnDConstants.ACTION_COPY); + setBackground(htColor); + repaint(); + } + + public void dragOver(DropTargetDragEvent e) { + System.err.println("[Target] dragOver"); + e.acceptDrag(DnDConstants.ACTION_COPY); + } + + public void dragExit(DropTargetEvent e) { + System.err.println("[Target] dragExit"); + setBackground(bgColor); + repaint(); + } + + public void drop(DropTargetDropEvent dtde) { + System.err.println("[Target] drop"); + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + return; + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + Object obj; + try { + obj = transfer.getTransferData(dfs[0]); + } catch (IOException | UnsupportedFlavorException ex) { + System.err.println(ex.getMessage()); + dtc.dropComplete(false); + return; + } + + if (obj != null) { + Button button; + try { + button = (Button) obj; + } catch (Exception e) { + System.err.println(e.getMessage()); + dtc.dropComplete(false); + return; + } + add(button); + repaint(); + } + } + setBackground(bgColor); + invalidate(); + validate(); + repaint(); + dtc.dropComplete(true); + } + + public void dropActionChanged(DropTargetDragEvent e) { + System.err.println("[Target] dropActionChanged"); + } +} diff --git a/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java b/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java new file mode 100644 index 0000000000000..ab2bff8ecc5a5 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDClipboardDeadlockTest.java @@ -0,0 +1,441 @@ +/* + * 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 4388802 + * @summary tests that clipboard operations during drag-and-drop don't deadlock + * @key headful + * @run main DnDClipboardDeadlockTest + */ + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.List; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +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.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; + +public class DnDClipboardDeadlockTest { + + public static final int CODE_NOT_RETURNED = -1; + public static final int CODE_OK = 0; + public static final int CODE_FAILURE = 1; + + private int returnCode = CODE_NOT_RETURNED; + + final Frame frame = new Frame(); + Robot robot = null; + Panel panel = null; + + public static void main(String[] args) throws Exception { + DnDClipboardDeadlockTest test = new DnDClipboardDeadlockTest(); + if (args.length == 4) { + test.run(args); + } else { + test.start(); + } + } + + public void run(String[] args) throws InterruptedException, AWTException { + try { + if (args.length != 4) { + throw new RuntimeException("Incorrect command line arguments."); + } + + int x = Integer.parseInt(args[0]); + int y = Integer.parseInt(args[1]); + int w = Integer.parseInt(args[2]); + int h = Integer.parseInt(args[3]); + + Transferable t = new StringSelection("TEXT"); + panel = new DragSourcePanel(t); + + frame.setTitle("DragSource frame"); + frame.setLocation(300, 200); + frame.add(panel); + frame.pack(); + frame.setVisible(true); + + Util.waitForInit(); + + Point sourcePoint = panel.getLocationOnScreen(); + Dimension d = panel.getSize(); + sourcePoint.translate(d.width / 2, d.height / 2); + + Point targetPoint = new Point(x + w / 2, y + h / 2); + + robot = new Robot(); + + if (!Util.pointInComponent(robot, sourcePoint, panel)) { + throw new RuntimeException("WARNING: Cannot locate source panel"); + } + + robot.mouseMove(sourcePoint.x, sourcePoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !sourcePoint.equals(targetPoint); + sourcePoint.translate(sign(targetPoint.x - sourcePoint.x), + sign(targetPoint.y - sourcePoint.y))) { + robot.mouseMove(sourcePoint.x, sourcePoint.y); + Thread.sleep(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + if (frame != null) { + frame.dispose(); + } + + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } // run() + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + public void start() { + panel = new DropTargetPanel(); + + frame.setTitle("DropTarget frame"); + frame.setLocation(10, 200); + frame.add(panel); + + frame.pack(); + frame.setVisible(true); + + try { + Util.waitForInit(); + + Point p = panel.getLocationOnScreen(); + Dimension d = panel.getSize(); + + try { + Robot robot = new Robot(); + Point center = new Point(p); + center.translate(d.width / 2, d.height / 2); + if (!Util.pointInComponent(robot, center, panel)) { + System.err.println("WARNING: Cannot locate target panel"); + return; + } + } catch (AWTException awte) { + awte.printStackTrace(); + return; + } + + String javaPath = System.getProperty("java.home", ""); + String command = javaPath + File.separator + "bin" + + File.separator + "java -cp " + + System.getProperty("java.class.path", ".") + + " DnDClipboardDeadlockTest " + + p.x + " " + p.y + " " + d.width + " " + d.height; + + Process process = Runtime.getRuntime().exec(command); + returnCode = process.waitFor(); + + InputStream errorStream = process.getErrorStream(); + int count = errorStream.available(); + if (count > 0) { + byte[] b = new byte[count]; + errorStream.read(b); + System.err.println("========= Child VM System.err ========"); + System.err.print(new String(b)); + System.err.println("======================================"); + } + + } catch (Throwable e) { + e.printStackTrace(); + } + switch (returnCode) { + case CODE_NOT_RETURNED: + System.err.println("Child VM: failed to start"); + break; + case CODE_OK: + System.err.println("Child VM: normal termination"); + break; + case CODE_FAILURE: + System.err.println("Child VM: abnormal termination"); + break; + } + if (returnCode != CODE_OK) { + throw new RuntimeException("The test failed."); + } + if (frame != null) { + frame.dispose(); + } + } // start() +} // class DnDClipboardDeadlockTest + +class Util implements AWTEventListener { + private static final Toolkit tk = Toolkit.getDefaultToolkit(); + private static final Object SYNC_LOCK = new Object(); + private Component clickedComponent = null; + private static final int PAINT_TIMEOUT = 10000; + private static final int MOUSE_RELEASE_TIMEOUT = 10000; + private static final Util util = new Util(); + + static { + tk.addAWTEventListener(util, 0xFFFFFFFF); + } + + private 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(); + } + } + } + + public static boolean pointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + return util.isPointInComponent(robot, p, comp); + } + + private boolean isPointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + tk.sync(); + 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; + } + + public static void waitForInit() throws InterruptedException { + final Frame f = new Frame() { + public void paint(Graphics g) { + dispose(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + }; + f.setBounds(600, 400, 200, 200); + synchronized (SYNC_LOCK) { + f.setVisible(true); + SYNC_LOCK.wait(PAINT_TIMEOUT); + } + tk.sync(); + } +} + +class DragSourceButton extends Button implements Serializable, + DragGestureListener, + DragSourceListener { + static final Clipboard systemClipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + final Transferable transferable; + + public DragSourceButton(Transferable t) { + super("DragSourceButton"); + + this.transferable = t; + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, transferable, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + } + + public void dragExit(DragSourceEvent dse) { + } + + public void dragOver(DragSourceDragEvent dsde) { + try { + Transferable t = systemClipboard.getContents(null); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + } + systemClipboard.setContents(new StringSelection("SOURCE"), null); + } catch (IOException ioe) { + ioe.printStackTrace(); + if (!ioe.getMessage().equals("Owner failed to convert data")) { + throw new RuntimeException("Owner failed to convert data"); + } + } catch (IllegalStateException e) { + // IllegalStateExceptions do not indicate a bug in this case. + // They result from concurrent modification of system clipboard + // contents by the parent and child processes. + // These exceptions are numerous, so we avoid dumping their + // backtraces to prevent blocking child process io, which + // causes test failure on timeout. + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.exit(DnDClipboardDeadlockTest.CODE_OK); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + } +} + +class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 200); + + public DragSourcePanel(Transferable t) { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton(t)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } +} + +class DropTargetPanel extends Panel implements DropTargetListener { + + static final Clipboard systemClipboard = + Toolkit.getDefaultToolkit().getSystemClipboard(); + final Dimension preferredDimension = new Dimension(200, 200); + + public DropTargetPanel() { + setBackground(Color.green); + setDropTarget(new DropTarget(this, this)); + setLayout(new GridLayout(1, 1)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) { + dtde.acceptDrag(DnDConstants.ACTION_COPY); + } + + public void dragExit(DropTargetEvent dte) { + } + + public void dragOver(DropTargetDragEvent dtde) { + dtde.acceptDrag(DnDConstants.ACTION_COPY); + try { + Transferable t = systemClipboard.getContents(null); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + } + systemClipboard.setContents(new StringSelection("TARGET"), null); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + return; + } + + removeAll(); + final List list = new List(); + add(list); + + Transferable t = dtde.getTransferable(); + DataFlavor[] dfs = t.getTransferDataFlavors(); + + for (int i = 0; i < dfs.length; i++) { + + DataFlavor flavor = dfs[i]; + String str = null; + + if (DataFlavor.stringFlavor.equals(flavor)) { + try { + str = (String) t.getTransferData(flavor); + } catch (Exception e) { + e.printStackTrace(); + } + } + + list.add(str + ":" + flavor.getMimeType()); + } + + dtc.dropComplete(true); + validate(); + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } +} diff --git a/test/jdk/java/awt/dnd/DnDCursorCrashTest.java b/test/jdk/java/awt/dnd/DnDCursorCrashTest.java new file mode 100644 index 0000000000000..7b1f4483ef991 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDCursorCrashTest.java @@ -0,0 +1,231 @@ +/* + * 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 + * 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 4343300 + * @summary tests that drag attempt doesn't cause crash when + * custom cursor is used + * @key headful + * @run main DnDCursorCrashTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class DnDCursorCrashTest { + static final Frame frame = new Frame(); + static final DragSourcePanel dragSourcePanel = new DragSourcePanel(); + static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + + public static void main(String[] args) throws Exception { + try { + EventQueue.invokeAndWait(() -> { + frame.setTitle("DnD Cursor Test Frame"); + frame.setLocation(200, 200); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourcePanel); + frame.add(dropTargetPanel); + frame.pack(); + frame.setVisible(true); + }); + + Robot robot = new Robot(); + robot.delay(1000); + robot.mouseMove(250, 250); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int y = 250; y < 350; y += 5) { + robot.mouseMove(250, y); + robot.delay(100); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + if (frame != null) { + EventQueue.invokeAndWait(() -> frame.dispose()); + } + } + } +} + +class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(new Cursor(Cursor.HAND_CURSOR), this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) {} + + public void dragExit(DragSourceEvent dse) {} + + public void dragOver(DragSourceDragEvent dsde) {} + + public void dragDropEnd(DragSourceDropEvent dsde) {} + + public void dropActionChanged(DragSourceDragEvent dsde) {} + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } +} + +class DragSourcePanel extends Panel { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DragSourcePanel() { + setLayout(new GridLayout(1, 1)); + add(new DragSourceButton()); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } +} + +class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) {} + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + + add(comp); + } +} diff --git a/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDHTMLToOutlookTest.java b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDHTMLToOutlookTest.java new file mode 100644 index 0000000000000..8a39ce537056e --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDHTMLToOutlookTest.java @@ -0,0 +1,82 @@ +/* + * 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 + * 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.Component; +import java.awt.Frame; +import java.awt.Panel; + + +/* + * @test + * @bug 6392086 + * @summary Tests dnd to another screen + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDHTMLToOutlookTest + */ + +public class DnDHTMLToOutlookTest { + + private static final String INSTRUCTIONS = """ + The window contains a yellow button. Click on the button + to copy HTML from DnDSource.html file into the clipboard or drag + HTML context. Paste into or drop over the HTML capable editor in + external application such as Outlook, Word. + + When the mouse enters the editor, cursor should change to indicate + that copy operation is about to happen and then release the mouse + button. HTML text without tags should appear inside the document. + + You should be able to repeat this operation multiple times. + If the above is true Press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(DnDHTMLToOutlookTest::createAndShowUI) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("DnDHTMLToOutlookTest"); + Panel mainPanel; + Component dragSource; + + mainPanel = new Panel(); + mainPanel.setLayout(new BorderLayout()); + + mainPanel.setBackground(Color.YELLOW); + dragSource = new DnDSource("Drag ME (HTML)!"); + + mainPanel.add(dragSource, BorderLayout.CENTER); + frame.add(mainPanel); + frame.setSize(200, 200); + return frame; + } +} diff --git a/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.html b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.html new file mode 100644 index 0000000000000..0f1b9751decdb --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.html @@ -0,0 +1,25 @@ + + +

            DnDHTMLToOutlookTest
            HTML Drag & Paste problem

            +

            if you see the bold header above without HTML tags and without StartHTML as the first word, press PASS

            diff --git a/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.java b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.java new file mode 100644 index 0000000000000..58f17e9415c50 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDHTMLToOutlookTest/DnDSource.java @@ -0,0 +1,142 @@ +/* + * 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 + * 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.Color; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor m_df; + private transient int m_dropAction; + private ByteArrayInputStream m_data = null; + + DnDSource(String label) { + super(label); + setBackground(Color.yellow); + setForeground(Color.blue); + setSize(200, 120); + + try { + m_df = new DataFlavor("text/html; Class=" + InputStream.class.getName() + "; charset=UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } + + DragSource dragSource = new DragSource(); + dragSource.createDefaultDragGestureRecognizer( + this, + DnDConstants.ACTION_COPY_OR_MOVE, + this + ); + dragSource.addDragSourceListener(this); + + String dir = System.getProperty("test.src", "."); + + try { + m_data = new ByteArrayInputStream(Files.readAllBytes( + Paths.get(dir, "DnDSource.html"))); + m_data.mark(m_data.available()); + addActionListener( + new ActionListener(){ + public void actionPerformed(ActionEvent ae){ + Toolkit.getDefaultToolkit().getSystemClipboard() + .setContents( DnDSource.this, null); + } + } + ); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + } + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] {m_df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + System.err.println("[Source] isDataFlavorSupported" + m_df.equals(sdf)); + return m_df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException { + if (!m_df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + System.err.println("[Source] Ok"); + m_data.reset(); + return m_data; + } +} diff --git a/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java new file mode 100644 index 0000000000000..1fc7c2e82b89c --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDRemoveFocusOwnerCrashTest.java @@ -0,0 +1,233 @@ +/* + * 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 + * 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 4357905 + * @summary Tests that removal of the focus owner component during + * drop processing doesn't cause crash + * @key headful + * @run main DnDRemoveFocusOwnerCrashTest + */ + +import java.awt.Button; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.Serializable; + +public class DnDRemoveFocusOwnerCrashTest { + public static final int FRAME_ACTIVATION_TIMEOUT = 1000; + public static Frame frame; + public static Robot robot; + public static DragSourceButton dragSourceButton; + static volatile Point p; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + EventQueue.invokeAndWait(() -> { + frame = new Frame(); + dragSourceButton = new DragSourceButton(); + DropTargetPanel dropTargetPanel = + new DropTargetPanel(dragSourceButton); + frame.add(new Button("Test")); + frame.setTitle("Remove Focus Owner Test Frame"); + frame.setLocation(200, 200); + frame.add(dropTargetPanel); + frame.pack(); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + + EventQueue.invokeAndWait(() -> { + p = dragSourceButton.getLocationOnScreen(); + p.translate(10, 10); + }); + + robot.delay(FRAME_ACTIVATION_TIMEOUT); + robot.mouseMove(p.x, p.y); + robot.delay(FRAME_ACTIVATION_TIMEOUT); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (int dy = 0; dy < 50; dy++) { + robot.mouseMove(p.x, p.y + dy); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + + private static DataFlavor dataflavor; + + static { + try { + dataflavor = + new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType); + dataflavor.setHumanPresentableName("Local Object Flavor"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new ExceptionInInitializerError(); + } + } + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + } + + public void dragExit(DragSourceEvent dse) { + } + + public void dragOver(DragSourceDragEvent dsde) { + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + return this; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{dataflavor}; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + static class DropTargetPanel extends Panel implements DropTargetListener { + + public DropTargetPanel(DragSourceButton button) { + setLayout(new FlowLayout(FlowLayout.CENTER, 50, 50)); + add(button); + setDropTarget(new DropTarget(this, this)); + } + + public void dragEnter(DropTargetDragEvent dtde) { + } + + public void dragExit(DropTargetEvent dte) { + } + + public void dragOver(DropTargetDragEvent dtde) { + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } + + public void drop(DropTargetDropEvent dtde) { + removeAll(); + + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component) transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + + add(comp); + validate(); + } + } +} diff --git a/test/jdk/java/awt/dnd/DnDToWordpadTest.java b/test/jdk/java/awt/dnd/DnDToWordpadTest.java new file mode 100644 index 0000000000000..181a6ee873a85 --- /dev/null +++ b/test/jdk/java/awt/dnd/DnDToWordpadTest.java @@ -0,0 +1,237 @@ +/* + * 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 + * 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 6362095 + * @summary Tests basic DnD functionality to a wordpad + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DnDToWordpadTest + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.InvalidDnDOperationException; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import javax.imageio.ImageIO; + +import static java.awt.image.BufferedImage.TYPE_INT_ARGB; + +public class DnDToWordpadTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + The test window contains a yellow button. Click on the button + to copy image into the clipboard or drag the image. + Paste or drop the image over Wordpad (when the mouse + enters the Wordpad during the drag, the application + should change the cursor to indicate that a copy operation is + about to happen; release the mouse button). + An image of a red rectangle should appear inside the document. + You should be able to repeat this operation multiple times. + Please, click "Pass" if above conditions are true, + otherwise click "Fail". + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(DnDToWordpadTest::createUI) + .build() + .awaitAndCheck(); + } + + public static Frame createUI() { + Frame f = new Frame("DnD To WordPad Test"); + Panel mainPanel; + Component dragSource; + + mainPanel = new Panel(); + mainPanel.setLayout(null); + + mainPanel.setBackground(Color.black); + try { + dragSource = new DnDSource("Drag ME!"); + mainPanel.add(dragSource); + f.add(mainPanel); + } catch (IOException e) { + e.printStackTrace(); + } + + f.setSize(200, 200); + return f; + } +} + +class DnDSource extends Button implements Transferable, + DragGestureListener, + DragSourceListener { + private DataFlavor m_df; + private transient int m_dropAction; + private Image m_img; + + DnDSource(String label) throws IOException { + super(label); + + setBackground(Color.yellow); + setForeground(Color.blue); + setSize(200, 120); + + m_df = DataFlavor.imageFlavor; + + DragSource dragSource = new DragSource(); + dragSource.createDefaultDragGestureRecognizer( + this, + DnDConstants.ACTION_COPY_OR_MOVE, + this + ); + dragSource.addDragSourceListener(this); + + // Create test gif image to drag + Path p = Path.of(System.getProperty("test.classes", ".")); + BufferedImage bImg = new BufferedImage(79, 109, TYPE_INT_ARGB); + Graphics2D cg = bImg.createGraphics(); + cg.setColor(Color.RED); + cg.fillRect(0, 0, 79, 109); + ImageIO.write(bImg, "png", new File(p + java.io.File.separator + + "DnDSource_Red.gif")); + + m_img = Toolkit.getDefaultToolkit() + .getImage(System.getProperty("test.classes", ".") + + java.io.File.separator + "DnDSource_Red.gif"); + + addActionListener( + ae -> Toolkit.getDefaultToolkit().getSystemClipboard().setContents( + (Transferable) DnDSource.this, + null + ) + ); + } + + public void paint(Graphics g) { + g.drawImage(m_img, 10, 10, null); + } + + /** + * a Drag gesture has been recognized + */ + + public void dragGestureRecognized(DragGestureEvent dge) { + System.err.println("starting Drag"); + try { + dge.startDrag(null, this, this); + } catch (InvalidDnDOperationException e) { + e.printStackTrace(); + } + } + + /** + * as the hotspot enters a platform dependent drop site + */ + + public void dragEnter(DragSourceDragEvent dsde) { + System.err.println("[Source] dragEnter"); + } + + /** + * as the hotspot moves over a platform dependent drop site + */ + + public void dragOver(DragSourceDragEvent dsde) { + System.err.println("[Source] dragOver"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + /** + * as the operation changes + */ + + public void dragGestureChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dragGestureChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + /** + * as the hotspot exits a platform dependent drop site + */ + + public void dragExit(DragSourceEvent dsde) { + System.err.println("[Source] dragExit"); + } + + /** + * as the operation completes + */ + + public void dragDropEnd(DragSourceDropEvent dsde) { + System.err.println("[Source] dragDropEnd"); + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + System.err.println("[Source] dropActionChanged"); + m_dropAction = dsde.getDropAction(); + System.out.println("m_dropAction = " + m_dropAction); + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{m_df}; + } + + public boolean isDataFlavorSupported(DataFlavor sdf) { + System.err.println("[Source] isDataFlavorSupported" + m_df.equals(sdf)); + return m_df.equals(sdf); + } + + public Object getTransferData(DataFlavor tdf) throws UnsupportedFlavorException { + if (!m_df.equals(tdf)) { + throw new UnsupportedFlavorException(tdf); + } + System.err.println("[Source] Ok"); + return m_img; + } +} diff --git a/test/jdk/java/awt/dnd/DragExitBeforeDropTest.java b/test/jdk/java/awt/dnd/DragExitBeforeDropTest.java new file mode 100644 index 0000000000000..2ef778a18ba46 --- /dev/null +++ b/test/jdk/java/awt/dnd/DragExitBeforeDropTest.java @@ -0,0 +1,257 @@ +/* + * 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.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.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/* + * @test + * @bug 4395290 + * @key headful + * @summary tests that dragExit() is not called before drop() + */ + +public class DragExitBeforeDropTest { + private static Frame frame; + private static final DragSourceButton dragSourceButton = new DragSourceButton(); + private static final DropTargetPanel dropTargetPanel = new DropTargetPanel(); + private static volatile Point srcPoint; + private static volatile Point dstPoint; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + EventQueue.invokeAndWait(DragExitBeforeDropTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point p = dragSourceButton.getLocationOnScreen(); + Dimension d = dragSourceButton.getSize(); + p.translate(d.width / 2, d.height / 2); + srcPoint = p; + + p = dropTargetPanel.getLocationOnScreen(); + d = dropTargetPanel.getSize(); + p.translate(d.width / 2, d.height / 2); + dstPoint = p; + }); + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(1000); + + if (!dropTargetPanel.getStatus()) { + throw new RuntimeException("The test failed: dragExit()" + + " is called before drop()"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowUI() { + frame = new Frame("DragExitBeforeDropTest"); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourceButton); + frame.add(dropTargetPanel); + frame.setLocationRelativeTo(null); + frame.setSize(300, 400); + frame.setVisible(true); + } + + public static int sign(int n) { + return Integer.compare(n, 0); + } + + private static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(Button.class, "DragSourceButton"); + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) {} + + public void dragExit(DragSourceEvent dse) {} + + public void dragOver(DragSourceDragEvent dsde) {} + + public void dragDropEnd(DragSourceDropEvent dsde) {} + + public void dropActionChanged(DragSourceDragEvent dsde) {} + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = + new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + private static class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredDimension = new Dimension(200, 100); + volatile boolean testPassed = true; + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public boolean getStatus() { + return testPassed; + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) {} + + public void dragExit(DropTargetEvent dte) { + testPassed = false; + } + + public void dragOver(DropTargetDragEvent dtde) {} + + public void dropActionChanged(DropTargetDragEvent dtde) {} + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if(dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + add(comp); + } + } +} + + + diff --git a/test/jdk/java/awt/dnd/DragSourceMotionListenerTest.java b/test/jdk/java/awt/dnd/DragSourceMotionListenerTest.java new file mode 100644 index 0000000000000..25bf7ef03fd1e --- /dev/null +++ b/test/jdk/java/awt/dnd/DragSourceMotionListenerTest.java @@ -0,0 +1,242 @@ +/* + * 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.AWTEvent; +import java.awt.Component; +import java.awt.Container; +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.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/* + * @test + * @key headful + * @bug 4422345 + * @summary tests that DragSourceMotionListeners work correctly and + DragSourceEvents position is correct + */ + +public class DragSourceMotionListenerTest implements AWTEventListener { + static class TestPanel extends Panel { + final Dimension preferredDimension = new Dimension(200, 200); + public Dimension getPreferredSize() { + return preferredDimension; + } + } + + private static Frame frame; + private static final Panel source = new TestPanel(); + private static final Panel target = new TestPanel(); + private static final DragSource ds = DragSource.getDefaultDragSource(); + private static volatile CountDownLatch mouseReleaseEvent; + + static volatile boolean passedTest1 = false; + static volatile boolean passedTest2 = false; + + private static final Point testPoint1 = new Point(); + private static final Point testPoint2 = new Point(); + private static volatile Point srcPoint; + private static volatile Point dstOutsidePoint; + private static volatile Point dstInsidePoint; + + private static final Transferable t = new StringSelection("TEXT"); + private static final DragGestureListener gestureListener = e -> e.startDrag(null, t); + + private static final DragSourceAdapter sourceAdapter = new DragSourceAdapter() { + public void dragMouseMoved(DragSourceDragEvent dsde) { + if (Math.abs(dsde.getX() - testPoint1.getX()) < 5) { + passedTest1 = true; + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + if (Math.abs(dsde.getX() - testPoint2.getX()) < 5) { + passedTest2 = true; + } + } + }; + + private static final DropTargetListener targetAdapter = new DropTargetAdapter() { + public void drop(DropTargetDropEvent e) { + e.acceptDrop(DnDConstants.ACTION_COPY); + try { + final Transferable t = e.getTransferable(); + final String str = + (String) t.getTransferData(DataFlavor.stringFlavor); + e.dropComplete(true); + } catch (Exception ex) { + ex.printStackTrace(); + e.dropComplete(false); + } + } + }; + + private static final DropTarget dropTarget = new DropTarget(target, targetAdapter); + Component clickedComponent = null; + + private void createAndShowUI() { + frame = new Frame("DragSourceMotionListenerTest"); + ds.addDragSourceListener(sourceAdapter); + ds.addDragSourceMotionListener(sourceAdapter); + ds.createDefaultDragGestureRecognizer(source, DnDConstants.ACTION_COPY, gestureListener); + target.setDropTarget(dropTarget); + + frame.setLayout(new GridLayout(1, 2)); + + frame.add(source); + frame.add(target); + + Toolkit.getDefaultToolkit() + .addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); + frame.pack(); + frame.setVisible(true); + } + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + robot.setAutoDelay(10); + + DragSourceMotionListenerTest dsmObj = new DragSourceMotionListenerTest(); + EventQueue.invokeAndWait(dsmObj::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + srcPoint = getPoint(source, 1); + + dstOutsidePoint = getPoint(frame, 3); + testPoint1.setLocation(dstOutsidePoint); + + dstInsidePoint = getPoint(target, 1); + testPoint2.setLocation(dstInsidePoint); + }); + robot.waitForIdle(); + + if (!dsmObj.pointInComponent(robot, srcPoint, source)) { + throw new RuntimeException("WARNING: Couldn't locate source panel."); + } + + if (!dsmObj.pointInComponent(robot, dstInsidePoint, target)) { + throw new RuntimeException("WARNING: Couldn't locate target panel."); + } + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (; !srcPoint.equals(dstOutsidePoint); + srcPoint.translate(sign(dstOutsidePoint.x - srcPoint.x), + sign(dstOutsidePoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + } + + for (int i = 0; i < 10; i++) { + robot.mouseMove(srcPoint.x, srcPoint.y++); + } + + for (;!srcPoint.equals(dstInsidePoint); + srcPoint.translate(sign(dstInsidePoint.x - srcPoint.x), + sign(dstInsidePoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(1000); + + if (!passedTest1) { + throw new RuntimeException("Failed first test."); + } + + if (!passedTest2) { + throw new RuntimeException("Failed second test."); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static Point getPoint(Container container, int multiple) { + Point p = container.getLocationOnScreen(); + Dimension d = container.getSize(); + p.translate(multiple * d.width / 2, d.height / 2); + return p; + } + + public static int sign(int n) { + return Integer.compare(n, 0); + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component)e.getSource(); + mouseReleaseEvent.countDown(); + } + } + + boolean pointInComponent(Robot robot, Point p, Component comp) throws Exception { + robot.waitForIdle(); + clickedComponent = null; + mouseReleaseEvent = new CountDownLatch(1); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + if (!mouseReleaseEvent.await(2, TimeUnit.SECONDS)) { + throw new RuntimeException("Mouse Release Event not received"); + } + + Component c = clickedComponent; + while (c != null && c != comp) { + c = c.getParent(); + } + return c == comp; + } +} diff --git a/test/jdk/java/awt/dnd/DragThresholdTest.java b/test/jdk/java/awt/dnd/DragThresholdTest.java new file mode 100644 index 0000000000000..bcb3bbf91c2ce --- /dev/null +++ b/test/jdk/java/awt/dnd/DragThresholdTest.java @@ -0,0 +1,134 @@ +/* + * 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. + */ + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; + +/* + @test + @key headful + @bug 4415175 + @summary tests DragSource.getDragThreshold() and + that the AWT default drag gesture recognizers + honor the drag gesture motion threshold +*/ + +public class DragThresholdTest { + private static Frame frame; + private static Panel panel; + private static MouseEvent lastMouseEvent; + private static volatile boolean failed; + private static volatile Point startPoint; + private static volatile Point endPoint; + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + + EventQueue.invokeAndWait(DragThresholdTest::createAndShowDnD); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(() -> { + Point p = panel.getLocationOnScreen(); + p.translate(50, 50); + startPoint = p; + endPoint = new Point(p.x + 2 * DragSource.getDragThreshold(), + p.y + 2 * DragSource.getDragThreshold()); + }); + + robot.mouseMove(startPoint.x, startPoint.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (Point p = new Point(startPoint); !p.equals(endPoint); + p.translate(sign(endPoint.x - p.x), + sign(endPoint.y - p.y))) { + robot.mouseMove(p.x, p.y); + robot.delay(100); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(200); + + if (failed) { + throw new RuntimeException("drag gesture recognized too early"); + } + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void createAndShowDnD() { + frame = new Frame("DragThresholdTest"); + panel = new Panel(); + // Mouse motion listener mml is added to the panel first. + // We rely on it that this listener will be called first. + panel.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseDragged(MouseEvent evt) { + lastMouseEvent = evt; + System.out.println(evt); + } + }); + frame.add(panel); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + + DragGestureListener dgl = dge -> { + Point dragOrigin = dge.getDragOrigin(); + int diffx = Math.abs(dragOrigin.x - lastMouseEvent.getX()); + int diffy = Math.abs(dragOrigin.y - lastMouseEvent.getY()); + System.out.println("dragGestureRecognized(): " + + " diffx=" + diffx + " diffy=" + diffy + + " DragSource.getDragThreshold()=" + + DragSource.getDragThreshold()); + if (diffx <= DragSource.getDragThreshold() && + diffy <= DragSource.getDragThreshold()) { + failed = true; + System.out.println("drag gesture recognized too early!"); + } + }; + + // Default drag gesture recognizer is a mouse motion listener. + // It is added to the panel second. + new DragSource().createDefaultDragGestureRecognizer( + panel, + DnDConstants.ACTION_COPY_OR_MOVE, dgl); + frame.setVisible(true); + } + + private static int sign(int n) { + return Integer.compare(n, 0); + } +} diff --git a/test/jdk/java/awt/dnd/DragToAnotherScreenTest.java b/test/jdk/java/awt/dnd/DragToAnotherScreenTest.java new file mode 100644 index 0000000000000..89f18061845fe --- /dev/null +++ b/test/jdk/java/awt/dnd/DragToAnotherScreenTest.java @@ -0,0 +1,169 @@ +/* + * 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 + * 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.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Label; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.util.List; + +import javax.swing.JOptionPane; + +/* + * @test + * @bug 6179157 + * @key multimon + * @summary Tests dnd to another screen + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DragToAnotherScreenTest + */ + +public class DragToAnotherScreenTest { + private static Label label0; + private static Label label1; + private static final int HGAP = 20; + + private static final String INSTRUCTIONS = """ + The following test is applicable for Single as well + as Multi-monitor screens. + + It is a semi-automated test, the test will prompt + the user whether the drag and drop action was successful or not + and automatically PASS/FAIL the test. + + If on multi-monitor screens then please position + the drag and drop windows on different screens. + + If you can not move the mouse from the frame "Drag Source" + to the frame "Drop Target" press PASS, + else proceed to the next step. + + Drag the label "Drag me" and drop it on the + label "Drop on me". + + If you can not drag to the second label (for example + if you can not drag across screens) press FAIL. + + After the drag and drop action, the test displays + Success/Failure msg in JOptionPane. + Click on OK button and the test is configured to + automatically PASS/FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(DragToAnotherScreenTest::createAndShowUI) + .positionTestUI(DragToAnotherScreenTest::positionMultiTestUI) + .logArea(10) + .build() + .awaitAndCheck(); + } + + private static List createAndShowUI() { + PassFailJFrame.log("----- System Configuration ----"); + PassFailJFrame.log("Toolkit:" + Toolkit.getDefaultToolkit() + .getClass() + .getName()); + + GraphicsDevice[] gd = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getScreenDevices(); + if (gd.length == 1) { + PassFailJFrame.log("Single Monitor"); + } else { + PassFailJFrame.log("Multi-Monitor"); + } + PassFailJFrame.log("--------------"); + PassFailJFrame.log("Test logs:\n"); + Frame frame0 = new Frame("Drag Source", gd[0].getDefaultConfiguration()); + frame0.setSize(300, 300); + label0 = new Label("Drag me"); + frame0.add(label0); + + Frame frame1 = new Frame("Drop Target", gd[(gd.length > 1 ? 1 : 0)].getDefaultConfiguration()); + frame1.setSize(300, 300); + label1 = new Label("Drop on me"); + frame1.add(label1); + + DragGestureListener dragGestureListener = dge -> dge.startDrag(null, new StringSelection(label0.getText()), null); + new DragSource().createDefaultDragGestureRecognizer(label0, + DnDConstants.ACTION_COPY, dragGestureListener); + + DropTargetAdapter dropTargetAdapter = new DropTargetAdapter() { + public void drop(DropTargetDropEvent dtde) { + Transferable t = dtde.getTransferable(); + if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + try { + String str = (String) t.getTransferData(DataFlavor.stringFlavor); + label1.setText(str); + JOptionPane.showMessageDialog(frame0, + "getTransferData was successful", + "Test Passed", JOptionPane.PLAIN_MESSAGE); + } catch (Exception e) { + dtde.dropComplete(false); + e.printStackTrace(); + PassFailJFrame.log("getTransferData() Failed"); + JOptionPane.showMessageDialog(frame0, + "getTransferData() Failed", + "Test Failed", JOptionPane.ERROR_MESSAGE); + PassFailJFrame.forceFail("getTransferData() Failed"); + } + dtde.dropComplete(true); + } else { + dtde.rejectDrop(); + PassFailJFrame.log("stringFlavor is not supported by Transferable"); + JOptionPane.showMessageDialog(frame0, + "stringFlavor is not supported by Transferable", + "Test Failed", JOptionPane.ERROR_MESSAGE); + PassFailJFrame.forceFail("stringFlavor is not supported by Transferable"); + } + } + }; + new DropTarget(label1, dropTargetAdapter); + return List.of(frame0, frame1); + } + + private static void positionMultiTestUI(List windows, + PassFailJFrame.InstructionUI instructionUI) { + int x = instructionUI.getLocation().x + instructionUI.getSize().width + HGAP; + for (Window w : windows) { + w.setLocation(x, instructionUI.getLocation().y); + x += w.getWidth() + HGAP; + } + } +} diff --git a/test/jdk/java/awt/dnd/NonAsciiFilenames.java b/test/jdk/java/awt/dnd/NonAsciiFilenames.java new file mode 100644 index 0000000000000..b508350c05ef3 --- /dev/null +++ b/test/jdk/java/awt/dnd/NonAsciiFilenames.java @@ -0,0 +1,158 @@ +/* + * 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 + * 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 4187490 + * @summary Verify that Non-ASCII file names can be dragged and dropped + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual NonAsciiFilenames + */ + +import java.awt.Color; +import java.awt.datatransfer.DataFlavor; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.File; +import java.util.AbstractList; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +public class NonAsciiFilenames { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test must be run on an OS which does not use ISO 8859-1 + as its default encoding. + + Open a native file browsing application, such as Windows + Explorer. Try to find a file whose name uses non-ISO 8859-1 + characters. Create a file and name it such that it contains + non-ISO 8859-1 characters (For ex. é, à, ö, €, ¥). Drag + the file from the native application and drop it on the test + Frame. If the file name appears normally, then the test passes. + If boxes or question marks appear for characters, or if you see + the word "Error", then the test fails. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(NonAsciiFilenames::createUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + JFrame frame = new JFrame(); + frame.setTitle("DropLabel test"); + frame.getContentPane().add(new DropLabel("Drop here")); + frame.setSize(300, 100); + return frame; + } +} + +class DropLabel extends JLabel implements DropTargetListener { + public DropLabel(String s) { + setText(s); + new DropTarget(this, DnDConstants.ACTION_COPY, this, true); + showDrop(false); + } + + private void showDrop(boolean b) { + setForeground(b ? Color.white : Color.black); + } + + /** + * Configure to desired flavor of dropped data. + */ + private DataFlavor getDesiredFlavor() { + return DataFlavor.javaFileListFlavor; + } + + /** + * Check to make sure that the contains the expected object types. + */ + private void checkDroppedData(Object data) { + System.out.println("Got data: " + data.getClass().getName()); + if (data instanceof AbstractList) { + AbstractList files = (AbstractList) data; + if (((File) files.get(0)).isFile()) + setText(((File) files.get(0)).toString()); + else + setText("Error: not valid file: " + + ((File) files.get(0)).toString()); + } else { + System.out.println("Error: wrong type of data dropped"); + } + } + + private boolean isDragOk(DropTargetDragEvent e) { + boolean canDrop = false; + try { + canDrop = e.isDataFlavorSupported(getDesiredFlavor()); + } catch (Exception ex) { + } + + if (canDrop) + e.acceptDrag(DnDConstants.ACTION_COPY); + else + e.rejectDrag(); + showDrop(canDrop); + return canDrop; + } + + public void dragEnter(DropTargetDragEvent e) { + isDragOk(e); + } + + + public void dragOver(DropTargetDragEvent e) { + isDragOk(e); + } + + public void dropActionChanged(DropTargetDragEvent e) { + isDragOk(e); + } + + public void dragExit(DropTargetEvent e) { + showDrop(false); + } + + public void drop(DropTargetDropEvent e) { + try { + e.acceptDrop(DnDConstants.ACTION_COPY); + checkDroppedData(e.getTransferable(). + getTransferData(getDesiredFlavor())); + } catch (Exception err) { + } + e.dropComplete(true); + showDrop(false); + } +} diff --git a/test/jdk/java/awt/dnd/RejectDragTest.java b/test/jdk/java/awt/dnd/RejectDragTest.java new file mode 100644 index 0000000000000..c65612436bc38 --- /dev/null +++ b/test/jdk/java/awt/dnd/RejectDragTest.java @@ -0,0 +1,174 @@ +/* + * 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 + * 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; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.StringSelection; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.InputEvent; + +/* + * @test + * @key headful + * @bug 4407521 + * @summary Tests that DragSourceListener.dragEnter() and + DragSourceListener.dragOver() are not called after + drag rejecting, but DragSourceListener.dragExit() is. + */ + +public class RejectDragTest { + private static Frame frame; + private static Robot robot; + private static volatile boolean dragEnterCalled; + private static volatile boolean dragOverCalled; + private static volatile boolean dragExitCalledAtFirst; + private static volatile Point startPoint; + private static volatile Point endPoint; + + public static void main(String[] args) throws Exception { + try { + robot = new Robot(); + + EventQueue.invokeAndWait(RejectDragTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + EventQueue.invokeAndWait(RejectDragTest::addDnDListeners); + robot.waitForIdle(); + + testDnD(); + robot.waitForIdle(); + robot.delay(200); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void addDnDListeners() { + final DragSourceListener dragSourceListener = new DragSourceAdapter() { + private boolean first = true; + + public void dragEnter(DragSourceDragEvent dsde) { + first = false; + dragEnterCalled = true; + } + + public void dragExit(DragSourceEvent dse) { + if (first) { + dragExitCalledAtFirst = true; + first = false; + } + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + first = false; + } + + public void dragOver(DragSourceDragEvent dsde) { + first = false; + dragOverCalled = true; + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + first = false; + } + }; + + DragGestureListener dragGestureListener = + dge -> dge.startDrag(null, new StringSelection("OKAY"), + dragSourceListener); + new DragSource().createDefaultDragGestureRecognizer(frame, + DnDConstants.ACTION_COPY, + dragGestureListener); + + DropTargetAdapter dropTargetListener = new DropTargetAdapter() { + public void dragEnter(DropTargetDragEvent dtde) { + dtde.rejectDrag(); + } + + public void drop(DropTargetDropEvent dtde) { + dtde.rejectDrop(); + } + }; + + new DropTarget(frame, dropTargetListener); + } + + private static void createAndShowUI() { + frame = new Frame("RejectDragTest"); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void testDnD() throws Exception { + EventQueue.invokeAndWait(() -> { + Point start = frame.getLocationOnScreen(); + start.translate(50, 50); + startPoint = start; + + Point end = new Point(start); + end.translate(150, 150); + endPoint = end; + }); + + robot.mouseMove(startPoint.x, startPoint.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + for (Point p = new Point(startPoint); !p.equals(endPoint); + p.translate(sign(endPoint.x - p.x), + sign(endPoint.y - p.y))) { + robot.mouseMove(p.x, p.y); + robot.delay(30); + } + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + if (dragEnterCalled || dragOverCalled) { + throw new RuntimeException("Test failed: " + + (dragEnterCalled ? "DragSourceListener.dragEnter() was called; " : "") + + (dragOverCalled ? "DragSourceListener.dragOver() was called; " : "") + + (!dragExitCalledAtFirst ? "DragSourceListener.dragExit() was not " + + "called immediately after rejectDrag() " : "")); + } + } + + public static int sign(int n) { + return Integer.compare(n, 0); + } +} diff --git a/test/jdk/java/awt/dnd/URLDragTest.java b/test/jdk/java/awt/dnd/URLDragTest/URLDragTest.java similarity index 100% rename from test/jdk/java/awt/dnd/URLDragTest.java rename to test/jdk/java/awt/dnd/URLDragTest/URLDragTest.java diff --git a/test/jdk/java/awt/dnd/WinMoveFileToShellTest.java b/test/jdk/java/awt/dnd/WinMoveFileToShellTest.java new file mode 100644 index 0000000000000..5ae7dd3890d71 --- /dev/null +++ b/test/jdk/java/awt/dnd/WinMoveFileToShellTest.java @@ -0,0 +1,131 @@ +/* + * 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 + * 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.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceListener; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @bug 4414739 + * @requires (os.family == "windows") + * @summary verifies that getDropSuccess() returns correct value for moving + a file from a Java drag source to the Windows shell + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual WinMoveFileToShellTest + */ + +public class WinMoveFileToShellTest { + private static final String INSTRUCTIONS = """ + Drag from the frame titled "Drag Frame" and drop on to Windows Desktop. + After Drag and Drop, check for "Drop Success" status in the log area. + If "Drop Success" is true press PASS else FAIL. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(WinMoveFileToShellTest::createAndShowUI) + .logArea(5) + .build() + .awaitAndCheck(); + } + + private static Frame createAndShowUI() { + Frame frame = new Frame("Drag Frame"); + final DragSourceListener dsl = new DragSourceAdapter() { + public void dragDropEnd(DragSourceDropEvent e) { + PassFailJFrame.log("Drop Success: " + e.getDropSuccess()); + } + }; + + DragGestureListener dgl = dge -> { + File file = new File(System.getProperty("test.classes", ".") + + File.separator + "move.me"); + try { + file.createNewFile(); + } catch (IOException exc) { + exc.printStackTrace(); + } + ArrayList list = new ArrayList<>(); + list.add(file); + dge.startDrag(null, new FileListSelection(list), dsl); + }; + + new DragSource().createDefaultDragGestureRecognizer(frame, + DnDConstants.ACTION_MOVE, dgl); + frame.setSize(200, 100); + return frame; + } + + private static class FileListSelection implements Transferable { + private static final int FL = 0; + + private static final DataFlavor[] flavors = + new DataFlavor[] { DataFlavor.javaFileListFlavor }; + + + private List data; + + public FileListSelection(List data) { + this.data = data; + } + + public DataFlavor[] getTransferDataFlavors() { + return flavors.clone(); + } + + public boolean isDataFlavorSupported(DataFlavor flavor) { + for (DataFlavor dataFlavor : flavors) { + if (flavor.equals(dataFlavor)) { + return true; + } + } + return false; + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException + { + if (flavor.equals(flavors[FL])) { + return data; + } else { + throw new UnsupportedFlavorException(flavor); + } + } + } +} diff --git a/test/jdk/java/awt/event/KeyEvent/FunctionKeyTest.java b/test/jdk/java/awt/event/KeyEvent/FunctionKeyTest.java index 02e7a890d83e4..78043cb5abdf4 100644 --- a/test/jdk/java/awt/event/KeyEvent/FunctionKeyTest.java +++ b/test/jdk/java/awt/event/KeyEvent/FunctionKeyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,18 @@ import java.awt.Robot; import java.awt.TextArea; import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; + +import static java.awt.Event.KEY_ACTION; +import static java.awt.Event.KEY_ACTION_RELEASE; +import static java.util.concurrent.TimeUnit.SECONDS; /* * @test @@ -39,35 +51,38 @@ * @key headful */ -public class FunctionKeyTest { - private static FunctionKeyTester frame; +public final class FunctionKeyTest { + private static Frame frame; private static Robot robot; - static volatile boolean keyPressReceived; - static volatile boolean keyReleaseReceived; + private static final CyclicBarrier keyPress = new CyclicBarrier(2); + private static final CyclicBarrier keyRelease = new CyclicBarrier(2); - static final StringBuilder failures = new StringBuilder(); + private static final CountDownLatch frameActivated = new CountDownLatch(1); - private static void testKey(int keyCode, String keyText) { - keyPressReceived = false; - keyReleaseReceived = false; + private static final List failures = new ArrayList<>(4); + private static final AtomicReference edtException = new AtomicReference<>(); + private static void testKey(int keyCode, String keyText) throws Exception { robot.keyPress(keyCode); - - if (!keyPressReceived) { - failures.append(keyText).append(" key press is not received\n"); + try { + keyPress.await(2, SECONDS); + } catch (TimeoutException e) { + keyPress.reset(); + failures.add(new Error(keyText + " key press is not received", e)); } robot.keyRelease(keyCode); - - if (!keyReleaseReceived) { - failures.append(keyText).append(" key release is not received\n"); + try { + keyRelease.await(2, SECONDS); + } catch (TimeoutException e) { + keyRelease.reset(); + failures.add(new Error(keyText + " key release is not received", e)); } } public static void main(String[] args) throws Exception { robot = new Robot(); - robot.setAutoWaitForIdle(true); robot.setAutoDelay(150); try { @@ -75,11 +90,20 @@ public static void main(String[] args) throws Exception { frame = new FunctionKeyTester(); frame.setSize(200, 200); frame.setLocationRelativeTo(null); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowActivated(WindowEvent e) { + System.out.println("frame.windowActivated"); + frameActivated.countDown(); + } + }); frame.setVisible(true); }); - robot.waitForIdle(); - robot.delay(1000); + if (!frameActivated.await(2, SECONDS)) { + throw new Error("Frame wasn't activated"); + } + robot.delay(100); testKey(KeyEvent.VK_F11, "F11"); testKey(KeyEvent.VK_F12, "F12"); @@ -91,46 +115,69 @@ public static void main(String[] args) throws Exception { }); } - if (failures.isEmpty()) { - System.out.println("Passed"); - } else { - throw new RuntimeException(failures.toString()); + if (!failures.isEmpty()) { + System.err.println("Failures detected:"); + failures.forEach(System.err::println); + if (edtException.get() != null) { + System.err.println("\nException on EDT:"); + edtException.get().printStackTrace(); + } + System.err.println(); + throw new RuntimeException("Test failed: " + failures.get(0).getMessage(), + failures.get(0)); } - } -} -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); + if (edtException.get() != null) { + throw new RuntimeException("Test failed because of exception on EDT", + edtException.get()); + } } - 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; + private static final class FunctionKeyTester extends Frame { + Label l = new Label ("NULL"); + Button b = new Button("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); } - return super.handleEvent(e); - } + @Override + @SuppressWarnings("deprecation") + public boolean handleEvent(Event e) { + String message = "e.id=" + e.id + "\n"; + System.out.print(message); + log.append(message); + + try { + switch (e.id) { + case KEY_ACTION + -> keyPress.await(); + case KEY_ACTION_RELEASE + -> keyRelease.await(); + } + } catch (Exception ex) { + if (!edtException.compareAndSet(null, ex)) { + edtException.get().addSuppressed(ex); + } + } - public boolean keyDown(Event e, int key) { - l.setText("e.key=" + Integer.valueOf(e.key).toString()); - return false; + return super.handleEvent(e); + } + + @Override + @SuppressWarnings("deprecation") + public boolean keyDown(Event e, int key) { + l.setText("e.key=" + e.key); + return false; + } } } diff --git a/test/jdk/java/awt/event/KeyEvent/KeyTyped/Numpad1KeyTyped.java b/test/jdk/java/awt/event/KeyEvent/KeyTyped/Numpad1KeyTyped.java new file mode 100644 index 0000000000000..a944be855810a --- /dev/null +++ b/test/jdk/java/awt/event/KeyEvent/KeyTyped/Numpad1KeyTyped.java @@ -0,0 +1,178 @@ +/* + * 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 + * 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.Robot; +import java.awt.TextField; +import java.awt.Toolkit; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.concurrent.CountDownLatch; + +import jdk.test.lib.Platform; + +import static java.util.concurrent.TimeUnit.SECONDS; + +/* + * @test + * @bug 4724007 + * @key headful + * @summary Tests that KeyTyped events are fired for the Numpad1 key + * @library /test/lib + * @build jdk.test.lib.Platform + * @run main Numpad1KeyTyped + */ +public final class Numpad1KeyTyped extends FocusAdapter implements KeyListener { + + private static final String ORIGINAL = "0123456789"; + private static final String EXPECTED = "10123456789"; + + private final CountDownLatch typedNum1 = new CountDownLatch(1); + private final CountDownLatch focusGained = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + Numpad1KeyTyped test = new Numpad1KeyTyped(); + test.start(); + } + + private void start() throws Exception { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Boolean oldState = null; + + Robot robot = new Robot(); + robot.setAutoDelay(100); + + Frame frame = new Frame("Numpad1KeyTyped"); + TextField tf = new TextField(ORIGINAL, 20); + frame.add(tf); + tf.addKeyListener(this); + + tf.addFocusListener(this); + + frame.setSize(300, 100); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + tf.requestFocusInWindow(); + + if (!focusGained.await(2, SECONDS)) { + throw new RuntimeException("TextField didn't receive focus"); + } + robot.waitForIdle(); + + try { + // Move cursor to start of TextField + robot.keyPress(KeyEvent.VK_HOME); + robot.keyRelease(KeyEvent.VK_HOME); + robot.waitForIdle(); + + if (Platform.isLinux()) { + // Press but don't release NumLock + robot.keyPress(KeyEvent.VK_NUM_LOCK); + } + if (Platform.isWindows()) { + oldState = toolkit.getLockingKeyState(KeyEvent.VK_NUM_LOCK); + toolkit.setLockingKeyState(KeyEvent.VK_NUM_LOCK, true); + } + + // Press and release Numpad-1 + robot.keyPress(KeyEvent.VK_NUMPAD1); + robot.keyRelease(KeyEvent.VK_NUMPAD1); + + if (!typedNum1.await(2, SECONDS)) { + throw new RuntimeException("TextField didn't receive keyTyped('1') - too slow"); + } + + final String text = tf.getText(); + if (!text.equals(EXPECTED)) { + throw new RuntimeException("Test FAILED: wrong string '" + + text + "' vs " + + "expected '" + EXPECTED + "'"); + } + System.out.println("Test PASSED"); + } finally { + if (Platform.isLinux()) { + // "release" + "press and release" NumLock to disable numlock + robot.keyRelease(KeyEvent.VK_NUM_LOCK); + robot.keyPress(KeyEvent.VK_NUM_LOCK); + robot.keyRelease(KeyEvent.VK_NUM_LOCK); + } + if (oldState != null) { + toolkit.setLockingKeyState(KeyEvent.VK_NUM_LOCK, oldState); + } + + frame.dispose(); + } + } + + @Override + public void focusGained(FocusEvent e) { + System.out.println("tf.focusGained"); + focusGained.countDown(); + } + + @Override + public void keyPressed(KeyEvent evt) { + printKey(evt); + } + + @Override + public void keyTyped(KeyEvent evt) { + printKey(evt); + + int keychar = evt.getKeyChar(); + if (keychar == '1') { + typedNum1.countDown(); + } + } + + @Override + public void keyReleased(KeyEvent evt) { + printKey(evt); + System.out.println(); + } + + private static void printKey(KeyEvent evt) { + int id = evt.getID(); + if (id != KeyEvent.KEY_TYPED + && id != KeyEvent.KEY_PRESSED + && id != KeyEvent.KEY_RELEASED) { + + System.out.println("Other Event"); + return; + } + + System.out.println("params= " + evt.paramString() + " \n" + + "KeyChar: " + evt.getKeyChar() + " = " + (int) evt.getKeyChar() + + " KeyCode: " + evt.getKeyCode() + + " Modifiers: " + evt.getModifiersEx()); + + if (evt.isActionKey()) { + System.out.println(" Action Key"); + } + + System.out.println("keyText= " + KeyEvent.getKeyText(evt.getKeyCode()) + "\n"); + } + +} diff --git a/test/jdk/java/awt/font/FontScaling/RotatedScaledFontTest.java b/test/jdk/java/awt/font/FontScaling/RotatedScaledFontTest.java new file mode 100644 index 0000000000000..3e17bbd92c18d --- /dev/null +++ b/test/jdk/java/awt/font/FontScaling/RotatedScaledFontTest.java @@ -0,0 +1,149 @@ +/* + * 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.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; + +import javax.imageio.ImageIO; + +/* + * @test + * @bug 8339974 + * @summary Verifies that text draws correctly using scaled and rotated fonts. + */ +public class RotatedScaledFontTest { + + public static void main(String[] args) throws Exception { + test(0); + test(1); + test(2); + test(3); + test(4); + } + + private static void test(int quadrants) throws Exception { + + int size = 2000; + int center = size / 2; + Font base = new Font("SansSerif", Font.PLAIN, 10); + BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_BINARY); + Graphics2D g2d = image.createGraphics(); + + try { + for (int scale = 1; scale <= 100; scale++) { + AffineTransform at = AffineTransform.getQuadrantRotateInstance(quadrants); + at.scale(scale, scale); + Font font = base.deriveFont(at); + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, image.getWidth(), image.getHeight()); + g2d.setColor(Color.BLACK); + g2d.setFont(font); + g2d.drawString("TEST", center, center); + Rectangle bounds = findTextBoundingBox(image); + if (bounds == null) { + saveImage("bounds", image); + throw new RuntimeException("Text missing: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center); + } + boolean horizontal = (bounds.width > bounds.height); + boolean expectedHorizontal = (quadrants % 2 == 0); + if (horizontal != expectedHorizontal) { + saveImage("orientation", image); + throw new RuntimeException("Wrong orientation: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center + + ", bounds=" + bounds + ", horizontal=" + horizontal + + ", expectedHorizontal=" + expectedHorizontal); + } + if (!roughlyEqual(center, bounds.x, scale) && !roughlyEqual(center, bounds.x + bounds.width, scale)) { + saveImage("xedge", image); + throw new RuntimeException("No x-edge at center: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center + + ", bounds=" + bounds); + } + if (!roughlyEqual(center, bounds.y, scale) && !roughlyEqual(center, bounds.y + bounds.height, scale)) { + saveImage("yedge", image); + throw new RuntimeException("No y-edge at center: scale=" + scale + + ", quadrants=" + quadrants + ", center=" + center + + ", bounds=" + bounds); + } + } + } finally { + g2d.dispose(); + } + } + + private static Rectangle findTextBoundingBox(BufferedImage image) { + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + int width = image.getWidth(); + int height = image.getHeight(); + int[] rowPixels = new int[width]; + for (int y = 0; y < height; y++) { + image.getRGB(0, y, width, 1, rowPixels, 0, width); + for (int x = 0; x < width; x++) { + boolean white = (rowPixels[x] == -1); + if (!white) { + if (x < minX) { + minX = x; + } + if (y < minY) { + minY = y; + } + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + } + } + } + if (minX != Integer.MAX_VALUE) { + return new Rectangle(minX, minY, maxX - minX, maxY - minY); + } else { + return null; + } + } + + private static boolean roughlyEqual(int x1, int x2, int scale) { + return Math.abs(x1 - x2) <= Math.ceil(scale / 2d) + 1; // higher scale = higher allowed variance + } + + private static void saveImage(String name, BufferedImage image) { + try { + String dir = System.getProperty("test.classes", "."); + String path = dir + File.separator + name + ".png"; + File file = new File(path); + ImageIO.write(image, "png", file); + } catch (Exception e) { + // we tried, and that's enough + } + } +} diff --git a/test/jdk/java/awt/geom/Arc2D/Arc2DHitTest.java b/test/jdk/java/awt/geom/Arc2D/Arc2DHitTest.java new file mode 100644 index 0000000000000..de6b39d5c6f7f --- /dev/null +++ b/test/jdk/java/awt/geom/Arc2D/Arc2DHitTest.java @@ -0,0 +1,100 @@ +/* + * 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 4178123 + * @summary Verifies that the Arc2D.contains(point) methods work correctly. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Arc2DHitTest + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.Arc2D; + +public class Arc2DHitTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays an arc figure and lets the user click on it. + The arc will initially be drawn in red and only when the user clicks + within the arc in the window it will be redrawn into green otherwise + it should stay red. + + For convenience, the point being tested is drawn in black. Note + that rounding in the arc rendering routines may cause points near + the boundary of the arc to render incorrectly. Allow for a pixel + or two of leeway near the boundary. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + private static Frame initialize() { + Frame f = new Frame("Arc2DHitTest"); + ArcHitPanel panel = new ArcHitPanel(); + f.add(panel); + f.setSize(300, 250); + return f; + } +} + +class ArcHitPanel extends Panel { + private Arc2D arc; + private Point hit; + public ArcHitPanel() { + arc = new Arc2D.Float(10, 10, 100, 100, 0, 120, Arc2D.PIE); + this.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + hit = e.getPoint(); + repaint(); + } + }); + } + + @Override + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + g2.setColor(Color.white); + g2.fill(g2.getClipBounds()); + g2.setColor((hit != null && arc.contains(hit)) + ? Color.green : Color.red); + g2.fill(arc); + if (hit != null) { + g2.setColor(Color.black); + g2.fillRect(hit.x, hit.y, 1, 1); + } + } +} diff --git a/test/jdk/java/awt/geom/Arc2D/BoundsBug.java b/test/jdk/java/awt/geom/Arc2D/BoundsBug.java new file mode 100644 index 0000000000000..a17f45f9dad37 --- /dev/null +++ b/test/jdk/java/awt/geom/Arc2D/BoundsBug.java @@ -0,0 +1,123 @@ +/* + * 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 4197746 + * @summary Verifies that the getBounds2D method of Arc2D returns the + * correct result. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual BoundsBug + */ + +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +public class BoundsBug { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays three figures and draws the outline of their + bounding boxes. The bounding boxes should correctly encompass + the 3 figures. + + This test also paints two highlight rectangles at the ends of the + angular extents of the arc. The two highlights should correctly + appear at the outer circumference of the arc where the radii lines + from its center intersect that circumference. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + private static Frame initialize() { + Frame f = new Frame("BoundsBug"); + ArcPanel panel = new ArcPanel(); + f.add(panel); + f.setSize(300, 250); + return f; + } +} + +class ArcPanel extends Panel { + protected void drawPoint(Graphics2D g2, Point2D p) { + g2.setColor(Color.green); + g2.fill(new Rectangle2D.Double(p.getX() - 5, p.getY() - 5, 10, 10)); + } + + protected void drawShapeAndBounds(Graphics2D g2, Shape s) { + g2.setColor(Color.orange); + g2.fill(s); + g2.setColor(Color.black); + g2.draw(s); + + Rectangle2D r = s.getBounds2D(); + g2.setColor(Color.gray); + g2.draw(r); + } + + @Override + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D)g; + g2.setColor(Color.white); + g2.fill(g.getClipBounds()); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + // Create some interesting shapes. + Ellipse2D ellipse = new Ellipse2D.Float(20, 40, 60, 80); + Arc2D arc = new Arc2D.Float(60, 40, 100, 120, + -30, -40, Arc2D.PIE); + GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD); + path.moveTo(0, 0); + path.lineTo(75, -25); + path.lineTo(25, 75); + path.lineTo(0, 25); + path.lineTo(100, 50); + path.lineTo(50, 0); + path.lineTo(25, 50); + path.closePath(); + // Now draw them and their bounds rectangles. + drawShapeAndBounds(g2, ellipse); + drawShapeAndBounds(g2, arc); + drawPoint(g2, arc.getStartPoint()); + drawPoint(g2, arc.getEndPoint()); + g2.translate(180, 65); + drawShapeAndBounds(g2, path); + } +} diff --git a/test/jdk/java/awt/geom/Area/Translate.java b/test/jdk/java/awt/geom/Area/Translate.java new file mode 100644 index 0000000000000..7519f1753bcf3 --- /dev/null +++ b/test/jdk/java/awt/geom/Area/Translate.java @@ -0,0 +1,122 @@ +/* + * 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 4183373 + * @summary Verifies that the translated Area objects display correctly + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Translate + */ + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.Rectangle2D; + +public class Translate { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + This test displays two sets of rectangular figures. The two sets + should be displayed one on top of the other and should be lined + up vertically with each other. If the two sets of figures are + not directly above and below each other then the test fails + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(INSTRUCTIONS) + .columns(35) + .testUI(initialize()) + .build() + .awaitAndCheck(); + } + private static Frame initialize() { + Frame f = new Frame("Translate"); + TranslatePanel panel = new TranslatePanel(); + f.add(panel); + f.setSize(300, 250); + return f; + } +} + +class TranslatePanel extends Panel { + private static Image bufferedImage; + private static Area a1, a2, a3; + + public TranslatePanel() { + a1 = new Area(new Rectangle2D.Double(20.0, 20.0, 60.0, 60.0)); + + a2 = new Area((Area) a1.clone()); + a2.subtract(new Area(new Rectangle2D.Double(30.0, 30.0, 40.0, 40.0))); + + a3 = new Area((Area) a2.clone()); + a3.add(new Area(new Rectangle2D.Double(40.0, 40.0, 20.0, 20.0))); + + AffineTransform at2 = new AffineTransform(); + at2.translate(100.0, 0.0); + a2.transform(at2); + + AffineTransform at3 = new AffineTransform(); + at3.translate(200.0, 0.0); + a3.transform(at3); + } + private void paintRects(Graphics2D g2) { + Rectangle clip = g2.getClipBounds(); + g2.setColor(Color.white); + g2.fillRect(clip.x, clip.y, clip.width, clip.height); + g2.setPaint(Color.red); + g2.fill(a1); + g2.setPaint(Color.yellow); + g2.fill(a2); + g2.setPaint(Color.blue); + g2.fill(a3); + } + + @Override + public void paint(Graphics g) { + if (bufferedImage == null) { + bufferedImage = createImage(300, 100); + Graphics big = bufferedImage.getGraphics(); + // Notice that if you remove the translate() call, it works fine. + big.translate(-1, -1); + big.setClip(1, 1, 300, 100); + paintRects((Graphics2D)big); + big.translate(1, 1); + } + paintRects((Graphics2D)g); + g.drawImage(bufferedImage, 1, 100, this); + g.setColor(Color.black); + g.drawString("From offscreen image (with translate):", 10, 95); + g.drawString(" (should line up with rectangles above)", 10, 110); + } +} diff --git a/test/jdk/java/awt/grab/CursorTest.java b/test/jdk/java/awt/grab/CursorTest.java new file mode 100644 index 0000000000000..f08008cbd8f76 --- /dev/null +++ b/test/jdk/java/awt/grab/CursorTest.java @@ -0,0 +1,101 @@ +/* + * 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 + * 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 6364746 6400007 + * @summary Cursor should be changed correctly while Swing menu is open (input is grabbed). + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CursorTest +*/ + +import java.awt.FlowLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +public class CursorTest { + + static final String INSTRUCTIONS = """ + After the test starts you will see a frame titled "Cursor Test Window", + with two menus in the menubar (menu1 and menu2), and a textfield and + a button, labeled "JButton". + 1. Open menu1 (it should be small and fit within the border of the frame), + 2. Verify that the pointer image (cursor) is the default desktop cursor. + 3. Move the mouse over the text field - the cursor should change its shape to caret, + 4. Move the mouse over the button - the cursor should be default one, + 5. Move the mouse to the border of the frame - cursor should be a resize one + (exact shape is dependent on the border you move over), + 6. Move the mouse out of the frame - cursor should be default one, + 7. Perform steps 2-6 in reverse order (after this the mouse should be over the open menu1), + 8. Open menu2, it should be big enough to not fit within the frame, + 9. Repeat steps 2-7 (you should end up with mouse over opened menu2 :), + 10. Close the menu. + 11. If on every step the cursor was as described, press Pass, press Fail otherwise. + """; + + static JFrame createUI() { + + JButton but = new JButton("JButton"); + JPanel panel = new JPanel(); + JTextField jtf = new JTextField("JTextField", 20); + + JFrame.setDefaultLookAndFeelDecorated(true); + JFrame frame = new JFrame("Cursor Test Window"); + frame.setLayout(new FlowLayout()); + panel.add(but); + + frame.getContentPane().add(jtf); + frame.getContentPane().add(panel); + + JMenu menu1 = new JMenu("menu1"); + menu1.add(new JMenuItem("menu1,item1")); + JMenuBar mb = new JMenuBar(); + mb.add(menu1); + JMenu menu2 = new JMenu("menu2"); + for (int i = 0; i < 10; i++) { + menu2.add(new JMenuItem("menu2,item"+i)); + } + mb.add(menu2); + frame.setJMenuBar(mb); + frame.pack(); + return frame; + } + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("Cursor Test") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(CursorTest::createUI) + .build() + .awaitAndCheck(); + + } +} diff --git a/test/jdk/java/awt/grab/SystemMenuTest.java b/test/jdk/java/awt/grab/SystemMenuTest.java new file mode 100644 index 0000000000000..07676b3191124 --- /dev/null +++ b/test/jdk/java/awt/grab/SystemMenuTest.java @@ -0,0 +1,141 @@ +/* + * 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 + * 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 6364741 + * @key headful + * @requires (os.family == "windows") + * @summary REG: Using frame's menu actions does not make swing menu disappear on WinXP, + * since Mustang-b53 + */ + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.Point; +import java.awt.Robot; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; + +public class SystemMenuTest implements MenuListener { + + static volatile JMenu menu; + static volatile JMenu sub_menu; + static volatile JFrame frame; + + static volatile int selectCount = 0; + static volatile int deselectCount = 0; + static volatile boolean failed = false; + static volatile String reason = "none"; + + static void createUI() { + SystemMenuTest smt = new SystemMenuTest(); + sub_menu = new JMenu("SubMenu"); + sub_menu.addMenuListener(smt); + sub_menu.add(new JMenuItem("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")); + sub_menu.add(new JMenuItem("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB")); + menu = new JMenu("Menu"); + menu.addMenuListener(smt); + menu.add(sub_menu); + JMenuBar mb = new JMenuBar(); + mb.add(menu); + + frame = new JFrame("JFrame"); + frame.setJMenuBar(mb); + frame.pack(); + frame.setVisible(true); + } + + public static void main(String[] args) throws Exception { + + Robot robot = new Robot(); + + SwingUtilities.invokeAndWait(SystemMenuTest::createUI); + + try { + robot.waitForIdle(); + robot.delay(2000); + + Point p = menu.getLocationOnScreen(); + robot.mouseMove(p.x + menu.getWidth() / 2, p.y + menu.getHeight() / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(2000); + + p = sub_menu.getLocationOnScreen(); + robot.mouseMove(p.x + sub_menu.getWidth() / 2, p.y + sub_menu.getHeight() /2 ); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.waitForIdle(); + robot.delay(2000); + + // Alt-Space to invoke System Menu, should close Swing menus. + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_SPACE); + robot.delay(50); + robot.keyRelease(KeyEvent.VK_SPACE); + robot.keyRelease(KeyEvent.VK_ALT); + robot.waitForIdle(); + robot.delay(2000); + + if (selectCount != 2 || deselectCount != 2) { + throw new RuntimeException("unexpected selection count " + selectCount + ", " + deselectCount); + } + if (failed) { + throw new RuntimeException("Failed because " + reason); + } + } finally { + if (frame != null) { + SwingUtilities.invokeAndWait(frame::dispose); + } + } + } + + public void menuCanceled(MenuEvent e) { + System.out.println("cancelled"+e.getSource()); + } + + public void menuDeselected(MenuEvent e) { + deselectCount++; + if (selectCount != 2) { + failed = true; + reason = "deselect without two selects"; + } + System.out.println("deselected"+e.getSource()); + } + + public void menuSelected(MenuEvent e) { + if (deselectCount != 0) { + failed = true; + reason = "select without non-zero deselects"; + } + selectCount++; + System.out.println("selected"+e.getSource()); + } +} diff --git a/test/jdk/java/awt/image/BufferedImage/GrayAATextTest.java b/test/jdk/java/awt/image/BufferedImage/GrayAATextTest.java new file mode 100644 index 0000000000000..a484dd392eb03 --- /dev/null +++ b/test/jdk/java/awt/image/BufferedImage/GrayAATextTest.java @@ -0,0 +1,104 @@ +/* + * 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 + * 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 4309915 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Check that Antialiased text drawn on a BYTE_GRAY image + * resolves the color correctly + * @run main/manual GrayAATextTest + */ + +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.Panel; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +public class GrayAATextTest extends Panel { + + public static final int WIDTH = 600; + public static final int HEIGHT = 200; + + private static final String INSTRUCTIONS = """ + All of the strings in a given column should be drawn + in the same color. If the bug is present, then the + Antialiased strings will all be of a fixed color that + is not the same as the other strings in their column."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("GrayAATextTest Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(GrayAATextTest::createTestUI) + .build() + .awaitAndCheck(); + } + + public void paint(Graphics g) { + BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, + BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2d = bi.createGraphics(); + g2d.setFont(new Font("Helvetica", Font.PLAIN, 24)); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, WIDTH / 2, HEIGHT); + drawText(g2d, Color.black, "Black", 25); + drawText(g2d, Color.lightGray, "Light Gray", 175); + g2d.setColor(Color.black); + g2d.fillRect(WIDTH / 2, 0, WIDTH / 2, HEIGHT); + drawText(g2d, Color.white, "White", 325); + drawText(g2d, Color.lightGray, "Light Gray", 475); + g2d.dispose(); + g.drawImage(bi, 0, 0, null); + } + + public void drawText(Graphics2D g2d, Color c, String colorname, int x) { + g2d.setColor(c); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + g2d.drawString(colorname, x, 50); + g2d.drawString("Aliased", x, 100); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.drawString("Antialiased", x, 150); + } + + public Dimension getPreferredSize() { + return new Dimension(WIDTH, HEIGHT); + } + + private static Frame createTestUI() { + Frame f = new Frame("GrayAATextTest Frame"); + f.add(new GrayAATextTest()); + f.setSize(WIDTH, HEIGHT); + return f; + } +} diff --git a/test/jdk/java/awt/image/GrayAlpha.java b/test/jdk/java/awt/image/GrayAlpha.java new file mode 100644 index 0000000000000..cf477c2638503 --- /dev/null +++ b/test/jdk/java/awt/image/GrayAlpha.java @@ -0,0 +1,175 @@ +/* + * 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 4243044 + * @summary This test should show two windows filled with checker + * board pattern. The transparency should runs from left to right from + * total transparent to total opaque. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual GrayAlpha + */ + +import java.util.List; +import java.awt.Frame; +import java.awt.color.ColorSpace; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.awt.Point; +import java.awt.Panel; +import java.awt.Transparency; +import java.awt.Window; + +public class GrayAlpha extends Panel { + + private static final String INSTRUCTIONS = """ + This test should show two windows filled with checker board + vpattern. The transparency should runs from left to right from + totally transparent to totally opaque. If either the pattern or + the transparency is not shown correctly, click Fail, otherwise + click Pass."""; + + BufferedImage bi; + AffineTransform identityTransform = new AffineTransform(); + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("GrayAlpha Instructions") + .instructions(INSTRUCTIONS) + .rows((int)INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(GrayAlpha::createTestUI) + .build() + .awaitAndCheck(); + } + + + public GrayAlpha(int width, int height, + boolean hasAlpha, boolean isAlphaPremultiplied, + boolean useRGB) { + boolean isAlphaPremuliplied = true; + int bands = useRGB ? 3 : 1; + bands = hasAlpha ? bands + 1 : bands; + + ColorSpace cs = useRGB ? + ColorSpace.getInstance(ColorSpace.CS_sRGB) : + ColorSpace.getInstance(ColorSpace.CS_GRAY); + int transparency = hasAlpha ? + Transparency.TRANSLUCENT : Transparency.OPAQUE; + int[] bits = new int[bands]; + for (int i = 0; i < bands; i++) { + bits[i] = 8; + } + + ColorModel cm = new ComponentColorModel(cs, + bits, + hasAlpha, + isAlphaPremultiplied, + transparency, + DataBuffer.TYPE_BYTE); + WritableRaster wr = + Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, + width, height, bands, + new Point(0, 0)); + + for (int b = 0; b < bands; b++) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int s; + + if (b != bands - 1 || !hasAlpha) { + // Gray band(s), fill with a checkerboard pattern + if (((x / 10) % 2) == ((y / 10) % 2)) { + s = 255; + } else { + s = 0; + } + if (isAlphaPremultiplied) { + int alpha = (x*255)/(width - 1); + s = (s*alpha)/255; + } + } else { + // Alpha band, increase opacity left to right + s = (x*255)/(width - 1); + } + + wr.setSample(x, y, b, s); + } + } + } + + this.bi = new BufferedImage(cm, wr, isAlphaPremultiplied, null); + } + + public Dimension getPreferredSize() { + return new Dimension(bi.getWidth(), bi.getHeight()); + } + + public void paint(Graphics g) { + if (bi != null) { + ((Graphics2D)g).drawImage(bi, 0, 0, null); + } + } + + public static Frame makeFrame(String title, + int x, int y, int width, int height, + boolean hasAlpha, + boolean isAlphaPremultiplied, + boolean useRGB) { + Frame f = new Frame(title); + f.add(new GrayAlpha(width, height, + hasAlpha, isAlphaPremultiplied, useRGB)); + f.pack(); + f.setLocation(x, y); + return f; + } + + private static List createTestUI() { + int width = 200; + int height = 200; + + int x = 100; + int y = 100; + + Frame f1 = makeFrame("Gray (non-premultiplied)", + x, y, width, height, + true, false, false); + x += width + 20; + + Frame f2 = makeFrame("Gray (premultiplied)", + x, y, width, height, + true, true, false); + + return List.of(f1, f2); + } +} diff --git a/test/jdk/java/awt/image/ImageOffsetTest.java b/test/jdk/java/awt/image/ImageOffsetTest.java new file mode 100644 index 0000000000000..c1d4c3931de91 --- /dev/null +++ b/test/jdk/java/awt/image/ImageOffsetTest.java @@ -0,0 +1,123 @@ +/* + * 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 + * 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 4259548 + * @summary tests that MemoryImageSource correctly handles images with offsets + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual ImageOffsetTest + */ + +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Panel; +import java.awt.Toolkit; +import java.awt.image.ColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.MemoryImageSource; + +public class ImageOffsetTest { + + static int height = 100; + static int width = 100; + static int levels = 3; + static IndexColorModel cm; + static Image image; + static boolean first = true; + + static byte[] db = new byte[height * width * levels] ; + + private static final String INSTRUCTIONS = """ + If on the appeared 'Test frame' all color squares are of one color + test failed, otherwise it's passed."""; + + public static void main(String[] args) throws Exception { + PassFailJFrame.builder() + .title("ImageOffsetTest") + .instructions(INSTRUCTIONS) + .rows((int) INSTRUCTIONS.lines().count() + 2) + .columns(35) + .testUI(ImageOffsetTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame frame = new Frame("ImageOffset Frame"); + frame.add(new Panel() { + public void paint(Graphics g) { + for ( int i=0 ; i<3 ; i++ ) { + g.drawImage( + generateBuggyImage(i * width * height), 10 + i * 110, 10, null); + } + } + }); + frame.setSize(400, 200); + frame.setLocation(300, 200); + createColorModel(); + int l = 0; + for (int k = 0; k < levels; k++) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + if( k == 0) { + db[l] = (byte)(70 & 0xff) ; + } + if (k == 1) { + db[l] = (byte)(150 & 0xff) ; + } + if (k == 2) { + db[l] = (byte)(230 & 0xff) ; + } + l++ ; + } + } + } + return frame; + } + + private static void createColorModel() { + byte[] red = new byte[256]; + byte[] green = new byte[256]; + byte[] blue = new byte[256]; + + for (int i = 0; i < 256; i++) { + red[i] = (byte)(i & 0xff); + //green[i] = (byte)( i & 0xff ) ; + blue[i] = (byte)( i & 0xff ) ; + //commented out green so I could get purple + } + + cm = new IndexColorModel( 8, 256, red, green, blue ) ; + } + + private static Image generateBuggyImage(int offset) { + // Initialize the database, Three slices, different shades of grey + // Here the image is created using the offset, + return Toolkit.getDefaultToolkit().createImage( + new MemoryImageSource(width, height, (ColorModel)cm, + db, offset, width)); + } +} diff --git a/test/jdk/java/awt/image/TransformImage.java b/test/jdk/java/awt/image/TransformImage.java new file mode 100644 index 0000000000000..30af3d27f55c0 --- /dev/null +++ b/test/jdk/java/awt/image/TransformImage.java @@ -0,0 +1,111 @@ +/* + * 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 4090743 + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @summary Make sure that there is no garbage drawn on the rotated image + * @run main/manual TransformImage + */ + +import java.net.URL; +import java.net.MalformedURLException; +import java.awt.Canvas; +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.geom.AffineTransform; +import java.awt.Image; +import java.awt.image.ImageObserver; +import java.awt.MediaTracker; +import java.awt.Toolkit; + +public class TransformImage extends Canvas { + static Image image; + + private static final String INSTRUCTIONS = """ + The rotated image should be drawn without garbage."""; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("TransformImage Instructions") + .instructions(INSTRUCTIONS) + .rows(5) + .columns(35) + .testUI(TransformImage::createTestUI) + .build() + .awaitAndCheck(); + } + + private static Frame createTestUI() { + Frame f = new Frame(); + String dir = System.getProperty("test.src"); + String sep = System.getProperty("file.separator"); + if (dir == null) { + dir = "."; + } + image = Toolkit.getDefaultToolkit().getImage(dir+sep+"duke.gif"); + f.add(new TransformImage()); + + f.pack(); + return f; + } + + public Dimension getPreferredSize() { + return new Dimension (256, 256); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public void paint(Graphics g) { + int w, h; + java.awt.Graphics2D g2d = (Graphics2D) g; + AffineTransform at = new AffineTransform(); + + MediaTracker mt = new MediaTracker(this); + mt.addImage(image, 0); + try { + mt.waitForAll(); + } catch (InterruptedException e) { + System.err.println("can't track"); + return; + } + w = image.getWidth(this); + h = image.getHeight(this); + g2d.drawImage(image, 0, 0, this); + g2d.drawRect(0, 0, w, h); + + double rad = .5; + at.rotate(-rad); + g2d.setTransform(at); + g2d.drawImage(image, 0, 100, this); + g2d.drawRect(0, 100, w, h); + } +} diff --git a/test/jdk/java/awt/image/duke.gif b/test/jdk/java/awt/image/duke.gif new file mode 100644 index 0000000000000..ed32e0ff79b05 Binary files /dev/null and b/test/jdk/java/awt/image/duke.gif differ diff --git a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java index dd94081c35421..41c5fdf8ccdbf 100644 --- a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java +++ b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java @@ -24,6 +24,7 @@ import java.awt.AWTException; import java.awt.BorderLayout; import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.Font; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; @@ -44,6 +45,7 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -69,24 +71,88 @@ import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.Timer; +import javax.swing.border.Border; import javax.swing.text.JTextComponent; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import static java.util.Collections.unmodifiableList; +import static javax.swing.BorderFactory.createEmptyBorder; import static javax.swing.SwingUtilities.invokeAndWait; import static javax.swing.SwingUtilities.isEventDispatchThread; /** - * Provides a framework for manual tests to display test instructions and - * Pass/Fail buttons. + * A framework for manual tests to display test instructions and + * Pass / Fail buttons. The framework automatically + * creates a frame to display the instructions, provides buttons + * to select the test result, and handles test timeout. + * + *

            + * The instruction UI frame displays a timer at the top which indicates + * how much time is left. The timer can be paused using the Pause + * button to the right of the time; the title of the button changes to + * Resume. To resume the timer, use the Resume button. + * + *

            + * In the center, the instruction UI frame displays instructions for the + * tester. The instructions can be either plain text or HTML. If the + * text of the instructions starts with {@code ""}, the + * instructions are displayed as HTML, as supported by Swing, which + * provides richer formatting options. *

            - * Instructions for the user can be either plain text or HTML as supported - * by Swing. If the instructions start with {@code }, the - * instructions are displayed as HTML. + * The instructions are displayed in a text component with word-wrapping + * so that there's no horizontal scroll bar. If the text doesn't fit, a + * vertical scroll bar is shown. Use {@code rows} and {@code columns} + * parameters to change the size of this text component. + * If possible, choose the number of rows and columns so that + * the instructions fit and no scroll bars are shown. + * + *

            + * At the bottom, the instruction UI frame displays the + * Pass and Fail buttons. The tester clicks either Pass + * or Fail button to finish the test. When the tester clicks the + * Fail button, the framework displays a dialog box prompting for + * a reason why the test fails. The tester enters the reason and clicks + * OK to close the dialog and fail the test, + * or simply closes the dialog to fail the test without providing any reason. + * + *

            + * If you enable the screenshot feature, a Screenshot button is + * added to the right of the Fail button. The tester can choose either + * Capture Full Screen (default) or Capture Frames and click the + * Screenshot button to take a screenshot. + * If there are multiple screens, screenshots of each screen are created. + * If the tester selects the Capture Frames mode, screenshots of all + * the windows or frames registered in the {@code PassFailJFrame} framework + * are created. + * + *

            + * If you enable a log area, the instruction UI frame adds a text component + * to display log messages below the buttons. + * Use {@link #log(String) log}, {@link #logSet(String) logSet} + * and {@link #logClear() logClear} static methods of {@code PassFailJFrame} + * to add or clear messages from the log area. + * + *

            + * After you create an instance of {@code PassFailJFrame}, call the + * {@link #awaitAndCheck() awaitAndCheck} method to stop the current thread + * (usually the main thread) and wait until the tester clicks + * either Pass or Fail button, + * or until the test times out. *

            + * The call to the {@code awaitAndCheck} method is usually the last + * statement in the {@code main} method of your test. + * If the test fails, an exception is thrown to signal the failure to jtreg. + * The test fails if the tester clicks the Fail button, + * if the timeout occurs, + * or if any window or frame is closed. + *

            + * Before returning from {@code awaitAndCheck}, the framework disposes of + * all the windows and frames. + * + *

            Sample Manual Test

            * A simple test would look like this: - *
            {@code
            + * {@snippet id='sampleManualTestCode' lang='java':
              * public class SampleManualTest {
              *     private static final String INSTRUCTIONS =
              *             "Click Pass, or click Fail if the test failed.";
            @@ -94,7 +160,7 @@
              *     public static void main(String[] args) throws Exception {
              *         PassFailJFrame.builder()
              *                       .instructions(INSTRUCTIONS)
            - *                       .testUI(() -> createTestUI())
            + *                       .testUI(SampleManualTest::createTestUI)
              *                       .build()
              *                       .awaitAndCheck();
              *     }
            @@ -105,39 +171,87 @@
              *         return testUI;
              *     }
              * }
            - * }
            + * } *

            - * The above example uses the {@link Builder Builder} to set the parameters of - * the instruction frame. It is the recommended way. + * The above example uses the {@link Builder Builder} class to set + * the parameters of the instruction frame. + * It is the recommended way. + * *

            - * The framework will create instruction UI, it will call - * the provided {@code createTestUI} on the Event Dispatch Thread (EDT), - * and it will automatically position the test UI and make it visible. + * The framework will create an instruction UI frame, it will call + * the provided {@code createTestUI} on the Event Dispatch Thread (EDT), + * and it will automatically position the test UI frame and make it visible. + * + *

            + * Add the following jtreg tags before the test class declaration + * {@snippet : + * /* + * * @test + * * @summary Sample manual test + * * @library /java/awt/regtesthelpers + * * @build PassFailJFrame + * * @run main/manual SampleManualTest + * } + * and the closing comment tag */. *

            + * The {@code @library} tag points to the location of the + * {@code PassFailJFrame} class in the source code; + * the {@code @build} tag makes jtreg compile the {@code PassFailJFrame} class, + * and finally the {@code @run} tag specifies it is a manual + * test and the class to run. + * + *

            Using {@code Builder}

            + * Use methods of the {@link Builder Builder} class to set or change + * parameters of {@code PassFailJFrame} and its instruction UI: + *
              + *
            • {@link Builder#title(String) title} sets + * the title of the instruction UI + * (the default is {@value #TITLE});
            • + *
            • {@link Builder#testTimeOut(long) testTimeOut} sets + * the timeout of the test + * (the default is {@value #TEST_TIMEOUT});
            • + *
            • {@link Builder#rows(int) rows} and + * {@link Builder#columns(int) columns} control the size + * the text component which displays the instructions + * (the default number of rows is the number of lines in the text + * of the instructions, + * the default number of columns is {@value #COLUMNS});
            • + *
            • {@link Builder#logArea() logArea} adds a log area;
            • + *
            • {@link Builder#screenCapture() screenCapture} + * enables screenshots.
            • + *
            + * + *

            Using {@code testUI} and {@code splitUI}

            * The {@code Builder.testUI} methods accept interfaces which create one window * 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 + * For simple test UI, use {@link Builder#splitUI(PanelCreator) splitUI}, + * or explicitly + * {@link Builder#splitUIRight(PanelCreator) splitUIRight} or + * {@link Builder#splitUIBottom(PanelCreator) splitUIBottom} with + * a {@link PanelCreator PanelCreator}. + * The framework will call the provided + * {@code createUIPanel} method 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. *

            + * Note: support for multiple windows is incomplete. + * + *

            Obsolete Sample Test

            * 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. * The following sample demonstrates it: - *
            {@code
            - * public class SampleOldManualTest {
            + * {@snippet id='obsoleteSampleTestCode' lang='java':
            + * public class ObsoleteManualTest {
              *     private static final String INSTRUCTIONS =
              *             "Click Pass, or click Fail if the test failed.";
              *
              *     public static void main(String[] args) throws Exception {
              *         PassFailJFrame passFail = new PassFailJFrame(INSTRUCTIONS);
              *
            - *         SwingUtilities.invokeAndWait(() -> createTestUI());
            + *         SwingUtilities.invokeAndWait(ObsoleteManualTest::createTestUI);
              *
              *         passFail.awaitAndCheck();
              *     }
            @@ -150,31 +264,54 @@
              *         testUI.setVisible(true);
              *     }
              * }
            - * }
            + * } *

            - * Use methods of the {@code Builder} class or constructors of the - * {@code PassFailJFrame} class to control other parameters: - *

              - *
            • the title of the instruction UI,
            • - *
            • the timeout of the test,
            • - *
            • the size of the instruction UI via rows and columns, and
            • - *
            • to add a log area
            • , - *
            • to enable screenshots.
            • - *
            + * This sample uses {@link #PassFailJFrame(String) a constructor} of + * {@code PassFailJFrame} to create its instance, + * there are several overloads provided which allow changing other parameters. + *

            + * When you use the constructors, you have to explicitly create + * your test UI window on EDT. After you create the window, + * you need to register it with the framework using + * {@link #addTestWindow(Window) addTestWindow} + * to ensure the window is disposed of when the test completes. + * Before showing the window, you have to call + * {@link #positionTestWindow(Window, Position) positionTestWindow} + * to position the test window near the instruction UI frame provided + * by the framework. And finally you have to explicitly show the test UI + * window by calling {@code setVisible(true)}. + *

            + * To avoid the complexity, use the {@link Builder Builder} class + * which provides a streamlined way to configure and create an + * instance of {@code PassFailJFrame}. + *

            + * Consider updating tests which use {@code PassFailJFrame} constructors to + * use the builder pattern. */ public final class PassFailJFrame { - private static final String TITLE = "Test Instruction Frame"; + /** A default title for the instruction frame. */ + private static final String TITLE = "Test Instructions"; + + /** A default test timeout. */ private static final long TEST_TIMEOUT = 5; + + /** A default number of rows for displaying the test instructions. */ private static final int ROWS = 10; + /** A default number of columns for displaying the test instructions. */ private static final int COLUMNS = 40; + /** + * A gap between windows. + */ + public static final int WINDOW_GAP = 8; + /** * Prefix for the user-provided failure reason. */ private static final String FAILURE_REASON = "Failure Reason:\n"; /** - * The failure reason message when the user didn't provide one. + * The failure reason message when the user doesn't provide one. */ private static final String EMPTY_REASON = "(no reason provided)"; @@ -212,90 +349,172 @@ public final class PassFailJFrame { public enum Position {HORIZONTAL, VERTICAL, TOP_LEFT_CORNER} - public PassFailJFrame(String instructions) throws InterruptedException, - InvocationTargetException { + /** + * Constructs a frame which displays test instructions and + * the Pass / Fail buttons with the given instructions, and + * the default timeout of {@value #TEST_TIMEOUT} minutes, + * the default title of {@value #TITLE} and + * the default values of {@value #ROWS} and {@value #COLUMNS} + * for rows and columns. + *

            + * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for + * more details. + * + * @param instructions the instructions for the tester + * + * @throws InterruptedException if the current thread is interrupted + * while waiting for EDT to finish creating UI components + * @throws InvocationTargetException if an exception is thrown while + * creating UI components on EDT + */ + public PassFailJFrame(String instructions) + throws InterruptedException, InvocationTargetException { this(instructions, TEST_TIMEOUT); } - public PassFailJFrame(String instructions, long testTimeOut) throws - InterruptedException, InvocationTargetException { + /** + * Constructs a frame which displays test instructions and + * the Pass / Fail buttons + * with the given instructions and timeout as well as + * the default title of {@value #TITLE} + * and the default values of {@value #ROWS} and {@value #COLUMNS} + * for rows and columns. + *

            + * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for + * more details. + * + * @param instructions the instructions for the tester + * @param testTimeOut the test timeout in minutes + * + * @throws InterruptedException if the current thread is interrupted + * while waiting for EDT to finish creating UI components + * @throws InvocationTargetException if an exception is thrown while + * creating UI components on EDT + */ + public PassFailJFrame(String instructions, long testTimeOut) + throws InterruptedException, InvocationTargetException { this(TITLE, instructions, testTimeOut); } + /** + * Constructs a frame which displays test instructions and + * the Pass / Fail buttons + * with the given title, instructions and timeout as well as + * the default values of {@value #ROWS} and {@value #COLUMNS} + * for rows and columns. + * The screenshot feature is not enabled, if you use this constructor. + *

            + * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for + * more details. + * + * @param title the title of the instruction frame + * @param instructions the instructions for the tester + * @param testTimeOut the test timeout in minutes + * + * @throws InterruptedException if the current thread is interrupted + * while waiting for EDT to finish creating UI components + * @throws InvocationTargetException if an exception is thrown while + * creating UI components on EDT + */ public PassFailJFrame(String title, String instructions, - long testTimeOut) throws InterruptedException, - InvocationTargetException { + long testTimeOut) + throws InterruptedException, InvocationTargetException { this(title, instructions, testTimeOut, ROWS, COLUMNS); } /** - * Constructs a JFrame with a given title & serves as test instructional - * frame where the user follows the specified test instruction in order - * to test the test case & mark the test pass or fail. If the expected - * result is seen then the user click on the 'Pass' button else click - * on the 'Fail' button and the reason for the failure should be - * specified in the JDialog JTextArea. + * Constructs a frame which displays test instructions and + * the Pass / Fail buttons + * with the given title, instructions, timeout, number of rows and columns. + * The screenshot feature is not enabled, if you use this constructor. + *

            + * See {@link #PassFailJFrame(String,String,long,int,int,boolean)} for + * more details. * - * @param title title of the Frame. - * @param instructions the instruction for the tester on how to test - * and what is expected (pass) and what is not - * expected (fail). - * @param testTimeOut test timeout where time is specified in minutes. - * @param rows number of visible rows of the JTextArea where the - * instruction is show. - * @param columns Number of columns of the instructional - * JTextArea - * @throws InterruptedException exception thrown when thread is - * interrupted + * @param title the title of the instruction frame + * @param instructions the instructions for the tester + * @param testTimeOut the test timeout in minutes + * @param rows the number of rows for the text component + * which displays test instructions + * @param columns the number of columns for the text component + * which displays test instructions + * + * @throws InterruptedException if the current thread is interrupted + * while waiting for EDT to finish creating UI components * @throws InvocationTargetException if an exception is thrown while - * creating the test instruction frame on - * EDT + * creating UI components on EDT */ - public PassFailJFrame(String title, String instructions, long testTimeOut, - int rows, int columns) throws InterruptedException, - InvocationTargetException { + public PassFailJFrame(String title, String instructions, + long testTimeOut, + int rows, int columns) + throws InterruptedException, InvocationTargetException { this(title, instructions, testTimeOut, rows, columns, false); } /** - * Constructs a JFrame with a given title & serves as test instructional - * frame where the user follows the specified test instruction in order - * to test the test case & mark the test pass or fail. If the expected - * result is seen then the user click on the 'Pass' button else click - * on the 'Fail' button and the reason for the failure should be - * specified in the JDialog JTextArea. + * Constructs a frame which displays test instructions and + * the Pass / Fail buttons + * as well as supporting UI components with the given title, instructions, + * timeout, number of rows and columns, + * and screen capture functionality. + * All the UI components are created on the EDT, so it is safe to call + * the constructor on the main thread. + *

            + * After you create a test UI window, register the window using + * {@link #addTestWindow(Window) addTestWindow} for disposal, and + * position it close to the instruction frame using + * {@link #positionTestWindow(Window, Position) positionTestWindow}. + * As the last step, make your test UI window visible. + *

            + * Call the {@link #awaitAndCheck() awaitAndCheck} method on the instance + * of {@code PassFailJFrame} when you set up the testing environment. *

            - * The test instruction frame also provides a way for the tester to take - * a screenshot (full screen or individual frame) if this feature - * is enabled by passing {@code true} as {@code enableScreenCapture} - * parameter. + * If the tester clicks the Fail button, a dialog prompting for + * a description of the problem is displayed, and then an exception + * is thrown which fails the test. + * If the tester clicks the Pass button, the test completes + * successfully. + * If the timeout occurs or the instruction frame is closed, + * the test fails. + *

            + * The {@code rows} and {@code columns} parameters control + * the size of a text component which displays the instructions. + * The preferred size of the instructions is calculated by + * creating {@code new JTextArea(rows, columns)}. + *

            + * If you enable screenshots by setting the {@code screenCapture} + * parameter to {@code true}, a Screenshot button is added. + * Clicking the Screenshot button takes screenshots of + * all the monitors or all the windows registered with + * {@code PassFailJFrame}. * - * @param title title of the Frame. - * @param instructions the instruction for the tester on how to test - * and what is expected (pass) and what is not - * expected (fail). - * @param testTimeOut test timeout where time is specified in minutes. - * @param rows number of visible rows of the JTextArea where the - * instruction is show. - * @param columns Number of columns of the instructional - * JTextArea - * @param enableScreenCapture if set to true, 'Capture Screen' button & its - * associated UIs are added to test instruction - * frame - * @throws InterruptedException exception thrown when thread is - * interrupted + * @param title the title of the instruction frame + * @param instructions the instructions for the tester + * @param testTimeOut the test timeout in minutes + * @param rows the number of rows for the text component + * which displays test instructions + * @param columns the number of columns for the text component + * which displays test instructions + * @param screenCapture if set to {@code true}, enables screen capture + * functionality + * + * @throws InterruptedException if the current thread is interrupted + * while waiting for EDT to finish creating UI components * @throws InvocationTargetException if an exception is thrown while - * creating the test instruction frame on - * EDT + * creating UI components on EDT + * + * @see JTextArea#JTextArea(int,int) JTextArea(int rows, int columns) + * @see Builder Builder */ - public PassFailJFrame(String title, String instructions, long testTimeOut, + public PassFailJFrame(String title, String instructions, + long testTimeOut, int rows, int columns, - boolean enableScreenCapture) + boolean screenCapture) throws InterruptedException, InvocationTargetException { invokeOnEDT(() -> createUI(title, instructions, testTimeOut, rows, columns, - enableScreenCapture)); + screenCapture)); } /** @@ -348,11 +567,9 @@ private PassFailJFrame(final Builder builder) builder.positionWindows .positionTestWindows(unmodifiableList(builder.testWindows), builder.instructionUIHandler)); - } else if (builder.testWindows.size() == 1) { + } else { Window window = builder.testWindows.get(0); positionTestWindow(window, builder.position); - } else { - positionTestWindow(null, builder.position); } } showAllWindows(); @@ -447,6 +664,8 @@ private static JComponent createInstructionUIPanel(String instructions, boolean addLogArea, int logAreaRows) { JPanel main = new JPanel(new BorderLayout()); + main.setBorder(createFrameBorder()); + timeoutHandlerPanel = new TimeoutHandlerPanel(testTimeOut); main.add(timeoutHandlerPanel, BorderLayout.NORTH); @@ -455,7 +674,11 @@ private static JComponent createInstructionUIPanel(String instructions, : configurePlainText(instructions, rows, columns); text.setEditable(false); - main.add(new JScrollPane(text), BorderLayout.CENTER); + JPanel textPanel = new JPanel(new BorderLayout()); + textPanel.setBorder(createEmptyBorder(GAP, 0, GAP, 0)); + textPanel.add(new JScrollPane(text), BorderLayout.CENTER); + + main.add(textPanel, BorderLayout.CENTER); JButton btnPass = new JButton("Pass"); btnPass.addActionListener((e) -> { @@ -469,7 +692,8 @@ private static JComponent createInstructionUIPanel(String instructions, timeoutHandlerPanel.stop(); }); - JPanel buttonsPanel = new JPanel(); + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, + GAP, 0)); buttonsPanel.add(btnPass); buttonsPanel.add(btnFail); @@ -480,10 +704,12 @@ private static JComponent createInstructionUIPanel(String instructions, if (addLogArea) { logArea = new JTextArea(logAreaRows, columns); logArea.setEditable(false); + logArea.setBorder(createTextBorder()); Box buttonsLogPanel = Box.createVerticalBox(); buttonsLogPanel.add(buttonsPanel); + buttonsLogPanel.add(Box.createVerticalStrut(GAP)); buttonsLogPanel.add(new JScrollPane(logArea)); main.add(buttonsLogPanel, BorderLayout.SOUTH); @@ -501,6 +727,7 @@ private static JTextComponent configurePlainText(String instructions, JTextArea text = new JTextArea(instructions, rows, columns); text.setLineWrap(true); text.setWrapStyleWord(true); + text.setBorder(createTextBorder()); return text; } @@ -522,6 +749,29 @@ private static JTextComponent configureHTML(String instructions, return text; } + /** A default gap between components. */ + private static final int GAP = 4; + + /** + * Creates a default border for frames or dialogs. + * It uses the default gap of {@value GAP}. + * + * @return the border for frames and dialogs + */ + private static Border createFrameBorder() { + return createEmptyBorder(GAP, GAP, GAP, GAP); + } + + /** + * Creates a border set to text area. + * It uses the default gap of {@value GAP}. + * + * @return the border for text area + */ + private static Border createTextBorder() { + return createEmptyBorder(GAP, GAP, GAP, GAP); + } + /** * Creates a test UI window. @@ -587,7 +837,7 @@ public interface PositionWindows { * @param testWindows the list of test windows * @param instructionUI information about the instruction frame */ - void positionTestWindows(List testWindows, + void positionTestWindows(List testWindows, InstructionUI instructionUI); } @@ -735,7 +985,7 @@ public void windowClosing(WindowEvent e) { private static JComponent createCapturePanel() { JComboBox screenShortType = new JComboBox<>(CaptureType.values()); - JButton capture = new JButton("ScreenShot"); + JButton capture = new JButton("Screenshot"); capture.addActionListener((e) -> captureScreen((CaptureType) screenShortType.getSelectedItem())); @@ -747,7 +997,7 @@ private static JComponent createCapturePanel() { private enum CaptureType { FULL_SCREEN("Capture Full Screen"), - WINDOWS("Capture Individual Frame"); + WINDOWS("Capture Frames"); private final String type; CaptureType(String type) { @@ -873,26 +1123,30 @@ public void awaitAndCheck() throws InterruptedException, InvocationTargetExcepti * Requests the description of the test failure reason from the tester. */ private static void requestFailureReason() { - final JDialog dialog = new JDialog(frame, "Test Failure ", true); - dialog.setTitle("Failure reason"); - JPanel jPanel = new JPanel(new BorderLayout()); - JTextArea jTextArea = new JTextArea(5, 20); + final JDialog dialog = new JDialog(frame, "Failure reason", true); + + JTextArea reason = new JTextArea(5, 20); + reason.setBorder(createTextBorder()); JButton okButton = new JButton("OK"); okButton.addActionListener((ae) -> { - String text = jTextArea.getText(); + String text = reason.getText(); setFailureReason(FAILURE_REASON + (!text.isEmpty() ? text : EMPTY_REASON)); dialog.setVisible(false); }); - jPanel.add(new JScrollPane(jTextArea), BorderLayout.CENTER); - - JPanel okayBtnPanel = new JPanel(); + JPanel okayBtnPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, + GAP, 0)); + okayBtnPanel.setBorder(createEmptyBorder(GAP, 0, 0, 0)); okayBtnPanel.add(okButton); - jPanel.add(okayBtnPanel, BorderLayout.SOUTH); - dialog.add(jPanel); + JPanel main = new JPanel(new BorderLayout()); + main.setBorder(createFrameBorder()); + main.add(new JScrollPane(reason), BorderLayout.CENTER); + main.add(okayBtnPanel, BorderLayout.SOUTH); + + dialog.add(main); dialog.setLocationRelativeTo(frame); dialog.pack(); dialog.setVisible(true); @@ -925,13 +1179,13 @@ private static void positionInstructionFrame(final Position position) { switch (position) { case HORIZONTAL: - int newX = ((screenSize.width / 2) - frame.getWidth()); + int newX = (((screenSize.width + WINDOW_GAP) / 2) - frame.getWidth()); frame.setLocation((newX + screenInsets.left), (frame.getY() + screenInsets.top)); break; case VERTICAL: - int newY = ((screenSize.height / 2) - frame.getHeight()); + int newY = (((screenSize.height + WINDOW_GAP) / 2) - frame.getHeight()); frame.setLocation((frame.getX() + screenInsets.left), (newY + screenInsets.top)); break; @@ -979,13 +1233,13 @@ public static void positionTestWindow(Window testWindow, Position position) { switch (position) { case HORIZONTAL: case TOP_LEFT_CORNER: - testWindow.setLocation((frame.getX() + frame.getWidth() + 5), + testWindow.setLocation((frame.getX() + frame.getWidth() + WINDOW_GAP), frame.getY()); break; case VERTICAL: testWindow.setLocation(frame.getX(), - (frame.getY() + frame.getHeight() + 5)); + (frame.getY() + frame.getHeight() + WINDOW_GAP)); break; } } @@ -1111,9 +1365,10 @@ public static void forceFail(String reason) { /** * Adds a {@code message} to the log area, if enabled by - * {@link Builder#logArea()} or {@link Builder#logArea(int)}. + * {@link Builder#logArea() logArea()} or + * {@link Builder#logArea(int) logArea(int)}. * - * @param message to log + * @param message the message to log */ public static void log(String message) { System.out.println("PassFailJFrame: " + message); @@ -1122,7 +1377,8 @@ public static void log(String message) { /** * Clears the log area, if enabled by - * {@link Builder#logArea()} or {@link Builder#logArea(int)}. + * {@link Builder#logArea() logArea()} or + * {@link Builder#logArea(int) logArea(int)}. */ public static void logClear() { System.out.println("\nPassFailJFrame: log cleared\n"); @@ -1131,7 +1387,9 @@ public static void logClear() { /** * Replaces the log area content with provided {@code text}, if enabled by - * {@link Builder#logArea()} or {@link Builder#logArea(int)}. + * {@link Builder#logArea() logArea()} or + * {@link Builder#logArea(int) logArea(int)}. + * * @param text new text for the log area */ public static void logSet(String text) { @@ -1182,11 +1440,45 @@ public Builder testTimeOut(long testTimeOut) { return this; } + /** + * Sets the number of rows for displaying the instruction text. + * The default value is the number of lines in the text plus 1: + * {@code ((int) instructions.lines().count() + 1)}. + * + * @param rows the number of rows for instruction text + * @return this builder + */ public Builder rows(int rows) { this.rows = rows; return this; } + private int getDefaultRows() { + return (int) instructions.lines().count() + 1; + } + + /** + * Adds a certain number of rows for displaying the instruction text. + * + * @param rowsAdd the number of rows to add to the number of rows + * @return this builder + * @see #rows + */ + public Builder rowsAdd(int rowsAdd) { + if (rows == 0) { + rows = getDefaultRows(); + } + rows += rowsAdd; + + return this; + } + + /** + * Sets the number of columns for displaying the instruction text. + * + * @param columns the number of columns for instruction text + * @return this builder + */ public Builder columns(int columns) { this.columns = columns; return this; @@ -1250,6 +1542,101 @@ public Builder testUI(WindowCreator windowCreator) { return this; } + + /** + * Adds an implementation of {@link PositionWindows PositionWindows} + * which the framework will use to position multiple test UI windows. + * + * @param positionWindows an implementation of {@code PositionWindows} + * to position multiple test UI windows + * @return this builder + * @throws IllegalArgumentException if the {@code positionWindows} + * parameter is {@code null} + * @throws IllegalStateException if the {@code positionWindows} field + * is already set + */ + public Builder positionTestUI(PositionWindows positionWindows) { + if (positionWindows == null) { + throw new IllegalArgumentException("positionWindows parameter can't be null"); + } + if (this.positionWindows != null) { + throw new IllegalStateException("PositionWindows is already set"); + } + this.positionWindows = positionWindows; + return this; + } + + /** + * Positions the test UI windows in a row to the right of + * the instruction frame. The top of the windows is aligned to + * that of the instruction frame. + * + * @return this builder + */ + public Builder positionTestUIRightRow() { + return position(Position.HORIZONTAL) + .positionTestUI(WindowLayouts::rightOneRow); + } + + /** + * Positions the test UI windows in a column to the right of + * the instruction frame. The top of the first window is aligned to + * that of the instruction frame. + * + * @return this builder + */ + public Builder positionTestUIRightColumn() { + return position(Position.HORIZONTAL) + .positionTestUI(WindowLayouts::rightOneColumn); + } + + /** + * Positions the test UI windows in a column to the right of + * the instruction frame centering the stack of the windows. + * + * @return this builder + */ + public Builder positionTestUIRightColumnCentered() { + return position(Position.HORIZONTAL) + .positionTestUI(WindowLayouts::rightOneColumnCentered); + } + + /** + * Positions the test UI windows in a row to the bottom of + * the instruction frame. The left of the first window is aligned to + * that of the instruction frame. + * + * @return this builder + */ + public Builder positionTestUIBottomRow() { + return position(Position.VERTICAL) + .positionTestUI(WindowLayouts::bottomOneRow); + } + + /** + * Positions the test UI windows in a row to the bottom of + * the instruction frame centering the row of the windows. + * + * @return this builder + */ + public Builder positionTestUIBottomRowCentered() { + return position(Position.VERTICAL) + .positionTestUI(WindowLayouts::bottomOneRowCentered); + } + + /** + * Positions the test UI windows in a column to the bottom of + * the instruction frame. The left of the first window is aligned to + * that of the instruction frame. + * + * @return this builder + */ + public Builder positionTestUIBottomColumn() { + return position(Position.VERTICAL) + .positionTestUI(WindowLayouts::bottomOneColumn); + } + + /** * Adds a {@code WindowListCreator} which the framework will use * to create a list of test UI windows. @@ -1352,6 +1739,7 @@ public Builder testUI(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. @@ -1443,9 +1831,41 @@ public PassFailJFrame build() throws InterruptedException, return new PassFailJFrame(this); } + /** + * Returns the file name of the test, if the {@code test.file} property + * is defined, concatenated with {@code " - "} which serves as a prefix + * to the default instruction frame title; + * or an empty string if the {@code test.file} property is not defined. + * + * @return the prefix to the default title: + * either the file name of the test or an empty string + * + * @see jtreg + * test-specific system properties and environment variables + */ + private static String getTestFileNamePrefix() { + String testFile = System.getProperty("test.file"); + if (testFile == null) { + return ""; + } + + return Paths.get(testFile).getFileName().toString() + + " - "; + } + + /** + * Validates the state of the builder and + * expands parameters that have no assigned values + * to their default values. + * + * @throws IllegalStateException if no instructions are provided, + * or if {@code PositionWindows} implementation is + * provided but neither window creator nor + * test window list are set + */ private void validate() { if (title == null) { - title = TITLE; + title = getTestFileNamePrefix() + TITLE; } if (instructions == null || instructions.isEmpty()) { @@ -1458,7 +1878,7 @@ private void validate() { } if (rows == 0) { - rows = ROWS; + rows = getDefaultRows(); } if (columns == 0) { diff --git a/test/jdk/java/awt/regtesthelpers/WindowLayouts.java b/test/jdk/java/awt/regtesthelpers/WindowLayouts.java new file mode 100644 index 0000000000000..4368e3a59432f --- /dev/null +++ b/test/jdk/java/awt/regtesthelpers/WindowLayouts.java @@ -0,0 +1,237 @@ +/* + * 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. + */ + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GraphicsConfiguration; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Window; +import java.util.List; + +import static java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment; +import static java.awt.Toolkit.getDefaultToolkit; + +/** + * A utility class which provides standard window layouts for multi-window + * manual tests using the {@link PassFailJFrame} framework. + * The layout methods {@code right-} and {@code bottom-} implement the + * {@link PassFailJFrame.PositionWindows PositionWindows} interface and + * can be used directly or via builder methods. + *

            + * There are several helper methods, such as + * {@link #getScreenCenter() getScreenCenter}, which could help you + * implement customized windows layouts. + */ +public final class WindowLayouts { + + /** Private constructor to prevent instantiating the utility class. */ + private WindowLayouts() { + } + + /** A gap between windows. (Local copy makes expressions shorter.) */ + private static final int WINDOW_GAP = PassFailJFrame.WINDOW_GAP; + + /** + * Lays out the window list in one row to the right of + * the instruction frame. The top of the windows is aligned to + * that of the instruction frame. + * + * @param windows the list of windows to lay out + * @param instructionUI information about the instruction frame + */ + public static void rightOneRow(final List windows, + final PassFailJFrame.InstructionUI instructionUI) { + layoutRow(instructionUI.getLocation().x + + instructionUI.getSize().width + + WINDOW_GAP, + instructionUI.getLocation().y, + windows); + } + + /** + * Lays out the window list in one column to the right of + * the instruction frame. The top of the first window is aligned to + * that of the instruction frame. + * + * @param windows the list of windows to lay out + * @param instructionUI information about the instruction frame + */ + public static void rightOneColumn(final List windows, + final PassFailJFrame.InstructionUI instructionUI) { + layoutColumn(instructionUI.getLocation().x + + instructionUI.getSize().width + + WINDOW_GAP, + instructionUI.getLocation().y, + windows); + } + + /** + * Lays out the window list in one column to the right of + * the instruction frame centering the stack of the windows. + * + * @param windows the list of windows to lay out + * @param instructionUI information about the instruction frame + */ + public static void rightOneColumnCentered(final List windows, + final PassFailJFrame.InstructionUI instructionUI) { + layoutColumn(instructionUI.getLocation().x + + instructionUI.getSize().width + + WINDOW_GAP, + getScreenCenter().y + - getWindowListHeight(windows) / 2, + windows); + } + + + /** + * Lays out the window list in one row to the bottom of + * the instruction frame. The left of the first window is aligned to + * that of the instruction frame. + * + * @param windows the list of windows to lay out + * @param instructionUI information about the instruction frame + */ + public static void bottomOneRow(final List windows, + final PassFailJFrame.InstructionUI instructionUI) { + layoutRow(instructionUI.getLocation().x, + instructionUI.getLocation().y + + instructionUI.getSize().height + + WINDOW_GAP, + windows); + } + + /** + * Lays out the window list in one row to the bottom of + * the instruction frame centering the row of the windows. + * + * @param windows the list of windows to lay out + * @param instructionUI information about the instruction frame + */ + public static void bottomOneRowCentered(final List windows, + final PassFailJFrame.InstructionUI instructionUI) { + layoutRow(getScreenCenter().x + - getWindowListWidth(windows) / 2, + instructionUI.getLocation().y + + instructionUI.getSize().height + + WINDOW_GAP, + windows); + } + + /** + * Lays out the window list in one column to the bottom of + * the instruction frame. The left of the first window is aligned to + * that of the instruction frame. + * + * @param windows the list of windows to lay out + * @param instructionUI information about the instruction frame + */ + public static void bottomOneColumn(final List windows, + final PassFailJFrame.InstructionUI instructionUI) { + layoutColumn(instructionUI.getLocation().x, + instructionUI.getLocation().y + + instructionUI.getSize().height + + WINDOW_GAP, + windows); + } + + + /** + * Lays out the window list in one row starting at + * ({@code x0}, {@code y}). + * + * @param x0 the starting x coordinate of the windows + * @param y the y coordinate of the windows + * @param windows the list of windows to lay out + */ + public static void layoutRow(final int x0, + final int y, + final List windows) { + int x = x0; + for (Window w : windows) { + w.setLocation(x, y); + x += w.getWidth() + WINDOW_GAP; + } + } + + /** + * Lays out the window list in one column starting at + * ({@code x}, {@code y0}). + * + * @param x the x coordinate of the windows + * @param y0 the starting y coordinate of the windows + * @param windows the list of windows to lay out + */ + public static void layoutColumn(final int x, + final int y0, + final List windows) { + int y = y0; + for (Window w : windows) { + w.setLocation(x, y); + y += w.getHeight() + WINDOW_GAP; + } + } + + + /** + * {@return the center point of the main screen} + */ + public static Point getScreenCenter() { + GraphicsConfiguration gc = getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration(); + Dimension size = gc.getBounds() + .getSize(); + Insets insets = getDefaultToolkit() + .getScreenInsets(gc); + + return new Point((size.width - insets.left - insets.right) / 2, + (size.height - insets.top - insets.bottom) / 2); + } + + /** + * {@return width of the windows in the list, taking into account + * the gap between windows} + * + * @param windows the list of windows to get the width of + */ + public static int getWindowListWidth(final List windows) { + return windows.stream() + .mapToInt(Component::getWidth) + .sum() + + WINDOW_GAP * (windows.size() - 1); + } + + /** + * {@return height of the windows in the list, taking into account + * the gap between windows} + * + * @param windows the list of windows to get the height of + */ + public static int getWindowListHeight(final List windows) { + return windows.stream() + .mapToInt(Component::getHeight) + .sum() + + WINDOW_GAP * (windows.size() - 1); + } +} diff --git a/test/jdk/java/foreign/TestUpcallStress.java b/test/jdk/java/foreign/TestUpcallStress.java new file mode 100644 index 0000000000000..4060774685641 --- /dev/null +++ b/test/jdk/java/foreign/TestUpcallStress.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 + * @requires jdk.foreign.linker != "FALLBACK" + * @requires (os.arch == "aarch64" | os.arch=="riscv64") & os.name == "Linux" + * @requires os.maxMemory > 4G + * @modules java.base/jdk.internal.foreign + * @build NativeTestHelper CallGeneratorHelper TestUpcallBase + * @bug 8337753 + * + * @run testng/othervm/timeout=3200 + * -Xcheck:jni + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:-VerifyDependencies + * --enable-native-access=ALL-UNNAMED + * -Dgenerator.sample.factor=17 + * TestUpcallStress + */ + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.MemorySegment; + +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandle; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.*; +import java.util.function.Consumer; + +public class TestUpcallStress extends TestUpcallBase { + + static { + System.loadLibrary("TestUpcall"); + } + + @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) + public void testUpcallsStress(int count, String fName, Ret ret, List paramTypes, + List fields) throws Throwable { + ExecutorService executor = Executors.newFixedThreadPool(16); + for (int threadIdx = 0; threadIdx < 16; threadIdx++) { + executor.submit(() -> { + for (int iter = 0; iter < 10000; iter++) { + List> returnChecks = new ArrayList<>(); + List> argChecks = new ArrayList<>(); + MemorySegment addr = findNativeOrThrow(fName); + try (Arena arena = Arena.ofConfined()) { + FunctionDescriptor descriptor = function(ret, paramTypes, fields); + MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor); + AtomicReference capturedArgs = new AtomicReference<>(); + Object[] args = makeArgs(capturedArgs, arena, descriptor, returnChecks, argChecks, 0); + + Object res = mh.invokeWithArguments(args); + + if (ret == Ret.NON_VOID) { + returnChecks.forEach(c -> c.accept(res)); + } + + Object[] capturedArgsArr = capturedArgs.get(); + for (int i = 0; i < capturedArgsArr.length; i++) { + argChecks.get(i).accept(capturedArgsArr[i]); + } + } catch (Throwable ex) { + throw new AssertionError(ex); + } + } + }); + } + // This shutdownNow is 'wrong', since it doesn't wait for tasks to terminate, + // but it seems to be the only way to reproduce the race of JDK-8337753 + executor.shutdownNow(); + } +} diff --git a/test/jdk/java/io/FileInputStream/ReadXBytes.java b/test/jdk/java/io/FileInputStream/ReadXBytes.java index 3b0fe7d0590de..9a38205cb13e1 100644 --- a/test/jdk/java/io/FileInputStream/ReadXBytes.java +++ b/test/jdk/java/io/FileInputStream/ReadXBytes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,7 +45,7 @@ public class ReadXBytes { private static final Random RND = RandomFactory.getRandom(); public static void main(String args[]) throws IOException { - File dir = new File(System.getProperty("test.src", ".")); + File dir = new File("."); dir.deleteOnExit(); File empty = File.createTempFile("foo", "bar", dir); diff --git a/test/jdk/java/lang/Math/HyperbolicTests.java b/test/jdk/java/lang/Math/HyperbolicTests.java index cf583ed7b5f27..4534396ee06dd 100644 --- a/test/jdk/java/lang/Math/HyperbolicTests.java +++ b/test/jdk/java/lang/Math/HyperbolicTests.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 @@ -969,6 +969,400 @@ static int testTanh() { failures += testTanhCaseWithUlpDiff(d, 1.0, 2.5); } + failures += testTanhAdditionalTests(); + + return failures; + } + + /** + * Test accuracy of {Math, StrictMath}.tanh using quad precision + * tanh implementation as the reference. There are additional tests. + * The specified accuracy is 2.5 ulps. + * + */ + static int testTanhAdditionalTests() { + int failures = 0; + /* + * Array elements below are generated using a quad precision tanh + * implementation (libquadmath). Rounded to a double, the quad result + * *should* be correctly rounded, unless we are quite unlucky. + * Assuming the quad value is a correctly rounded double, the + * allowed error is 3.0 ulps instead of 2.5 since the quad + * value rounded to double can have its own 1/2 ulp error. + */ + double[][] testCases = { + // x tanh(x) + {1.09951162777600024414062500000000000e+12, 1.00000000000000000000000000000000000e+00}, + {1.56250000000000416333634234433702659e-02, 1.56237285584089068255495133849899136e-02}, + {1.61254882812500000000000000000000000e+01, 9.99999999999980293529906376885389048e-01}, + {2.53165043529127054000582575099542737e-01, 2.47891535884497437358843835970604812e-01}, + {2.05669906337718799704816774465143681e+00, 9.67821952180774991463712302156014956e-01}, + {8.73243486124784240587359818164259195e+00, 9.99999947984421044859570034536492937e-01}, + {1.35302734375000000000000000000000000e+00, 8.74765946489987955543753077657414741e-01}, + {7.51299319580434721288497712521348149e-01, 6.35923468395323117288273690770900477e-01}, + {9.53088818012631927567568368431238923e-02, 9.50213381512267711017656118902912332e-02}, + {7.64443165964961757197215774795040488e-01, 6.43686625696507143760198874608796949e-01}, + {9.80772770147126660145175947036477737e-02, 9.77640088885469645387119927980991050e-02}, + {8.00000000000000044408920985006261617e-01, 6.64036770267848988511881426480887109e-01}, + {6.58800443825626694943631278533757722e-03, 6.58790912948844334160953310959647563e-03}, + {3.50634723606509357551885841530747712e+00, 9.98200861366828007281302037717336212e-01}, + {8.80951107580675074615328412619419396e-01, 7.06895478355484050029724917425086249e-01}, + {9.41693953354077795125931515940465033e-01, 7.35999567964351009171211613664845735e-01}, + {4.86714106743433794211028953213826753e-01, 4.51604571680788935707314000261162601e-01}, + {4.99999999970896114032115065128891729e-01, 4.62117157237121073362068671381593592e-01}, + {1.27999999999999971578290569595992565e+02, 1.00000000000000000000000000000000000e+00}, + {1.00000000000000022204460492503130808e+00, 7.61594155955764981372495044941331753e-01}, + {1.09951162777600024414062500000000000e+12, 1.00000000000000000000000000000000000e+00}, + {5.00000000000000777156117237609578297e-01, 4.62117157260010369694985045764006657e-01}, + {3.90625000000001474514954580286030250e-03, 3.90623013190635482599726614938805467e-03}, + {1.56250000000000659194920871186695877e-02, 1.56237285584089311057499113400637264e-02}, + {1.25000000000001332267629550187848508e-01, 1.24353001771597519720531117125519878e-01}, + {1.56250000000005169475958410885141348e-02, 1.56237285584093820237573019342883109e-02}, + {2.00000000000022737367544323205947876e+00, 9.64027580075832948084133680630298643e-01}, + {6.25000000000080352391407245704613160e-02, 6.24187467475205184231888372622987839e-02}, + {2.50000000000049737991503207013010979e-01, 2.44918662403755883728363950973251081e-01}, + {2.50000000000454747350886464118957520e-01, 2.44918662404136598540089724354621762e-01}, + {7.81250000001537658889105841808486730e-03, 7.81234105817638947180855590780540396e-03}, + {8.00000000002179945113311987370252609e+00, 9.99999774929675899622836792366347278e-01}, + {8.00000000002182787284255027770996094e+00, 9.99999774929675899635630557632573807e-01}, + {1.00000000004506106598967107856879011e+00, 7.61594155974689379640247120538425632e-01}, + {5.00000000024432678102925819985102862e-01, 4.62117157279224782806433798595181278e-01}, + {5.00000000124148025193449029757175595e-01, 4.62117157357645691462301850285961295e-01}, + {1.25000000043655745685100555419921875e-01, 1.24353001814576875736126314329404676e-01}, + {1.56250130385160446166992187500000000e-02, 1.56237415937421937398207048034470765e-02}, + {6.25000596046447753906250000000000000e-02, 6.24188061199314157260056878713262148e-02}, + {6.25001570879248902201652526855468750e-02, 6.24189032234056148184566765458350515e-02}, + {3.12509536743164062500000000000000000e-02, 3.12407841896026978197614959195842857e-02}, + {1.00024414062500000000000000000000000e+00, 7.61696669690972358277739369649969500e-01}, + {1.25091552734375000000000000000000000e-01, 1.24443137738349286849917747910445080e-01}, + {6.25703578334750876166481248219497502e-02, 6.24888301519391612116796252071905868e-02}, + {2.52525252525252597024518763646483421e-01, 2.47290965006585965880182136581880733e-01}, + {1.00000000164410457817454336293394590e-03, 9.99999668310902934017090322313224382e-04}, + {1.00000000966720672609944209341392707e-03, 9.99999676333997058845099107943491685e-04}, + {5.13687551499984795810860305209644139e-01, 4.72813376851263299460550751434331149e-01}, + {1.03125000000000000000000000000000000e+00, 7.74409187434213568286703209738132986e-01}, + {1.03372912114974835340319714305223897e+00, 7.75399652279487427958938283855319050e-01}, + {8.73243486124791523650401359191164374e+00, 9.99999947984421044867146689152891277e-01}, + {5.46364074509520181166521979321260005e-01, 4.97790203319363272413440879555555135e-01}, + {5.48776992118357842542764046811498702e-01, 4.99603030846724465253358333732665160e-01}, + {8.62884521484375000000000000000000000e-03, 8.62863106199946057455229821459862778e-03}, + {5.56840723899044820477399753144709393e-01, 5.05629619734278492036257594911833276e-01}, + {1.12042968912429174999090264464030042e+00, 8.07718324543002512898290101804260243e-01}, + {2.80761718750000000000000000000000000e-01, 2.73609921989813966516244201735889906e-01}, + {4.50982142857161694138312668655999005e+00, 9.99758010610690750512553927515350523e-01}, + {1.79803946803892764072507759465224808e-02, 1.79784572761372499903768063141254578e-02}, + {2.90674624105783541150316295897937380e-01, 2.82755618405959946459876962574827861e-01}, + {3.00000000019552404140199541870970279e-01, 2.91312612469484033539387970973996561e-01}, + {1.52306844600212459850396840010944288e-01, 1.51139964502163284820786391222343666e-01}, + {1.21913138136517762433186362613923848e+00, 8.39397762830401796350294214789399315e-01}, + {1.91612901016097944562055488404439529e-02, 1.91589453912240029886209020645693670e-02}, + {1.23194037796136601770058405236341059e+00, 8.43141232466373734055029303451281784e-01}, + {5.14544145441922751160745974630117416e+00, 9.99932120037417992977353814124626761e-01}, + {1.29608715898613313655118872702587396e+00, 8.60712461444305632100271902930674052e-01}, + {1.35302734375000000000000000000000000e+00, 8.74765946489987955543753077657414741e-01}, + {6.89141205308152926534148718928918242e-01, 5.97430012402391408227990740295335202e-01}, + {2.16702398900134561576802383342510439e-02, 2.16668484172600518601166701940771309e-02}, + {6.95330121814107471323040954302996397e-01, 6.01395252733578654705526153849150843e-01}, + {1.70127028180982076566857275068400668e-04, 1.70127026539641570641832179464678521e-04}, + {6.98731899876564921392230189667316154e-01, 6.03562246839061712657431798989209285e-01}, + {2.82308042901865396956395670713391155e+00, 9.92962754889608618611084237181745775e-01}, + {8.85009765625000000000000000000000000e-02, 8.82706391518277914218106043840064600e-02}, + {1.44086021505376304929768593865446746e+00, 8.93870759190524111186764508137227647e-01}, + {4.52708479240923750142044923450157512e-02, 4.52399464814195843886615749285771251e-02}, + {7.42434201630502221824770003877347335e-01, 6.30613596749571014884527941122811076e-01}, + {7.47314453125000000000000000000000000e-01, 6.33544059591028741704251380359792317e-01}, + {2.33572976827208893257914468222224968e-02, 2.33530509808936286709795071423335732e-02}, + {7.51392746195329142011587464367039502e-01, 6.35979110106607807348963004903609067e-01}, + {7.51649175412362091641682582121575251e-01, 6.36131796640758591543062918907122080e-01}, + {7.62560692649938864917658065678551793e-01, 6.42582785959772486552828548950126373e-01}, + {7.64660852335945273594575155584607273e-01, 6.43814099671361386286313072270915932e-01}, + {1.92871093750000000000000000000000000e-01, 1.90514597602311764623059750704793759e-01}, + {2.43864313521849479515779535176989157e-02, 2.43815983142663741885939851467013521e-02}, + {3.97705078125000000000000000000000000e-01, 3.77983627858614640283948547303348236e-01}, + {7.98034667968750000000000000000000000e-01, 6.62936606884708330125541187161682941e-01}, + {7.99316406250000000000000000000000000e-01, 6.63654430152659513562528989372441102e-01}, + {1.99890136718750000000000000000000000e-01, 1.97269734600247465099938891830640889e-01}, + {2.00000000008910994164779140191967599e-01, 1.97375320233467849151455260287892058e-01}, + {4.00000000093461316463816501709516160e-01, 3.79948962335194012629557116519596150e-01}, + {2.00000000069810862646235705142316874e-01, 1.97375320291995240418209079080646997e-01}, + {1.00000000056612609045103567950718571e-01, 9.96679946810060529704707312198636589e-02}, + {1.00000000080404896629637789828848327e-01, 9.96679947045619948897444345018478492e-02}, + {1.66666666666666696272613990004174411e+00, 9.31109608667577693190680177920119455e-01}, + {1.31034851074218750000000000000000000e-02, 1.31027351970209980614612589861988504e-02}, + {8.43444227005861080215254332870244980e-01, 6.87629045782656322925865941652078512e-01}, + {4.25596815032856623517432126391213387e-01, 4.01634947321531793299729470086813678e-01}, + {8.54614885269050605920426733064232394e-01, 6.93472710492835200064966331725774025e-01}, + {8.63777419830865200722769259300548583e-01, 6.98198780318331041148483592329099127e-01}, + {2.70117449276632004551146337689715438e-02, 2.70051772786722224566765342032635559e-02}, + {2.16282487792377908775165451515931636e-01, 2.12971988592557031781365419455581001e-01}, + {1.73204653003120601084674490266479552e+00, 9.39297315789076214802641716736658105e-01}, + {2.71436010781672190650404274947504746e-02, 2.71369367992428389623549774823710978e-02}, + {8.69092640155437079485523099720012397e-01, 7.00912831250687651307196017605464473e-01}, + {2.78015136718750000000000000000000000e-02, 2.77943530651526982827985645341156701e-02}, + {9.10156250000000000000000000000000000e-01, 7.21207240669352050307412688531998165e-01}, + {2.27787862235060922788676407435559668e-01, 2.23928183342045426304404589794035157e-01}, + {5.71524498377538048288215577485971153e-02, 5.70903033991663026980553749418981725e-02}, + {3.66406250000000000000000000000000000e+00, 9.98687254335130669671645868977829517e-01}, + {5.72863132979952241474741470028675394e-02, 5.72237295373843844708720164610859507e-02}, + {1.15265335196343660095763539175095502e-01, 1.14757558082362397172277983632352767e-01}, + {9.22871508732805212460448274214286357e-01, 7.27253018562057912939305739474564638e-01}, + {1.44882202148437500000000000000000000e-02, 1.44872065663080247568859985337817684e-02}, + {2.33459472656250000000000000000000000e-01, 2.29308506606965514793638071915067313e-01}, + {4.67608948699241744328958247933769599e-01, 4.36265367328226513916944408221096440e-01}, + {2.34375000000000000000000000000000000e-01, 2.30175711032132981819570603563403063e-01}, + {2.93977526337722387672624080323657836e-02, 2.93892867747176836833509722656208701e-02}, + {1.89257812500000000000000000000000000e+00, 9.55597542193888546329823463414294394e-01}, + {2.95798696005085230698039566732404637e-02, 2.95712454656271068251835101101858187e-02}, + {1.89360756176453159937977943627629429e+00, 9.55686843743833788059988471898348142e-01}, + {4.74943000289441419337066463413066231e-01, 4.42184502480986035803118250513914071e-01}, + {4.76562500000000000000000000000000000e-01, 4.43486412595195826790440814160101630e-01}, + {9.59027831303091549131067949929274619e-01, 7.43842915769532264613887042424467929e-01}, + {3.09784640940728682456661857713697827e-02, 3.09685582448730820784897541732374591e-02}, + {1.98437499999999977795539507496869192e+00, 9.62906870975765387287608356129776957e-01}, + {9.97205648659918675313917901803506538e-01, 7.60418100316000600203658397859135661e-01}, + {3.90291213989257769131913100579822640e-03, 3.90289232268659551022766662201699736e-03}, + {3.90481948852539019131913100579822640e-03, 3.90479964225138860483705936617354227e-03}, + {3.12423706054687500000000000000000000e-02, 3.12322094954161727499363714231262395e-02}, + {3.90535406768321947598709975579822640e-03, 3.90533421325712750455622728849037270e-03}, + {7.81154632568359288263826201159645279e-03, 7.81138744204279466358299901763388375e-03}, + {1.24999789521095569511111023075500270e-01, 1.24352794547462473786771370350680283e-01}, + {9.99999444341875043384959553804947063e-01, 7.61593922593510941370556728778707492e-01}, + {9.99999895691871532044103787484345958e-01, 7.61594112149023829770882693858011645e-01}, + {2.49999998130078865399283927217766177e-01, 2.44918660645955495851244772320875652e-01}, + {2.49999998603016110321206610933586489e-01, 2.44918661090523528987141309434443089e-01}, + {4.99999999970896114032115065128891729e-01, 4.62117157237121073362068671381593592e-01}, + {9.99999999999829358721115113439736888e-01, 7.61594155955693223160706417649502130e-01}, + {3.12499999999979183318288278314867057e-02, 3.12398314460291771315638233977623908e-02}, + {6.24999999999973701592104191604448715e-02, 6.24187467475098948954758673929811576e-02}, + {9.99999999999998556710067987296497449e-01, 7.61594155955764281974719327416526334e-01}, + {1.27999999999999971578290569595992565e+02, 1.00000000000000000000000000000000000e+00}, + {3.44827586206896546938693859374325257e-02, 3.44690977543900329082306735053903756e-02}, + {6.89655172413793093877387718748650514e-02, 6.88563859490195017187269420471893052e-02}, + {1.03448275862068964081608157812297577e-01, 1.03080829858086020470241143281488892e-01}, + {1.37931034482758618775477543749730103e-01, 1.37062928881132531260309423128680656e-01}, + {1.72413793103448287347134737501619384e-01, 1.70725445282084714146447066718646674e-01}, + {2.06896551724137955918791931253508665e-01, 2.03994088403983264406130799853712156e-01}, + {2.41379310344827624490449125005397946e-01, 2.36798141876826809207868665407968027e-01}, + {2.75862068965517293062106318757287227e-01, 2.69071023201531202536913498454407638e-01}, + {3.10344827586206961633763512509176508e-01, 3.00750767242988858303859696730149916e-01}, + {3.44827586206896630205420706261065789e-01, 3.31780427497542984412066808006924260e-01}, + {3.79310344827586298777077900012955070e-01, 3.62108391409330839416919529705937418e-01}, + {4.13793103448275967348735093764844351e-01, 3.91688608393346163715111892758489641e-01}, + {4.48275862068965635920392287516733631e-01, 4.20480731486975012003415012347372452e-01}, + {4.82758620689655304492049481268622912e-01, 4.48450175615929701255698232730127770e-01}, + {5.17241379310344973063706675020512193e-01, 4.75568097261544496368767486763625886e-01}, + {5.51724137931034586124212637514574453e-01, 5.01811301809605377924874787743959204e-01}, + {5.86206896551724199184718600008636713e-01, 5.27162086020673527345538794213535134e-01}, + {6.20689655172413812245224562502698973e-01, 5.51608023880856575817362987825134405e-01}, + {6.55172413793103425305730524996761233e-01, 5.75141704579102279221464447290041163e-01}, + {6.89655172413793038366236487490823492e-01, 5.97760431534850182076591161239096491e-01}, + {7.24137931034482651426742449984885752e-01, 6.19465891301270655454827665546029664e-01}, + {7.58620689655172264487248412478948012e-01, 6.40263800834536321750527885396253899e-01}, + {7.93103448275861877547754374973010272e-01, 6.60163541092833363676687202005166905e-01}, + {8.27586206896551490608260337467072532e-01, 6.79177784255529339466238655218797135e-01}, + {8.62068965517241103668766299961134791e-01, 6.97322121077226884958095667604561029e-01}, + {8.96551724137930716729272262455197051e-01, 7.14614694054361357412620518070189428e-01}, + {9.31034482758620329789778224949259311e-01, 7.31075841220047215751737025073520835e-01}, + {9.65517241379309942850284187443321571e-01, 7.46727754527182387965057729340710925e-01}, + {9.99999999999999555910790149937383831e-01, 7.61594155955764701613384757931622516e-01}, + {1.26765060022822940149670320537600000e+30, 1.00000000000000000000000000000000000e+00}, + {1.33436905287182034855574634496000000e+30, 1.00000000000000000000000000000000000e+00}, + {1.40108750551541129561478948454400000e+30, 1.00000000000000000000000000000000000e+00}, + {1.46780595815900224267383262412800000e+30, 1.00000000000000000000000000000000000e+00}, + {1.53452441080259318973287576371200000e+30, 1.00000000000000000000000000000000000e+00}, + {1.60124286344618413679191890329600000e+30, 1.00000000000000000000000000000000000e+00}, + {1.66796131608977508385096204288000000e+30, 1.00000000000000000000000000000000000e+00}, + {1.73467976873336603091000518246400000e+30, 1.00000000000000000000000000000000000e+00}, + {1.80139822137695697796904832204800000e+30, 1.00000000000000000000000000000000000e+00}, + {1.86811667402054792502809146163200000e+30, 1.00000000000000000000000000000000000e+00}, + {1.93483512666413887208713460121600000e+30, 1.00000000000000000000000000000000000e+00}, + {2.00155357930772981914617774080000000e+30, 1.00000000000000000000000000000000000e+00}, + {2.06827203195132076620522088038400000e+30, 1.00000000000000000000000000000000000e+00}, + {2.13499048459491171326426401996800000e+30, 1.00000000000000000000000000000000000e+00}, + {2.20170893723850266032330715955200000e+30, 1.00000000000000000000000000000000000e+00}, + {2.26842738988209360738235029913600000e+30, 1.00000000000000000000000000000000000e+00}, + {2.33514584252568455444139343872000000e+30, 1.00000000000000000000000000000000000e+00}, + {2.40186429516927550150043657830400000e+30, 1.00000000000000000000000000000000000e+00}, + {2.46858274781286644855947971788800000e+30, 1.00000000000000000000000000000000000e+00}, + {2.53530120045645739561852285747200000e+30, 1.00000000000000000000000000000000000e+00}, + {1.60693804425899027554196209234116260e+60, 1.00000000000000000000000000000000000e+00}, + {1.69151373079893703825155926128281056e+60, 1.00000000000000000000000000000000000e+00}, + {1.77608941733888380096115643022445853e+60, 1.00000000000000000000000000000000000e+00}, + {1.86066510387883056367075359916610649e+60, 1.00000000000000000000000000000000000e+00}, + {1.94524079041877732638035076810775445e+60, 1.00000000000000000000000000000000000e+00}, + {2.02981647695872408908994793704940241e+60, 1.00000000000000000000000000000000000e+00}, + {2.11439216349867085179954510599105038e+60, 1.00000000000000000000000000000000000e+00}, + {2.19896785003861761450914227493269834e+60, 1.00000000000000000000000000000000000e+00}, + {2.28354353657856437721873944387434630e+60, 1.00000000000000000000000000000000000e+00}, + {2.36811922311851113992833661281599426e+60, 1.00000000000000000000000000000000000e+00}, + {2.45269490965845790263793378175764222e+60, 1.00000000000000000000000000000000000e+00}, + {2.53727059619840466534753095069929019e+60, 1.00000000000000000000000000000000000e+00}, + {2.62184628273835142805712811964093815e+60, 1.00000000000000000000000000000000000e+00}, + {2.70642196927829819076672528858258611e+60, 1.00000000000000000000000000000000000e+00}, + {2.79099765581824495347632245752423407e+60, 1.00000000000000000000000000000000000e+00}, + {2.87557334235819171618591962646588203e+60, 1.00000000000000000000000000000000000e+00}, + {2.96014902889813847889551679540753000e+60, 1.00000000000000000000000000000000000e+00}, + {3.04472471543808524160511396434917796e+60, 1.00000000000000000000000000000000000e+00}, + {3.12930040197803200431471113329082592e+60, 1.00000000000000000000000000000000000e+00}, + {3.21387608851797876702430830223247388e+60, 1.00000000000000000000000000000000000e+00}, + {1.07150860718626732094842504906000181e+301, 1.00000000000000000000000000000000000e+00}, + {1.12790379703817606470289337889334663e+301, 1.00000000000000000000000000000000000e+00}, + {1.18429898689008480845736170872669145e+301, 1.00000000000000000000000000000000000e+00}, + {1.24069417674199355221183003856003627e+301, 1.00000000000000000000000000000000000e+00}, + {1.29708936659390229596629836839338109e+301, 1.00000000000000000000000000000000000e+00}, + {1.35348455644581103972076669822672591e+301, 1.00000000000000000000000000000000000e+00}, + {1.40987974629771978347523502806007073e+301, 1.00000000000000000000000000000000000e+00}, + {1.46627493614962852722970335789341555e+301, 1.00000000000000000000000000000000000e+00}, + {1.52267012600153727098417168772676037e+301, 1.00000000000000000000000000000000000e+00}, + {1.57906531585344601473864001756010519e+301, 1.00000000000000000000000000000000000e+00}, + {1.63546050570535475849310834739345001e+301, 1.00000000000000000000000000000000000e+00}, + {1.69185569555726350224757667722679483e+301, 1.00000000000000000000000000000000000e+00}, + {1.74825088540917224600204500706013965e+301, 1.00000000000000000000000000000000000e+00}, + {1.80464607526108098975651333689348447e+301, 1.00000000000000000000000000000000000e+00}, + {1.86104126511298973351098166672682928e+301, 1.00000000000000000000000000000000000e+00}, + {1.91743645496489847726544999656017410e+301, 1.00000000000000000000000000000000000e+00}, + {1.97383164481680722101991832639351892e+301, 1.00000000000000000000000000000000000e+00}, + {2.03022683466871596477438665622686374e+301, 1.00000000000000000000000000000000000e+00}, + {2.08662202452062470852885498606020856e+301, 1.00000000000000000000000000000000000e+00}, + {2.14301721437253345228332331589355338e+301, 1.00000000000000000000000000000000000e+00}, + {4.94065645841246544176568792868221372e-324, 4.94065645841246544176568792868221372e-324}, + {4.94065645841246544176568792868221372e-324, 4.94065645841246544176568792868221372e-324}, + {4.99999999999999944488848768742172979e-01, 4.62117157260009714845699443492203290e-01}, + {5.00000000000000000000000000000000000e-01, 4.62117157260009758502318483643672557e-01}, + {5.00000000000000111022302462515654042e-01, 4.62117157260009845815556563946604302e-01}, + {5.49306144334054669009503868437604979e-01, 4.99999999999999867483910937482244858e-01}, + {5.49306144334054780031806330953259021e-01, 4.99999999999999950750637784368995452e-01}, + {5.49306144334054891054108793468913063e-01, 5.00000000000000034017364631255736851e-01}, + {2.19999999999999964472863211994990706e+01, 9.99999999999999999844377355177323009e-01}, + {2.20000000000000000000000000000000000e+01, 9.99999999999999999844377355177324068e-01}, + {2.20000000000000035527136788005009294e+01, 9.99999999999999999844377355177325223e-01}, + {6.93147180559945397249066445510834455e-01, 6.00000000000000056212373967393698031e-01}, + {6.93147180559945286226763982995180413e-01, 5.99999999999999985158100391383682202e-01}, + {6.93147180559945175204461520479526371e-01, 5.99999999999999914103826815373657032e-01}, + {3.46573590279972698624533222755417228e-01, 3.33333333333333372369704144023402903e-01}, + {3.46573590279972643113381991497590207e-01, 3.33333333333333323026458605127557235e-01}, + {3.46573590279972587602230760239763185e-01, 3.33333333333333273683213066231709688e-01}, + {1.73286795139986349312266611377708614e-01, 1.71572875253809923708199182915954510e-01}, + {1.73286795139986321556690995748795103e-01, 1.71572875253809896769671427846052946e-01}, + {1.73286795139986293801115380119881593e-01, 1.71572875253809869831143672776151118e-01}, + {8.66433975699931746561333056888543069e-02, 8.64272337258898029408455765418952337e-02}, + {8.66433975699931607783454978743975516e-02, 8.64272337258897891667202185946638536e-02}, + {8.66433975699931469005576900599407963e-02, 8.64272337258897753925948606474324374e-02}, + {4.33216987849965873280666528444271535e-02, 4.32946174993891841617996586480128793e-02}, + {4.33216987849965803891727489371987758e-02, 4.32946174993891772359121833444914284e-02}, + {4.33216987849965734502788450299703982e-02, 4.32946174993891703100247080409699774e-02}, + {2.16608493924982936640333264222135767e-02, 2.16574623262262954492383391751347008e-02}, + {2.16608493924982901945863744685993879e-02, 2.16574623262262919814187163069359478e-02}, + {2.16608493924982867251394225149851991e-02, 2.16574623262262885135990934387371889e-02}, + {2.16608493924982867251394225149851991e-02, 2.16574623262262885135990934387371889e-02}, + {2.16608493924982901945863744685993879e-02, 2.16574623262262919814187163069359478e-02}, + {2.16608493924982936640333264222135767e-02, 2.16574623262262954492383391751347008e-02}, + {4.33216987849965734502788450299703982e-02, 4.32946174993891703100247080409699774e-02}, + {4.33216987849965803891727489371987758e-02, 4.32946174993891772359121833444914284e-02}, + {4.33216987849965873280666528444271535e-02, 4.32946174993891841617996586480128793e-02}, + {8.66433975699931469005576900599407963e-02, 8.64272337258897753925948606474324374e-02}, + {8.66433975699931607783454978743975516e-02, 8.64272337258897891667202185946638536e-02}, + {8.66433975699931746561333056888543069e-02, 8.64272337258898029408455765418952337e-02}, + {1.73286795139986293801115380119881593e-01, 1.71572875253809869831143672776151118e-01}, + {1.73286795139986321556690995748795103e-01, 1.71572875253809896769671427846052946e-01}, + {1.73286795139986349312266611377708614e-01, 1.71572875253809923708199182915954510e-01}, + {3.46573590279972587602230760239763185e-01, 3.33333333333333273683213066231709688e-01}, + {3.46573590279972643113381991497590207e-01, 3.33333333333333323026458605127557235e-01}, + {3.46573590279972698624533222755417228e-01, 3.33333333333333372369704144023402903e-01}, + {6.93147180559945175204461520479526371e-01, 5.99999999999999914103826815373657032e-01}, + {6.93147180559945286226763982995180413e-01, 5.99999999999999985158100391383682202e-01}, + {6.93147180559945397249066445510834455e-01, 6.00000000000000056212373967393698031e-01}, + {7.09782712893383859409368596971035004e+02, 1.00000000000000000000000000000000000e+00}, + {7.09782712893383973096206318587064743e+02, 1.00000000000000000000000000000000000e+00}, + {7.09782712893384086783044040203094482e+02, 1.00000000000000000000000000000000000e+00}, + {7.41782712893384086783044040203094482e+02, 1.00000000000000000000000000000000000e+00}, + {7.41782712893383973096206318587064743e+02, 1.00000000000000000000000000000000000e+00}, + {7.41782712893383859409368596971035004e+02, 1.00000000000000000000000000000000000e+00}, + {7.10475860073943749739555642008781433e+02, 1.00000000000000000000000000000000000e+00}, + {7.10475860073943863426393363624811172e+02, 1.00000000000000000000000000000000000e+00}, + {7.10475860073943977113231085240840912e+02, 1.00000000000000000000000000000000000e+00}, + {7.09782712893384086783044040203094482e+02, 1.00000000000000000000000000000000000e+00}, + {7.09782712893383973096206318587064743e+02, 1.00000000000000000000000000000000000e+00}, + {7.09782712893383859409368596971035004e+02, 1.00000000000000000000000000000000000e+00}, + {9.22337203685477478400000000000000000e+18, 1.00000000000000000000000000000000000e+00}, + {9.22337203685477580800000000000000000e+18, 1.00000000000000000000000000000000000e+00}, + {9.22337203685477785600000000000000000e+18, 1.00000000000000000000000000000000000e+00}, + {1.34217727999999985098838806152343750e+08, 1.00000000000000000000000000000000000e+00}, + {1.34217728000000000000000000000000000e+08, 1.00000000000000000000000000000000000e+00}, + {1.34217728000000029802322387695312500e+08, 1.00000000000000000000000000000000000e+00}, + {1.67772159999999981373548507690429688e+07, 1.00000000000000000000000000000000000e+00}, + {1.67772160000000000000000000000000000e+07, 1.00000000000000000000000000000000000e+00}, + {1.67772160000000037252902984619140625e+07, 1.00000000000000000000000000000000000e+00}, + {3.19999999999999964472863211994990706e+01, 9.99999999999999999999999999679237812e-01}, + {3.20000000000000000000000000000000000e+01, 9.99999999999999999999999999679237812e-01}, + {3.20000000000000071054273576010018587e+01, 9.99999999999999999999999999679237812e-01}, + {1.59999999999999982236431605997495353e+01, 9.99999999999974671668901811879331665e-01}, + {1.60000000000000000000000000000000000e+01, 9.99999999999974671668901811969315927e-01}, + {1.60000000000000035527136788005009294e+01, 9.99999999999974671668901812149284547e-01}, + {7.99999999999999911182158029987476766e+00, 9.99999774929675889809619027791781323e-01}, + {8.00000000000000000000000000000000000e+00, 9.99999774929675889810018832956368404e-01}, + {8.00000000000000177635683940025046468e+00, 9.99999774929675889810818443285542469e-01}, + {3.99999999999999955591079014993738383e+00, 9.99329299739067043196741615068852355e-01}, + {4.00000000000000000000000000000000000e+00, 9.99329299739067043792243344341724993e-01}, + {4.00000000000000088817841970012523234e+00, 9.99329299739067044983246802887468536e-01}, + {1.99999999999999977795539507496869192e+00, 9.64027580075816868258779231952432911e-01}, + {2.00000000000000000000000000000000000e+00, 9.64027580075816883946413724100923171e-01}, + {2.00000000000000044408920985006261617e+00, 9.64027580075816915321682708397883469e-01}, + {9.99999999999999888977697537484345958e-01, 7.61594155955764841492939901436512668e-01}, + {1.00000000000000000000000000000000000e+00, 7.61594155955764888119458282604793657e-01}, + {1.00000000000000022204460492503130808e+00, 7.61594155955764981372495044941331753e-01}, + {4.99999999999999944488848768742172979e-01, 4.62117157260009714845699443492203290e-01}, + {5.00000000000000000000000000000000000e-01, 4.62117157260009758502318483643672557e-01}, + {5.00000000000000111022302462515654042e-01, 4.62117157260009845815556563946604302e-01}, + {2.49999999999999972244424384371086489e-01, 2.44918662403709103187147915631612892e-01}, + {2.50000000000000000000000000000000000e-01, 2.44918662403709129277801131491016945e-01}, + {2.50000000000000055511151231257827021e-01, 2.44918662403709181459107563209824042e-01}, + {1.24999999999999986122212192185543245e-01, 1.24353001771596194391460985792144305e-01}, + {1.25000000000000000000000000000000000e-01, 1.24353001771596208054647275805892707e-01}, + {1.25000000000000027755575615628913511e-01, 1.24353001771596235381019855833389378e-01}, + {6.24999999999999930611060960927716224e-02, 6.24187467475125075782836114480350829e-02}, + {6.25000000000000000000000000000000000e-02, 6.24187467475125144901428911942113317e-02}, + {6.25000000000000138777878078144567553e-02, 6.24187467475125283138614506865638292e-02}, + {3.12499999999999965305530480463858112e-02, 3.12398314460312533021176543496182149e-02}, + {3.12500000000000000000000000000000000e-02, 3.12398314460312567681786791091369499e-02}, + {3.12500000000000069388939039072283776e-02, 3.12398314460312637003007286281744168e-02}, + {1.56249999999999982652765240231929056e-02, 1.56237285584088634680488027509294906e-02}, + {1.56250000000000000000000000000000000e-02, 1.56237285584088652023488311762919065e-02}, + {1.56250000000000034694469519536141888e-02, 1.56237285584088686709488880270167445e-02}, + {6.10351562499999932237364219655972875e-05, 6.10351561742087681889301535131725312e-05}, + {6.10351562500000000000000000000000000e-05, 6.10351561742087749651937063040263414e-05}, + {6.10351562500000135525271560688054251e-05, 6.10351561742087885177208118857339557e-05}, + {9.31322574615478412227423430871540641e-10, 9.31322574615478411958158908556102005e-10}, + {9.31322574615478515625000000000000000e-10, 9.31322574615478515355735477684561274e-10}, + {9.31322574615478722420153138256918718e-10, 9.31322574615478722150888615941479902e-10}, + {2.77555756156289104291028806826931429e-17, 2.77555756156289104291028806826931349e-17}, + {2.77555756156289135105907917022705078e-17, 2.77555756156289135105907917022704998e-17}, + {2.77555756156289196735666137414252376e-17, 2.77555756156289196735666137414252322e-17}, + {1.79769313486231570814527423731704357e+308, 1.00000000000000000000000000000000000e+00}, + {1.79769313486231570814527423731704357e+308, 1.00000000000000000000000000000000000e+00}, + {1.79769313486231570814527423731704357e+308, 1.00000000000000000000000000000000000e+00}, + {1.79769313486231550856124328384506240e+308, 1.00000000000000000000000000000000000e+00}, + {3.14159265358979311599796346854418516e+00, 9.96272076220749943353314537833579484e-01}, + {1.57079632679489655799898173427209258e+00, 9.17152335667274336647462811870662140e-01}, + {1.00000000000000022204460492503130808e+00, 7.61594155955764981372495044941331753e-01}, + {1.00000000000000000000000000000000000e+00, 7.61594155955764888119458282604793657e-01}, + {9.99999999999999888977697537484345958e-01, 7.61594155955764841492939901436512668e-01}, + {7.85398163397448278999490867136046290e-01, 6.55794202632672418203926030568705821e-01}, + {2.22507385850720187715587855857894824e-308, 2.22507385850720187715587855857894824e-308}, + {2.22507385850720138309023271733240406e-308, 2.22507385850720138309023271733240406e-308}, + {2.22507385850720088902458687608585989e-308, 2.22507385850720088902458687608585989e-308}, + {2.22507385850720039495894103483931571e-308, 2.22507385850720039495894103483931571e-308}, + {9.88131291682493088353137585736442745e-324, 9.88131291682493088353137585736442745e-324}, + {4.94065645841246544176568792868221372e-324, 4.94065645841246544176568792868221372e-324}, + }; + + for (int i = 0; i < testCases.length; i++) { + double[] testCase = testCases[i]; + failures += testTanhCaseWithUlpDiff(testCase[0], + testCase[1], + 3.0); + } + return failures; } diff --git a/test/jdk/java/lang/StringBuffer/HugeCapacity.java b/test/jdk/java/lang/StringBuffer/HugeCapacity.java index e3c98496c50c8..d1ff51dddb2cf 100644 --- a/test/jdk/java/lang/StringBuffer/HugeCapacity.java +++ b/test/jdk/java/lang/StringBuffer/HugeCapacity.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 @@ -21,11 +21,14 @@ * questions. */ +import jdk.internal.util.ArraysSupport; + /** * @test * @bug 8218227 * @summary StringBuilder/StringBuffer constructor throws confusing * NegativeArraySizeException + * @modules java.base/jdk.internal.util * @requires (sun.arch.data.model == "64" & os.maxMemory >= 8G) * @run main/othervm -Xms6G -Xmx6G HugeCapacity */ @@ -43,7 +46,7 @@ public static void main(String[] args) { private static void testHugeInitialString() { try { - String str = "Z".repeat(Integer.MAX_VALUE - 8); + String str = "Z".repeat(ArraysSupport.SOFT_MAX_ARRAY_LENGTH); StringBuffer sb = new StringBuffer(str); } catch (OutOfMemoryError ignore) { } catch (Throwable unexpected) { diff --git a/test/jdk/java/lang/StringBuilder/HugeCapacity.java b/test/jdk/java/lang/StringBuilder/HugeCapacity.java index a584ce1f07a9d..a81f1614fe590 100644 --- a/test/jdk/java/lang/StringBuilder/HugeCapacity.java +++ b/test/jdk/java/lang/StringBuilder/HugeCapacity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, 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 @@ -21,11 +21,14 @@ * questions. */ +import jdk.internal.util.ArraysSupport; + /** * @test * @bug 8149330 8218227 * @summary Capacity should not get close to Integer.MAX_VALUE unless * necessary + * @modules java.base/jdk.internal.util * @requires (sun.arch.data.model == "64" & os.maxMemory >= 8G) * @run main/othervm -Xms6G -Xmx6G -XX:+CompactStrings HugeCapacity true * @run main/othervm -Xms6G -Xmx6G -XX:-CompactStrings HugeCapacity false @@ -75,7 +78,7 @@ private static void testUtf16() { private static void testHugeInitialString() { try { - String str = "Z".repeat(Integer.MAX_VALUE - 8); + String str = "Z".repeat(ArraysSupport.SOFT_MAX_ARRAY_LENGTH); StringBuilder sb = new StringBuilder(str); } catch (OutOfMemoryError ignore) { } catch (Throwable unexpected) { diff --git a/test/jdk/java/lang/invoke/BigArityTest.java b/test/jdk/java/lang/invoke/BigArityTest.java index 338903f31630d..2dba056a183ab 100644 --- a/test/jdk/java/lang/invoke/BigArityTest.java +++ b/test/jdk/java/lang/invoke/BigArityTest.java @@ -24,7 +24,7 @@ /* @test * @summary High arity invocations * @compile BigArityTest.java - * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -esa -DBigArityTest.ITERATION_COUNT=1 test.java.lang.invoke.BigArityTest + * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -XX:CompileCommand=memlimit,*.*,0 -esa -DBigArityTest.ITERATION_COUNT=1 test.java.lang.invoke.BigArityTest */ package test.java.lang.invoke; diff --git a/test/jdk/java/lang/invoke/TestLambdaFormCustomization.java b/test/jdk/java/lang/invoke/TestLambdaFormCustomization.java new file mode 100644 index 0000000000000..60ba4af590e78 --- /dev/null +++ b/test/jdk/java/lang/invoke/TestLambdaFormCustomization.java @@ -0,0 +1,70 @@ +/* + * 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.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; + +/** + * @test + * @bug 8340812 + * @summary Verify that LambdaForm customization via MethodHandle::updateForm is thread safe. + * @run main TestLambdaFormCustomization + * @run main/othervm -Djava.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD=0 TestLambdaFormCustomization + */ +public class TestLambdaFormCustomization { + + String str = "test"; + static final String value = "test" + 42; + + // Trigger concurrent LambdaForm customization for VarHandle invokers + void test() throws NoSuchFieldException, IllegalAccessException { + VarHandle varHandle = MethodHandles.lookup().in(getClass()).findVarHandle(getClass(), "str", String.class); + + ArrayList threads = new ArrayList<>(); + for (int threadIdx = 0; threadIdx < 10; threadIdx++) { + threads.add(new Thread(() -> { + for (int i = 0; i < 1000; i++) { + varHandle.compareAndExchange(this, value, value); + varHandle.compareAndExchange(this, value, value); + varHandle.compareAndExchange(this, value, value); + } + })); + } + threads.forEach(Thread::start); + threads.forEach(t -> { + try { + t.join(); + } catch (Throwable e) { + throw new IllegalStateException(e); + } + }); + } + + public static void main(String[] args) throws Exception { + TestLambdaFormCustomization t = new TestLambdaFormCustomization(); + for (int i = 0; i < 4000; ++i) { + t.test(); + } + } +} diff --git a/test/jdk/java/lang/management/MemoryMXBean/MemoryUtil.java b/test/jdk/java/lang/management/MemoryMXBean/MemoryUtil.java index 55605c6c1c445..5fae7468b79df 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/MemoryUtil.java +++ b/test/jdk/java/lang/management/MemoryMXBean/MemoryUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 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 @@ -56,6 +56,8 @@ public static void printMemoryPool(MemoryPoolMXBean pool) { (pool.isUsageThresholdSupported() ? pool.getUsageThreshold() : -1)); System.out.println(INDENT + "ThresholdCount: " + (pool.isUsageThresholdSupported() ? pool.getUsageThresholdCount() : -1)); + System.out.println(INDENT + "CollectionThresholdCount: " + + (pool.isCollectionUsageThresholdSupported() ? pool.getCollectionUsageThresholdCount() : -1)); System.out.print(INDENT + "Manager = ["); String[] mgrs = pool.getMemoryManagerNames(); for (int i = 0; i < mgrs.length; i++) { diff --git a/test/jdk/java/lang/management/MemoryMXBean/RunUtil.java b/test/jdk/java/lang/management/MemoryMXBean/RunUtil.java index e94bc6c3c2c78..314aab44c4b9b 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/RunUtil.java +++ b/test/jdk/java/lang/management/MemoryMXBean/RunUtil.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 @@ -73,6 +73,7 @@ private static void runTest(String main, boolean clearGcOpts, String... testOpts } opts.addAll(Arrays.asList(testOpts)); opts.add(main); + opts.add("trace"); OutputAnalyzer output = ProcessTools.executeProcess(opts.toArray(new String[0])); output.shouldHaveExitValue(0); diff --git a/test/jdk/java/lang/management/ThreadMXBean/Locks.java b/test/jdk/java/lang/management/ThreadMXBean/Locks.java index a9c45887780f1..5b4d360ae21c8 100644 --- a/test/jdk/java/lang/management/ThreadMXBean/Locks.java +++ b/test/jdk/java/lang/management/ThreadMXBean/Locks.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 @@ -422,6 +422,9 @@ private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock) lock + " expected to have owner"); } for (ThreadInfo info1 : infos) { + if (info1 == null) { + continue; // Missing thread, e.g. completed. Ignore. + } if (info1.getThreadId() == threadId) { ownerInfo = info1; break; diff --git a/test/jdk/java/lang/reflect/Generics/TestMissingTypeVariable.java b/test/jdk/java/lang/reflect/Generics/TestMissingTypeVariable.java new file mode 100644 index 0000000000000..64f1457bdf75e --- /dev/null +++ b/test/jdk/java/lang/reflect/Generics/TestMissingTypeVariable.java @@ -0,0 +1,70 @@ +/* + * 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 + * @library /test/lib + * @bug 8337302 + * @enablePreview + * @summary Tests that an exception is thrown if a type variable is not declared + */ + +import jdk.test.lib.ByteCodeLoader; + +import java.lang.classfile.ClassFile; +import java.lang.classfile.Signature; +import java.lang.classfile.attribute.SignatureAttribute; +import java.lang.constant.ClassDesc; +import java.lang.reflect.AccessFlag; +import java.lang.reflect.Type; + +public class TestMissingTypeVariable { + + public static void main(String[] args) throws Exception { + ClassFile cf = ClassFile.of(); + byte[] bytes = cf.build( + ClassDesc.of("sample.MissingVariable"), + classBuilder -> { + classBuilder.withSuperclass(ClassDesc.of("java.lang.Object")); + classBuilder.withFlags(AccessFlag.PUBLIC); + classBuilder.withField("f", + ClassDesc.of("java.lang.Object"), + fieldBuilder -> fieldBuilder.withFlags(AccessFlag.PUBLIC).with(SignatureAttribute.of(Signature.parseFrom("TA;")))); + }); + /* + package sample; + public class MissingVariable { + public A f; // undeclared type variable + } + */ + Class missing = ByteCodeLoader.load("sample.MissingVariable", bytes); + try { + Type type = missing.getField("f").getGenericType(); + throw new IllegalStateException("Expected TypeNotPresentException but got: " + type); + } catch (TypeNotPresentException e) { + if (!"A".equals(e.typeName())) { + throw new IllegalStateException("Unexpected name: " + e.typeName()); + } + } + } +} diff --git a/test/jdk/java/math/BigInteger/MutableBigIntegerShiftTests.java b/test/jdk/java/math/BigInteger/MutableBigIntegerShiftTests.java new file mode 100644 index 0000000000000..e64cae480d3f0 --- /dev/null +++ b/test/jdk/java/math/BigInteger/MutableBigIntegerShiftTests.java @@ -0,0 +1,191 @@ +/* + * 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 jdk.test.lib.RandomFactory; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.Arguments; + +import java.math.BigInteger; +import java.math.MutableBigIntegerBox; +import java.util.Arrays; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static java.math.MutableBigIntegerBox.*; + +/** + * @test + * @bug 8336274 + * @summary Tests for correctness of MutableBigInteger.leftShift(int) + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @build java.base/java.math.MutableBigIntegerBox + * @key randomness + * @run junit MutableBigIntegerShiftTests + */ +public class MutableBigIntegerShiftTests { + + private static final int ORDER_SMALL = 60; + private static final int ORDER_MEDIUM = 100; + + private static final Random random = RandomFactory.getRandom(); + + private static int[] orders() { + return new int[] { ORDER_SMALL, ORDER_MEDIUM }; + } + + @ParameterizedTest + @MethodSource("orders") + public void shift(int order) { + for (int i = 0; i < 100; i++) { + test(fetchNumber(order), random.nextInt(200)); + } + } + + @ParameterizedTest + @MethodSource("pathTargetedCases") + public void test(MutableBigIntegerBox x, int n) { + leftShiftAssertions(x, n); + } + + private static Arguments[] pathTargetedCases() { + return new Arguments[] { + // intLen == 0 + Arguments.of(MutableBigIntegerBox.ZERO, + random.nextInt(33)), + // intLen != 0 && n <= leadingZeros + Arguments.of(new MutableBigIntegerBox(new int[] { (int) random.nextLong(1L, 1L << 16) }), + random.nextInt(1, 17)), + // intLen != 0 && n > leadingZeros && nBits <= leadingZeros && value.length < newLen && nBits == 0 + Arguments.of(new MutableBigIntegerBox(new int[] { (int) random.nextLong(1L, 1L << 32) }), + 32), + // intLen != 0 && n > leadingZeros && nBits <= leadingZeros && value.length < newLen && nBits != 0 + Arguments.of(new MutableBigIntegerBox(new int[] { (int) random.nextLong(1L, 1L << 16) }), + 32 + random.nextInt(1, 17)), + // intLen != 0 && n > leadingZeros && nBits <= leadingZeros && value.length >= newLen && nBits == 0 + // && newOffset != offset + Arguments.of(new MutableBigIntegerBox(new int[] { random.nextInt(), (int) random.nextLong(1L, 1L << 32) }, 1, 1), + 32), + // intLen != 0 && n > leadingZeros && nBits <= leadingZeros && value.length >= newLen && nBits == 0 + // && newOffset == offset + Arguments.of(new MutableBigIntegerBox(new int[] { (int) random.nextLong(1L, 1L << 32), random.nextInt() }, 0, 1), + 32), + // intLen != 0 && n > leadingZeros && nBits <= leadingZeros && value.length >= newLen && nBits != 0 + // && newOffset != offset + Arguments.of(new MutableBigIntegerBox(new int[] { random.nextInt(), (int) random.nextLong(1L, 1L << 16) }, 1, 1), + 32 + random.nextInt(1, 17)), + // intLen != 0 && n > leadingZeros && nBits <= leadingZeros && value.length >= newLen && nBits != 0 + // && newOffset == offset + Arguments.of(new MutableBigIntegerBox(new int[] { (int) random.nextLong(1L, 1L << 16), random.nextInt() }, 0, 1), + 32 + random.nextInt(1, 17)), + // intLen != 0 && n > leadingZeros && nBits > leadingZeros && value.length < newLen + Arguments.of(new MutableBigIntegerBox(new int[] { (int) random.nextLong(1L << 15, 1L << 32) }), + random.nextInt(17, 32)), + // intLen != 0 && n > leadingZeros && nBits > leadingZeros && value.length >= newLen && newOffset != offset + Arguments.of(new MutableBigIntegerBox(new int[] { random.nextInt(), (int) random.nextLong(1L << 15, 1L << 32) }, 1, 1), + random.nextInt(17, 32)), + // intLen != 0 && n > leadingZeros && nBits > leadingZeros && value.length >= newLen && newOffset == offset + Arguments.of(new MutableBigIntegerBox(new int[] { (int) random.nextLong(1L << 15, 1L << 32), random.nextInt() }, 0, 1), + random.nextInt(17, 32)), + }; + } + + private static void leftShiftAssertions(MutableBigIntegerBox x, int n) { + MutableBigIntegerBox xShifted = x.shiftLeft(n); + assertEquals(x.multiply(new MutableBigIntegerBox(BigInteger.TWO.pow(n))), xShifted); + assertEquals(x, xShifted.shiftRight(n)); + } + + /* + * Get a random or boundary-case number. This is designed to provide + * a lot of numbers that will find failure points, such as max sized + * numbers, empty MutableBigIntegers, etc. + * + * If order is less than 2, order is changed to 2. + */ + private static MutableBigIntegerBox fetchNumber(int order) { + int numType = random.nextInt(8); + MutableBigIntegerBox result = null; + if (order < 2) order = 2; + + int[] val; + switch (numType) { + case 0: // Empty + result = MutableBigIntegerBox.ZERO; + break; + + case 1: // One + result = MutableBigIntegerBox.ONE; + break; + + case 2: // All bits set in number + int numInts = (order + 31) >> 5; + int[] fullBits = new int[numInts]; + Arrays.fill(fullBits, -1); + + fullBits[0] &= -1 >>> -order; + result = new MutableBigIntegerBox(fullBits); + break; + + case 3: // One bit in number + result = MutableBigIntegerBox.ONE.shiftLeft(random.nextInt(order)); + break; + + case 4: // Random bit density + val = new int[(order + 31) >> 5]; + int iterations = random.nextInt(order); + for (int i = 0; i < iterations; i++) { + int bitIdx = random.nextInt(order); + val[bitIdx >> 5] |= 1 << bitIdx; + } + result = new MutableBigIntegerBox(val); + break; + case 5: // Runs of consecutive ones and zeros + result = ZERO; + int remaining = order; + int bit = random.nextInt(2); + while (remaining > 0) { + int runLength = Math.min(remaining, random.nextInt(order)); + result = result.shiftLeft(runLength); + if (bit > 0) + result = result.add(ONE.shiftLeft(runLength).subtract(ONE)); + remaining -= runLength; + bit = 1 - bit; + } + break; + case 6: // random bits with trailing space + int len = random.nextInt((order + 31) >> 5) + 1; + int offset = random.nextInt(len); + val = new int[len << 1]; + for (int i = 0; i < val.length; i++) + val[i] = random.nextInt(); + result = new MutableBigIntegerBox(val, offset, len); + break; + default: // random bits + result = new MutableBigIntegerBox(new BigInteger(order, random)); + } + + return result; + } +} diff --git a/test/jdk/java/math/BigInteger/java.base/java/math/MutableBigIntegerBox.java b/test/jdk/java/math/BigInteger/java.base/java/math/MutableBigIntegerBox.java new file mode 100644 index 0000000000000..1f82e4491987f --- /dev/null +++ b/test/jdk/java/math/BigInteger/java.base/java/math/MutableBigIntegerBox.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.math; + +import java.util.Arrays; + +/** + * A class for tests. + */ +public class MutableBigIntegerBox { + + /** + * Constant zero + */ + public static final MutableBigIntegerBox ZERO = new MutableBigIntegerBox(new MutableBigInteger()); + + /** + * Constant one + */ + public static final MutableBigIntegerBox ONE = new MutableBigIntegerBox(MutableBigInteger.ONE); + + /** + * Constant two + */ + public static final MutableBigIntegerBox TWO = new MutableBigIntegerBox(new MutableBigInteger(2)); + + private MutableBigInteger val; + + MutableBigIntegerBox(MutableBigInteger val) { + this.val = val; + } + + /** + * Construct MutableBigIntegerBox from magnitude, starting from + * offset and with a length of intLen ints. + * The value is normalized. + * @param mag the magnitude + * @param offset the offset where the value starts + * @param intLen the length of the value, in int words. + */ + public MutableBigIntegerBox(int[] mag, int offset, int intLen) { + this(new MutableBigInteger(mag)); + val.offset = offset; + val.intLen = intLen; + val.normalize(); + } + + /** + * Construct MutableBigIntegerBox from magnitude. + * The value is normalized. + * @param mag the magnitude + */ + public MutableBigIntegerBox(int[] mag) { + this(mag, 0, mag.length); + } + + /** + * Construct MutableBigIntegerBox from BigInteger val + * @param val the value + */ + public MutableBigIntegerBox(BigInteger val) { + this(val.mag); + } + + /** + * Returns the bit length of this MutableBigInteger value + * @return the bit length of this MutableBigInteger value + */ + public long bitLength() { + return val.bitLength(); + } + + /** + * Return {@code this << n} + * @return {@code this << n} + * @param n the shift + */ + public MutableBigIntegerBox shiftLeft(int n) { + MutableBigIntegerBox res = new MutableBigIntegerBox(val.value.clone(), val.offset, val.intLen); + res.val.safeLeftShift(n); + return res; + } + + /** + * Return {@code this >> n} + * @return {@code this >> n} + * @param n the shift + */ + public MutableBigIntegerBox shiftRight(int n) { + MutableBigInteger res = new MutableBigInteger(val); + res.safeRightShift(n); + return new MutableBigIntegerBox(res); + } + + /** + * Return this + addend + * @return this + addend + * @param addend the addend + */ + public MutableBigIntegerBox add(MutableBigIntegerBox addend) { + MutableBigInteger res = new MutableBigInteger(val); + res.add(addend.val); + return new MutableBigIntegerBox(res); + } + + /** + * Return this - subtraend + * @return this - subtraend + * @param subtraend the subtraend + */ + public MutableBigIntegerBox subtract(MutableBigIntegerBox subtraend) { + MutableBigInteger res = new MutableBigInteger(val); + res.subtract(subtraend.val); + return new MutableBigIntegerBox(res); + } + + /** + * Return this * multiplier + * @return this * multiplier + * @param multiplier the multiplier + */ + public MutableBigIntegerBox multiply(MutableBigIntegerBox multiplier) { + MutableBigInteger res = new MutableBigInteger(); + if (!(val.isZero() || multiplier.val.isZero())) + val.multiply(multiplier.val, res); + + return new MutableBigIntegerBox(res); + } + + /** + * Compare the magnitude of two MutableBigIntegers. Returns -1, 0 or 1 + * as this is numerically less than, equal to, or greater than {@code b}. + * @return -1, 0 or 1 as this is numerically less than, equal to, or + * greater than {@code b}. + * @param b the value to compare + */ + public int compare(MutableBigIntegerBox b) { + return val.compare(b.val); + } + + /** + * Compares this MutableBigIntegerBox with the specified Object for equality. + * + * @param x Object to which this MutableBigIntegerBox is to be compared. + * @return {@code true} if and only if the specified Object is a + * MutableBigIntegerBox whose value is numerically equal to this MutableBigIntegerBox. + */ + @Override + public boolean equals(Object x) { + return (x instanceof MutableBigIntegerBox xInt) + && Arrays.equals(val.value, val.offset, val.offset + val.intLen, + xInt.val.value, xInt.val.offset, xInt.val.offset + xInt.val.intLen); + } + + @Override + public String toString() { + return val.toString(); + } +} diff --git a/test/jdk/java/net/InetAddress/ptr/Lookup.java b/test/jdk/java/net/InetAddress/ptr/Lookup.java index 79c53190c24d0..1248916023ec0 100644 --- a/test/jdk/java/net/InetAddress/ptr/Lookup.java +++ b/test/jdk/java/net/InetAddress/ptr/Lookup.java @@ -114,6 +114,9 @@ public static void main(String args[]) throws IOException { // Now check that a reverse lookup will succeed with the dual stack. InetAddress ia = InetAddress.getByName(addr); String name = ia.getHostName(); + // output details of dual stack lookup by address + System.out.println("dual stack lookup for addr " + addr + " returned IP address " + ia); + System.out.println(" with hostname " + name); System.out.println("(default) " + addr + "--> " + name + " (reversed IPv4: " + ipv4Reversed + ")"); diff --git a/test/jdk/java/net/Socket/SocketImplTest.java b/test/jdk/java/net/Socket/SocketImplTest.java deleted file mode 100644 index 28f49b2618363..0000000000000 --- a/test/jdk/java/net/Socket/SocketImplTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. - * 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.applet.Applet; -import java.io.*; -import java.net.*; - -/** - * Simple Applet for exposing the Socket constructor - * bug. - */ -public class SocketImplTest extends Applet { - - static public void main(String[] args) { - System.setSecurityManager(new SecurityManager()); - SocketImplTest s = new SocketImplTest(); - s.init(); - s.start(); - } - - - /** - * A no-op SocketImpl descendant. - */ - class MySocketImpl extends SocketImpl { - protected void accept(SocketImpl impl) throws IOException { - } - - protected int available(){ - return 0; - } - - protected void bind(InetAddress host, int port){ - } - - protected void close(){ - } - - protected void connect(InetAddress address, int port){ - } - - protected void connect(String host, int port){ - } - - protected void connect(SocketAddress a, int t) throws IOException { - } - - - protected void create(boolean stream){ - } - - protected InputStream getInputStream(){ - return null; - } - - protected OutputStream getOutputStream(){ - return null; - } - - protected void listen(int backlog){ - } - - public Object getOption(int optID){ - return null; - } - - public void setOption(int optID, Object value){ - } - - protected void sendUrgentData(int i){ - } - } - - class MyDatagramSocketImpl extends DatagramSocketImpl { - protected void create() throws SocketException { - } - - protected void bind(int lport, InetAddress laddr) throws SocketException { - } - - protected void send(DatagramPacket p) throws IOException { - } - - protected int peek(InetAddress i) throws IOException { - return 0; - } - - protected int peekData(DatagramPacket p) throws IOException { - return 0; - } - - protected void receive(DatagramPacket p) throws IOException { - } - - protected void setTTL(byte ttl) throws IOException { - } - - protected byte getTTL() throws IOException { - return 0; - } - - protected void setTimeToLive(int ttl) throws IOException { - } - - protected int getTimeToLive() throws IOException { - return 0; - } - - protected void join(InetAddress inetaddr) throws IOException { - } - - protected void leave(InetAddress inetaddr) throws IOException { - } - - protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) - throws IOException { - } - - protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) - throws IOException { - } - - protected void close() { - } - - public Object getOption(int optID){ - return null; - } - - public void setOption(int optID, Object value){ - } - - } - - /** - * A no-op Socket descendant. - */ - class MySocket extends Socket { - public MySocket(SocketImpl impl) throws IOException { - super(impl); - } - } - - class MyDatagramSocket extends DatagramSocket { - public MyDatagramSocket(DatagramSocketImpl impl) { - super(impl); - } - } - - /** - * Our test case entrypoint. Generates - * a SecurityException. - */ - public void init(){ - MySocketImpl socketImpl = new MySocketImpl(); - MyDatagramSocketImpl dgramSocketImpl = new MyDatagramSocketImpl(); - - try{ - MySocket socko = new MySocket(socketImpl); - MyDatagramSocket dsock = new MyDatagramSocket(dgramSocketImpl); - } catch(IOException ioex){ - System.err.println(ioex); - } catch(SecurityException sec) { - throw new RuntimeException("Failed. Creation of socket throwing SecurityException: "); - } - } -} diff --git a/test/jdk/java/net/Socket/UdpSocket.java b/test/jdk/java/net/Socket/UdpSocket.java index a15f9255b450a..5d13c1f916a60 100644 --- a/test/jdk/java/net/Socket/UdpSocket.java +++ b/test/jdk/java/net/Socket/UdpSocket.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 @@ -47,6 +47,8 @@ @Test public class UdpSocket { + private static final int MAX_RETRIES = 3; + /** * Test using the Socket API to send/receive datagrams */ @@ -133,16 +135,21 @@ public void testMaxSockets() throws Exception { } - private Socket newUdpSocket() throws IOException { - Socket s = null; - - try { - s = new Socket(InetAddress.getLoopbackAddress(), 8000, false); - } catch (BindException unexpected) { - System.out.println("BindException caught retry Socket creation"); - s = new Socket(InetAddress.getLoopbackAddress(), 8000, false); + private Socket newUdpSocket() throws IOException, InterruptedException { + BindException unexpected = null; + for (int i=0; i < MAX_RETRIES; i++) { + try { + return new Socket(InetAddress.getLoopbackAddress(), 8000, false); + } catch (BindException be) { + unexpected = be; + if (i != MAX_RETRIES - 1) { + System.out.printf("BindException caught: retry Socket creation [%s/%s]%n", + i + 1, MAX_RETRIES); + Thread.sleep(10 + 10 * i); + } + } } - return s; + throw unexpected; } private void closeAll(Deque sockets) throws IOException { diff --git a/test/jdk/java/net/httpclient/ExpectContinueTest.java b/test/jdk/java/net/httpclient/ExpectContinueTest.java index 3d28ae8c8b49c..50e43099255ac 100644 --- a/test/jdk/java/net/httpclient/ExpectContinueTest.java +++ b/test/jdk/java/net/httpclient/ExpectContinueTest.java @@ -59,6 +59,7 @@ import java.io.Writer; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.ProtocolException; import java.net.ServerSocket; import java.net.Socket; import java.net.URI; @@ -361,7 +362,7 @@ private void verifyRequest(String path, int expectedStatusCode, HttpResponse cf.join()); - assertEquals(t.getCause().getClass(), IOException.class, "Expected an IOException but got " + t.getCause()); + 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(); } @@ -222,7 +224,10 @@ private void verify(HttpResponse resp) { 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) { + 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); } 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 36498684a9a95..27dbe637b94ef 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 @@ -241,6 +241,7 @@ public static abstract class HttpTestExchange implements AutoCloseable { public abstract void close(); public abstract InetSocketAddress getRemoteAddress(); public abstract String getConnectionKey(); + public abstract InetSocketAddress getLocalAddress(); public void serverPush(URI uri, HttpHeaders headers, byte[] body) { ByteArrayInputStream bais = new ByteArrayInputStream(body); serverPush(uri, headers, bais); @@ -303,7 +304,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 @@ -370,6 +374,10 @@ void doFilter(Filter.Chain filter) throws IOException { public InetSocketAddress getRemoteAddress() { return exchange.getRemoteAddress(); } + @Override + public InetSocketAddress getLocalAddress() { + return exchange.getLocalAddress(); + } @Override public String getConnectionKey() { 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 0000000000000..f54a4a766b86e --- /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 d982349dac500..828c939f53f20 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 d25019f9094c0..fa7589c023249 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 @@ -27,6 +27,7 @@ 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 jdk.internal.net.http.frame.ResetFrame; import javax.net.ssl.SSLSession; @@ -39,6 +40,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.function.BiPredicate; public class Http2TestExchangeImpl implements Http2TestExchange { @@ -132,8 +134,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 { // Do not set Content-Length for 100, and do not set END_STREAM if (rCode == 100) responseLength = 0; @@ -147,7 +154,7 @@ public void sendResponseHeaders(int rCode, long responseLength) throws IOExcepti HttpHeaders headers = rspheadersBuilder.build(); ResponseHeaders response - = new ResponseHeaders(headers); + = new ResponseHeaders(headers, insertionPolicy); response.streamid(streamid); response.setFlag(HeaderFrame.END_HEADERS); @@ -172,6 +179,11 @@ public void sendResponseHeaders(ResponseHeaders response) throws IOException { conn.outputQ.put(response); } + @Override + public void sendFrames(List frames) throws IOException { + conn.sendFrames(frames); + } + @Override public InetSocketAddress getRemoteAddress() { return (InetSocketAddress) conn.socket.getRemoteSocketAddress(); 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 1aeeee60b197e..c98e986ca85ab 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 @@ -24,6 +24,8 @@ 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; @@ -80,6 +82,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Predicate; @@ -87,6 +90,7 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; import static jdk.internal.net.http.frame.ErrorFrame.REFUSED_STREAM; +import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_MAX_FRAME_SIZE; import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE; /** @@ -105,7 +109,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; @@ -421,7 +425,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. */ @@ -482,7 +488,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) { @@ -812,6 +818,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(); } @@ -938,26 +952,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); @@ -970,18 +996,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); @@ -1008,10 +1039,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 @@ -1322,11 +1393,29 @@ synchronized void updateConnectionWindow(int amount) { // for the hashmap. public 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); + } + + 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/ipv6tests/Tests.java b/test/jdk/java/net/ipv6tests/Tests.java index 398a3e716936c..7a6917a60382f 100644 --- a/test/jdk/java/net/ipv6tests/Tests.java +++ b/test/jdk/java/net/ipv6tests/Tests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, 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 @@ -123,7 +123,8 @@ public static void datagramEcho (DatagramSocket s1, DatagramSocket s2, } dprintln ("dest2 = " + dest2); - + dprintln ("sender endpoint = " + s1.getLocalSocketAddress()); + dprintln ("echo endpoint = " + s2.getLocalSocketAddress()); DatagramPacket r1 = new DatagramPacket (new byte[256], 256); DatagramPacket r2 = new DatagramPacket (new byte[256], 256); diff --git a/test/jdk/java/nio/MappedByteBuffer/ForceException.java b/test/jdk/java/nio/MappedByteBuffer/ForceException.java index dea63db42bf9c..c8bd06967d027 100644 --- a/test/jdk/java/nio/MappedByteBuffer/ForceException.java +++ b/test/jdk/java/nio/MappedByteBuffer/ForceException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -40,7 +40,7 @@ public static void main(String[] args) throws IOException { int numberOfBlocks = 200; int fileLength = numberOfBlocks * blockSize; - File file = new File(System.getProperty("test.src", "."), "test.dat"); + File file = new File(".", "test.dat"); file.deleteOnExit(); try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { raf.setLength(fileLength); diff --git a/test/jdk/java/nio/MappedByteBuffer/ForceViews.java b/test/jdk/java/nio/MappedByteBuffer/ForceViews.java index 83727f8096859..57ddac28cdfa0 100644 --- a/test/jdk/java/nio/MappedByteBuffer/ForceViews.java +++ b/test/jdk/java/nio/MappedByteBuffer/ForceViews.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -51,7 +51,7 @@ static record Segment(int position, int length) {} @BeforeTest(alwaysRun=true) public void openChannel() throws IOException { - Path file = Path.of(System.getProperty("test.src", "."), "junk"); + Path file = Path.of(".", "junk"); fc = FileChannel.open(file, CREATE_NEW, READ, WRITE, DELETE_ON_CLOSE); ByteBuffer buf = ByteBuffer.wrap(new byte[1024]); fc.write(buf); diff --git a/test/jdk/java/nio/charset/CharsetDecoder/XcodeOverflow.java b/test/jdk/java/nio/charset/CharsetDecoder/XcodeOverflow.java index 7570da3945cef..197b61c0ac561 100644 --- a/test/jdk/java/nio/charset/CharsetDecoder/XcodeOverflow.java +++ b/test/jdk/java/nio/charset/CharsetDecoder/XcodeOverflow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -27,6 +27,7 @@ * @summary Make sure IAE is not thrown on `int` overflow, turning negative * size. The test should either not throw any Throwable, or an OOME * with real Java heap space error (not "exceeds VM limit"). + * @modules java.base/jdk.internal.util * @requires sun.arch.data.model == "64" * @run junit/othervm XcodeOverflow */ @@ -37,6 +38,7 @@ import java.nio.charset.StandardCharsets; import java.util.stream.Stream; +import jdk.internal.util.ArraysSupport; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.Arguments; @@ -44,8 +46,8 @@ public class XcodeOverflow { private static Stream sizes() { return Stream.of( - // SOFT_MAX_ARRAY_LENGTH: copied from ArraysSupport. No overflow; no OOME. - Arguments.of(Integer.MAX_VALUE - 8), + // No overflow; no OOME. + Arguments.of(ArraysSupport.SOFT_MAX_ARRAY_LENGTH), // overflow case: OOME w/ "Java heap space" is thrown on decoding Arguments.of(Integer.MAX_VALUE - 1000000) diff --git a/test/jdk/java/nio/charset/spi/CharsetProviderAsModuleTest.java b/test/jdk/java/nio/charset/spi/CharsetProviderAsModuleTest.java new file mode 100644 index 0000000000000..ba6aae234c6dc --- /dev/null +++ b/test/jdk/java/nio/charset/spi/CharsetProviderAsModuleTest.java @@ -0,0 +1,60 @@ +/* + * 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 8340404 + * @summary Check that a CharsetProvider SPI can be deployed as a module + * @build provider/* + * @run main/othervm CharsetProviderAsModuleTest + */ + +import java.nio.charset.Charset; + +public class CharsetProviderAsModuleTest { + + // Basic test ensures that our BAZ charset is loaded via the BazProvider + public static void main(String[] args) { + var cs = Charset.availableCharsets(); + Charset bazCs; + // check provider is providing BAZ via charsets() + if (!cs.containsKey("BAZ")) { + throw new RuntimeException("SPI BazProvider did not provide BAZ Charset"); + } else { + bazCs = cs.get("BAZ"); + // check provider is in a named module + if (!bazCs.getClass().getModule().isNamed()) { + throw new RuntimeException("BazProvider is not a named module"); + } + var aliases = bazCs.aliases(); + // check BAZ cs aliases were loaded correctly + if (!aliases.contains("BAZ-1") || !aliases.contains("BAZ-2")) { + throw new RuntimeException("BAZ Charset did not provide correct aliases"); + } + // check provider implements charsetForName() + if (!bazCs.equals(Charset.forName("BAZ"))) { + throw new RuntimeException("SPI BazProvider provides bad charsetForName()"); + } + } + } +} diff --git a/test/jdk/java/nio/charset/spi/provider/module-info.java b/test/jdk/java/nio/charset/spi/provider/module-info.java new file mode 100644 index 0000000000000..5cfe6db92db9f --- /dev/null +++ b/test/jdk/java/nio/charset/spi/provider/module-info.java @@ -0,0 +1,25 @@ +/* + * 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. + */ +module provider { + provides java.nio.charset.spi.CharsetProvider with spi.BazProvider; +} diff --git a/test/jdk/java/nio/charset/spi/provider/spi/BazProvider.java b/test/jdk/java/nio/charset/spi/provider/spi/BazProvider.java new file mode 100644 index 0000000000000..7def239e64c0f --- /dev/null +++ b/test/jdk/java/nio/charset/spi/provider/spi/BazProvider.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package spi; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.spi.CharsetProvider; +import java.util.Collections; +import java.util.Iterator; + +// Provides some simple BAZ related attributes to our provider +public class BazProvider extends CharsetProvider { + + @Override + public Iterator charsets() { + return Collections.singleton(new BazCharset()).iterator(); + } + + @Override + public Charset charsetForName(String charsetName) { + if (charsetName.equals("BAZ")) { + return new BazCharset(); + } else { + return null; + } + } + + public static class BazCharset extends Charset { + + public BazCharset() { + super("BAZ", new String[] { "BAZ-1", "BAZ-2" }); + } + + // Overrides to satisfy Charset + @Override + public boolean contains(Charset cs) { + return false; + } + + @Override + public CharsetDecoder newDecoder() { + return null; + } + + @Override + public CharsetEncoder newEncoder() { + return null; + } + } +} diff --git a/test/jdk/java/nio/file/Files/Links.java b/test/jdk/java/nio/file/Files/Links.java index a87647e08a639..23059b0829abb 100644 --- a/test/jdk/java/nio/file/Files/Links.java +++ b/test/jdk/java/nio/file/Files/Links.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, 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 @@ -22,7 +22,7 @@ */ /* @test - * @bug 4313887 6838333 6863864 + * @bug 4313887 6838333 6863864 8340329 * @summary Unit test for java.nio.file.Files createSymbolicLink, * readSymbolicLink, and createLink methods * @library .. @@ -45,7 +45,7 @@ static void assertTrue(boolean okay) { } /** - * Exercise createSymbolicLink and readLink methods + * Exercise createSymbolicLink and readSymbolicLink methods */ static void testSymLinks(Path dir) throws IOException { final Path link = dir.resolve("link"); @@ -131,6 +131,27 @@ static void testSymLinks(Path dir) throws IOException { Files.deleteIfExists(mydir); Files.deleteIfExists(link); } + + // Check message of NotLinkException + try { + Files.createDirectory(mydir); + + try { + Path mytarget = Files.readSymbolicLink(mydir); + } catch (NotLinkException expected) { + String filename = mydir.getFileName().toString(); + String message = expected.getMessage(); + boolean okay = message.contains(filename); + if (!okay) { + System.err.println("Message \"" + message + "\"" + + " does not contain the filename \"" + + filename + "\""); + assertTrue(okay); + } + } + } finally { + Files.deleteIfExists(mydir); + } } /** diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java index ad85da7ae63b1..65e801b0a9f35 100644 --- a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,18 +26,18 @@ * @bug 8011536 8151430 8316304 8334339 * @summary Basic test for creationTime attribute on platforms/file systems * that support it, tests using /tmp directory. - * @library ../.. /test/lib - * @build jdk.test.lib.Platform - * @run main CreationTime + * @library ../.. /test/lib /java/foreign + * @build jdk.test.lib.Platform NativeTestHelper + * @run main/othervm/native --enable-native-access=ALL-UNNAMED 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 CreationTime . + * @library ../.. /test/lib /java/foreign + * @build jdk.test.lib.Platform NativeTestHelper + * @run main/othervm/native --enable-native-access=ALL-UNNAMED CreationTime . */ import java.lang.foreign.Linker; @@ -51,8 +52,6 @@ public class CreationTime { - private static final java.io.PrintStream err = System.err; - /** * Reads the creationTime attribute */ @@ -78,14 +77,9 @@ static void test(Path top) throws IOException { FileTime creationTime = creationTime(file); Instant now = Instant.now(); if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) { - 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); - } + System.err.println("creationTime.toMillis() == " + creationTime.toMillis()); + System.err.println("File creation time reported as: " + creationTime); + throw new RuntimeException("Expected to be close to: " + now); } /** @@ -107,7 +101,12 @@ static void test(Path top) throws IOException { } } else if (Platform.isLinux()) { // Creation time read depends on statx system call support - supportsCreationTimeRead = Linker.nativeLinker().defaultLookup().find("statx").isPresent(); + try { + supportsCreationTimeRead = CreationTimeHelper. + linuxIsCreationTimeSupported(file.toAbsolutePath().toString()); + } catch (Throwable e) { + supportsCreationTimeRead = false; + } // Creation time updates are not supported on Linux supportsCreationTimeWrite = false; } @@ -122,8 +121,11 @@ static void test(Path top) throws IOException { Instant plusHour = Instant.now().plusSeconds(60L * 60L); Files.setLastModifiedTime(file, FileTime.from(plusHour)); FileTime current = creationTime(file); - if (!current.equals(creationTime)) + if (!current.equals(creationTime)) { + System.err.println("current = " + current); + System.err.println("creationTime = " + creationTime); throw new RuntimeException("Creation time should not have changed"); + } } /** diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTimeHelper.java b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTimeHelper.java new file mode 100644 index 0000000000000..592aeba322dd0 --- /dev/null +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/CreationTimeHelper.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 Alibaba Group Holding Limited. All Rights Reserved. + * 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.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.lang.foreign.ValueLayout; +import java.lang.invoke.MethodHandle; + +public class CreationTimeHelper extends NativeTestHelper { + + static { + System.loadLibrary("CreationTimeHelper"); + } + + final static Linker abi = Linker.nativeLinker(); + static final SymbolLookup lookup = SymbolLookup.loaderLookup(); + final static MethodHandle methodHandle = abi. + downcallHandle(lookup.findOrThrow("linuxIsCreationTimeSupported"), + FunctionDescriptor.of(C_BOOL, C_POINTER)); + + // Helper so as to determine birth time support or not on Linux. + // Support is determined in a two-step process: + // 1. Determine if `statx` system call is available. If available proceed, + // otherwise return false. + // 2. Perform an actual `statx` call on the given file and check for birth + // time support in the mask returned from the call. This is needed, + // since some file systems, like nfs/tmpfs etc., don't support birth + // time even though the `statx` system call is available. + static boolean linuxIsCreationTimeSupported(String file) throws Throwable { + if (!abi.defaultLookup().find("statx").isPresent()) { + return false; + } + try (var arena = Arena.ofConfined()) { + MemorySegment s = arena.allocateFrom(file); + return (boolean)methodHandle.invokeExact(s); + } + } +} diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c new file mode 100644 index 0000000000000..fb518b3b701eb --- /dev/null +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/libCreationTimeHelper.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024 Alibaba Group Holding Limited. All Rights Reserved. + * 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. + */ +#include "export.h" +#include +#if defined(__linux__) +#include +#include +#include +#include +#include +#ifndef STATX_BASIC_STATS +#define STATX_BASIC_STATS 0x000007ffU +#endif +#ifndef STATX_BTIME +#define STATX_BTIME 0x00000800U +#endif +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT RTLD_LOCAL +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD -100 +#endif + +/* + * Timestamp structure for the timestamps in struct statx. + */ +struct my_statx_timestamp { + __int64_t tv_sec; + __uint32_t tv_nsec; + __int32_t __reserved; +}; + +/* + * struct statx used by statx system call on >= glibc 2.28 + * systems + */ +struct my_statx +{ + __uint32_t stx_mask; + __uint32_t stx_blksize; + __uint64_t stx_attributes; + __uint32_t stx_nlink; + __uint32_t stx_uid; + __uint32_t stx_gid; + __uint16_t stx_mode; + __uint16_t __statx_pad1[1]; + __uint64_t stx_ino; + __uint64_t stx_size; + __uint64_t stx_blocks; + __uint64_t stx_attributes_mask; + struct my_statx_timestamp stx_atime; + struct my_statx_timestamp stx_btime; + struct my_statx_timestamp stx_ctime; + struct my_statx_timestamp stx_mtime; + __uint32_t stx_rdev_major; + __uint32_t stx_rdev_minor; + __uint32_t stx_dev_major; + __uint32_t stx_dev_minor; + __uint64_t __statx_pad2[14]; +}; + +typedef int statx_func(int dirfd, const char *restrict pathname, int flags, + unsigned int mask, struct my_statx *restrict statxbuf); + +static statx_func* my_statx_func = NULL; +#endif //#defined(__linux__) + +// static boolean linuxIsCreationTimeSupported(char* file) +EXPORT bool linuxIsCreationTimeSupported(char* file) { +#if defined(__linux__) + struct my_statx stx = {0}; + int ret, atflag = AT_SYMLINK_NOFOLLOW; + unsigned int mask = STATX_BASIC_STATS | STATX_BTIME; + + my_statx_func = (statx_func*) dlsym(RTLD_DEFAULT, "statx"); + if (my_statx_func == NULL) { + return false; + } + + if (file == NULL) { + printf("input file error!\n"); + return false; + } + + ret = my_statx_func(AT_FDCWD, file, atflag, mask, &stx); + if (ret != 0) { + return false; + } + // On some systems where statx is available but birth time might still not + // be supported as it's file system specific. The only reliable way to + // check for supported or not is looking at the filled in STATX_BTIME bit + // in the returned statx buffer mask. + if ((stx.stx_mask & STATX_BTIME) != 0) + return true; + return false; +#else + return false; +#endif +} diff --git a/test/jdk/java/security/Security/ConfigFileTest.java b/test/jdk/java/security/Security/ConfigFileTest.java index b9264b937ec74..caf657005e1ba 100644 --- a/test/jdk/java/security/Security/ConfigFileTest.java +++ b/test/jdk/java/security/Security/ConfigFileTest.java @@ -21,155 +21,907 @@ * questions. */ +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; import jdk.test.lib.Utils; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.FileUtils; +import sun.net.www.ParseUtil; +import java.io.Closeable; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; import java.io.UncheckedIOException; -import java.nio.file.*; - -import java.security.Provider; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.nio.CharBuffer; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.security.Security; +import java.time.Instant; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Optional; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; /* * @test - * @summary Throw error if default java.security file is missing - * @bug 8155246 8292297 8292177 8281658 + * @summary Tests security properties passed through java.security, + * java.security.properties or included from other properties files. + * @bug 8155246 8292297 8292177 8281658 8319332 + * @modules java.base/sun.net.www * @library /test/lib * @run main ConfigFileTest */ + public class ConfigFileTest { + static final String SEPARATOR_THIN = "----------------------------"; + + private static void printTestHeader(String testName) { + System.out.println(); + System.out.println(SEPARATOR_THIN); + System.out.println(testName); + System.out.println(SEPARATOR_THIN); + System.out.println(); + } + + public static void main(String[] args) throws Exception { + if (args.length == 1 && Executor.RUNNER_ARG.equals(args[0])) { + // Executed by a test-launched JVM. + // Force the initialization of java.security.Security. + Security.getProviders(); + Security.setProperty("postInitTest", "shouldNotRecord"); + System.out.println(FilesManager.LAST_FILE_PROP_NAME + ": " + + Security.getProperty(FilesManager.LAST_FILE_PROP_NAME)); + assertTestSecuritySetPropertyShouldNotInclude(); + } else { + // Executed by the test JVM. + try (FilesManager filesMgr = new FilesManager()) { + for (Method m : ConfigFileTest.class.getDeclaredMethods()) { + if (m.getName().startsWith("test")) { + printTestHeader(m.getName()); + Executor.run(m, filesMgr); + } + } + } + } + } + + /* + * Success cases + */ + + static void testShowSettings(Executor ex, FilesManager filesMgr) + throws Exception { + // Sanity test passing the -XshowSettings:security option. + ex.addJvmArg("-XshowSettings:security"); + ex.setMasterFile(filesMgr.newMasterFile()); + ex.assertSuccess(); + ex.getOutputAnalyzer() + .shouldContain("Security properties:") + .shouldContain("Security provider static configuration:") + .shouldContain("Security TLS configuration"); + } - private static final String EXPECTED_DEBUG_OUTPUT = - "Initial security property: crypto.policy=unlimited"; + static void testIncludeBasic(Executor ex, FilesManager filesMgr) + throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); + PropsFile file2 = filesMgr.newFile("dir1/dir2/file2.properties"); - private static final String UNEXPECTED_DEBUG_OUTPUT = - "Initial security property: postInitTest=shouldNotRecord"; + masterFile.addAbsoluteInclude(file0); + extraFile.addRelativeInclude(file2); + file2.addAbsoluteInclude(file1); - private static boolean overrideDetected = false; + ex.setMasterFile(masterFile); + ex.setExtraFile(extraFile, Executor.ExtraMode.FILE_URI, false); + ex.assertSuccess(); + } - private static Path COPY_JDK_DIR = Path.of("./jdk-8155246-tmpdir"); - private static Path COPIED_JAVA = COPY_JDK_DIR.resolve("bin", "java"); + static void testRepeatedInclude(Executor ex, FilesManager filesMgr) + throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); - public static void main(String[] args) throws Exception { - Path copyJdkDir = Path.of("./jdk-8155246-tmpdir"); - Path copiedJava = Optional.of( - Path.of(copyJdkDir.toString(), "bin", "java")) - .orElseThrow(() -> new RuntimeException("Unable to locate new JDK") - ); - - if (args.length == 1) { - // set up is complete. Run code to exercise loading of java.security - Provider[] provs = Security.getProviders(); - Security.setProperty("postInitTest", "shouldNotRecord"); - System.out.println(Arrays.toString(provs) + "NumProviders: " + provs.length); + masterFile.addAbsoluteInclude(file0); + masterFile.addAbsoluteInclude(file1); + masterFile.addAbsoluteInclude(file0); + file1.addRelativeInclude(file0); + + ex.setMasterFile(masterFile); + ex.assertSuccess(); + } + + static void testIncludeWithOverrideAll(Executor ex, FilesManager filesMgr) + throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); + + masterFile.addRelativeInclude(file0); + extraFile.addAbsoluteInclude(file1); + + ex.setMasterFile(masterFile); + ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.assertSuccess(); + } + + static void extraPropertiesByHelper(Executor ex, FilesManager filesMgr, + Executor.ExtraMode mode) throws Exception { + ExtraPropsFile extraFile = filesMgr.newExtraFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + + extraFile.addRelativeInclude(file0); + + ex.setMasterFile(filesMgr.newMasterFile()); + ex.setExtraFile(extraFile, mode, true); + ex.assertSuccess(); + } + + static void testExtraPropertiesByPathAbsolute(Executor ex, + FilesManager filesMgr) throws Exception { + extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_ABS); + } + + static void testExtraPropertiesByPathRelative(Executor ex, + FilesManager filesMgr) throws Exception { + extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_REL); + } + + static void specialCharsIncludes(Executor ex, FilesManager filesMgr, + char specialChar, Executor.ExtraMode extraMode, + boolean useRelativeIncludes) throws Exception { + String suffix = specialChar + ".properties"; + ExtraPropsFile extraFile; + PropsFile file0, file1; + try { + extraFile = filesMgr.newExtraFile("extra" + suffix); + file0 = filesMgr.newFile("file0" + suffix); + file1 = filesMgr.newFile("file1" + suffix); + } catch (InvalidPathException ipe) { + // The platform encoding may not allow to create files with some + // special characters. Skip the test in these cases. + return; + } + + if (useRelativeIncludes) { + extraFile.addRelativeInclude(file0); } else { - Files.createDirectory(copyJdkDir); - Path jdkTestDir = Path.of(Optional.of(System.getProperty("test.jdk")) - .orElseThrow(() -> new RuntimeException("Couldn't load JDK Test Dir")) - ); - - copyJDK(jdkTestDir, copyJdkDir); - String extraPropsFile = Path.of(System.getProperty("test.src"), "override.props").toString(); - - // sanity test -XshowSettings:security option - exerciseShowSettingsSecurity(buildCommand("-cp", System.getProperty("test.classes"), - "-Djava.security.debug=all", "-XshowSettings:security", "ConfigFileTest", "runner")); - - // exercise some debug flags while we're here - // regular JDK install - should expect success - exerciseSecurity(0, "java", - buildCommand("-cp", System.getProperty("test.classes"), - "-Djava.security.debug=all", "-Djavax.net.debug=all", "ConfigFileTest", "runner")); - - // given an overriding security conf file that doesn't exist, we shouldn't - // overwrite the properties from original/master security conf file - exerciseSecurity(0, "SUN version", - buildCommand("-cp", System.getProperty("test.classes"), - "-Djava.security.debug=all", "-Djavax.net.debug=all", - "-Djava.security.properties==file:///" + extraPropsFile + "badFileName", - "ConfigFileTest", "runner")); - - // test JDK launch with customized properties file - exerciseSecurity(0, "NumProviders: 6", - buildCommand("-cp", System.getProperty("test.classes"), - "-Djava.security.debug=all", "-Djavax.net.debug=all", - "-Djava.security.properties==file:///" + extraPropsFile, - "ConfigFileTest", "runner")); - - // delete the master conf file - Files.delete(Path.of(copyJdkDir.toString(), "conf", - "security","java.security")); - - // launch JDK without java.security file being present or specified - exerciseSecurity(1, "Error loading java.security file", - buildCommand("-cp", System.getProperty("test.classes"), - "-Djava.security.debug=all", "-Djavax.net.debug=all", - "ConfigFileTest", "runner")); - - // test the override functionality also. Should not be allowed since - // "security.overridePropertiesFile=true" Security property is missing. - exerciseSecurity(1, "Error loading java.security file", - buildCommand("-cp", System.getProperty("test.classes"), - "-Djava.security.debug=all", "-Djavax.net.debug=all", - "-Djava.security.properties==file:///" + extraPropsFile, "ConfigFileTest", "runner")); - - if (!overrideDetected) { - throw new RuntimeException("Override scenario not seen"); + extraFile.addAbsoluteInclude(file0); + } + extraFile.addAbsoluteInclude(file1); + + ex.setMasterFile(filesMgr.newMasterFile()); + ex.setExtraFile(extraFile, extraMode, false); + ex.assertSuccess(); + } + + static void testUnicodeIncludes1(Executor ex, FilesManager filesMgr) + throws Exception { + specialCharsIncludes(ex, filesMgr, '\u2022', + Executor.ExtraMode.PATH_ABS, true); + } + + static void testUnicodeIncludes2(Executor ex, FilesManager filesMgr) + throws Exception { + specialCharsIncludes(ex, filesMgr, '\u2022', + Executor.ExtraMode.FILE_URI, true); + } + + static void testUnicodeIncludes3(Executor ex, FilesManager filesMgr) + throws Exception { + // Backward compatibility check. Malformed URLs such as + // file:/tmp/extra•.properties are supported for the extra file. + // However, relative includes are not allowed in these cases. + specialCharsIncludes(ex, filesMgr, '\u2022', + Executor.ExtraMode.RAW_FILE_URI1, false); + } + + static void testUnicodeIncludes4(Executor ex, FilesManager filesMgr) + throws Exception { + // Backward compatibility check. Malformed URLs such as + // file:///tmp/extra•.properties are supported for the extra file. + // However, relative includes are not allowed in these cases. + specialCharsIncludes(ex, filesMgr, '\u2022', + Executor.ExtraMode.RAW_FILE_URI2, false); + } + + static void testSpaceIncludes1(Executor ex, FilesManager filesMgr) + throws Exception { + specialCharsIncludes(ex, filesMgr, ' ', + Executor.ExtraMode.PATH_ABS, true); + } + + static void testSpaceIncludes2(Executor ex, FilesManager filesMgr) + throws Exception { + specialCharsIncludes(ex, filesMgr, ' ', + Executor.ExtraMode.FILE_URI, true); + } + + static void testSpaceIncludes3(Executor ex, FilesManager filesMgr) + throws Exception { + // Backward compatibility check. Malformed URLs such as + // file:/tmp/extra .properties are supported for the extra file. + // However, relative includes are not allowed in these cases. + specialCharsIncludes(ex, filesMgr, ' ', + Executor.ExtraMode.RAW_FILE_URI1, false); + } + + static void testSpaceIncludes4(Executor ex, FilesManager filesMgr) + throws Exception { + // Backward compatibility check. Malformed URLs such as + // file:///tmp/extra .properties are supported for the extra file. + // However, relative includes are not allowed in these cases. + specialCharsIncludes(ex, filesMgr, ' ', + Executor.ExtraMode.RAW_FILE_URI2, false); + } + + static void notOverrideOnFailureHelper(Executor ex, FilesManager filesMgr, + String nonExistentExtraFile) throws Exception { + // An overriding extra properties file that does not exist + // should not erase properties from the master file. + ex.setIgnoredExtraFile(nonExistentExtraFile, true); + ex.setMasterFile(filesMgr.newMasterFile()); + ex.assertSuccess(); + ex.getOutputAnalyzer().shouldContain("unable to load security " + + "properties from " + nonExistentExtraFile); + } + + static void testNotOverrideOnEmptyFailure(Executor ex, + FilesManager filesMgr) throws Exception { + notOverrideOnFailureHelper(ex, filesMgr, ""); + ex.getOutputAnalyzer() + .shouldContain("Empty extra properties file path"); + } + + static void testNotOverrideOnURLFailure(Executor ex, FilesManager filesMgr) + throws Exception { + notOverrideOnFailureHelper(ex, filesMgr, + "file:///nonExistentFile.properties"); + } + + static void testNotOverrideOnPathFailure(Executor ex, FilesManager filesMgr) + throws Exception { + notOverrideOnFailureHelper(ex, filesMgr, "nonExistentFile.properties"); + } + + static void testNotOverrideOnDirFailure(Executor ex, FilesManager filesMgr) + throws Exception { + notOverrideOnFailureHelper(ex, filesMgr, "file:///"); + ex.getOutputAnalyzer().shouldContain("Is a directory"); + } + + static void testNotOverrideOnBadFileURLFailure(Executor ex, + FilesManager filesMgr) throws Exception { + notOverrideOnFailureHelper(ex, filesMgr, "file:///%00"); + } + + static void testDisabledExtraPropertiesFile(Executor ex, + FilesManager filesMgr) throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + + masterFile.addRawProperty("security.overridePropertiesFile", "false"); + + ex.setMasterFile(masterFile); + ex.setIgnoredExtraFile(file0.path.toString(), true); + ex.assertSuccess(); + } + + static final String SECURITY_SET_PROP_FILE_PATH = + "testSecuritySetPropertyShouldNotInclude.propsFilePath"; + + static void testSecuritySetPropertyShouldNotInclude(Executor ex, + FilesManager filesMgr) throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + + ex.addSystemProp(SECURITY_SET_PROP_FILE_PATH, file0.path.toString()); + ex.setMasterFile(masterFile); + ex.assertSuccess(); + } + + static void assertTestSecuritySetPropertyShouldNotInclude() { + // This check is executed by the launched JVM. + String propsFilePath = System.getProperty(SECURITY_SET_PROP_FILE_PATH); + if (propsFilePath != null) { + String name = Path.of(propsFilePath).getFileName().toString(); + String setPropInvokeRepr = "Security.setProperty(\"include\", " + + "\"" + propsFilePath + "\")"; + try { + Security.setProperty("include", propsFilePath); + throw new RuntimeException(setPropInvokeRepr + " was " + + "expected to throw IllegalArgumentException."); + } catch (IllegalArgumentException expected) {} + if (FilesManager.APPLIED_PROP_VALUE.equals( + Security.getProperty(name))) { + throw new RuntimeException(setPropInvokeRepr + " caused " + + "a file inclusion."); } + try { + Security.getProperty("include"); + throw new RuntimeException("Security.getProperty(\"include\")" + + " was expected to throw IllegalArgumentException."); + } catch (IllegalArgumentException expected) {} } } - private static ProcessBuilder buildCommand(String... command) { - ArrayList args = new ArrayList<>(); - args.add(COPIED_JAVA.toString()); - Collections.addAll(args, Utils.prependTestJavaOpts(command)); - return new ProcessBuilder(args); + /* + * Error cases + */ + + static void testCannotResolveRelativeFromHTTPServed(Executor ex, + FilesManager filesMgr) throws Exception { + ExtraPropsFile extraFile = filesMgr.newExtraFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + + extraFile.addRelativeInclude(file0); + + ex.setMasterFile(filesMgr.newMasterFile()); + ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.assertError("InternalError: Cannot resolve '" + file0.fileName + + "' relative path when included from a non-regular " + + "properties file (e.g. HTTP served file)"); } - private static void exerciseSecurity(int exitCode, String output, ProcessBuilder process) throws Exception { - OutputAnalyzer oa = ProcessTools.executeProcess(process); - oa.shouldHaveExitValue(exitCode) - .shouldContain(output); + static void testCannotIncludeCycles(Executor ex, FilesManager filesMgr) + throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + PropsFile file0 = filesMgr.newFile("file0.properties"); + PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); + + // Includes chain: master -> file0 -> file1 -> master. + file1.addRelativeInclude(masterFile); + file0.addRelativeInclude(file1); + masterFile.addRelativeInclude(file0); - // extra checks on debug output - if (exitCode != 1) { - if (oa.getStderr().contains("overriding other security properties files!")) { - overrideDetected = true; - // master file is not in use - only provider properties are set in custom file - oa.shouldContain("security.provider.2=SunRsaSign") - .shouldNotContain(EXPECTED_DEBUG_OUTPUT) - .shouldNotContain(UNEXPECTED_DEBUG_OUTPUT); + ex.setMasterFile(masterFile); + ex.assertError( + "InternalError: Cyclic include of '" + masterFile.path + "'"); + } + + static void testCannotIncludeURL(Executor ex, FilesManager filesMgr) + throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(); + + masterFile.addRawProperty("include", extraFile.url.toString()); + + ex.setMasterFile(masterFile); + ex.assertError("InternalError: Unable to include 'http://127.0.0.1:"); + } + + static void testCannotIncludeNonexistentFile(Executor ex, + FilesManager filesMgr) throws Exception { + PropsFile masterFile = filesMgr.newMasterFile(); + + String nonexistentPath = "/nonExistentFile.properties"; + masterFile.addRawProperty("include", nonexistentPath); + + ex.setMasterFile(masterFile); + ex.assertError( + "InternalError: Unable to include '" + nonexistentPath + "'"); + } + + static void testMustHaveMasterFile(Executor ex, FilesManager filesMgr) + throws Exception { + // Launch a JDK without a master java.security file present. + ex.assertError("InternalError: Error loading java.security file"); + } + + static void testMustHaveMasterFileEvenWithExtraFile(Executor ex, + FilesManager filesMgr) throws Exception { + // Launch a JDK without a master java.security file present, but with an + // extra file passed. Since the "security.overridePropertiesFile=true" + // security property is missing, it should fail anyway. + ex.setExtraFile( + filesMgr.newExtraFile(), Executor.ExtraMode.FILE_URI, true); + ex.assertError("InternalError: Error loading java.security file"); + } +} + +sealed class PropsFile permits ExtraPropsFile { + protected static final class Include { + final PropsFile propsFile; + final String value; + + private Include(PropsFile propsFile, String value) { + this.propsFile = propsFile; + this.value = value; + } + + static Include of(PropsFile propsFile) { + return new Include(propsFile, propsFile.path.toString()); + } + + static Include of(PropsFile propsFile, String value) { + return new Include(propsFile, value); + } + } + + protected final List includes = new ArrayList<>(); + protected final PrintWriter writer; + protected boolean includedFromExtra = false; + final String fileName; + final Path path; + + PropsFile(String fileName, Path path) throws IOException { + this.fileName = fileName; + this.path = path; + this.writer = new PrintWriter(Files.newOutputStream(path, + StandardOpenOption.CREATE, StandardOpenOption.APPEND), true); + } + + private static String escape(String text, boolean escapeSpace) { + StringBuilder sb = new StringBuilder(text.length()); + CharBuffer cb = CharBuffer.wrap(text); + while (cb.hasRemaining()) { + char c = cb.get(); + if (c == '\\' || escapeSpace && c == ' ') { + sb.append('\\'); + } + if (Character.UnicodeBlock.of(c) == + Character.UnicodeBlock.BASIC_LATIN) { + sb.append(c); } else { - oa.shouldContain(EXPECTED_DEBUG_OUTPUT) - .shouldNotContain(UNEXPECTED_DEBUG_OUTPUT); + sb.append("\\u%04x".formatted((int) c)); } } + return sb.toString(); } - // exercise the -XshowSettings:security launcher - private static void exerciseShowSettingsSecurity(ProcessBuilder process) throws Exception { - OutputAnalyzer oa = ProcessTools.executeProcess(process); - oa.shouldHaveExitValue(0) - .shouldContain("Security properties:") - .shouldContain("Security provider static configuration:") - .shouldContain("Security TLS configuration"); + private void addRawProperty(String key, String value, String sep) { + writer.println(escape(key, true) + sep + escape(value, false)); + } + + protected void addIncludeDefinition(Include include) { + if (include.propsFile instanceof ExtraPropsFile) { + throw new RuntimeException("ExtraPropsFile should not be included"); + } + includes.add(include); + addRawProperty("include", include.value, " "); + } + + void addComment(String comment) { + writer.println("# " + comment); + } + + void addRawProperty(String key, String value) { + addRawProperty(key, value, "="); + } + + void addAbsoluteInclude(PropsFile propsFile) { + addIncludeDefinition(Include.of(propsFile)); + } + + void addRelativeInclude(PropsFile propsFile) { + addIncludeDefinition(Include.of(propsFile, + path.getParent().relativize(propsFile.path).toString())); + } + + void assertApplied(OutputAnalyzer oa) { + oa.shouldContain(Executor.INITIAL_PROP_LOG_MSG + fileName + "=" + + FilesManager.APPLIED_PROP_VALUE); + for (Include include : includes) { + include.propsFile.assertApplied(oa); + oa.shouldContain("processing include: '" + include.value + "'"); + oa.shouldContain("finished processing " + include.propsFile.path); + } + } + + void assertWasOverwritten(OutputAnalyzer oa) { + oa.shouldNotContain(Executor.INITIAL_PROP_LOG_MSG + fileName + "=" + + FilesManager.APPLIED_PROP_VALUE); + for (Include include : includes) { + if (!include.propsFile.includedFromExtra) { + include.propsFile.assertWasOverwritten(oa); + } + oa.shouldContain("processing include: '" + include.value + "'"); + oa.shouldContain("finished processing " + include.propsFile.path); + } + } + + void markAsIncludedFromExtra() { + includedFromExtra = true; + for (Include include : includes) { + include.propsFile.markAsIncludedFromExtra(); + } + } + + PropsFile getLastFile() { + return includes.isEmpty() ? + this : includes.getLast().propsFile.getLastFile(); } - private static void copyJDK(Path src, Path dst) throws Exception { - Files.walk(src) - .skip(1) - .forEach(file -> { + void close() { + writer.close(); + } +} + +final class ExtraPropsFile extends PropsFile { + private final Map systemProps = new LinkedHashMap<>(); + final URI url; + + ExtraPropsFile(String fileName, URI url, Path path) throws IOException { + super(fileName, path); + this.url = url; + } + + @Override + protected void addIncludeDefinition(Include include) { + if (includes.isEmpty()) { + String propName = "props.fileName"; + systemProps.put(propName, include.propsFile.fileName); + include = Include.of(include.propsFile, + include.value.replace(include.propsFile.fileName, + "${props.none}${" + propName + "}")); + } + include.propsFile.markAsIncludedFromExtra(); + super.addIncludeDefinition(include); + } + + Map getSystemProperties() { + return Collections.unmodifiableMap(systemProps); + } +} + +final class FilesManager implements Closeable { + private static final Path ROOT_DIR = + Path.of(ConfigFileTest.class.getSimpleName()).toAbsolutePath(); + private static final Path PROPS_DIR = ROOT_DIR.resolve("properties"); + private static final Path JDK_DIR = ROOT_DIR.resolve("jdk"); + private static final Path MASTER_FILE = + JDK_DIR.resolve("conf/security/java.security"); + private static final Path MASTER_FILE_TEMPLATE = + MASTER_FILE.resolveSibling("java.security.template"); + static final String JAVA_EXECUTABLE = + JDK_DIR.resolve("bin/java").toString(); + static final String LAST_FILE_PROP_NAME = "last-file"; + static final String APPLIED_PROP_VALUE = "applied"; + + private final List createdFiles; + private final Set fileNamesInUse; + private final HttpServer httpServer; + private final URI serverUri; + private final long masterFileLines; + + FilesManager() throws Exception { + createdFiles = new ArrayList<>(); + fileNamesInUse = new HashSet<>(); + httpServer = HttpServer.create( + new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); + httpServer.createContext("/", this::handleRequest); + InetSocketAddress address = httpServer.getAddress(); + httpServer.start(); + serverUri = new URI("http", null, address.getHostString(), + address.getPort(), null, null, null); + copyJDK(); + try (Stream s = Files.lines(MASTER_FILE_TEMPLATE)) { + masterFileLines = s.count(); + } + } + + private static void copyJDK() throws Exception { + Path testJDK = Path.of(Objects.requireNonNull( + System.getProperty("test.jdk"), "unspecified test.jdk")); + if (!Files.exists(testJDK)) { + throw new RuntimeException("test.jdk -> nonexistent JDK"); + } + Files.createDirectories(JDK_DIR); + try (Stream pathStream = Files.walk(testJDK)) { + pathStream.skip(1).forEach((Path file) -> { try { - Files.copy(file, dst.resolve(src.relativize(file)), StandardCopyOption.COPY_ATTRIBUTES); + Files.copy(file, JDK_DIR.resolve(testJDK.relativize(file)), + StandardCopyOption.COPY_ATTRIBUTES); } catch (IOException ioe) { throw new UncheckedIOException(ioe); } }); + } + Files.move(MASTER_FILE, MASTER_FILE_TEMPLATE); + } + + private void handleRequest(HttpExchange x) throws IOException { + String rawPath = x.getRequestURI().getRawPath(); + Path f = ROOT_DIR.resolve(x.getRequestURI().getPath().substring(1)); + int statusCode; + byte[] responseBody; + // Check for unescaped space, unresolved parent or backward slash. + if (rawPath.matches("^.*( |(\\.|%2[Ee]){2}|\\\\|%5[Cc]).*$")) { + statusCode = HttpURLConnection.HTTP_BAD_REQUEST; + responseBody = new byte[0]; + } else if (Files.isRegularFile(f)) { + x.getResponseHeaders().add("Content-type", "text/plain"); + statusCode = HttpURLConnection.HTTP_OK; + responseBody = Files.readAllBytes(f); + } else { + statusCode = HttpURLConnection.HTTP_NOT_FOUND; + responseBody = new byte[0]; + } + System.out.println("[" + Instant.now() + "] " + + getClass().getSimpleName() + ": " + + x.getRequestMethod() + " " + rawPath + " -> " + + statusCode + " (" + responseBody.length + " bytes)"); + try (OutputStream responseStream = x.getResponseBody()) { + x.sendResponseHeaders(statusCode, responseBody.length); + responseStream.write(responseBody); + } + } + + @FunctionalInterface + private interface PropsFileBuilder { + PropsFile build(String fileName, Path path) throws IOException; + } + + private PropsFile newFile(Path path, PropsFileBuilder builder) + throws IOException { + String fileName = path.getFileName().toString(); + if (!fileNamesInUse.add(fileName)) { + // Names must be unique in order for the special + // property = to work. + throw new RuntimeException(fileName + " is repeated"); + } + Files.createDirectories(path.getParent()); + PropsFile propsFile = builder.build(fileName, path); + propsFile.addComment("Property to determine if this properties file " + + "was parsed and not overwritten:"); + propsFile.addRawProperty(fileName, APPLIED_PROP_VALUE); + propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + propsFile.addComment("Property to be overwritten by every properties " + + "file (master, extra or included):"); + propsFile.addRawProperty(LAST_FILE_PROP_NAME, fileName); + propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + createdFiles.add(propsFile); + return propsFile; + } + + PropsFile newFile(String relPathStr) throws IOException { + return newFile(PROPS_DIR.resolve(relPathStr), PropsFile::new); + } + + PropsFile newMasterFile() throws IOException { + Files.copy(MASTER_FILE_TEMPLATE, MASTER_FILE); + return newFile(MASTER_FILE, PropsFile::new); + } + + ExtraPropsFile newExtraFile() throws IOException { + return newExtraFile("extra.properties"); + } + + ExtraPropsFile newExtraFile(String extraFileName) throws IOException { + return (ExtraPropsFile) newFile(PROPS_DIR.resolve(extraFileName), + (fileName, path) -> { + URI uri = serverUri.resolve(ParseUtil.encodePath( + ROOT_DIR.relativize(path).toString())); + return new ExtraPropsFile(fileName, uri, path); + }); + } + + void reportCreatedFiles() throws IOException { + for (PropsFile propsFile : createdFiles) { + System.err.println(); + System.err.println(propsFile.path.toString()); + System.err.println(ConfigFileTest.SEPARATOR_THIN.repeat(3)); + try (Stream lines = Files.lines(propsFile.path)) { + long lineNumber = 1L; + Iterator it = lines.iterator(); + while (it.hasNext()) { + String line = it.next(); + if (!propsFile.path.equals(MASTER_FILE) || + lineNumber > masterFileLines) { + System.err.println(line); + } + lineNumber++; + } + } + System.err.println(); + } + } + + void clear() throws IOException { + if (!createdFiles.isEmpty()) { + for (PropsFile propsFile : createdFiles) { + propsFile.close(); + Files.delete(propsFile.path); + } + FileUtils.deleteFileTreeUnchecked(PROPS_DIR); + createdFiles.clear(); + fileNamesInUse.clear(); + } + } + + @Override + public void close() throws IOException { + clear(); + httpServer.stop(0); + FileUtils.deleteFileTreeUnchecked(ROOT_DIR); + } +} + +final class Executor { + enum ExtraMode { + HTTP_SERVED, FILE_URI, RAW_FILE_URI1, RAW_FILE_URI2, PATH_ABS, PATH_REL + } + static final String RUNNER_ARG = "runner"; + static final String INITIAL_PROP_LOG_MSG = "Initial security property: "; + private static final String OVERRIDING_LOG_MSG = + "overriding other security properties files!"; + private static final String[] ALWAYS_UNEXPECTED_LOG_MSGS = { + "java.lang.AssertionError", + INITIAL_PROP_LOG_MSG + "postInitTest=shouldNotRecord", + INITIAL_PROP_LOG_MSG + "include=", + }; + private static final Path CWD = Path.of(".").toAbsolutePath(); + private static final String JAVA_SEC_PROPS = "java.security.properties"; + private static final String CLASS_PATH = Objects.requireNonNull( + System.getProperty("test.classes"), "unspecified test.classes"); + private static final String DEBUG_ARG = + "-Xrunjdwp:transport=dt_socket,address=localhost:8000,suspend=y"; + private final Map systemProps = new LinkedHashMap<>( + Map.of("java.security.debug", "all", "javax.net.debug", "all", + // Ensure we get UTF-8 debug outputs in Windows: + "stderr.encoding", "UTF-8", "stdout.encoding", "UTF-8")); + private final List jvmArgs = new ArrayList<>( + List.of(FilesManager.JAVA_EXECUTABLE, "-enablesystemassertions", + // Uncomment DEBUG_ARG to debug test-launched JVMs: + "-classpath", CLASS_PATH//, DEBUG_ARG + )); + private PropsFile masterPropsFile; + private ExtraPropsFile extraPropsFile; + private boolean expectedOverrideAll = false; + private OutputAnalyzer oa; + + static void run(Method m, FilesManager filesMgr) throws Exception { + try { + m.invoke(null, new Executor(), filesMgr); + } catch (Throwable e) { + filesMgr.reportCreatedFiles(); + throw e; + } finally { + filesMgr.clear(); + } + } + + void addSystemProp(String key, String value) { + systemProps.put(key, value); + } + + private void setRawExtraFile(String extraFile, boolean overrideAll) { + addSystemProp(JAVA_SEC_PROPS, (overrideAll ? "=" : "") + extraFile); + } + + void setMasterFile(PropsFile masterPropsFile) { + this.masterPropsFile = masterPropsFile; + } + + void setExtraFile(ExtraPropsFile extraPropsFile, ExtraMode mode, + boolean overrideAll) { + this.extraPropsFile = extraPropsFile; + expectedOverrideAll = overrideAll; + setRawExtraFile(switch (mode) { + case HTTP_SERVED -> extraPropsFile.url.toString(); + case FILE_URI -> extraPropsFile.path.toUri().toString(); + case RAW_FILE_URI1 -> "file:" + extraPropsFile.path; + case RAW_FILE_URI2 -> "file://" + + (extraPropsFile.path.startsWith("/") ? "" : "/") + + extraPropsFile.path; + case PATH_ABS -> extraPropsFile.path.toString(); + case PATH_REL -> CWD.relativize(extraPropsFile.path).toString(); + }, overrideAll); + } + + void setIgnoredExtraFile(String extraPropsFile, boolean overrideAll) { + setRawExtraFile(extraPropsFile, overrideAll); + expectedOverrideAll = false; + } + + void addJvmArg(String arg) { + jvmArgs.add(arg); + } + + private void execute(boolean successExpected) throws Exception { + List command = new ArrayList<>(jvmArgs); + Collections.addAll(command, Utils.getTestJavaOpts()); + addSystemPropertiesAsJvmArgs(command); + command.add(ConfigFileTest.class.getSimpleName()); + command.add(RUNNER_ARG); + oa = ProcessTools.executeProcess(new ProcessBuilder(command)); + oa.shouldHaveExitValue(successExpected ? 0 : 1); + for (String output : ALWAYS_UNEXPECTED_LOG_MSGS) { + oa.shouldNotContain(output); + } + } + + private void addSystemPropertiesAsJvmArgs(List command) { + Map allSystemProps = new LinkedHashMap<>(systemProps); + if (extraPropsFile != null) { + allSystemProps.putAll(extraPropsFile.getSystemProperties()); + } + for (Map.Entry e : allSystemProps.entrySet()) { + command.add("-D" + e.getKey() + "=" + e.getValue()); + } + } + + void assertSuccess() throws Exception { + execute(true); + + // Ensure every file was processed by checking a unique property used as + // a flag. Each file defines =applied. + // + // For example: + // + // file0 + // --------------- + // file0=applied + // include file1 + // + // file1 + // --------------- + // file1=applied + // + // The assertion would be file0 == applied AND file1 == applied. + // + if (extraPropsFile != null) { + extraPropsFile.assertApplied(oa); + } + if (expectedOverrideAll) { + // When overriding with an extra file, check that neither + // the master file nor its includes are visible. + oa.shouldContain(OVERRIDING_LOG_MSG); + masterPropsFile.assertWasOverwritten(oa); + } else { + oa.shouldNotContain(OVERRIDING_LOG_MSG); + masterPropsFile.assertApplied(oa); + } + + // Ensure the last included file overwrote a fixed property. Each file + // defines last-file=. + // + // For example: + // + // file0 + // --------------- + // last-file=file0 + // include file1 + // + // file1 + // --------------- + // last-file=file1 + // + // The assertion would be last-file == file1. + // + PropsFile lastFile = (extraPropsFile == null ? + masterPropsFile : extraPropsFile).getLastFile(); + oa.shouldContain(FilesManager.LAST_FILE_PROP_NAME + "=" + + lastFile.fileName); + oa.stdoutShouldContain(FilesManager.LAST_FILE_PROP_NAME + ": " + + lastFile.fileName); + } + + void assertError(String message) throws Exception { + execute(false); + oa.shouldContain(message); + } + + OutputAnalyzer getOutputAnalyzer() { + return oa; } } diff --git a/test/jdk/java/security/Security/override.props b/test/jdk/java/security/Security/override.props deleted file mode 100644 index d0190f576fd82..0000000000000 --- a/test/jdk/java/security/Security/override.props +++ /dev/null @@ -1,7 +0,0 @@ -# exercise ServiceLoader and legacy (class load) approach -security.provider.1=sun.security.provider.Sun -security.provider.2=SunRsaSign -security.provider.3=sun.security.ssl.SunJSSE -security.provider.4=com.sun.crypto.provider.SunJCE -security.provider.5=SunJGSS -security.provider.6=SunSASL \ No newline at end of file diff --git a/test/jdk/java/security/Security/signedfirst/DynStatic.java b/test/jdk/java/security/Security/signedfirst/DynStatic.java index 59e30de54623f..17fab4cac5bfa 100644 --- a/test/jdk/java/security/Security/signedfirst/DynStatic.java +++ b/test/jdk/java/security/Security/signedfirst/DynStatic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,7 +33,6 @@ import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.ProcessTools; @@ -52,9 +51,8 @@ public class DynStatic { Paths.get(TEST_SRC, "DynSignedProvFirst.java"); private static final Path STATIC_SRC = Paths.get(TEST_SRC, "StaticSignedProvFirst.java"); - - private static final String STATIC_PROPS = - Paths.get(TEST_SRC, "Static.props").toString(); + private static final Path STATIC_PROPS = + Paths.get(TEST_SRC, "Static.props"); public static void main(String[] args) throws Exception { @@ -89,7 +87,7 @@ public static void main(String[] args) throws Exception { // Run the StaticSignedProvFirst test program ProcessTools.executeTestJava("-classpath", TEST_CLASSES.toString() + File.pathSeparator + "exp.jar", - "-Djava.security.properties=file:" + STATIC_PROPS, + "-Djava.security.properties=" + STATIC_PROPS.toUri(), "StaticSignedProvFirst") .shouldContain("test passed"); } diff --git a/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java b/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java index 8ce11445ca0e0..e74bd41ddf9f6 100644 --- a/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java +++ b/test/jdk/java/text/Format/DateFormat/TimeZoneNameTest.java @@ -75,7 +75,7 @@ static void initAll() { "N", "GMT", "GMT", "Greenwich Mean Time", "GMT", "Greenwich Mean Time", "N", "Europe/London", "GMT", "Greenwich Mean Time", "BST", "British Summer Time", "N", "Europe/Paris", "CET", "Central European Standard Time", "CEST", "Central European Summer Time", - "N", "WET", "WET", "GMT", "WEST", "GMT+01:00", + "N", "WET", "WET", "Western European Standard Time", "WEST", "Western European Summer Time", "N", "Europe/Berlin", "CET", "Central European Standard Time", "CEST", "Central European Summer Time", "N", "Asia/Jerusalem", "IST", "Israel Standard Time", "IDT", "Israel Daylight Time", "N", "Europe/Helsinki", "EET", "Eastern European Standard Time", "EEST", "Eastern European Summer Time", 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 0000000000000..e12dabb638388 --- /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 0000000000000..9191c5caef35f --- /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/time/tck/java/time/TCKInstant.java b/test/jdk/java/time/tck/java/time/TCKInstant.java index d7a6341527962..c666a1340d388 100644 --- a/test/jdk/java/time/tck/java/time/TCKInstant.java +++ b/test/jdk/java/time/tck/java/time/TCKInstant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, 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 @@ -187,10 +187,21 @@ public void constant_MAX() { //----------------------------------------------------------------------- @Test public void now() { - Instant expected = Instant.now(Clock.systemUTC()); - Instant test = Instant.now(); - long diff = Math.abs(test.toEpochMilli() - expected.toEpochMilli()); - assertTrue(diff < 100); // less than 0.1 secs + long beforeMillis, instantMillis, afterMillis, diff; + int retryRemaining = 5; // MAX_RETRY_COUNT + do { + beforeMillis = Instant.now(Clock.systemUTC()).toEpochMilli(); + instantMillis = Instant.now().toEpochMilli(); + afterMillis = Instant.now(Clock.systemUTC()).toEpochMilli(); + diff = instantMillis - beforeMillis; + if (instantMillis < beforeMillis || instantMillis > afterMillis) { + throw new RuntimeException(": Invalid instant: (~" + instantMillis + "ms)" + + " when systemUTC in millis is in [" + + beforeMillis + ", " + + afterMillis + "]"); + } + } while (diff > 100 && --retryRemaining > 0); // retry if diff more than 0.1 sec + assertTrue(retryRemaining > 0); } //----------------------------------------------------------------------- diff --git a/test/jdk/java/time/tck/java/time/TCKZoneId.java b/test/jdk/java/time/tck/java/time/TCKZoneId.java index a90c9abf48e37..a1d0065d39e68 100644 --- a/test/jdk/java/time/tck/java/time/TCKZoneId.java +++ b/test/jdk/java/time/tck/java/time/TCKZoneId.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 @@ -96,11 +96,11 @@ public class TCKZoneId extends AbstractTCKTest { //----------------------------------------------------------------------- // SHORT_IDS //----------------------------------------------------------------------- - public void test_constant_OLD_IDS_POST_2005() { + public void test_constant_OLD_IDS_POST_2024b() { Map ids = ZoneId.SHORT_IDS; - assertEquals(ids.get("EST"), "-05:00"); - assertEquals(ids.get("MST"), "-07:00"); - assertEquals(ids.get("HST"), "-10:00"); + assertEquals(ids.get("EST"), "America/Panama"); + assertEquals(ids.get("MST"), "America/Phoenix"); + assertEquals(ids.get("HST"), "Pacific/Honolulu"); assertEquals(ids.get("ACT"), "Australia/Darwin"); assertEquals(ids.get("AET"), "Australia/Sydney"); assertEquals(ids.get("AGT"), "America/Argentina/Buenos_Aires"); @@ -129,7 +129,7 @@ public void test_constant_OLD_IDS_POST_2005() { } @Test(expectedExceptions=UnsupportedOperationException.class) - public void test_constant_OLD_IDS_POST_2005_immutable() { + public void test_constant_OLD_IDS_POST_2024b_immutable() { Map ids = ZoneId.SHORT_IDS; ids.clear(); } diff --git a/test/jdk/java/util/ArrayList/SortingModCount.java b/test/jdk/java/util/ArrayList/SortingModCount.java new file mode 100644 index 0000000000000..394c74cf4448c --- /dev/null +++ b/test/jdk/java/util/ArrayList/SortingModCount.java @@ -0,0 +1,59 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.List; + +/** + * @test + * @bug 8340572 + * @summary ConcurrentModificationException when sorting ArrayList sublists + */ + +public class SortingModCount { + public static void main(String[] args) { + testSortingSubListsDoesNotIncrementModCount(); + testSortingListDoesIncrementModCount(); + } + + private static void testSortingSubListsDoesNotIncrementModCount() { + List l = new ArrayList<>(List.of(1, 2, 3, 4)); + var a = l.subList(0, 2); + var b = l.subList(2, 4); + Collections.sort(a); + Collections.sort(b); + } + + private static void testSortingListDoesIncrementModCount() { + List l = new ArrayList<>(List.of(1, 2, 3, 4)); + var b = l.subList(2, 4); + Collections.sort(l); + try { + Collections.sort(b); + throw new Error("expected ConcurrentModificationException not thrown"); + } catch (ConcurrentModificationException expected) { + } + } +} diff --git a/test/jdk/java/util/Base64/TestEncodingDecodingLength.java b/test/jdk/java/util/Base64/TestEncodingDecodingLength.java index ce6d0db4adc5e..452f11ebea9e8 100644 --- a/test/jdk/java/util/Base64/TestEncodingDecodingLength.java +++ b/test/jdk/java/util/Base64/TestEncodingDecodingLength.java @@ -21,6 +21,7 @@ * questions. */ +import jdk.internal.util.ArraysSupport; import org.junit.jupiter.api.Test; import java.lang.reflect.InvocationTargetException; @@ -37,6 +38,7 @@ * Base64.Decoder.decode behavior with large, (Integer.MAX_VALUE) sized * input array/buffer. Tests the private methods "encodedOutLength" and * "decodedOutLength". + * @modules java.base/jdk.internal.util * @run junit/othervm --add-opens java.base/java.util=ALL-UNNAMED TestEncodingDecodingLength */ @@ -45,7 +47,7 @@ public class TestEncodingDecodingLength { // A value large enough to test the desired memory conditions in encode and decode - private static final int LARGE_MEM_SIZE = Integer.MAX_VALUE - 8; + private static final int LARGE_MEM_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; private static final Base64.Decoder DECODER = Base64.getDecoder(); private static final Base64.Encoder ENCODER = Base64.getEncoder(); diff --git a/test/jdk/java/util/PluggableLocale/DateFormatProviderTest.java b/test/jdk/java/util/PluggableLocale/DateFormatProviderTest.java index 876fe3526cf9d..4e3fdfbaf7a05 100644 --- a/test/jdk/java/util/PluggableLocale/DateFormatProviderTest.java +++ b/test/jdk/java/util/PluggableLocale/DateFormatProviderTest.java @@ -31,7 +31,7 @@ * java.base/sun.util.resources * @build com.foobar.Utils * com.foo.* - * @run main/othervm -Djava.locale.providers=CLDR,SPI DateFormatProviderTest + * @run main/othervm/timeout=300 -Djava.locale.providers=CLDR,SPI DateFormatProviderTest */ import java.text.DateFormat; diff --git a/test/jdk/java/util/TimeZone/DefaultTimeZoneTest.html b/test/jdk/java/util/TimeZone/DefaultTimeZoneTest.html deleted file mode 100644 index 1fa0659ed6392..0000000000000 --- a/test/jdk/java/util/TimeZone/DefaultTimeZoneTest.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - Disable Auto-adjust Daylight Saving Time Test - - - -This applet tests the platform time zone detection on all platforms (Part I) -and on/off of DST adjustment on Windows (Part II). - -Part I: - -Observe the displayed Time zone ID and the local time. If you can change -the platform time zone setting, try several time zones. If both the ID and -the local time, including the time zone name and its time zone offset, are -always correct, Part I passes. Note that some time zone IDs have their -aliases that may be displayed. For example, "US/Pacific" is an alias of -"America/Los_Angeles". - -If you are running this applet in non-English locale, the time zone names -can be displayed in the local language and English by pushing the -English/Local button. - -If platform time zones are NOT detected correctly, press the Fail button -to finish this applet. - -If this platform is Windows, proceed to Part II. Otherwise, press the Pass -button to finish this applet. - -Part II: - -Note that Part II may require the Administrator privilege to change -Windows setting. - - 1. Open the Date and Time control panel. - 2. Select any time zone where daylight saving time is *currently* in effect, - such as "(GMT-08:00) Pacific Time (US & Canada); Tijuana", - "(GMT+10:00) Canberra, Melbourne, Sydney", and Apply. - 3. Observe the local time on the control panel (Date&Time pane) and - the applet local time should be the same (daylight time). - 4. Clear "Automatically adjust clock for daylight saving changes" and Apply. - 5. Observe the two local times should be the same (standard time). - 6. Select "Automatically adjust clock for daylight saving changes" and Apply. - -If the local time in the control panel and applet are always the same, -then this test passes. Press the Pass or Fail button based on the Part II -result and finish this applet. - - - - diff --git a/test/jdk/java/util/TimeZone/DefaultTimeZoneTest.java b/test/jdk/java/util/TimeZone/DefaultTimeZoneTest.java index a6d3ac50866c0..4dd644d58b529 100644 --- a/test/jdk/java/util/TimeZone/DefaultTimeZoneTest.java +++ b/test/jdk/java/util/TimeZone/DefaultTimeZoneTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, 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 @@ -24,85 +24,94 @@ /* * @test * @bug 4296930 5033603 7092679 - * @summary Make sure that Java runtime detects the platform time zone - * correctly. Also make sure that the system time zone detection code - * detects the "Automatically adjust clock for daylight saving - * changes" setting correctly on Windows. - * @run applet/manual=yesno DefaultTimeZoneTest.html + * @summary Ensure that Java detects the platform time zone correctly, even + * if changed during runtime. Also ensure that the system time zone detection code + * detects the "Automatically adjust clock for daylight saving changes" setting + * correctly on Windows. This is a manual test dependent on making changes to + * the platform setting of the machine and thus cannot be automated. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual DefaultTimeZoneTest */ -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import java.text.*; -import java.util.*; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Window; +import java.lang.reflect.InvocationTargetException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; -public class DefaultTimeZoneTest extends JApplet implements Runnable { - static final String FORMAT = "yyyy-MM-dd HH:mm:ss zzzz (XXX)"; - JLabel tzid; - JLabel label; - SimpleDateFormat sdf = new SimpleDateFormat(FORMAT); - JButton button = new JButton("English"); - Thread clock; - boolean english = false; +public class DefaultTimeZoneTest { - @Override - public void init() { - tzid = new JLabel("Time zone ID: " + sdf.getTimeZone().getID(), SwingConstants.CENTER); - tzid.setAlignmentX(Component.CENTER_ALIGNMENT); - label = new JLabel(sdf.format(new Date()), SwingConstants.CENTER); - label.setAlignmentX(Component.CENTER_ALIGNMENT); - button.addActionListener(new ActionListener() { - @Override - @SuppressWarnings("deprecation") - public void actionPerformed(ActionEvent e) { - english = (english == false); - Locale loc = english ? Locale.US : Locale.getDefault(); - sdf = new SimpleDateFormat(FORMAT, loc); - button.setLabel(!english ? "English" : "Local"); - } - }); - button.setAlignmentX(Component.CENTER_ALIGNMENT); - JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); - panel.add(Box.createRigidArea(new Dimension(0, 10))); - panel.add(tzid); - panel.add(Box.createRigidArea(new Dimension(0, 5))); - panel.add(label); - panel.add(Box.createRigidArea(new Dimension(0, 10))); - panel.add(button); - getContentPane().add(panel); - } + private static final SimpleDateFormat SDF = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzzz (XXX)"); + private static final String INSTRUCTIONS = + """ + Tests the platform time zone detection on all platforms. + (Part I) and on/off of DST adjustment on Windows (Part II). - @Override - public void start() { - clock = new Thread(this); - clock.start(); - } + Part I: + Observe the displayed Time zone ID and the local time. + Change the platform time zone setting, then click the + "Update Time Zone" button. If the ID and local time + update correctly, part I passes, otherwise press fail. Note that + some time zone IDs have their aliases that may be displayed. + For example, "US/Pacific" is an alias of "America/Los_Angeles". + If this platform is Windows, proceed to Part II. Otherwise, press + the Pass button to complete this test. - @Override - public void stop() { - clock = null; - } + Part II: + Note that Part II may require the Administrator privilege to change + Windows setting. - @Override - public void run() { - Thread me = Thread.currentThread(); + 1. Open the Settings app and navigate to Time & Language > Date & Time + 2. Select any time zone where daylight saving time is *currently* + in effect, such as "(GMT-08:00) Pacific Time (US & Canada); + Tijuana", "(GMT+10:00) Canberra, Melbourne, Sydney", and Apply. + 3. After pressing "Update Time Zone" button, observe that the local + time on the Settings app and the test local time are the same (daylight time). + 4. Turn off "Adjust for daylight saving time automatically" + 5. Observe the two local times should be the same (standard time). + 6. Turn on "Adjust for daylight saving time automatically" - while (clock == me) { - // Reset the default time zone so that - // TimeZone.getDefault will detect the platform time zone - TimeZone.setDefault(null); - System.setProperty("user.timezone", ""); + If the local time in the Settings app and test window are always the same, + then this test passes. Press the Pass or Fail button based on the Part II + result and complete the test. + """; + + public static void main(String[] args) + throws InterruptedException, InvocationTargetException { + // Force platform time zone as default time zone + TimeZone.setDefault(null); + System.setProperty("user.timezone", ""); + // Construct test window + PassFailJFrame.builder() + .title("DefaultTimeZoneTest Instructions") + .testUI(createTest()) + .instructions(INSTRUCTIONS) + .build().awaitAndCheck(); + } + + private static Window createTest() { + var contents = new JFrame("DefaultTimeZoneTest"); + var label = new JLabel(SDF.format(new Date())); + var panel = new JPanel(); + var button = new JButton("Update Time Zone"); + panel.add(button); + contents.setSize(350, 250); + contents.add(label, BorderLayout.NORTH); + contents.add(panel, BorderLayout.CENTER); + // Update default time zone on button click + button.addActionListener(e -> { TimeZone tz = TimeZone.getDefault(); - sdf.setTimeZone(tz); - tzid.setText("Time zone ID: " + tz.getID()); - label.setText(sdf.format(new Date())); - repaint(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - } + SDF.setTimeZone(tz); + label.setText(SDF.format(new Date())); + contents.repaint(); + }); + return contents; } } diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION index bf027918ce734..f40be22e9abc1 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION +++ b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION @@ -1 +1 @@ -tzdata2024a +tzdata2024b diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt b/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt index 82bad17c55320..3217f68b82578 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt +++ b/test/jdk/java/util/TimeZone/TimeZoneData/aliases.txt @@ -15,6 +15,8 @@ Link America/Rio_Branco Brazil/Acre #= America/Porto_Acre Link America/Noronha Brazil/DeNoronha Link America/Sao_Paulo Brazil/East Link America/Manaus Brazil/West +Link Europe/Brussels CET +Link America/Chicago CST6CDT Link America/Halifax Canada/Atlantic Link America/Winnipeg Canada/Central Link America/Toronto Canada/Eastern @@ -26,6 +28,9 @@ Link America/Whitehorse Canada/Yukon Link America/Santiago Chile/Continental Link Pacific/Easter Chile/EasterIsland Link America/Havana Cuba +Link Europe/Athens EET +Link America/Panama EST +Link America/New_York EST5EDT Link Africa/Cairo Egypt Link Europe/Dublin Eire Link Etc/GMT Etc/GMT+0 @@ -49,6 +54,9 @@ Link America/Jamaica Jamaica Link Asia/Tokyo Japan Link Pacific/Kwajalein Kwajalein Link Africa/Tripoli Libya +Link Europe/Brussels MET +Link America/Phoenix MST +Link America/Denver MST7MDT Link America/Tijuana Mexico/BajaNorte Link America/Mazatlan Mexico/BajaSur Link America/Mexico_City Mexico/General @@ -212,6 +220,7 @@ Link America/Denver America/Shiprock Link America/Toronto America/Thunder_Bay Link America/Edmonton America/Yellowknife Link Pacific/Auckland Antarctica/South_Pole +Link Asia/Ulaanbaatar Asia/Choibalsan Link Asia/Shanghai Asia/Chongqing Link Asia/Shanghai Asia/Harbin Link Asia/Urumqi Asia/Kashgar @@ -226,6 +235,7 @@ Link Europe/Kyiv Europe/Zaporozhye Link Pacific/Kanton Pacific/Enderbury Link Pacific/Honolulu Pacific/Johnston Link Pacific/Port_Moresby Pacific/Yap +Link Europe/Lisbon WET Link Africa/Nairobi Africa/Asmera #= Africa/Asmara Link America/Nuuk America/Godthab Link Asia/Ashgabat Asia/Ashkhabad @@ -243,5 +253,7 @@ Link Asia/Ulaanbaatar Asia/Ulan_Bator Link Atlantic/Faroe Atlantic/Faeroe Link Europe/Kyiv Europe/Kiev Link Asia/Nicosia Europe/Nicosia +Link Pacific/Honolulu HST +Link America/Los_Angeles PST8PDT Link Pacific/Guadalcanal Pacific/Ponape #= Pacific/Pohnpei Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt index 2bcccf8f88005..e7b6ea8bf3cb7 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt +++ b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt @@ -122,11 +122,6 @@ Australia/Lindeman AEST AEDT Australia/Melbourne AEST AEDT Australia/Perth AWST AWDT Australia/Sydney AEST AEDT -CET CET CEST -CST6CDT CST CDT -EET EET EEST -EST EST -EST5EDT EST EDT Europe/Andorra CET CEST Europe/Athens EET EEST Europe/Belgrade CET CEST @@ -159,13 +154,7 @@ Europe/Vilnius EET EEST Europe/Volgograd MSK Europe/Warsaw CET CEST Europe/Zurich CET CEST -HST HST -MET MET MEST -MST MST -MST7MDT MST MDT -PST8PDT PST PDT Pacific/Auckland NZST NZDT Pacific/Guam ChST Pacific/Honolulu HST Pacific/Pago_Pago SST -WET WET WEST diff --git a/test/jdk/java/util/concurrent/tck/ArrayDeque8Test.java b/test/jdk/java/util/concurrent/tck/ArrayDeque8Test.java index eb606b0c7818e..fb21ebfed36ff 100644 --- a/test/jdk/java/util/concurrent/tck/ArrayDeque8Test.java +++ b/test/jdk/java/util/concurrent/tck/ArrayDeque8Test.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.Spliterator; +import jdk.internal.util.ArraysSupport; import junit.framework.Test; public class ArrayDeque8Test extends JSR166TestCase { @@ -86,7 +87,7 @@ public void testHugeCapacity() { return; final Item e = fortytwo; - final int maxArraySize = Integer.MAX_VALUE - 8; + final int maxArraySize = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; assertThrows(OutOfMemoryError.class, () -> new ArrayDeque(Integer.MAX_VALUE)); diff --git a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java index 451864bcf5d89..19897fa0785ef 100644 --- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java +++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java @@ -38,7 +38,7 @@ * @test id=default * @summary Conformance testing variant of JSR-166 tck tests. * @build * - * @modules java.management + * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 JSR166TestCase */ @@ -47,7 +47,7 @@ * @summary Conformance testing variant of JSR-166 tck tests * with java security manager set to allow. * @build * - * @modules java.management + * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 -Djava.security.manager=allow JSR166TestCase */ @@ -56,7 +56,7 @@ * @summary Test implementation details variant of JSR-166 * tck tests with ForkJoinPool common parallelism. * @build * - * @modules java.management + * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 * --add-opens java.base/java.util.concurrent=ALL-UNNAMED * --add-opens java.base/java.lang=ALL-UNNAMED @@ -78,7 +78,7 @@ * JSR-166 tck tests apart from ForkJoinPool common * parallelism. * @build * - * @modules java.management + * @modules java.management java.base/jdk.internal.util * @run junit/othervm/timeout=1000 * --add-opens java.base/java.util.concurrent=ALL-UNNAMED * --add-opens java.base/java.lang=ALL-UNNAMED diff --git a/test/jdk/java/util/logging/TestMainAppContext.java b/test/jdk/java/util/logging/TestMainAppContext.java index 49f371aa32160..0bfcbbad9d85b 100644 --- a/test/jdk/java/util/logging/TestMainAppContext.java +++ b/test/jdk/java/util/logging/TestMainAppContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,8 +46,8 @@ public static void main(String... args) throws Exception { rootTG = rootTG.getParent(); } - ThreadGroup tg = new ThreadGroup(rootTG, "FakeApplet"); - final Thread t1 = new Thread(tg, "createNewAppContext") { + ThreadGroup tg = new ThreadGroup(rootTG, "main"); + final Thread t1 = new Thread(tg, "child") { @Override public void run() { try { diff --git a/test/jdk/java/util/zip/ZipFile/CenSizeMaximum.java b/test/jdk/java/util/zip/ZipFile/CenSizeMaximum.java new file mode 100644 index 0000000000000..a63464eb72b88 --- /dev/null +++ b/test/jdk/java/util/zip/ZipFile/CenSizeMaximum.java @@ -0,0 +1,246 @@ +/* + * 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. + * + * 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 8341595 + * @modules java.base/jdk.internal.util + * @summary Verify that ZipFile can read from a ZIP file with a maximally large CEN size + * @run junit/othervm/manual -Xmx2500M CenSizeMaximum + */ + +import jdk.internal.util.ArraysSupport; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import static org.junit.jupiter.api.Assertions.*; + +public class CenSizeMaximum { + + // Maximum allowed CEN size allowed by the ZipFile implementation + static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; + + /** + * From the APPNOTE.txt specification: + * 4.4.10 file name length: (2 bytes) + * 4.4.11 extra field length: (2 bytes) + * 4.4.12 file comment length: (2 bytes) + * + * The length of the file name, extra field, and comment + * fields respectively. The combined length of any + * directory record and these three fields SHOULD NOT + * generally exceed 65,535 bytes. + *. + * Create a maximum extra field which does not exceed 65,535 bytes + */ + static final int MAX_EXTRA_FIELD_SIZE = 65_535 - ZipFile.CENHDR; + + // Tag for the 'unknown' field type, specified in APPNOTE.txt 'Third party mappings' + static final short UNKNOWN_ZIP_TAG = (short) 0x9902; + + // The size of one CEN header, including the name and the extra field + static final int CEN_HEADER_SIZE = ZipFile.CENHDR + MAX_EXTRA_FIELD_SIZE; + + // The size of the extra data field header (tag id + data block length) + static final int EXTRA_FIELD_HEADER_SIZE = 2 * Short.BYTES; + + // Zip file to create for testing + private Path hugeZipFile = Path.of("cen-size-on-limit.zip"); + + /** + * Clean up ZIP file created in this test + */ + @AfterEach + public void cleanup() throws IOException { + //Files.deleteIfExists(hugeZipFile); + } + + /** + * Validates that ZipFile opens a ZIP file with a CEN size close + * to the {@link #MAX_CEN_SIZE} implementation limit. + * + * @throws IOException if an unexpected IO error occurs + */ + @Test + public void maximumCenSize() throws IOException { + int numCenHeaders = zipWithWithExactCenSize(MAX_CEN_SIZE, true, false); + try (var zf = new ZipFile(hugeZipFile.toFile())) { + assertEquals(numCenHeaders, zf.size()); + } + } + + /** + * Validates that ZipFile rejects a ZIP where the last CEN record + * overflows the CEN size and the END header CENTOT field is smaller + * than the actual number of headers + * + * @throws IOException if an unexpected IO error occurs + */ + @Test + public void lastCENHeaderBadSize() throws IOException { + int numCenHeaders = zipWithWithExactCenSize(1024, true, true); + ZipException zipException = assertThrows(ZipException.class, () -> { + try (var zf = new ZipFile(hugeZipFile.toFile())) { + assertEquals(numCenHeaders, zf.size()); + } + }); + assertEquals("invalid CEN header (bad header size)", zipException.getMessage()); + + } + + /** + * Produce a ZIP file with an exact CEN size. To minimize the number of CEN headers + * written, maximally large, empty extra data blocks are written sparsely. + * + * @param cenSize the exact CEN size of the ZIP file to produce + * @param invalidateEndTotal whether to decrement the END header's TOT field by one + * @return the number of CEN headers produced + * @throws IOException if an unexpected IO error occurs + */ + private int zipWithWithExactCenSize(long cenSize, boolean invalidateEndTotal, boolean overflowLastCEN) + throws IOException { + // Sanity check + assertTrue(cenSize <= MAX_CEN_SIZE); + + // The number of CEN headers we need to write + int numCenHeaders = (int) (cenSize / CEN_HEADER_SIZE) + 1; + // Size if all extra data fields were of maximum size + long overSized = numCenHeaders * (long) CEN_HEADER_SIZE; + // Length to trim from the first CEN's extra data + int negativPadding = (int) (overSized - cenSize); + int firstExtraSize = MAX_EXTRA_FIELD_SIZE - negativPadding; + + // Sanity check + long computedCenSize = (numCenHeaders -1L ) * CEN_HEADER_SIZE + ZipEntry.CENHDR + firstExtraSize; + assertEquals(computedCenSize, cenSize); + + // A CEN header, followed by the four-bytes extra data header + ByteBuffer cenHeader = createCENHeader(); + // An END header + ByteBuffer endHeader = createENDHeader(); + // Update the END header + if (invalidateEndTotal) { + // To trigger countCENHeaders + endHeader.putShort(ZipEntry.ENDTOT, (short) (numCenHeaders -1 & 0xFFFF)); + } else { + endHeader.putShort(ZipEntry.ENDTOT, (short) (numCenHeaders & 0xFFFF)); + } + // Update CEN size and offset fields + endHeader.putInt(ZipEntry.ENDSIZ, (int) (cenSize & 0xFFFFFFFFL)); + endHeader.putInt(ZipEntry.ENDOFF, 0); + + // When creating a sparse file, the file must not already exit + Files.deleteIfExists(hugeZipFile); + + // Open a FileChannel for writing a sparse file + EnumSet options = EnumSet.of(StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE, + StandardOpenOption.SPARSE); + + try (FileChannel channel = FileChannel.open(hugeZipFile, options)) { + // Write CEN headers + for (int i = 0; i < numCenHeaders; i++) { + // The first CEN header has trimmed extra data + int extraSize = i == 0 ? firstExtraSize : MAX_EXTRA_FIELD_SIZE; + if (overflowLastCEN && i == numCenHeaders - 1) { + // make last CEN header overflow the CEN size + cenHeader.putShort(ZipEntry.CENNAM, Short.MAX_VALUE); + } + // update elen field + cenHeader.putShort(ZipEntry.CENEXT, (short) (extraSize & 0xFFFF)); + // update data block len of the extra field header + short dlen = (short) ((extraSize - EXTRA_FIELD_HEADER_SIZE) & 0xFFFF); + cenHeader.putShort(ZipEntry.CENHDR + Short.BYTES, dlen); + // Write the CEN header plus the four-byte extra header + channel.write(cenHeader.rewind()); + // Sparse "write" of the extra data block + channel.position(channel.position() + extraSize - EXTRA_FIELD_HEADER_SIZE); + } + // Sanity check + assertEquals(cenSize, channel.position()); + // Write the END header + channel.write(endHeader.rewind()); + } + return numCenHeaders; + } + + // Creates a ByteBuffer representing a CEN header with a trailing extra field header + private ByteBuffer createCENHeader() throws IOException { + byte[] bytes = smallZipfile(); + ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + int endOff = bytes.length - ZipEntry.ENDHDR; + int cenSize = buf.getInt(endOff + ZipEntry.ENDSIZ); + int cenOff = buf.getInt(endOff + ZipEntry.ENDOFF); + return ByteBuffer.wrap( + Arrays.copyOfRange(bytes, cenOff, cenOff + ZipEntry.CENHDR + EXTRA_FIELD_HEADER_SIZE) + ).order(ByteOrder.LITTLE_ENDIAN); + } + + // Creates a ByteBuffer representing an END header + private ByteBuffer createENDHeader() throws IOException { + byte[] bytes = smallZipfile(); + ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + int endOff = bytes.length - ZipEntry.ENDHDR; + return ByteBuffer.wrap( + Arrays.copyOfRange(bytes, endOff, endOff + ZipEntry.ENDHDR) + ).order(ByteOrder.LITTLE_ENDIAN); + } + + // Create a byte array with a minimal ZIP file + private static byte[] smallZipfile() throws IOException { + var out = new ByteArrayOutputStream(); + try (var zo = new ZipOutputStream(out)) { + ZipEntry entry = new ZipEntry(""); + entry.setExtra(makeDummyExtraField()); + zo.putNextEntry(entry); + } + return out.toByteArray(); + } + + // Create a minimally sized extra field + private static byte[] makeDummyExtraField() { + byte[] extra = new byte[EXTRA_FIELD_HEADER_SIZE]; + // Little-endian ByteBuffer for updating the header fields + ByteBuffer buffer = ByteBuffer.wrap(extra).order(ByteOrder.LITTLE_ENDIAN); + + // We use the 'unknown' tag, specified in APPNOTE.TXT, 4.6.1 Third party mappings' + buffer.putShort(UNKNOWN_ZIP_TAG); + + // Size of the actual (empty) data + buffer.putShort((short) 0); + return extra; + } +} diff --git a/test/jdk/java/util/zip/ZipFile/CenSizeTooLarge.java b/test/jdk/java/util/zip/ZipFile/CenSizeTooLarge.java index 4336377e0c898..e3f0b20fce87c 100644 --- a/test/jdk/java/util/zip/ZipFile/CenSizeTooLarge.java +++ b/test/jdk/java/util/zip/ZipFile/CenSizeTooLarge.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 @@ -23,10 +23,12 @@ /* @test * @bug 8272746 + * @modules java.base/jdk.internal.util * @summary Verify that ZipFile rejects a ZIP with a CEN size which does not fit in a Java byte array * @run junit CenSizeTooLarge */ +import jdk.internal.util.ArraysSupport; import org.junit.Before; import org.junit.Test; @@ -34,9 +36,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; -import java.util.Arrays; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; @@ -46,8 +46,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows; public class CenSizeTooLarge { + + // Entry names produced in this test are fixed-length + public static final int NAME_LENGTH = 10; + // Maximum allowed CEN size allowed by the ZipFile implementation - static final int MAX_CEN_SIZE = Integer.MAX_VALUE - ZipFile.ENDHDR - 1; + static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; /** * From the APPNOTE.txt specification: @@ -59,11 +63,10 @@ public class CenSizeTooLarge { * fields respectively. The combined length of any * directory record and these three fields SHOULD NOT * generally exceed 65,535 bytes. - * - * Since ZipOutputStream does not enforce the 'combined length' clause, - * we simply use 65,535 (0xFFFF) for the purpose of this test. + *. + * Create a maximum extra field which does not exceed 65,535 bytes */ - static final int MAX_EXTRA_FIELD_SIZE = 65_535; + static final int MAX_EXTRA_FIELD_SIZE = 65_535 - ZipFile.CENHDR - NAME_LENGTH; // Data size (unsigned short) // Field size minus the leading header 'tag' and 'data size' fields (2 bytes each) @@ -72,9 +75,6 @@ public class CenSizeTooLarge { // Tag for the 'unknown' field type, specified in APPNOTE.txt 'Third party mappings' static final short UNKNOWN_ZIP_TAG = (short) 0x9902; - // Entry names produced in this test are fixed-length - public static final int NAME_LENGTH = 10; - // Use a shared LocalDateTime on all entries to save processing time static final LocalDateTime TIME_LOCAL = LocalDateTime.now(); @@ -84,16 +84,16 @@ public class CenSizeTooLarge { // The number of entries needed to exceed the MAX_CEN_SIZE static final int NUM_ENTRIES = (MAX_CEN_SIZE / CEN_HEADER_SIZE) + 1; - // Helps SparseOutputStream detect write of the last CEN entry - private static final String LAST_CEN_COMMENT = "LastCEN"; - private static final byte[] LAST_CEN_COMMENT_BYTES = LAST_CEN_COMMENT.getBytes(StandardCharsets.UTF_8); - // Expected ZipException message when the CEN does not fit in a Java byte array private static final String CEN_TOO_LARGE_MESSAGE = "invalid END header (central directory size too large)"; // Zip file to create for testing private File hugeZipFile; + private static final byte[] EXTRA_BYTES = makeLargeExtraField(); + // Helps SparseOutputStream detect write of the last CEN entry + private static final byte[] LAST_EXTRA_BYTES = makeLargeExtraField(); + /** * Create a zip file with a CEN size which does not fit within a Java byte array */ @@ -125,23 +125,18 @@ public void setup() throws IOException { // Set the time/date field for faster processing entry.setTimeLocal(TIME_LOCAL); - if (i == NUM_ENTRIES -1) { - // Help SparseOutputStream detect the last CEN entry write - entry.setComment(LAST_CEN_COMMENT); - } // Add the entry zip.putNextEntry(entry); - - } // Finish writing the last entry zip.closeEntry(); // Before the CEN headers are written, set the extra data on each entry - byte[] extra = makeLargeExtraField(); for (ZipEntry entry : entries) { - entry.setExtra(extra); + entry.setExtra(EXTRA_BYTES); } + // Help SparseOutputSream detect the last entry + entries[entries.length-1].setExtra(LAST_EXTRA_BYTES); } } @@ -165,7 +160,7 @@ public void centralDirectoryTooLargeToFitInByteArray() { * Data Size (Two byte short) * Data Block (Contents depend on field type) */ - private byte[] makeLargeExtraField() { + private static byte[] makeLargeExtraField() { // Make a maximally sized extra field byte[] extra = new byte[MAX_EXTRA_FIELD_SIZE]; // Little-endian ByteBuffer for updating the header fields @@ -203,7 +198,7 @@ public void write(byte[] b, int off, int len) throws IOException { // but instead simply advance the position, creating a sparse file channel.position(position); // Check for last CEN record - if (Arrays.equals(LAST_CEN_COMMENT_BYTES, 0, LAST_CEN_COMMENT_BYTES.length, b, off, len)) { + if (b == LAST_EXTRA_BYTES) { // From here on, write actual bytes sparse = false; } diff --git a/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java b/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java index 51ed2c28c12c2..7adcfb9c12845 100644 --- a/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java +++ b/test/jdk/java/util/zip/ZipFile/EndOfCenValidation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -23,13 +23,17 @@ /* @test * @bug 8272746 + * @modules java.base/jdk.internal.util * @summary Verify that ZipFile rejects files with CEN sizes exceeding the implementation limit - * @run testng/othervm EndOfCenValidation + * @run junit/othervm EndOfCenValidation */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; +import jdk.internal.util.ArraysSupport; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.io.*; import java.nio.ByteBuffer; @@ -41,12 +45,13 @@ import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.EnumSet; +import java.util.HexFormat; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * This test augments {@link TestTooManyEntries}. It creates sparse ZIPs where @@ -68,7 +73,7 @@ public class EndOfCenValidation { private static final int ENDSIZ = ZipFile.ENDSIZ; // Offset of CEN size field within ENDHDR private static final int ENDOFF = ZipFile.ENDOFF; // Offset of CEN offset field within ENDHDR // Maximum allowed CEN size allowed by ZipFile - private static int MAX_CEN_SIZE = Integer.MAX_VALUE - ENDHDR - 1; + private static final int MAX_CEN_SIZE = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; // Expected message when CEN size does not match file size private static final String INVALID_CEN_BAD_SIZE = "invalid END header (bad central directory size)"; @@ -76,6 +81,8 @@ public class EndOfCenValidation { private static final String INVALID_CEN_BAD_OFFSET = "invalid END header (bad central directory offset)"; // Expected message when CEN size is too large private static final String INVALID_CEN_SIZE_TOO_LARGE = "invalid END header (central directory size too large)"; + // Expected message when total entry count is too large + private static final String INVALID_BAD_ENTRY_COUNT = "invalid END header (total entries count too large)"; // A valid ZIP file, used as a template private byte[] zipBytes; @@ -84,7 +91,7 @@ public class EndOfCenValidation { * Create a valid ZIP file, used as a template * @throws IOException if an error occurs */ - @BeforeTest + @BeforeEach public void setup() throws IOException { zipBytes = templateZip(); } @@ -93,7 +100,7 @@ public void setup() throws IOException { * Delete big files after test, in case the file system did not support sparse files. * @throws IOException if an error occurs */ - @AfterTest + @AfterEach public void cleanup() throws IOException { Files.deleteIfExists(CEN_TOO_LARGE_ZIP); Files.deleteIfExists(INVALID_CEN_SIZE); @@ -111,11 +118,11 @@ public void shouldRejectTooLargeCenSize() throws IOException { Path zip = zipWithModifiedEndRecord(size, true, 0, CEN_TOO_LARGE_ZIP); - ZipException ex = expectThrows(ZipException.class, () -> { + ZipException ex = assertThrows(ZipException.class, () -> { new ZipFile(zip.toFile()); }); - assertEquals(ex.getMessage(), INVALID_CEN_SIZE_TOO_LARGE); + assertEquals(INVALID_CEN_SIZE_TOO_LARGE, ex.getMessage()); } /** @@ -131,11 +138,11 @@ public void shouldRejectInvalidCenSize() throws IOException { Path zip = zipWithModifiedEndRecord(size, false, 0, INVALID_CEN_SIZE); - ZipException ex = expectThrows(ZipException.class, () -> { + ZipException ex = assertThrows(ZipException.class, () -> { new ZipFile(zip.toFile()); }); - assertEquals(ex.getMessage(), INVALID_CEN_BAD_SIZE); + assertEquals(INVALID_CEN_BAD_SIZE, ex.getMessage()); } /** @@ -151,11 +158,138 @@ public void shouldRejectInvalidCenOffset() throws IOException { Path zip = zipWithModifiedEndRecord(size, true, 100, BAD_CEN_OFFSET_ZIP); - ZipException ex = expectThrows(ZipException.class, () -> { + ZipException ex = assertThrows(ZipException.class, () -> { new ZipFile(zip.toFile()); }); - assertEquals(ex.getMessage(), INVALID_CEN_BAD_OFFSET); + assertEquals(INVALID_CEN_BAD_OFFSET, ex.getMessage()); + } + + /** + * Validate that a 'Zip64 End of Central Directory' record (the END header) + * where the value of the 'total entries' field is larger than what fits + * in the CEN size is rejected. + * + * @throws IOException if an error occurs + */ + @ParameterizedTest + @ValueSource(longs = { + -1, // Negative + Long.MIN_VALUE, // Very negative + 0x3B / 3L - 1, // Cannot fit in test ZIP's CEN + MAX_CEN_SIZE / 3 + 1, // Too large to allocate int[] entries array + Long.MAX_VALUE // Unreasonably large + }) + public void shouldRejectBadTotalEntries(long totalEntries) throws IOException { + /** + * A small ZIP using the ZIP64 format. + * + * ZIP created using: "echo -n hello | zip zip64.zip -" + * Hex encoded using: "cat zip64.zip | xxd -ps" + * + * The file has the following structure: + * + * 0000 LOCAL HEADER #1 04034B50 + * 0004 Extract Zip Spec 2D '4.5' + * 0005 Extract OS 00 'MS-DOS' + * 0006 General Purpose Flag 0000 + * 0008 Compression Method 0000 'Stored' + * 000A Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' + * 000E CRC 363A3020 + * 0012 Compressed Length FFFFFFFF + * 0016 Uncompressed Length FFFFFFFF + * 001A Filename Length 0001 + * 001C Extra Length 0014 + * 001E Filename '-' + * 001F Extra ID #0001 0001 'ZIP64' + * 0021 Length 0010 + * 0023 Uncompressed Size 0000000000000006 + * 002B Compressed Size 0000000000000006 + * 0033 PAYLOAD hello. + * + * 0039 CENTRAL HEADER #1 02014B50 + * 003D Created Zip Spec 1E '3.0' + * 003E Created OS 03 'Unix' + * 003F Extract Zip Spec 2D '4.5' + * 0040 Extract OS 00 'MS-DOS' + * 0041 General Purpose Flag 0000 + * 0043 Compression Method 0000 'Stored' + * 0045 Last Mod Time 5947AB78 'Mon Oct 7 21:27:48 2024' + * 0049 CRC 363A3020 + * 004D Compressed Length 00000006 + * 0051 Uncompressed Length FFFFFFFF + * 0055 Filename Length 0001 + * 0057 Extra Length 000C + * 0059 Comment Length 0000 + * 005B Disk Start 0000 + * 005D Int File Attributes 0001 + * [Bit 0] 1 Text Data + * 005F Ext File Attributes 11B00000 + * 0063 Local Header Offset 00000000 + * 0067 Filename '-' + * 0068 Extra ID #0001 0001 'ZIP64' + * 006A Length 0008 + * 006C Uncompressed Size 0000000000000006 + * + * 0074 ZIP64 END CENTRAL DIR 06064B50 + * RECORD + * 0078 Size of record 000000000000002C + * 0080 Created Zip Spec 1E '3.0' + * 0081 Created OS 03 'Unix' + * 0082 Extract Zip Spec 2D '4.5' + * 0083 Extract OS 00 'MS-DOS' + * 0084 Number of this disk 00000000 + * 0088 Central Dir Disk no 00000000 + * 008C Entries in this disk 0000000000000001 + * 0094 Total Entries 0000000000000001 + * 009C Size of Central Dir 000000000000003B + * 00A4 Offset to Central dir 0000000000000039 + * + * 00AC ZIP64 END CENTRAL DIR 07064B50 + * LOCATOR + * 00B0 Central Dir Disk no 00000000 + * 00B4 Offset to Central dir 0000000000000074 + * 00BC Total no of Disks 00000001 + * + * 00C0 END CENTRAL HEADER 06054B50 + * 00C4 Number of this disk 0000 + * 00C6 Central Dir Disk no 0000 + * 00C8 Entries in this disk 0001 + * 00CA Total Entries 0001 + * 00CC Size of Central Dir 0000003B + * 00D0 Offset to Central Dir FFFFFFFF + * 00D4 Comment Length 0000 + */ + + byte[] zipBytes = HexFormat.of().parseHex(""" + 504b03042d000000000078ab475920303a36ffffffffffffffff01001400 + 2d010010000600000000000000060000000000000068656c6c6f0a504b01 + 021e032d000000000078ab475920303a3606000000ffffffff01000c0000 + 00000001000000b011000000002d010008000600000000000000504b0606 + 2c000000000000001e032d00000000000000000001000000000000000100 + 0000000000003b000000000000003900000000000000504b060700000000 + 740000000000000001000000504b050600000000010001003b000000ffff + ffff0000 + """.replaceAll("\n","")); + + // Buffer to manipulate the above ZIP + ByteBuffer buf = ByteBuffer.wrap(zipBytes).order(ByteOrder.LITTLE_ENDIAN); + // Offset of the 'total entries' in the 'ZIP64 END CENTRAL DIR' record + // Update ZIP64 entry count to a value which cannot possibly fit in the small CEN + buf.putLong(0x94, totalEntries); + // The corresponding END field needs the ZIP64 magic value + buf.putShort(0xCA, (short) 0xFFFF); + // Write the ZIP to disk + Path zipFile = Path.of("bad-entry-count.zip"); + Files.write(zipFile, zipBytes); + + // Verify that the END header is rejected + ZipException ex = assertThrows(ZipException.class, () -> { + try (var zf = new ZipFile(zipFile.toFile())) { + } + }); + + assertEquals(INVALID_BAD_ENTRY_COUNT, ex.getMessage()); } /** diff --git a/test/jdk/java/util/zip/ZipFile/ReadAfterClose.java b/test/jdk/java/util/zip/ZipFile/ReadAfterClose.java new file mode 100644 index 0000000000000..a083daf766871 --- /dev/null +++ b/test/jdk/java/util/zip/ZipFile/ReadAfterClose.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. + */ + +/* @test + @bug 8340684 + @summary Verify unspecified, but long-standing behavior when reading + from an input stream obtained using ZipFile::getInputStream after + the ZipFile has been closed. + @run junit ReadAfterClose + */ + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ReadAfterClose { + + // ZIP file used in this test + private Path zip = Path.of("read-after-close.zip"); + + /** + * Create a sample ZIP file for use by this test + * @throws IOException if an unexpected IOException occurs + */ + @BeforeEach + public void setUp() throws IOException { + byte[] content = "hello".repeat(1000).getBytes(StandardCharsets.UTF_8); + try (OutputStream out = Files.newOutputStream(zip); + ZipOutputStream zo = new ZipOutputStream(out)) { + { + zo.putNextEntry(new ZipEntry("deflated.txt")); + zo.write(content); + } + { + ZipEntry entry = new ZipEntry("stored.txt"); + entry.setMethod(ZipEntry.STORED); + CRC32 crc = new CRC32(); + crc.update(content); + entry.setCrc(crc.getValue()); + entry.setSize(content.length); + zo.putNextEntry(entry); + zo.write(content); + } + } + } + + /** + * Delete the ZIP file produced by this test + * @throws IOException if an unexpected IOException occurs + */ + @AfterEach + public void cleanup() throws IOException { + Files.deleteIfExists(zip); + } + + /** + * Produce arguments with a variation of stored / deflated entries, + * and read behavior before closing the ZipFile. + * @return + */ + public static Stream arguments() { + return Stream.of( + Arguments.of("stored.txt", true), + Arguments.of("stored.txt", false), + Arguments.of("deflated.txt", true), + Arguments.of("deflated.txt", false) + ); + } + /** + * Attempting to read from an InputStream obtained by ZipFile.getInputStream + * after the backing ZipFile is closed should throw IOException + * + * @throws IOException if an unexpected IOException occurs + */ + @ParameterizedTest + @MethodSource("arguments") + public void readAfterClose(String entryName, boolean readFirst) throws IOException { + // Retain a reference to an input stream backed by a closed ZipFile + InputStream in; + try (ZipFile zf = new ZipFile(zip.toFile())) { + in = zf.getInputStream(new ZipEntry(entryName)); + // Optionally consume a single byte from the stream before closing + if (readFirst) { + in.read(); + } + } + + assertThrows(IOException.class, () -> { + in.read(); + }); + } +} \ No newline at end of file diff --git a/test/jdk/java/util/zip/ZipOutputStream/ZipOutputStreamMaxCenHdrTest.java b/test/jdk/java/util/zip/ZipOutputStream/ZipOutputStreamMaxCenHdrTest.java new file mode 100644 index 0000000000000..286e043c2256f --- /dev/null +++ b/test/jdk/java/util/zip/ZipOutputStream/ZipOutputStreamMaxCenHdrTest.java @@ -0,0 +1,169 @@ +/* + * 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 8336025 + * @summary Verify that ZipOutputStream throws a ZipException when the + * CEN header size + name length + comment length + extra length exceeds + * 65,535 bytes + * @run junit ZipOutputStreamMaxCenHdrTest + */ +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.*; +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.Arrays; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import static org.junit.jupiter.api.Assertions.*; + +public class ZipOutputStreamMaxCenHdrTest { + + // CEN header size + name length + comment length + extra length + // should not exceed 65,535 bytes per the PKWare APP.NOTE + // 4.4.10, 4.4.11, & 4.4.12. + static final int MAX_COMBINED_CEN_HEADER_SIZE = 0xFFFF; + + // Maximum possible size of name length + comment length + extra length + // for entries in order to not exceed 65,489 bytes minus 46 bytes for the CEN + // header length + static final int MAX_NAME_COMMENT_EXTRA_SIZE = + MAX_COMBINED_CEN_HEADER_SIZE - ZipFile.CENHDR; + + // Tag for the 'unknown' field type, specified in APPNOTE.txt 'Third party mappings' + static final short UNKNOWN_ZIP_TAG = (short) 0x9902; + + // ZIP file to be used by the tests + static final Path ZIP_FILE = Path.of("maxCENHdrTest.zip"); + + /** + * Clean up prior to test run + * + * @throws IOException if an error occurs + */ + @BeforeEach + public void startUp() throws IOException { + Files.deleteIfExists(ZIP_FILE); + } + + /** + * Validate a ZipException is thrown when the combined CEN Header, name + * length, comment length, and extra data length exceeds 65,535 bytes when + * the ZipOutputStream is closed. + */ + @ParameterizedTest + @ValueSource(ints = {MAX_COMBINED_CEN_HEADER_SIZE, + MAX_COMBINED_CEN_HEADER_SIZE - 1, + MAX_NAME_COMMENT_EXTRA_SIZE, + MAX_NAME_COMMENT_EXTRA_SIZE - 1}) + void setCommentTest(int length) throws IOException { + boolean expectZipException = length > MAX_NAME_COMMENT_EXTRA_SIZE; + final byte[] bytes = new byte[length]; + Arrays.fill(bytes, (byte) 'a'); + ZipEntry zipEntry = new ZipEntry(""); + // The comment length will trigger the ZipException + zipEntry.setComment(new String(bytes, StandardCharsets.UTF_8)); + boolean receivedException = writeZipEntry(zipEntry, expectZipException); + assertEquals(receivedException, expectZipException); + } + + /** + * Validate an ZipException is thrown when the combined CEN Header, name + * length, comment length, and extra data length exceeds 65,535 bytes when + * the ZipOutputStream is closed. + */ + @ParameterizedTest + @ValueSource(ints = {MAX_COMBINED_CEN_HEADER_SIZE, + MAX_COMBINED_CEN_HEADER_SIZE - 1, + MAX_NAME_COMMENT_EXTRA_SIZE, + MAX_NAME_COMMENT_EXTRA_SIZE - 1}) + void setNameTest(int length) throws IOException { + boolean expectZipException = length > MAX_NAME_COMMENT_EXTRA_SIZE; + final byte[] bytes = new byte[length]; + Arrays.fill(bytes, (byte) 'a'); + // The name length will trigger the ZipException + ZipEntry zipEntry = new ZipEntry(new String(bytes, StandardCharsets.UTF_8)); + boolean receivedException = writeZipEntry(zipEntry, expectZipException); + assertEquals(receivedException, expectZipException); + } + + /** + * Validate an ZipException is thrown when the combined CEN Header, name + * length, comment length, and extra data length exceeds 65,535 bytes when + * the ZipOutputStream is closed. + */ + @ParameterizedTest + @ValueSource(ints = {MAX_COMBINED_CEN_HEADER_SIZE, + MAX_COMBINED_CEN_HEADER_SIZE - 1, + MAX_NAME_COMMENT_EXTRA_SIZE, + MAX_NAME_COMMENT_EXTRA_SIZE - 1}) + void setExtraTest(int length) throws IOException { + boolean expectZipException = length > MAX_NAME_COMMENT_EXTRA_SIZE; + final byte[] bytes = new byte[length]; + // Little-endian ByteBuffer for updating the header fields + ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + // We use the 'unknown' tag, specified in APPNOTE.TXT, 4.6.1 Third party mappings' + buffer.putShort(UNKNOWN_ZIP_TAG); + // Size of the actual (empty) data + buffer.putShort((short) (length - 2 * Short.BYTES)); + ZipEntry zipEntry = new ZipEntry(""); + // The extra data length will trigger the ZipException + zipEntry.setExtra(bytes); + boolean receivedException = writeZipEntry(zipEntry, expectZipException); + assertEquals(receivedException, expectZipException); + } + + /** + * Write a single Zip entry using ZipOutputStream + * @param zipEntry the ZipEntry to write + * @param expectZipException true if a ZipException is expected, false otherwse + * @return true if a ZipException was thrown + * @throws IOException if an error occurs + */ + private static boolean writeZipEntry(ZipEntry zipEntry, boolean expectZipException) + throws IOException { + boolean receivedException = false; + try (ZipOutputStream zos = new ZipOutputStream( + new BufferedOutputStream(Files.newOutputStream(ZIP_FILE)))) { + zos.putNextEntry(zipEntry); + if (expectZipException) { + ZipException ex = assertThrows(ZipException.class, zos::close); + assertTrue(ex.getMessage().matches(".*bad header size.*"), + "Unexpected ZipException message: " + ex.getMessage()); + receivedException = true; + } + } catch (Exception e) { + throw new RuntimeException("Received Unexpected Exception", e); + } + return receivedException; + } +} diff --git a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java index 304cb0695d618..120e6b258e630 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/DTLS/TEST.properties b/test/jdk/javax/net/ssl/DTLS/TEST.properties index ceb6c8c9c42c7..a50cba09c0f81 100644 --- a/test/jdk/javax/net/ssl/DTLS/TEST.properties +++ b/test/jdk/javax/net/ssl/DTLS/TEST.properties @@ -6,3 +6,4 @@ modules = \ java.security.jgss/sun.security.krb5.internal:+open \ java.security.jgss/sun.security.krb5.internal.ktab \ java.base/sun.security.util +maxOutputSize = 2500000 diff --git a/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java b/test/jdk/javax/net/ssl/TLSCommon/MFLNTest.java index ee50f21ae27d1..0867925f1359e 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/print/PostScriptRotatedScaledFontTest.java b/test/jdk/javax/print/PostScriptRotatedScaledFontTest.java new file mode 100644 index 0000000000000..e6e274f7cac42 --- /dev/null +++ b/test/jdk/javax/print/PostScriptRotatedScaledFontTest.java @@ -0,0 +1,211 @@ +/* + * 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.Graphics; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import javax.print.Doc; +import javax.print.DocFlavor; +import javax.print.DocPrintJob; +import javax.print.SimpleDoc; +import javax.print.StreamPrintService; +import javax.print.StreamPrintServiceFactory; +import javax.print.event.PrintJobAdapter; +import javax.print.event.PrintJobEvent; + +/* + * @test + * @bug 8339974 + * @summary Verifies that text prints correctly using scaled and rotated fonts. + */ +public class PostScriptRotatedScaledFontTest { + + public static void main(String[] args) throws Exception { + test(0); + test(1); + test(2); + test(3); + test(4); + } + + private static void test(int quadrants) throws Exception { + + DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; + String mime = "application/postscript"; + StreamPrintServiceFactory[] factories = StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor, mime); + if (factories.length == 0) { + throw new RuntimeException("Unable to find PostScript print service factory"); + } + + StreamPrintServiceFactory factory = factories[0]; + + // required to trigger "text-as-shapes" code path in + // PSPathGraphics.drawString(String, float, float, Font, FontRenderContext, float) + // for *all* text, not just text that uses a transformed font + String shapeText = "sun.java2d.print.shapetext"; + System.setProperty(shapeText, "true"); + + try { + for (int scale = 1; scale <= 100; scale++) { + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + StreamPrintService service = factory.getPrintService(output); + DocPrintJob job = service.createPrintJob(); + + PrintJobMonitor monitor = new PrintJobMonitor(); + job.addPrintJobListener(monitor); + + Printable printable = new TestPrintable(scale, quadrants); + Doc doc = new SimpleDoc(printable, flavor, null); + job.print(doc, null); + monitor.waitForJobToFinish(); + + byte[] ps = output.toByteArray(); + Rectangle2D.Double bounds = findTextBoundingBox(ps); + if (bounds == null) { + throw new RuntimeException("Text missing: scale=" + scale + + ", quadrants=" + quadrants); + } + + boolean horizontal = (bounds.width > bounds.height); + boolean expectedHorizontal = (quadrants % 2 == 0); + if (horizontal != expectedHorizontal) { + throw new RuntimeException("Wrong orientation: scale=" + scale + + ", quadrants=" + quadrants + ", bounds=" + bounds + + ", expectedHorizontal=" + expectedHorizontal + + ", horizontal=" + horizontal); + } + } + } finally { + System.clearProperty(shapeText); + } + } + + // very basic, uses moveto ("x y M"), lineto ("x y L"), and curveto ("x1 y1 x2 y2 x3 y3 C") + private static Rectangle2D.Double findTextBoundingBox(byte[] ps) { + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + double maxX = Double.MIN_VALUE; + double maxY = Double.MIN_VALUE; + boolean pastPageClip = false; + List< String > lines = new String(ps, StandardCharsets.ISO_8859_1).lines().toList(); + for (String line : lines) { + if (!pastPageClip) { + pastPageClip = "WC".equals(line); + continue; + } + String[] values = line.split(" "); + if (values.length == 3 || values.length == 7) { + String cmd = values[values.length - 1]; + if ("M".equals(cmd) || "L".equals(cmd) || "C".equals(cmd)) { + String sx = values[values.length - 3]; + String sy = values[values.length - 2]; + double x = Double.parseDouble(sx); + double y = Double.parseDouble(sy); + if (x < minX) { + minX = x; + } + if (y < minY) { + minY = y; + } + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + } + } + } + if (minX != Double.MAX_VALUE) { + return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); + } else { + return null; + } + } + + private static final class TestPrintable implements Printable { + private final int scale; + private final int quadrants; + public TestPrintable(int scale, int quadrants) { + this.scale = scale; + this.quadrants = quadrants; + } + @Override + public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { + if (pageIndex > 0) { + return NO_SUCH_PAGE; + } + AffineTransform at = AffineTransform.getQuadrantRotateInstance(quadrants); + at.scale(scale, scale); + Font base = new Font("SansSerif", Font.PLAIN, 10); + Font font = base.deriveFont(at); + graphics.setFont(font); + graphics.drawString("TEST", 300, 300); + return PAGE_EXISTS; + } + } + + private static class PrintJobMonitor extends PrintJobAdapter { + private boolean finished; + @Override + public void printJobCanceled(PrintJobEvent pje) { + finished(); + } + @Override + public void printJobCompleted(PrintJobEvent pje) { + finished(); + } + @Override + public void printJobFailed(PrintJobEvent pje) { + finished(); + } + @Override + public void printJobNoMoreEvents(PrintJobEvent pje) { + finished(); + } + private synchronized void finished() { + finished = true; + notify(); + } + public synchronized void waitForJobToFinish() { + try { + while (!finished) { + wait(); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/test/jdk/javax/swing/JColorChooser/Test4887836.java b/test/jdk/javax/swing/JColorChooser/Test4887836.java index 4043dbdd88ae6..82a09c460853b 100644 --- a/test/jdk/javax/swing/JColorChooser/Test4887836.java +++ b/test/jdk/javax/swing/JColorChooser/Test4887836.java @@ -26,10 +26,12 @@ import javax.swing.JColorChooser; import javax.swing.UIManager; +import jtreg.SkippedException; + /* * @test * @bug 4887836 - * @library /java/awt/regtesthelpers + * @library /java/awt/regtesthelpers /test/lib * @build PassFailJFrame * @summary Checks for white area under the JColorChooser Swatch tab * @run main/manual Test4887836 @@ -38,6 +40,13 @@ public class Test4887836 { public static void main(String[] args) throws Exception { + + // ColorChooser UI design is different for GTK L&F. + // There is no Swatches tab available for GTK L&F, skip the testing. + if (UIManager.getLookAndFeel().getName().contains("GTK")) { + throw new SkippedException("Test not applicable for GTK L&F"); + } + String instructions = """ If you do not see white area under the \"Swatches\" tab, then test passed, otherwise it failed."""; @@ -45,9 +54,7 @@ public static void main(String[] args) throws Exception { PassFailJFrame.builder() .title("Test4759306") .instructions(instructions) - .rows(5) .columns(40) - .testTimeOut(10) .testUI(Test4887836::createColorChooser) .build() .awaitAndCheck(); diff --git a/test/jdk/javax/swing/JFileChooser/bug4587721.java b/test/jdk/javax/swing/JFileChooser/bug4587721.java new file mode 100644 index 0000000000000..408f4e47f49f7 --- /dev/null +++ b/test/jdk/javax/swing/JFileChooser/bug4587721.java @@ -0,0 +1,77 @@ +/* + * 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 + * 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 4587721 + * @summary Tests if JFileChooser details view chops off text + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual bug4587721 + */ + +import java.awt.Font; +import java.util.Enumeration; + +import javax.swing.JFileChooser; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; + +public class bug4587721 { + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + + String instructions = """ + Click on the Details button in JFileChooser Window. + If the filename text is chopped off by height, + then Press FAIL else Press PASS. + """; + + PassFailJFrame.builder() + .title("bug4587721") + .instructions(instructions) + .columns(40) + .testUI(bug4587721::createUI) + .build() + .awaitAndCheck(); + } + + public static JFileChooser createUI() { + setFonts(); + JFileChooser fc = new JFileChooser(); + return fc; + } + + public static void setFonts() { + UIDefaults defaults = UIManager.getDefaults(); + Enumeration keys = defaults.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + if (defaults.get(key) instanceof Font) + UIManager.put(key, new FontUIResource(new Font("Courier", Font.BOLD, 30))); + } + } +} diff --git a/test/jdk/javax/swing/JTabbedPane/8007563/Test8007563.java b/test/jdk/javax/swing/JTabbedPane/8007563/Test8007563.java deleted file mode 100644 index 25d3ad41eca59..0000000000000 --- a/test/jdk/javax/swing/JTabbedPane/8007563/Test8007563.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.*; -import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JTabbedPane; - -import static javax.swing.UIManager.*; -import static javax.swing.SwingUtilities.*; - -/* - * @test - * @key headful - * @bug 8007563 - * @summary Tests JTabbedPane background - * @author Sergey Malenkov - */ - -public class Test8007563 implements Runnable { - private static final ArrayList LIST = new ArrayList<>(); - private static final LookAndFeelInfo[] INFO = getInstalledLookAndFeels(); - private static final CountDownLatch LATCH = new CountDownLatch(INFO.length); - private static Robot ROBOT; - - public static void main(String[] args) throws Exception { - ROBOT = new Robot(); - invokeLater(new Test8007563()); - LATCH.await(); - if (!LIST.isEmpty()) { - throw new Error(LIST.toString()); - } - } - - private static void addOpaqueError(boolean opaque) { - LIST.add(getLookAndFeel().getName() + " opaque=" + opaque); - } - - private static boolean updateLookAndFeel() { - int index = (int) LATCH.getCount() - 1; - if (index >= 0) { - try { - LookAndFeelInfo info = INFO[index]; - System.err.println("L&F: " + info.getName()); - setLookAndFeel(info.getClassName()); - return true; - } catch (Exception exception) { - exception.printStackTrace(); - } - } - return false; - } - - private JFrame frame; - private JTabbedPane pane; - - public void run() { - if (this.frame == null) { - if (!updateLookAndFeel()) { - return; - } - this.pane = new JTabbedPane(); - this.pane.setOpaque(false); - this.pane.setBackground(Color.RED); - for (int i = 0; i < 3; i++) { - this.pane.addTab("Tab " + i, new JLabel("Content area " + i)); - } - this.frame = new JFrame(getClass().getSimpleName()); - this.frame.getContentPane().setBackground(Color.BLUE); - this.frame.add(this.pane); - this.frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - this.frame.setSize(400, 200); - this.frame.setLocationRelativeTo(null); - this.frame.setVisible(true); - } else { - Point point = new Point(this.pane.getWidth() - 2, 2); - convertPointToScreen(point, this.pane); - Color actual = ROBOT.getPixelColor(point.x, point.y); - - boolean opaque = this.pane.isOpaque(); - Color expected = opaque - ? this.pane.getBackground() - : this.frame.getContentPane().getBackground(); - - if (!expected.equals(actual)){ - addOpaqueError(opaque); - } - if (!opaque) { - this.pane.setOpaque(true); - this.pane.repaint(); - } else { - this.frame.dispose(); - this.frame = null; - this.pane = null; - LATCH.countDown(); - } - - } - SecondaryLoop secondaryLoop = - Toolkit.getDefaultToolkit().getSystemEventQueue() - .createSecondaryLoop(); - new Thread() { - @Override - public void run() { - try { - Thread.sleep(200); - } catch (InterruptedException e) { - } - secondaryLoop.exit(); - invokeLater(Test8007563.this); - } - }.start(); - secondaryLoop.enter(); - } -} diff --git a/test/jdk/javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java b/test/jdk/javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java new file mode 100644 index 0000000000000..d4bed5ac2dafa --- /dev/null +++ b/test/jdk/javax/swing/JTabbedPane/TestJTabbedPaneBackgroundColor.java @@ -0,0 +1,137 @@ +/* + * 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 + * 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.Point; +import java.awt.Robot; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTabbedPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +/* + * @test + * @key headful + * @bug 8007563 + * @summary Tests JTabbedPane background + */ + +public class TestJTabbedPaneBackgroundColor { + private static ArrayList lafList = new ArrayList<>(); + private static JFrame frame; + private static JTabbedPane pane; + private static Robot robot; + private static volatile Dimension dim; + private static volatile Point loc; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + + for (UIManager.LookAndFeelInfo laf : + UIManager.getInstalledLookAndFeels()) { + System.out.println("Testing: " + laf.getName()); + setLookAndFeel(laf); + + try { + SwingUtilities.invokeAndWait(TestJTabbedPaneBackgroundColor::createAndShowUI); + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> { + loc = pane.getLocationOnScreen(); + dim = pane.getSize(); + }); + + loc = new Point(loc.x + dim.width - 2, loc.y + 2); + doTesting(loc, laf); + + if (!pane.isOpaque()) { + pane.setOpaque(true); + pane.repaint(); + } + robot.waitForIdle(); + robot.delay(500); + + doTesting(loc, laf); + + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + if (!lafList.isEmpty()) { + throw new RuntimeException(lafList.toString()); + } + } + + private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (UnsupportedLookAndFeelException ignored) { + System.out.println("Unsupported LAF: " + laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static void createAndShowUI() { + pane = new JTabbedPane(); + pane.setOpaque(false); + pane.setBackground(Color.RED); + for (int i = 0; i < 3; i++) { + pane.addTab("Tab " + i, new JLabel("Content area " + i)); + } + frame = new JFrame("Test Background Color"); + frame.getContentPane().setBackground(Color.BLUE); + frame.add(pane); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setSize(400, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private static void doTesting(Point p, UIManager.LookAndFeelInfo laf) { + boolean isOpaque = pane.isOpaque(); + Color actual = robot.getPixelColor(p.x, p.y); + Color expected = isOpaque + ? pane.getBackground() + : frame.getContentPane().getBackground(); + + if (!expected.equals(actual)) { + addOpaqueError(laf.getName(), isOpaque); + } + } + + private static void addOpaqueError(String lafName, boolean opaque) { + lafList.add(lafName + " opaque=" + opaque); + } +} diff --git a/test/jdk/javax/swing/ProgressMonitor/ProgressMonitorEscapeKeyPress.java b/test/jdk/javax/swing/ProgressMonitor/ProgressMonitorEscapeKeyPress.java index 4ea1f48ae7a34..77fac43d26263 100644 --- a/test/jdk/javax/swing/ProgressMonitor/ProgressMonitorEscapeKeyPress.java +++ b/test/jdk/javax/swing/ProgressMonitor/ProgressMonitorEscapeKeyPress.java @@ -29,66 +29,64 @@ * @run main ProgressMonitorEscapeKeyPress */ -import java.awt.AWTException; -import java.awt.EventQueue; -import java.awt.Robot; -import java.awt.event.KeyEvent; + import javax.swing.JFrame; import javax.swing.ProgressMonitor; import javax.swing.SwingUtilities; +import java.awt.Robot; +import java.awt.event.KeyEvent; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; public class ProgressMonitorEscapeKeyPress { + static volatile int counter = 0; static ProgressMonitor monitor; - static int counter = 0; - static TestThread robotThread; + static TestThread testThread; static JFrame frame; + static CountDownLatch progressLatch; + static Robot robot; public static void main(String[] args) throws Exception { - - createTestUI(); - - monitor = new ProgressMonitor(frame, "Progress", null, 0, 100); - - robotThread = new TestThread(); - robotThread.start(); - - for (counter = 0; counter <= 100; counter += 10) { - Thread.sleep(1000); - - EventQueue.invokeAndWait(new Runnable() { - @Override - public void run() { - if (!monitor.isCanceled()) { - monitor.setProgress(counter); - System.out.println("Progress bar is in progress"); - } - } - }); - - if (monitor.isCanceled()) { - break; + try { + progressLatch = new CountDownLatch(20); + createTestUI(); + robot = new Robot(); + robot.setAutoDelay(50); + robot.setAutoWaitForIdle(true); + testThread = new TestThread(); + testThread.start(); + Thread.sleep(100); + if (progressLatch.await(15, TimeUnit.SECONDS)) { + System.out.println("Progress monitor completed 20%, lets press Esc..."); + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + System.out.println("ESC pressed...."); + } else { + System.out.println("Failure : No status available from Progress monitor..."); + throw new RuntimeException( + "Can't get the status from Progress monitor even after waiting too long.."); } - } - - disposeTestUI(); - if (counter >= monitor.getMaximum()) { - throw new RuntimeException("Escape key did not cancel the ProgressMonitor"); + if (counter >= monitor.getMaximum()) { + throw new RuntimeException("Escape key did not cancel the ProgressMonitor"); + } + System.out.println("Test Passed..."); + } finally { + disposeTestUI(); } } - private static void createTestUI() throws Exception { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - frame = new JFrame("Test"); - frame.setSize(300, 300); - frame.setLocationByPlatform(true); - frame.setVisible(true); - }}); - } + private static void createTestUI() throws Exception { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("Test"); + frame.setSize(300, 300); + monitor = new ProgressMonitor(frame, "Progress", "1", 0, 100); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setLocationByPlatform(true); + }); + } private static void disposeTestUI() throws Exception { @@ -100,26 +98,25 @@ private static void disposeTestUI() throws Exception { class TestThread extends Thread { - - Robot testRobot; - - TestThread() throws AWTException { - super(); - testRobot = new Robot(); - } - @Override public void run() { - try { - // Sleep for 5 seconds - so that the ProgressMonitor starts - Thread.sleep(5000); - - // Press and release Escape key - testRobot.keyPress(KeyEvent.VK_ESCAPE); - testRobot.delay(20); - testRobot.keyRelease(KeyEvent.VK_ESCAPE); - } catch (InterruptedException ex) { - throw new RuntimeException("Exception in TestThread"); + System.out.println("TestThread started........."); + for (ProgressMonitorEscapeKeyPress.counter = 0; + ProgressMonitorEscapeKeyPress.counter <= 100; + ProgressMonitorEscapeKeyPress.counter += 1) { + ProgressMonitorEscapeKeyPress.robot.delay(100); + ProgressMonitor monitor = ProgressMonitorEscapeKeyPress.monitor; + if (!monitor.isCanceled()) { + monitor.setNote("" + ProgressMonitorEscapeKeyPress.counter); + monitor.setProgress(ProgressMonitorEscapeKeyPress.counter); + ProgressMonitorEscapeKeyPress.progressLatch.countDown(); + System.out.println("Progress bar is in progress....." + + ProgressMonitorEscapeKeyPress.counter + "%"); + } + if (monitor.isCanceled()) { + System.out.println("$$$$$$$$$$$$$$$ Monitor canceled"); + break; + } } } } diff --git a/test/jdk/javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java b/test/jdk/javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java index c3bb610bb871e..d6cd1fddaa6d5 100644 --- a/test/jdk/javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java +++ b/test/jdk/javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.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 @@ -225,7 +225,7 @@ private static void test(Object[] listeners) { private static Process runProcess(LookAndFeelInfo laf) throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-Dswing.defaultlaf=" + laf.getClassName(), "-mx9m", + "-Dswing.defaultlaf=" + laf.getClassName(), "-Xmx9m", "-XX:+HeapDumpOnOutOfMemoryError", UnninstallUIMemoryLeaks.class.getSimpleName(), "mark"); return ProcessTools.startProcess(laf.getName(), pb); diff --git a/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4419255.java b/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4419255.java new file mode 100644 index 0000000000000..eb23901f60d4b --- /dev/null +++ b/test/jdk/javax/swing/plaf/basic/BasicSliderUI/bug4419255.java @@ -0,0 +1,65 @@ +/* + * 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 javax.swing.JColorChooser; +import javax.swing.UIManager; + +import jtreg.SkippedException; + +/* + * @test + * @bug 4419255 + * @library /java/awt/regtesthelpers /test/lib + * @build PassFailJFrame + * @summary Tests if Metal Slider's thumb isn't clipped + * @run main/manual bug4419255 + */ + +public class bug4419255 { + + public static void main(String[] args) throws Exception { + + // ColorChooser UI design is different for GTK L&F. + // There is no RGB tab available for GTK L&F, skip the testing. + if (UIManager.getLookAndFeel().getName().contains("GTK")) { + throw new SkippedException("Test not applicable for GTK L&F"); + } + String instructions = """ + Choose RGB tab. If sliders' thumbs are painted correctly + (top is not clipped, black line is visible), + then test passed. Otherwise it failed."""; + + PassFailJFrame.builder() + .title("bug4419255") + .instructions(instructions) + .columns(40) + .testUI(bug4419255::createColorChooser) + .build() + .awaitAndCheck(); + } + + private static JColorChooser createColorChooser() { + return new JColorChooser(Color.BLUE); + } +} diff --git a/test/jdk/jdk/classfile/ConstantDescSymbolsTest.java b/test/jdk/jdk/classfile/ConstantDescSymbolsTest.java index 7c97c9dd5a9b1..b5d3ba5d584fb 100644 --- a/test/jdk/jdk/classfile/ConstantDescSymbolsTest.java +++ b/test/jdk/jdk/classfile/ConstantDescSymbolsTest.java @@ -23,11 +23,14 @@ /* * @test - * @bug 8304031 8338406 + * @bug 8304031 8338406 8338546 * @summary Testing handling of various constant descriptors in ClassFile API. + * @modules java.base/jdk.internal.constant + * java.base/jdk.internal.classfile.impl * @run junit ConstantDescSymbolsTest */ +import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.constant.ClassDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; @@ -36,8 +39,14 @@ import java.lang.invoke.MethodType; import java.util.function.Supplier; import java.lang.classfile.ClassFile; +import java.util.stream.Stream; + +import jdk.internal.classfile.impl.AbstractPoolEntry; +import jdk.internal.constant.ConstantUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import static java.lang.classfile.ClassFile.ACC_PUBLIC; import static java.lang.constant.ConstantDescs.*; @@ -102,4 +111,58 @@ record CondyBoot(MethodHandles.Lookup lookup, String name, Class type) {} assertEquals(DEFAULT_NAME, cb.name); assertEquals(CondyBoot.class, cb.type); } + + static Stream classOrInterfaceEntries() { + return Stream.of( + CD_Object, CD_Float, CD_Long, CD_String, ClassDesc.of("Ape"), + CD_String.nested("Whatever"), CD_MethodHandles_Lookup, ClassDesc.ofInternalName("one/Two"), + ClassDesc.ofDescriptor("La/b/C;"), ConstantDescSymbolsTest.class.describeConstable().orElseThrow(), + CD_Boolean, CD_ConstantBootstraps, CD_MethodHandles + ); + } + + @ParameterizedTest + @MethodSource("classOrInterfaceEntries") + void testConstantPoolBuilderClassOrInterfaceEntry(ClassDesc cd) { + assertTrue(cd.isClassOrInterface()); + ConstantPoolBuilder cp = ConstantPoolBuilder.of(); + var internal = ConstantUtils.dropFirstAndLastChar(cd.descriptorString()); + + // 1. ClassDesc + var ce = cp.classEntry(cd); + assertSame(cd, ce.asSymbol(), "Symbol propagation on create"); + + // 1.1. Bare addition + assertTrue(ce.name().equalsString(internal), "Adding to bare pool"); + + // 1.2. Lookup existing + assertSame(ce, cp.classEntry(cd), "Finding by identical CD"); + + // 1.3. Lookup existing - equal but different ClassDesc + var cd1 = ClassDesc.ofDescriptor(cd.descriptorString()); + assertSame(ce, cp.classEntry(cd1), "Finding by another equal CD"); + + // 1.3.1. Lookup existing - equal but different ClassDesc, equal but different string + var cd2 = ClassDesc.ofDescriptor("" + cd.descriptorString()); + assertSame(ce, cp.classEntry(cd2), "Finding by another equal CD"); + + // 1.4. Lookup existing - with utf8 internal name + var utf8 = cp.utf8Entry(internal); + assertSame(ce, cp.classEntry(utf8), "Finding CD by UTF8"); + + // 2. ClassEntry exists, no ClassDesc + cp = ConstantPoolBuilder.of(); + utf8 = cp.utf8Entry(internal); + ce = cp.classEntry(utf8); + var found = cp.classEntry(cd); + assertSame(ce, found, "Finding non-CD CEs with CD"); + assertEquals(cd, ce.asSymbol(), "Symbol propagation on find"); + + // 3. Utf8Entry exists, no ClassEntry + cp = ConstantPoolBuilder.of(); + utf8 = cp.utf8Entry(internal); + ce = cp.classEntry(cd); + assertSame(utf8, ce.name(), "Reusing existing utf8 entry"); + assertEquals(cd, ce.asSymbol(), "Symbol propagation on create with utf8"); + } } diff --git a/test/jdk/jdk/classfile/InstructionValidationTest.java b/test/jdk/jdk/classfile/InstructionValidationTest.java new file mode 100644 index 0000000000000..17210e1173b3a --- /dev/null +++ b/test/jdk/jdk/classfile/InstructionValidationTest.java @@ -0,0 +1,182 @@ +/* + * 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. + * + * 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 8341277 + * @summary Testing ClassFile instruction argument validation. + * @run junit InstructionValidationTest + */ + +import java.lang.classfile.*; +import java.lang.classfile.constantpool.ClassEntry; +import java.lang.classfile.constantpool.ConstantPoolBuilder; +import java.lang.classfile.instruction.*; +import java.util.List; +import java.util.function.ObjIntConsumer; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import static java.lang.constant.ConstantDescs.*; +import static org.junit.jupiter.api.Assertions.*; +import static java.lang.classfile.Opcode.*; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class InstructionValidationTest { + + @Test + void testArgumentConstant() { + assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, 0)); + assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MIN_VALUE)); + assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MAX_VALUE)); + assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, 0)); + assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MIN_VALUE)); + assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MAX_VALUE)); + + assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int) Short.MIN_VALUE - 1)); + assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int) Short.MAX_VALUE + 1)); + assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int) Byte.MIN_VALUE - 1)); + assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int) Byte.MAX_VALUE + 1)); + } + + /** + * Tests the bad slot argument IAE for load, store, increment, and ret. + */ + @Test + void testSlots() { + record Result(boolean shouldFail, int slot) { + } + + List badSlots = List.of(-1, 72694, -42, 0x10000, Integer.MIN_VALUE, Integer.MAX_VALUE); + List u2OnlySlots = List.of(0x100, 1000, 0xFFFF); + List u1Slots = List.of(0, 2, 15, 0xFF); + + List badU1Slots = Stream.concat(badSlots.stream(), u2OnlySlots.stream()).toList(); + List u2Slots = Stream.concat(u1Slots.stream(), u2OnlySlots.stream()).toList(); + List u2Cases = Stream.concat( + badSlots.stream().map(i -> new Result(true, i)), + u2Slots.stream().map(i -> new Result(false, i)) + ).toList(); + List u1Cases = Stream.concat( + badU1Slots.stream().map(i -> new Result(true, i)), + u1Slots.stream().map(i -> new Result(false, i)) + ).toList(); + List nonIntrinsicValues = Stream.of(badSlots, u2Slots, u1Slots).mapMulti(List::forEach) + .filter(i -> i < 0 || i > 3).toList(); + + Label[] capture = new Label[1]; + ClassFile.of().build(CD_Object, clb -> clb.withMethodBody("test", MTD_void, 0, cob -> { + capture[0] = cob.startLabel(); + cob.return_(); + })); + Label dummyLabel = capture[0]; + + List> cbFactories = List.of( + CodeBuilder::aload, + CodeBuilder::iload, + CodeBuilder::lload, + CodeBuilder::dload, + CodeBuilder::fload, + CodeBuilder::astore, + CodeBuilder::istore, + CodeBuilder::lstore, + CodeBuilder::dstore, + CodeBuilder::fstore + ); + + for (var r : u2Cases) { + var fails = r.shouldFail; + var i = r.slot; + for (var fac : cbFactories) { + //check(fails, () -> execute(cob -> fac.accept(cob, i))); + } + for (TypeKind tk : TypeKind.values()) { + if (tk == TypeKind.VOID) + continue; + //check(fails, () -> execute(cob -> cob.loadLocal(tk, i))); + //check(fails, () -> execute(cob -> cob.storeLocal(tk, i))); + check(fails, () -> LoadInstruction.of(tk, i)); + check(fails, () -> StoreInstruction.of(tk, i)); + } + //check(fails, () -> execute(cob -> cob.iinc(i, 1))); + check(fails, () -> IncrementInstruction.of(i, 1)); + check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i)); + check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i)); + check(fails, () -> LocalVariable.of(i, "test", CD_Object, dummyLabel, dummyLabel)); + check(fails, () -> LocalVariableType.of(i, "test", Signature.of(CD_Object), dummyLabel, dummyLabel)); + } + + for (var r : u1Cases) { + var fails = r.shouldFail; + var i = r.slot; + for (var u1Op : List.of(ALOAD, ILOAD, LLOAD, FLOAD, DLOAD)) + check(fails, () -> LoadInstruction.of(u1Op, i)); + for (var u1Op : List.of(ASTORE, ISTORE, LSTORE, FSTORE, DSTORE)) + check(fails, () -> StoreInstruction.of(u1Op, i)); + check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET, i)); + } + + for (var i : nonIntrinsicValues) { + for (var intrinsicOp : List.of(ALOAD_0, ILOAD_0, LLOAD_0, FLOAD_0, DLOAD_0, ALOAD_1, ILOAD_1, LLOAD_1, FLOAD_1, DLOAD_1, + ALOAD_2, ILOAD_2, LLOAD_2, FLOAD_2, DLOAD_2, ALOAD_3, ILOAD_3, LLOAD_3, FLOAD_3, DLOAD_3)) { + assertThrows(IllegalArgumentException.class, () -> LoadInstruction.of(intrinsicOp, i)); + } + for (var intrinsicOp : List.of(ASTORE_0, ISTORE_0, LSTORE_0, FSTORE_0, DSTORE_0, ASTORE_1, ISTORE_1, LSTORE_1, FSTORE_1, DSTORE_1, + ASTORE_2, ISTORE_2, LSTORE_2, FSTORE_2, DSTORE_2, ASTORE_3, ISTORE_3, LSTORE_3, FSTORE_3, DSTORE_3)) { + assertThrows(IllegalArgumentException.class, () -> StoreInstruction.of(intrinsicOp, i)); + } + } + } + + static void check(boolean fails, Executable exec) { + if (fails) { + assertThrows(IllegalArgumentException.class, exec); + } else { + assertDoesNotThrow(exec); + } + } + + @Test + void testIincConstant() { + IncrementInstruction.of(0, 2); + IncrementInstruction.of(0, Short.MAX_VALUE); + IncrementInstruction.of(0, Short.MIN_VALUE); + assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, Short.MAX_VALUE + 1)); + assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, Short.MIN_VALUE - 1)); + } + + @Test + void testNewMultiArrayDimension() { + ClassEntry ce = ConstantPoolBuilder.of().classEntry(CD_Class); + NewMultiArrayInstruction.of(ce, 1); + NewMultiArrayInstruction.of(ce, 13); + NewMultiArrayInstruction.of(ce, 0xFF); + assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, 0)); + assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, 0x100)); + assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, -1)); + assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, Integer.MIN_VALUE)); + assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, Integer.MAX_VALUE)); + } +} diff --git a/test/jdk/jdk/classfile/LimitsTest.java b/test/jdk/jdk/classfile/LimitsTest.java index 9c7b8d9e72d4a..a1899ac1c842b 100644 --- a/test/jdk/jdk/classfile/LimitsTest.java +++ b/test/jdk/jdk/classfile/LimitsTest.java @@ -28,6 +28,7 @@ * @run junit LimitsTest */ import java.lang.classfile.Attributes; +import java.lang.classfile.constantpool.PoolEntry; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; @@ -36,12 +37,10 @@ import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.attribute.LineNumberInfo; import java.lang.classfile.attribute.LineNumberTableAttribute; -import java.lang.classfile.attribute.LocalVariableInfo; import java.lang.classfile.attribute.LocalVariableTableAttribute; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.constantpool.ConstantPoolException; import java.lang.classfile.constantpool.IntegerEntry; -import java.lang.classfile.instruction.LocalVariable; import java.util.List; import jdk.internal.classfile.impl.BufWriterImpl; @@ -113,13 +112,13 @@ void testReadingOutOfBounds() { @Test void testInvalidClassEntry() { assertThrows(ConstantPoolException.class, () -> ClassFile.of().parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, - 0, 0, 0, 0, 0, 2, ClassFile.TAG_METHODREF, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}).thisClass()); + 0, 0, 0, 0, 0, 2, PoolEntry.TAG_METHODREF, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}).thisClass()); } @Test void testInvalidUtf8Entry() { var cp = ClassFile.of().parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, - 0, 0, 0, 0, 0, 3, ClassFile.TAG_INTEGER, 0, 0, 0, 0, ClassFile.TAG_NAMEANDTYPE, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}).constantPool(); + 0, 0, 0, 0, 0, 3, PoolEntry.TAG_INTEGER, 0, 0, 0, 0, PoolEntry.TAG_NAME_AND_TYPE, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}).constantPool(); assertTrue(cp.entryByIndex(1) instanceof IntegerEntry); //parse valid int entry first assertThrows(ConstantPoolException.class, () -> cp.entryByIndex(2)); } diff --git a/test/jdk/jdk/classfile/OpcodesValidationTest.java b/test/jdk/jdk/classfile/OpcodesValidationTest.java deleted file mode 100644 index 2470fcf132c54..0000000000000 --- a/test/jdk/jdk/classfile/OpcodesValidationTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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. - * - * 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 Testing ClassFile constant instruction argument validation. - * @run junit OpcodesValidationTest - */ -import java.lang.classfile.instruction.ConstantInstruction; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; -import static java.lang.classfile.Opcode.*; - -class OpcodesValidationTest { - - @Test - void testArgumentConstant() { - assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, 0)); - assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MIN_VALUE)); - assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MAX_VALUE)); - assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, 0)); - assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MIN_VALUE)); - assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MAX_VALUE)); - - assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int)Short.MIN_VALUE - 1)); - assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int)Short.MAX_VALUE + 1)); - assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int)Byte.MIN_VALUE - 1)); - assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int)Byte.MAX_VALUE + 1)); - } -} diff --git a/test/jdk/jdk/classfile/OptionsTest.java b/test/jdk/jdk/classfile/OptionsTest.java index fda155cfd2a4b..4c9525f0f7f45 100644 --- a/test/jdk/jdk/classfile/OptionsTest.java +++ b/test/jdk/jdk/classfile/OptionsTest.java @@ -62,7 +62,7 @@ static Path[] corpus() throws IOException, URISyntaxException { @MethodSource("corpus") void testAttributesProcessingOptionOnTransform(Path path) throws Exception { testNoUnstable(path, ClassFile.of().parse( - ClassFile.of(ClassFile.AttributesProcessingOption.DROP_UNSTABLE_ATRIBUTES).transformClass( + ClassFile.of(ClassFile.AttributesProcessingOption.DROP_UNSTABLE_ATTRIBUTES).transformClass( ClassFile.of().parse(path), ClassTransform.transformingMethodBodies(CodeTransform.ACCEPT_ALL)))); } diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index 09be56f0de2a1..1ccd7255ed4b9 100644 --- a/test/jdk/jdk/classfile/StackMapsTest.java +++ b/test/jdk/jdk/classfile/StackMapsTest.java @@ -385,9 +385,9 @@ void testDeadCodeCountersWithCustomSMTA() { StackMapTableAttribute.of(List.of( StackMapFrameInfo.of(f2, List.of(), - List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.ITEM_LONG)), + List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.LONG)), StackMapFrameInfo.of(f3, - List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.ITEM_LONG), + List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.LONG), List.of())))); } )); diff --git a/test/jdk/jdk/classfile/UtilTest.java b/test/jdk/jdk/classfile/UtilTest.java index d9d8240ae9149..6ba7e146b0371 100644 --- a/test/jdk/jdk/classfile/UtilTest.java +++ b/test/jdk/jdk/classfile/UtilTest.java @@ -23,18 +23,25 @@ /* * @test + * @bug 8338546 * @summary Testing ClassFile Util. + * @library java.base + * @modules java.base/jdk.internal.constant + * java.base/jdk.internal.classfile.impl + * @build java.base/jdk.internal.classfile.impl.* * @run junit UtilTest */ -import java.lang.classfile.ClassFile; import java.lang.classfile.Opcode; import java.lang.constant.MethodTypeDesc; -import java.lang.invoke.MethodHandles; import java.util.Arrays; -import java.util.BitSet; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + import jdk.internal.classfile.impl.RawBytecodeHelper; import jdk.internal.classfile.impl.Util; +import jdk.internal.classfile.impl.UtilAccess; +import jdk.internal.constant.ConstantUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -84,6 +91,50 @@ private void assertSlots(String methodDesc, int slots) { assertEquals(Util.parameterSlots(MethodTypeDesc.ofDescriptor(methodDesc)), slots); } + @Test + void testPow31() { + int p = 1; + // Our calculation only prepares up to 65536, + // max length of CP Utf8 + 1 + for (int i = 0; i <= 65536; i++) { + final int t = i; + assertEquals(p, Util.pow31(i), () -> "31's power to " + t); + p *= 31; + } + } + + @ParameterizedTest + @ValueSource(classes = { + Long.class, + Object.class, + Util.class, + Test.class, + CopyOnWriteArrayList.class, + AtomicReferenceFieldUpdater.class + }) + void testInternalNameHash(Class type) { + var cd = type.describeConstable().orElseThrow(); + assertEquals(ConstantUtils.binaryToInternal(type.getName()).hashCode(), Util.internalNameHash(cd.descriptorString())); + } + + // Ensures the initialization statement of the powers array is filling in the right values + @Test + void testPowersArray() { + int[] powers = new int[64]; + for (int i = 1, k = 31; i <= 7; i++, k *= 31) { + int t = powers[UtilAccess.powersIndex(i, 0)] = k; + + for (int j = 1; j < UtilAccess.significantOctalDigits(); j++) { + t *= t; + t *= t; + t *= t; + powers[UtilAccess.powersIndex(i, j)] = t; + } + } + + assertArrayEquals(powers, UtilAccess.powersTable()); + } + @Test void testOpcodeLengthTable() { var lengths = new byte[0x100]; diff --git a/test/jdk/jdk/classfile/VerifierSelfTest.java b/test/jdk/jdk/classfile/VerifierSelfTest.java index d0943d2eee9f5..b6a2f08c9ecd7 100644 --- a/test/jdk/jdk/classfile/VerifierSelfTest.java +++ b/test/jdk/jdk/classfile/VerifierSelfTest.java @@ -29,6 +29,7 @@ * @run junit VerifierSelfTest */ import java.io.IOException; +import java.lang.classfile.constantpool.PoolEntry; import java.lang.constant.ClassDesc; import static java.lang.constant.ConstantDescs.*; import java.lang.invoke.MethodHandleInfo; @@ -116,7 +117,7 @@ public void writeBody(BufWriterImpl b) { void testInvalidClassNameEntry() { var cc = ClassFile.of(); var bytes = cc.parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, - 0, 0, 0, 0, 0, 2, ClassFile.TAG_INTEGER, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + 0, 0, 0, 0, 0, 2, PoolEntry.TAG_INTEGER, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); assertTrue(cc.verify(bytes).stream().anyMatch(e -> e.getMessage().contains("expected ClassEntry"))); } diff --git a/test/jdk/jdk/classfile/helpers/ClassRecord.java b/test/jdk/jdk/classfile/helpers/ClassRecord.java index 329e41fa0f484..e7a239015f605 100644 --- a/test/jdk/jdk/classfile/helpers/ClassRecord.java +++ b/test/jdk/jdk/classfile/helpers/ClassRecord.java @@ -22,21 +22,13 @@ */ package helpers; -import java.io.IOException; -import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.RecordComponent; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -53,9 +45,10 @@ import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; -import static java.lang.classfile.ClassFile.*; import static java.lang.classfile.Attributes.*; +import static java.lang.classfile.constantpool.PoolEntry.*; import static helpers.ClassRecord.CompatibilityFilter.By_ClassBuilder; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; /** * ClassRecord @@ -916,17 +909,17 @@ public static ConstantPoolEntryRecord ofCPEntry(PoolEntry cpInfo) { CpFieldRefRecord.ofFieldRefEntry((FieldRefEntry) cpInfo); case TAG_METHODREF -> CpMethodRefRecord.ofMethodRefEntry((MethodRefEntry) cpInfo); - case TAG_INTERFACEMETHODREF -> + case TAG_INTERFACE_METHODREF -> CpInterfaceMethodRefRecord.ofInterfaceMethodRefEntry((InterfaceMethodRefEntry) cpInfo); - case TAG_NAMEANDTYPE -> + case TAG_NAME_AND_TYPE -> CpNameAndTypeRecord.ofNameAndTypeEntry((NameAndTypeEntry) cpInfo); - case TAG_METHODHANDLE -> + case TAG_METHOD_HANDLE -> CpMethodHandleRecord.ofMethodHandleEntry((MethodHandleEntry) cpInfo); - case TAG_METHODTYPE -> + case TAG_METHOD_TYPE -> new CpMethodTypeRecord(((MethodTypeEntry) cpInfo).descriptor().stringValue()); - case TAG_CONSTANTDYNAMIC -> + case TAG_DYNAMIC -> CpConstantDynamicRecord.ofConstantDynamicEntry((ConstantDynamicEntry) cpInfo); - case TAG_INVOKEDYNAMIC -> + case TAG_INVOKE_DYNAMIC -> CpInvokeDynamicRecord.ofInvokeDynamicEntry((InvokeDynamicEntry) cpInfo); case TAG_MODULE -> new CpModuleRecord(((ModuleEntry) cpInfo).name().stringValue()); diff --git a/test/jdk/jdk/classfile/java.base/jdk/internal/classfile/impl/UtilAccess.java b/test/jdk/jdk/classfile/java.base/jdk/internal/classfile/impl/UtilAccess.java new file mode 100644 index 0000000000000..27cefd6d9441a --- /dev/null +++ b/test/jdk/jdk/classfile/java.base/jdk/internal/classfile/impl/UtilAccess.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.classfile.impl; + +public final class UtilAccess { + public static int significantOctalDigits() { + return Util.SIGNIFICANT_OCTAL_DIGITS; + } + + public static int powersIndex(int digit, int index) { + return Util.powersIndex(digit, index); + } + + public static int[] powersTable() { + return Util.powers; + } + + public static int reverse31() { + return Util.INVERSE_31; + } +} diff --git a/test/jdk/jdk/internal/loader/URLClassPath/ClassnameCharTest.java b/test/jdk/jdk/internal/loader/URLClassPath/ClassnameCharTest.java index 9e14957ef6229..85987ef6f5e1c 100644 --- a/test/jdk/jdk/internal/loader/URLClassPath/ClassnameCharTest.java +++ b/test/jdk/jdk/internal/loader/URLClassPath/ClassnameCharTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, 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 @@ -95,9 +95,9 @@ public void handle(HttpExchange exchange) { server.stop(0); } } - // the class loader code was copied from the now deleted AppletClassLoader + static class MyURLClassLoader extends URLClassLoader { - private URL base; /* applet code base URL */ + private URL base; /* code base URL */ private CodeSource codesource; /* codesource for the base URL */ private AccessControlContext acc; MyURLClassLoader(URL base) { diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java b/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java index 5518943a6e666..e236292de984a 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerBasic.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Red Hat, Inc. + * 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 @@ -26,7 +27,7 @@ * @bug 8293540 * @summary Verify that -XshowSettings:system works * @key cgroups - * @requires docker.support + * @requires container.support * @library /test/lib * @run main/timeout=360 TestDockerBasic */ diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java index f08e80c76f422..4d452f20eefe1 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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 @@ -34,7 +34,7 @@ * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build MetricsCpuTester diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java index 416e91bec5ca2..e8dc616b5e79a 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java @@ -32,7 +32,7 @@ * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build MetricsMemoryTester diff --git a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java index 92f3364da10b0..204d7a215d89f 100644 --- a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java +++ b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2020, 2022 THL A29 Limited, a Tencent company. All rights reserved. + * 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 @@ -25,7 +26,7 @@ * @test * @key cgroups * @bug 8242480 - * @requires docker.support + * @requires container.support * @library /test/lib * @build GetFreeSwapSpaceSize * @run driver TestGetFreeSwapSpaceSize diff --git a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java index 22e03293c486e..1544088f68858 100644 --- a/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java +++ b/test/jdk/jdk/internal/platform/docker/TestLimitsUpdating.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,7 +29,7 @@ * @bug 8308090 * @key cgroups * @summary Test container limits updating as they get updated at runtime without restart - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build LimitUpdateChecker diff --git a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java index 6c6ff76fa9998..9fedeb55234ca 100644 --- a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java +++ b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,7 +27,7 @@ * @key cgroups * @summary Test JDK Metrics class when running inside a docker container with limited pids * @bug 8266490 - * @requires docker.support + * @requires container.support * @library /test/lib * @build TestPidsLimit * @run driver TestPidsLimit diff --git a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java index 854d24b2279f8..93efff64cc428 100644 --- a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, 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 @@ -25,7 +25,7 @@ * @test * @key cgroups * @summary Test JDK Metrics class when running inside docker container - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @run main TestSystemMetrics diff --git a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java index 7a2f77b3ce41a..6a96514771ce4 100644 --- a/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java +++ b/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Red Hat, Inc. + * 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 @@ -24,7 +25,7 @@ /* * @test * @summary UseContainerSupport flag should reflect Metrics being available - * @requires docker.support + * @requires container.support * @library /test/lib * @modules java.base/jdk.internal.platform * @build CheckUseContainerSupport diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java index 8d070db8262a7..e13bcaa80b5cc 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerCompile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, 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 @@ -41,7 +41,7 @@ * @test * @key jfr * @requires vm.hasJFR - * @requires vm.compMode!="Xint" + * @requires vm.compMode == "Xmixed" * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java index 56005fa745832..b13fa71410926 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java @@ -57,7 +57,7 @@ * @key jfr * @summary Verifies that corresponding JFR events are emitted in case of inlining. * @requires vm.hasJFR - * + * @requires vm.compMode == "Xmixed" * @requires vm.opt.Inline == true | vm.opt.Inline == null * @library /test/lib * @modules jdk.jfr diff --git a/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.java b/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.java index 32c634fc59a90..bd6d57b31762d 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.java +++ b/test/jdk/jdk/jfr/event/compiler/TestDeoptimization.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 @@ -51,8 +51,9 @@ public static void dummyMethod(boolean b) { * @key jfr * @summary sanity test for Deoptimization event, depends on Compilation event * @requires vm.hasJFR - * @requires vm.compMode != "Xint" + * @requires vm.compMode == "Xmixed" * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == 4 | vm.opt.TieredStopAtLevel == null) + * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java index bd3a7f63a8a99..e0ea218a07f0b 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, 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 @@ -41,7 +41,7 @@ public static void main(String[] args) throws Exception { String[] vmFlags = {"-XX:+UseG1GC", "-XX:+ExplicitGCInvokesConcurrent"}; String[] gcNames = {GCHelper.gcG1New, GCHelper.gcG1Old, GCHelper.gcG1Full}; String[] gcCauses = {"Metadata GC Threshold", "G1 Evacuation Pause", "G1 Preventive Collection", - "G1 Compaction Pause", "System.gc()"}; + "G1 Compaction Pause", "CodeCache GC Threshold", "System.gc()"}; GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); } } diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java index 072c3905baf59..308a544adeb17 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, 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 @@ -41,7 +41,7 @@ public static void main(String[] args) throws Exception { String[] vmFlags = {"-XX:+UseG1GC"}; String[] gcNames = {GCHelper.gcG1New, GCHelper.gcG1Old, GCHelper.gcG1Full}; String[] gcCauses = {"Metadata GC Threshold", "G1 Evacuation Pause", "G1 Preventive Collection", - "G1 Compaction Pause", "System.gc()"}; + "G1 Compaction Pause", "CodeCache GC Threshold", "System.gc()"}; GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); } } diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java index 4d8f697d83719..16217c0cbe1b7 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, 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 @@ -39,7 +39,8 @@ public static void main(String[] args) throws Exception { String testID = "ParallelOld"; String[] vmFlags = {"-XX:+UseParallelGC"}; String[] gcNames = {GCHelper.gcParallelScavenge, GCHelper.gcParallelOld}; - String[] gcCauses = {"Allocation Failure", "System.gc()", "GCLocker Initiated GC"}; + String[] gcCauses = {"Allocation Failure", "System.gc()", "GCLocker Initiated GC", + "CodeCache GC Threshold"}; GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); } } diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithSerial.java b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithSerial.java index 403bd07758524..344c61043a2e1 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithSerial.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithSerial.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, 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 @@ -39,7 +39,8 @@ public static void main(String[] args) throws Exception { String testID = "Serial"; String[] vmFlags = {"-XX:+UseSerialGC"}; String[] gcNames = {GCHelper.gcDefNew, GCHelper.gcSerialOld}; - String[] gcCauses = {"Allocation Failure", "System.gc()", "GCLocker Initiated GC"}; + String[] gcCauses = {"Allocation Failure", "System.gc()", "GCLocker Initiated GC", + "CodeCache GC Threshold"}; GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); } } diff --git a/test/jdk/jdk/management/VirtualThreadSchedulerMXBean/VirtualThreadSchedulerMXBeanTest.java b/test/jdk/jdk/management/VirtualThreadSchedulerMXBean/VirtualThreadSchedulerMXBeanTest.java index 891f6e62e6fc7..b256d2b89ab0b 100644 --- a/test/jdk/jdk/management/VirtualThreadSchedulerMXBean/VirtualThreadSchedulerMXBeanTest.java +++ b/test/jdk/jdk/management/VirtualThreadSchedulerMXBean/VirtualThreadSchedulerMXBeanTest.java @@ -34,6 +34,8 @@ import java.lang.management.ManagementFactory; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.IntPredicate; +import java.util.function.LongPredicate; import java.util.stream.Stream; import java.util.stream.IntStream; import javax.management.MBeanServer; @@ -140,23 +142,37 @@ void testReduceParallelism(VirtualThreadSchedulerMXBean bean) throws Exception { try { // increase parallelism + saturate - int newParallelism = parallelism + 4; - bean.setParallelism(newParallelism); - IntStream.range(0, newParallelism).forEach(_ -> executor.submit(busyTask)); - awaitMountedVirtualThreadCountGte(bean, newParallelism); + int highParallelism = parallelism + 4; + bean.setParallelism(highParallelism); + IntStream.range(0, highParallelism).forEach(_ -> executor.submit(busyTask)); + + // mounted virtual thread count should increase to highParallelism. + // Sample the count at highParallelism a few times. + for (int i = 0; i < 5; i++) { + Thread.sleep(100); + awaitMountedVirtualThreadCountEq(bean, highParallelism); + } // reduce parallelism and workload - newParallelism = Math.clamp(parallelism / 2, 1, parallelism); - bean.setParallelism(newParallelism); + int lowParallelism = Math.clamp(parallelism / 2, 1, parallelism); + bean.setParallelism(lowParallelism); sleep.set(true); - // mounted virtual thread count should reduce - awaitMountedVirtualThreadCountLte(bean, newParallelism); - // increase workload, the mounted virtual thread count should not increase + // mounted virtual thread count should reduce to lowParallelism or less. + // Sample the count at lowParallelism or less a few times. + for (int i = 0; i < 5; i++) { + Thread.sleep(100); + awaitMountedVirtualThreadCountLte(bean, lowParallelism); + } + + // increase workload sleep.set(false); + + // mounted virtual thread count should not exceed lowParallelism. + // Sample the count at lowParallelism a few times. for (int i = 0; i < 5; i++) { Thread.sleep(100); - assertTrue(bean.getMountedVirtualThreadCount() <= newParallelism); + awaitMountedVirtualThreadCountEq(bean, lowParallelism); } } finally { @@ -230,31 +246,64 @@ void testQueuedVirtualThreadCount(VirtualThreadSchedulerMXBean bean) throws Exce * Waits for pool size >= target to be true. */ void awaitPoolSizeGte(VirtualThreadSchedulerMXBean bean, int target) throws InterruptedException { - System.err.format("await pool size >= %d ...%n", target); - while (bean.getPoolSize() < target) { - Thread.sleep(10); - } + awaitPoolSize(bean, ps -> ps >= target, ">= " + target); } /** * Waits for the mounted virtual thread count >= target to be true. */ void awaitMountedVirtualThreadCountGte(VirtualThreadSchedulerMXBean bean, - int target) throws InterruptedException { - System.err.format("await mounted virtual thread count >= %d ...%n", target); - while (bean.getMountedVirtualThreadCount() < target) { - Thread.sleep(10); - } + long target) throws InterruptedException { + awaitMountedVirtualThreadCount(bean, c -> c >= target, ">= " + target); } /** * Waits for the mounted virtual thread count <= target to be true. */ void awaitMountedVirtualThreadCountLte(VirtualThreadSchedulerMXBean bean, - int target) throws InterruptedException { - System.err.format("await mounted virtual thread count <= %d ...%n", target); - while (bean.getMountedVirtualThreadCount() > target) { - Thread.sleep(10); + long target) throws InterruptedException { + awaitMountedVirtualThreadCount(bean, c -> c <= target, "<= " + target); + } + + /** + * Waits for the mounted virtual thread count == target to be true. + */ + void awaitMountedVirtualThreadCountEq(VirtualThreadSchedulerMXBean bean, + long target) throws InterruptedException { + awaitMountedVirtualThreadCount(bean, c -> c == target, "== " + target); + } + + /** + * Waits until evaluating the given predicte on the pool size is true. + */ + void awaitPoolSize(VirtualThreadSchedulerMXBean bean, + IntPredicate predicate, + String reason) throws InterruptedException { + int poolSize = bean.getPoolSize(); + if (!predicate.test(poolSize)) { + System.err.format("poolSize = %d, await %s ...%n", poolSize, reason); + while (!predicate.test(poolSize)) { + Thread.sleep(10); + poolSize = bean.getPoolSize(); + } + System.err.format("poolSize = %d%n", poolSize); + } + } + + /** + * Waits until evaluating the given predicte on the mounted thread count is true. + */ + void awaitMountedVirtualThreadCount(VirtualThreadSchedulerMXBean bean, + LongPredicate predicate, + String reason) throws InterruptedException { + long count = bean.getMountedVirtualThreadCount(); + if (!predicate.test(count)) { + System.err.format("mountedVirtualThreadCount = %d, await %s ...%n", count, reason); + while (!predicate.test(count)) { + Thread.sleep(10); + count = bean.getMountedVirtualThreadCount(); + } + System.err.format("mountedVirtualThreadCount = %d%n", count); } } } \ No newline at end of file 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 ab04391b1f385..2bfd3ea760320 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 @@ -26,7 +26,7 @@ * @bug 8189131 * @summary Interoperability tests with Actalis CA * Before this test set to manual, the original timeout - * value if 180 + * value is 180 * @library /test/lib * @build jtreg.SkippedException ValidatePathWithURL CAInterop * @run main/othervm/manual -Djava.security.debug=certpath,ocsp @@ -535,6 +535,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 @@ -713,6 +735,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/sun/awt/font/CacheFlushTest.java b/test/jdk/sun/awt/font/CacheFlushTest.java new file mode 100644 index 0000000000000..f3b3293127530 --- /dev/null +++ b/test/jdk/sun/awt/font/CacheFlushTest.java @@ -0,0 +1,106 @@ +/* + * 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 4286726 + * @summary Java2D raster printing: large text may overflow glyph cache. + * Draw a large glyphvector, the 'A' glyph should appear and not get flushed. +*/ + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.util.HashMap; + +/** + * Draw a very large glyphvector on a surface. + * If the cache was flushed the first glyph is not rendered. + * Note: the implementation no longer uses glyphs for rendering large text, + * but in principle the test is still useful. + */ +public class CacheFlushTest { + + static final int WIDTH = 400, HEIGHT = 600; + static final int FONTSIZE = 250; + static final String TEST = "ABCDEFGHIJKLMNOP"; + static final HashMap HINTS = new HashMap<>(); + + static { + HINTS.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + HINTS.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + HINTS.put(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + public static void main(String args[]) { + BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); + + Graphics2D g2d = bi.createGraphics(); + g2d.addRenderingHints(HINTS); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, WIDTH, HEIGHT); + g2d.setColor(Color.black); + + FontRenderContext frc = g2d.getFontRenderContext(); + Font font = new Font(Font.DIALOG, Font.PLAIN, 250); + GlyphVector gv = font.createGlyphVector(frc, TEST); + + /* Set the positions of all but the first glyph to be offset vertically but + * FONTSIZE pixels. So if the first glyph "A" is not flushed we can tell this + * by checking for non-white pixels in the range for the default y offset of 0 + * from the specified y location. + */ + Point2D.Float pt = new Point2D.Float(20f, FONTSIZE); + for (int i = 1; i < gv.getNumGlyphs(); ++i) { + gv.setGlyphPosition(i, pt); + pt.x += 25f; + pt.y = FONTSIZE; + } + g2d.drawGlyphVector(gv, 20, FONTSIZE); + /* Now expect to find at least one black pixel in the rect (0,0) -> (WIDTH, FONTSIZE) */ + boolean found = false; + int blackPixel = Color.black.getRGB(); + for (int y = 0; y < FONTSIZE; y++) { + for (int x = 0; x < WIDTH; x++) { + if (bi.getRGB(x, y) == blackPixel) { + found = true; + break; + } + } + if (found == true) { + break; + } + } + if (!found) { + throw new RuntimeException("NO BLACK PIXELS"); + } + } +} diff --git a/test/jdk/sun/awt/font/TestArabicHebrew.java b/test/jdk/sun/awt/font/TestArabicHebrew.java new file mode 100644 index 0000000000000..61cbe931c32c4 --- /dev/null +++ b/test/jdk/sun/awt/font/TestArabicHebrew.java @@ -0,0 +1,215 @@ +/* + * 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 4198081 + * @key headful + * @summary Arabic characters should appear instead of boxes and be correctly shaped. + * Hebrew characters should appear instead of boxes. + * Test is made headful so there's no excuse for test systems not having the fonts. + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.Rectangle2D; + +public class TestArabicHebrew extends Panel { + + static volatile Frame frame; + static volatile Font font = new Font(Font.DIALOG, Font.PLAIN, 36); + + static void createUI() { + frame = new Frame("Test Arabic/Hebrew"); + frame.setLayout(new BorderLayout()); + TestArabicHebrew panel = new TestArabicHebrew(); + frame.add(panel, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } + + public static void main(String args[]) throws Exception { + EventQueue.invokeAndWait(TestArabicHebrew::createUI); + try { + checkStrings(); + } finally { + if (frame != null && args.length == 0) { + EventQueue.invokeAndWait(frame::dispose); + } + } + } + + static void checkString(String script, String str) { + int index = font.canDisplayUpTo(str); + if (index != -1) { + throw new RuntimeException("Cannot display char " + index + " for " + script); + } + } + + static void checkStrings() { + checkString("Arabic", arabic); + checkString("Hebrew", hebrew); + checkString("Latin-1 Supplement", latin1sup); + } + + // Table of arabic unicode characters - minimal support level + // Includes arabic chars from basic block up to 0652 and + // corresponding shaped characters from the arabic + // extended-B block from fe80 to fefc (does include lam-alef + // ligatures). + // Does not include arabic-indic digits nor "arabic extended" + // range. + + static final String arabic = + "\u060c\u061b\u061f\u0621\u0622\u0623\u0624\u0625\u0626\u0627" + + "\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631" + + "\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0640" + + "\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a" + + "\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\ufe80\ufe81" + + "\ufe82\ufe83\ufe84\ufe85\ufe86\ufe87\ufe88\ufe89\ufe8a\ufe8b" + + "\ufe8c\ufe8d\ufe8e\ufe8f\ufe90\ufe91\ufe92\ufe93\ufe94\ufe95" + + "\ufe96\ufe97\ufe98\ufe99\ufe9a\ufe9b\ufe9c\ufe9d\ufe9e\ufe9f" + + "\ufea0\ufea1\ufea2\ufea3\ufea4\ufea5\ufea6\ufea7\ufea8\ufea9" + + "\ufeaa\ufeab\ufeac\ufead\ufeae\ufeaf\ufeb0\ufeb1\ufeb2\ufeb3" + + "\ufeb4\ufeb5\ufeb6\ufeb7\ufeb8\ufeb9\ufeba\ufebb\ufebc\ufebd" + + "\ufebe\ufebf\ufec0\ufec1\ufec2\ufec3\ufec4\ufec5\ufec6\ufec7" + + "\ufec8\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1" + + "\ufed2\ufed3\ufed4\ufed5\ufed6\ufed7\ufed8\ufed9\ufeda\ufedb" + + "\ufedc\ufedd\ufede\ufedf\ufee0\ufee1\ufee2\ufee3\ufee4\ufee5" + + "\ufee6\ufee7\ufee8\ufee9\ufeea\ufeeb\ufeec\ufeed\ufeee\ufeef" + + "\ufef0\ufef1\ufef2\ufef3\ufef4\ufef5\ufef6\ufef7\ufef8\ufef9" + + "\ufefa\ufefb\ufefc"; + + // hebrew table includes all characters in hebrew block + + static final String hebrew = + "\u0591\u0592\u0593\u0594\u0595\u0596\u0597\u0598\u0599\u059a" + + "\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a3\u05a4\u05a5" + + "\u05a6\u05a7\u05a8\u05a9\u05aa\u05ab\u05ac\u05ad\u05ae\u05af" + + "\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9" + + "\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05c4" + + "\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9" + + "\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3" + + "\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2" + + "\u05f3\u05f4"; + + // latin 1 supplement table includes all non-control characters + // in this range. Included because of comment in code that claims + // some problems displaying this range with some SJIS fonts. + + static final String latin1sup = + "\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7" + + "\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af\u00b0\u00b1" + + "\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb" + + "\u00bc\u00bd\u00be\u00bf\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5" + + "\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf" + + "\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9" + + "\u00da\u00db\u00dc\u00dd\u00de\u00df\u00e0\u00e1\u00e2\u00e3" + + "\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed" + + "\u00ee\u00ef\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7" + + "\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff"; + + public TestArabicHebrew() { + setLayout(new GridLayout(3, 1)); + + FontRenderContext frc = new FontRenderContext(null, false, false); + add(new SubGlyphPanel("Arabic", arabic, font, frc)); + add(new SubGlyphPanel("Hebrew", hebrew, font, frc)); + add(new SubGlyphPanel("Latin-1 Supplement", latin1sup, font, frc)); + } + + static class SubGlyphPanel extends Panel { + String title; + Dimension extent; + GlyphVector[] vectors; + + static final int kGlyphsPerLine = 20; + + SubGlyphPanel(String title, String chars, Font font, FontRenderContext frc) { + + this.title = title; + setBackground(Color.white); + + double width = 0; + double height = 0; + + int max = chars.length(); + vectors = new GlyphVector[(max + kGlyphsPerLine - 1) / kGlyphsPerLine]; + for (int i = 0; i < vectors.length; i++) { + int start = i * 20; + int limit = Math.min(max, (i + 1) * kGlyphsPerLine); + String substr = ""; + for (int j = start; j < limit; ++j) { + substr = substr.concat(chars.charAt(j) + " "); + } + GlyphVector gv = font.createGlyphVector(frc, substr); + vectors[i] = gv; + Rectangle2D bounds = gv.getLogicalBounds(); + + width = Math.max(width, bounds.getWidth()); + height += bounds.getHeight(); + } + + extent = new Dimension((int)(width + 1), (int)(height + 1 + 30)); // room for title + + setSize(getPreferredSize()); + } + + public Dimension getPreferredSize() { + return new Dimension(extent); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D)g; + + g.drawString(title, 10, 20); + + float x = 10; + float y = 30; + for (int i = 0; i < vectors.length; ++i) { + GlyphVector gv = vectors[i]; + Rectangle2D bounds = gv.getLogicalBounds(); + g2d.drawGlyphVector(gv, x, (float)(y - bounds.getY())); + y += bounds.getHeight(); + } + } + } +} diff --git a/test/jdk/sun/awt/font/TestDevTransform.java b/test/jdk/sun/awt/font/TestDevTransform.java new file mode 100644 index 0000000000000..2783401c0f2db --- /dev/null +++ b/test/jdk/sun/awt/font/TestDevTransform.java @@ -0,0 +1,174 @@ +/* + * 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 4269775 8341535 + * @summary Check that different text rendering APIs agree + */ + +/** + * Draw into an image rendering the same text string nine different + * ways: as a TextLayout, a simple String, and a GlyphVector, each + * with three different x scale factors. The expectation is that each + * set of three strings would appear the same although offset in y to + * avoid overlap. The bug was that the y positions of the individual characters + * of the TextLayout and GlyphVector were wrong, so the strings appeared + * to be rendered at different angles. + */ + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import javax.imageio.ImageIO; +import java.io.File; +import java.util.HashMap; + +public class TestDevTransform { + + static HashMap hints = new HashMap<>(); + + static { + hints.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + hints.put(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + static String test = "This is only a test"; + static double angle = Math.PI / 6.0; // Rotate 30 degrees + static final int W = 400, H = 400; + + static void draw(Graphics2D g2d, TextLayout layout, + float x, float y, float scalex) { + AffineTransform saveTransform = g2d.getTransform(); + g2d.translate(x, y); + g2d.rotate(angle); + g2d.scale(scalex, 1f); + layout.draw(g2d, 0f, 0f); + g2d.setTransform(saveTransform); + } + + static void draw(Graphics2D g2d, String string, + float x, float y, float scalex) { + AffineTransform saveTransform = g2d.getTransform(); + g2d.translate(x, y); + g2d.rotate(angle); + g2d.scale(scalex, 1f); + g2d.drawString(string, 0f, 0f); + g2d.setTransform(saveTransform); + } + + static void draw(Graphics2D g2d, GlyphVector gv, + float x, float y, float scalex) { + AffineTransform saveTransform = g2d.getTransform(); + g2d.translate(x, y); + g2d.rotate(angle); + g2d.scale(scalex, 1f); + g2d.drawGlyphVector(gv, 0f, 0f); + g2d.setTransform(saveTransform); + } + + static void init(Graphics2D g2d) { + g2d.setColor(Color.white); + g2d.fillRect(0, 0, W, H); + g2d.setColor(Color.black); + g2d.scale(1.481f, 1.481); // Convert to 108 dpi + g2d.addRenderingHints(hints); + Font font = new Font(Font.DIALOG, Font.PLAIN, 12); + g2d.setFont(font); + } + + static void compare(BufferedImage bi1, String name1, BufferedImage bi2, String name2) throws Exception { + int nonWhite1 = 0; + int nonWhite2 = 0; + int differences = 0; + int whitePixel = Color.white.getRGB(); + for (int x = 0; x < bi1.getWidth(); x++) { + for (int y = 0; y < bi1.getHeight(); y++) { + int pix1 = bi1.getRGB(x, y); + int pix2 = bi2.getRGB(x, y); + if (pix1 != whitePixel) { nonWhite1++; } + if (pix2 != whitePixel) { nonWhite2++; } + if (bi1.getRGB(x, y) != bi2.getRGB(x, y)) { + differences++; + } + } + } + int nonWhite = (nonWhite1 < nonWhite2) ? nonWhite1 : nonWhite2; + if (differences > 0 && ((nonWhite / differences) < 20)) { + ImageIO.write(bi1, "png", new File(name1 + ".png")); + ImageIO.write(bi2, "png", new File(name2 + ".png")); + System.err.println("nonWhite image 1 = " + nonWhite1); + System.err.println("nonWhite image 2 = " + nonWhite2); + System.err.println("Number of non-white differing pixels=" + differences); + throw new RuntimeException("Different rendering: " + differences + " pixels differ."); + } + } + + public static void main(String args[]) throws Exception { + + BufferedImage tl_Image = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB); + { + Graphics2D tl_g2d = tl_Image.createGraphics(); + init(tl_g2d); + FontRenderContext frc = tl_g2d.getFontRenderContext(); + // Specify font from graphics to be sure it is the same as the other cases. + TextLayout tl = new TextLayout(test, tl_g2d.getFont(), frc); + draw(tl_g2d, tl, 10f, 12f, 3.0f); + draw(tl_g2d, tl, 10f, 24f, 1.0f); + draw(tl_g2d, tl, 10f, 36f, 0.33f); + } + + BufferedImage st_Image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB); + { + Graphics2D st_g2d = st_Image.createGraphics(); + init(st_g2d); + draw(st_g2d, test, 10f, 12f, 3.0f); + draw(st_g2d, test, 10f, 24f, 1.0f); + draw(st_g2d, test, 10f, 36f, .33f); + } + + BufferedImage gv_Image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB); + { + Graphics2D gv_g2d = gv_Image.createGraphics(); + init(gv_g2d); + FontRenderContext frc = gv_g2d.getFontRenderContext(); + GlyphVector gv = gv_g2d.getFont().createGlyphVector(frc, test); + draw(gv_g2d, gv, 10f, 12f, 3.0f); + draw(gv_g2d, gv, 10f, 24f, 1.0f); + draw(gv_g2d, gv, 10f, 36f, .33f); + } + + compare(tl_Image, "textlayout", st_Image, "string"); + compare(gv_Image, "glyphvector", st_Image, "string"); + } +} diff --git a/test/jdk/sun/awt/image/BytePackedRaster/DitherTest.java b/test/jdk/sun/awt/image/BytePackedRaster/DitherTest.java new file mode 100644 index 0000000000000..29b42b49fe778 --- /dev/null +++ b/test/jdk/sun/awt/image/BytePackedRaster/DitherTest.java @@ -0,0 +1,147 @@ +/* + * 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 4184283 + * @summary Checks rendering of dithered byte packed image does not crash. + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.IndexColorModel; +import java.awt.image.MemoryImageSource; +import java.awt.image.WritableRaster; + +public class DitherTest extends Component { + + final static int NOOP = 0; + final static int RED = 1; + final static int GREEN = 2; + final static int BLUE = 3; + final static int ALPHA = 4; + final static int SATURATION = 5; + + final static byte red[] = {(byte)0, (byte)132, (byte)0, (byte)132, (byte)0, (byte)132, + (byte)0, (byte)198, (byte)198, (byte)165, (byte)255, (byte)165, (byte)132, + (byte)255, (byte)0, (byte)255}; + + final static byte green[] = {(byte)0, (byte)0, (byte)130, (byte)130, (byte)0, + (byte)0, (byte)130, (byte)195, (byte)223, (byte)203, (byte)251, (byte)162, + (byte)132, (byte)0, (byte)255, (byte)255}; + + final static byte blue[] = {(byte)0, (byte)0, (byte)0, (byte)0, (byte)132, (byte)132, + (byte)132, (byte)198, (byte)198, (byte)247, (byte)247, (byte)165, (byte)132, + (byte)0, (byte)0, (byte)0}; + + static IndexColorModel cm16 = new IndexColorModel( 4, 16, red, green, blue); + + + public static void main(String args[]) { + + int imageWidth = 256; + int imageHeight = 256; + WritableRaster raster = cm16.createCompatibleWritableRaster(imageWidth, imageHeight); + BufferedImage intermediateImage = new BufferedImage(cm16, raster, false, null); + Image calculatedImage = calculateImage(); + + Graphics2D ig = intermediateImage.createGraphics(); + // Clear background and fill a red rectangle just to prove that we can draw on intermediateImage + ig.setColor(Color.white); + ig.fillRect(0,0,imageWidth,imageHeight); + ig.drawImage(calculatedImage, 0, 0, imageWidth, imageHeight, null); + ig.setColor(Color.red); + ig.fillRect(0,0,5,5); + + BufferedImage destImage = new BufferedImage(imageWidth, imageWidth, BufferedImage.TYPE_INT_RGB); + Graphics2D dg = destImage.createGraphics(); + dg.drawImage(intermediateImage, 0, 0, imageWidth, imageHeight, null); + } + + private static void applymethod(int c[], int method, int step, int total, int vals[]) { + if (method == NOOP) + return; + int val = ((total < 2) + ? vals[0] + : vals[0] + ((vals[1] - vals[0]) * step / (total - 1))); + switch (method) { + case RED: + c[0] = val; + break; + case GREEN: + c[1] = val; + break; + case BLUE: + c[2] = val; + break; + case ALPHA: + c[3] = val; + break; + case SATURATION: + int max = Math.max(Math.max(c[0], c[1]), c[2]); + int min = max * (255 - val) / 255; + if (c[0] == 0) c[0] = min; + if (c[1] == 0) c[1] = min; + if (c[2] == 0) c[2] = min; + break; + } + } + + private static Image calculateImage() { + + int xvals[] = { 0, 255 }; + int yvals[] = { 0, 255 }; + int xmethod = RED; + int ymethod = BLUE; + int width = 256; + int height = 256; + int pixels[] = new int[width * height]; + int c[] = new int[4]; + int index = 0; + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + c[0] = c[1] = c[2] = 0; + c[3] = 255; + if (xmethod < ymethod) { + applymethod(c, xmethod, i, width, xvals); + applymethod(c, ymethod, j, height, yvals); + } else { + applymethod(c, ymethod, j, height, yvals); + applymethod(c, xmethod, i, width, xvals); + } + pixels[index++] = ((c[3] << 24) | + (c[0] << 16) | + (c[1] << 8) | + (c[2] << 0)); + } + } + + DitherTest dt = new DitherTest(); + return dt.createImage(new MemoryImageSource(width, height, ColorModel.getRGBdefault(), pixels, 0, width)); + } +} + diff --git a/test/jdk/sun/awt/image/BytePackedRaster/MultiOp.java b/test/jdk/sun/awt/image/BytePackedRaster/MultiOp.java new file mode 100644 index 0000000000000..2c396792548f2 --- /dev/null +++ b/test/jdk/sun/awt/image/BytePackedRaster/MultiOp.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 4213160 + * @summary Should generate a black image + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.IndexColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.awt.geom.AffineTransform; + +public class MultiOp { + + public static void main(String[] argv) { + + int width = 256; + int height = 256; + + int pixelBits = 2; // 1, 2, 4, or 8 + // 1 and 8 make the code throw ImagingOpException, 2 and 4 + // make the code SEGV on Sol. + + byte[] lut1Arr = new byte[] {0, (byte)255 }; + byte[] lut2Arr = new byte[] {0, (byte)85, (byte)170, (byte)255}; + byte[] lut4Arr = new byte[] {0, (byte)17, (byte)34, (byte)51, + (byte)68, (byte)85,(byte) 102, (byte)119, + (byte)136, (byte)153, (byte)170, (byte)187, + (byte)204, (byte)221, (byte)238, (byte)255}; + byte[] lut8Arr = new byte[256]; + for (int i = 0; i < 256; i++) { + lut8Arr[i] = (byte)i; + } + + // Create the binary image + int bytesPerRow = width * pixelBits / 8; + byte[] imageData = new byte[height * bytesPerRow]; + ColorModel cm = null; + + switch (pixelBits) { + case 1: + cm = new IndexColorModel(pixelBits, lut1Arr.length, + lut1Arr, lut1Arr, lut1Arr); + break; + case 2: + cm = new IndexColorModel(pixelBits, lut2Arr.length, + lut2Arr, lut2Arr, lut2Arr); + break; + case 4: + cm = new IndexColorModel(pixelBits, lut4Arr.length, + lut4Arr, lut4Arr, lut4Arr); + break; + case 8: + cm = new IndexColorModel(pixelBits, lut8Arr.length, + lut8Arr, lut8Arr, lut8Arr); + break; + default: + {new Exception("Invalid # of bit per pixel").printStackTrace();} + } + + DataBuffer db = new DataBufferByte(imageData, imageData.length); + WritableRaster r = Raster.createPackedRaster(db, width, height, + pixelBits, null); + BufferedImage srcImage = new BufferedImage(cm, r, false, null); + + BufferedImage destImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = destImage.createGraphics(); + AffineTransform af = AffineTransform.getScaleInstance(.5, .5); + // This draw image is the problem + g.drawImage(srcImage, af, null); + int blackPixel = Color.black.getRGB(); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + if (destImage.getRGB(x, y) != blackPixel) { + throw new RuntimeException("Not black"); + } + } + } + } +} diff --git a/test/jdk/sun/awt/image/ImageRepresentation/ByteBinaryBitmask.java b/test/jdk/sun/awt/image/ImageRepresentation/ByteBinaryBitmask.java new file mode 100644 index 0000000000000..26edca7f09b72 --- /dev/null +++ b/test/jdk/sun/awt/image/ImageRepresentation/ByteBinaryBitmask.java @@ -0,0 +1,88 @@ +/* + * 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 + * 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 4673490 + * @summary This test verifies that Toolkit images with a 1-bit + * IndexColorModel (known as ByteBinary) and a transparent index are rendered properly. + */ + +import java.awt.Color; +import java.awt.Graphics2D; + +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; + +public class ByteBinaryBitmask { + + public static void main(String argv[]) throws Exception { + + /* Create the image */ + int w = 16, h = 16; + byte[] bw = { (byte)255, (byte)0, }; + IndexColorModel icm = new IndexColorModel(1, 2, bw, bw, bw, 0); + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY, icm); + Graphics2D g2d = img.createGraphics(); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, w, h); + g2d.setColor(Color.black); + int xoff = 5; + g2d.fillRect(xoff, 5, 1, 10); // 1 pixel wide + + int dw = 200, dh = 50; + BufferedImage dest = new BufferedImage(dw, dh, BufferedImage.TYPE_INT_RGB); + Graphics2D g = dest.createGraphics(); + g.setColor(Color.green); + g.fillRect(0, 0, dw, dh); + int x1 = 10; + int x2 = 50; + int x3 = 90; + int x4 = 130; + g.drawImage(img, x1, 10, null); + g.drawImage(img, x2, 10, null); + g.drawImage(img, x3, 10, null); + g.drawImage(img, x4, 10, null); + + int blackPix = Color.black.getRGB(); + for (int y = 0; y < dh; y++) { + boolean isBlack = false; + for (int x = 0; x < dw; x++) { + int rgb = dest.getRGB(x, y); + if (rgb == blackPix) { + /* Src image has a one pixel wide vertical rect at off "xoff" and + * this is drawn at x1/x2/x3/x4) so the sum of those are the x locations + * to expect black. + */ + if (x != (x1 + xoff) && x != (x2 + xoff) && x != (x3 + xoff) && x!= (x4 + xoff)) { + throw new RuntimeException("wrong x location: " +x); + } + if (isBlack) { + throw new RuntimeException("black after black"); + } + } + isBlack = rgb == blackPix; + } + } + } +} diff --git a/test/jdk/sun/awt/image/ImageRepresentation/CustomSourceCM.java b/test/jdk/sun/awt/image/ImageRepresentation/CustomSourceCM.java new file mode 100644 index 0000000000000..c56b4ef88c807 --- /dev/null +++ b/test/jdk/sun/awt/image/ImageRepresentation/CustomSourceCM.java @@ -0,0 +1,142 @@ +/* + * 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 4192756 + * @summary Tests that using a non-default colormodel generates correct images under 16/24 bit mode + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DirectColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.MemoryImageSource; +import java.util.Arrays; + +/* + * NOTE: This bug only appears under specific conditions. If the background of + * the surface is red, then you are not running under the conditions necessary + * to test for the regression so the results of this test will be inconclusive. + * + * The test should be run under any of the following screen depths/surfaces: + * + * 15-bit, otherwise known as 555 RGB or 32768 (thousands) colors + * 16-bit, otherwise known as 565 RGB or 65536 (thousands) colors + * 24-bit, otherwise known as 16777216 (millions) colors + * + * The test draws 2 rectangles. Both rectangles should be half black (left) + * and half blue (right). If the top rectangle is all black, the test fails. + * If the background is red, the results are inconclusive (see above). +*/ + +public class CustomSourceCM extends Component { + + public static int IMG_W = 80; + public static int IMG_H = 30; + + static void test(int imageType) { + + int w = IMG_W + 20; + int h = IMG_H * 2 + 40; + BufferedImage bi = new BufferedImage(w, h, imageType); + + DirectColorModel dcm; + + /* the next dozen lines or so are intended to help + * ascertain if the destination surface is of the type + * that exhibited the original bug, making the background + * white in those cases. It is not strictly necessary. + * It is only for a manual tester to be able to tell by looking. + * The real test is the check for black and blue later on. + */ + Graphics2D g = bi.createGraphics(); + g.setColor(Color.red); + g.fillRect(0, 0, w, h); + + ColorModel cm = bi.getColorModel(); + if (cm instanceof ComponentColorModel) { + g.setColor(Color.white); + g.fillRect(0, 0, w, h); + } else if (cm instanceof DirectColorModel) { + dcm = (DirectColorModel) cm; + if (dcm.getPixelSize() < 24) { + g.setColor(Color.white); + g.fillRect(0, 0, w, h); + } + } + + // Construct a ColorModel and data for a 16-bit 565 image... + dcm = new DirectColorModel(16, 0x1f, 0x7e0, 0xf800); + + // Create an image which is black on the left, blue on the right. + int[] pixels = new int[IMG_W * IMG_H]; + int blue = dcm.getBlueMask(); + int off = 0; + for (int y = 0; y < IMG_H; y++) { + Arrays.fill(pixels, off, off+IMG_W/2, 0); + Arrays.fill(pixels, off+IMG_W/2, off+IMG_W, blue); + off += IMG_W; + } + MemoryImageSource mis = new MemoryImageSource(IMG_W, IMG_H, dcm, + pixels, 0, IMG_W); + CustomSourceCM comp = new CustomSourceCM(); + Image img = comp.createImage(mis); + + // Draw the image on to the surface. + g.drawImage(img, 10, 10, null); + + // Create a similar effect with 2 fillrects, below the image. + g.setColor(Color.black); + g.fillRect(10, 60, IMG_W/2, IMG_H); + g.setColor(Color.blue); + g.fillRect(10+IMG_W/2, 60, IMG_W/2, IMG_H); + + // Now sample points in the image to confirm they are the expected color. + int bluePix = Color.blue.getRGB(); + int blackPix = Color.black.getRGB(); + int black_topLeft = bi.getRGB(10+IMG_W/4, 10+IMG_H/2); + int blue_topRight = bi.getRGB(10+IMG_W*3/4, 10+IMG_H/2); + int black_bottomLeft = bi.getRGB(10+IMG_W/4, 60+IMG_H/2); + int blue_bottomRight = bi.getRGB(10+IMG_W*3/4, 60+IMG_H/2); + if ((black_topLeft != blackPix) || (black_bottomLeft != blackPix) || + (blue_topRight != bluePix) || (blue_bottomRight != bluePix)) { + + String fileName = "failed " + imageType + ".png"; + try { + javax.imageio.ImageIO.write(bi, "png", new java.io.File(fileName)); + } catch (Exception e) { }; + throw new RuntimeException("unexpected colors"); + } + } + + public static void main(String argv[]) { + test(BufferedImage.TYPE_USHORT_555_RGB); + test(BufferedImage.TYPE_USHORT_565_RGB); + test(BufferedImage.TYPE_3BYTE_BGR); + } +} diff --git a/test/jdk/sun/awt/windows/TestPen.java b/test/jdk/sun/awt/windows/TestPen.java new file mode 100644 index 0000000000000..25b7304bdc862 --- /dev/null +++ b/test/jdk/sun/awt/windows/TestPen.java @@ -0,0 +1,102 @@ +/* + * 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 4277201 + * @summary verifies that invoking a fill on a brand new Graphics object + * does not stroke the shape in addition to filling it + * @key headful + */ + +/* + * This test case tests for a problem with initializing GDI graphics + * contexts (HDCs) where a pen is left installed in the graphics object + * even though the AWT believes that there is no Pen installed. The + * result is that when you try to fill a shape, GDI will both fill and + * stroke it. +*/ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; + +public class TestPen extends Panel { + + static volatile TestPen pen; + static volatile Frame frame; + + public TestPen() { + setForeground(Color.black); + setBackground(Color.white); + } + + public Dimension getPreferredSize() { + return new Dimension(200, 200); + } + + public void paint(Graphics g) { + g.setColor(Color.green); + g.fillOval(50, 50, 100, 100); + } + + static void createUI() { + frame = new Frame(); + pen = new TestPen(); + frame.add(pen); + frame.pack(); + frame.setVisible(true); + } + + public static void main(String argv[]) throws Exception { + try { + EventQueue.invokeAndWait(TestPen::createUI); + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(2000); + Point p = pen.getLocationOnScreen(); + Dimension d = pen.getSize(); + Rectangle r = new Rectangle(p.x + 1, p.y + 1, d.width - 2, d.height - 2); + BufferedImage bi = robot.createScreenCapture(r); + int blackPixel = Color.black.getRGB(); + for (int y = 0; y < bi.getHeight(); y++ ) { + for (int x = 0; x < bi.getWidth(); x++ ) { + if (bi.getRGB(x, y) == blackPixel) { + throw new RuntimeException("Black pixel !"); + } + } + } + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + } +} diff --git a/test/jdk/sun/java2d/GdiRendering/GdiBlitOffscreenTest.java b/test/jdk/sun/java2d/GdiRendering/GdiBlitOffscreenTest.java new file mode 100644 index 0000000000000..99cb2bcd10661 --- /dev/null +++ b/test/jdk/sun/java2d/GdiRendering/GdiBlitOffscreenTest.java @@ -0,0 +1,121 @@ +/* + * 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 + * 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 4725045 + * @key headful + * @summary verifies that there are no artifacts due to using + * GDI for copies to the back buffer (GDI should only be used + * for copies to the screen) + * @run main GdiBlitOffscreenTest +*/ + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class GdiBlitOffscreenTest { + + static volatile JFrame f; + static final int imageW = 100, imageH = 100, FW = 500, FH = 500; + static volatile BufferedImage greenImage; + + public static void main(String[] args) throws Exception { + + // First, create an image. + greenImage = new BufferedImage(imageW, imageH, + BufferedImage.TYPE_INT_RGB); + Graphics redG = greenImage.getGraphics(); + redG.setColor(Color.green); + redG.fillRect(0, 0, imageW, imageH); + redG.setColor(Color.white); + redG.drawString("Passed!", 30, 80); + + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(GdiBlitOffscreenTest::createUI); + robot.delay(1000); + robot.waitForIdle(); + Point p = f.getLocationOnScreen(); + Color c = robot.getPixelColor(p.x+FW/2, p.y+FH/2); + if (!c.equals(Color.green)) { + throw new RuntimeException("Color is " + c); + } + } finally { + if (f != null) { + SwingUtilities.invokeAndWait(f::dispose); + } + } + } + + private static void createUI() { + f = new JFrame("GdiBlitOffscreenTest"); + f.setSize(FW, FH); + f.setVisible(true); + + // copy the image to the window. + Graphics g = f.getGraphics(); + g.drawImage(greenImage, 0, 0, null); + + // Now, get on with the rest of the test + JComponent app = new GdiBlitOffscreenTestComponent(imageW, imageH, greenImage); + app.setSize(500, 500); + f.getContentPane().add(app); + f.validate(); + f.repaint(); + } +} + +class GdiBlitOffscreenTestComponent extends JComponent { + + int imageW, imageH; + Image theImage; + + public GdiBlitOffscreenTestComponent(int imageW, int imageH, + Image theImage) + { + this.theImage = theImage; + this.imageW = imageW; + this.imageH = imageH; + } + + public void paintComponent(Graphics g) { + int imageX = (getWidth() - imageW) / 2; + int imageY = (getHeight() - imageH) / 2; + g.setColor(Color.blue); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.red); + g.fillRect(imageX, imageY, imageW, imageH); + g.setColor(Color.white); + g.drawString("Failed!", imageX + 30, imageY + 80); + g.drawImage(theImage, imageX, imageY, null); + } + +} diff --git a/test/jdk/sun/java2d/GdiRendering/GdiLockTest.java b/test/jdk/sun/java2d/GdiRendering/GdiLockTest.java new file mode 100644 index 0000000000000..ea2111f9d2e05 --- /dev/null +++ b/test/jdk/sun/java2d/GdiRendering/GdiLockTest.java @@ -0,0 +1,95 @@ +/* + * 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 + * 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 4693644 + * @summary verifies that there are no artifacts due to copying with GDI + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual GdiLockTest +*/ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Graphics; + +public class GdiLockTest { + + static final String INSTRUCTIONS = """ + A window will open up next to these instructions. + The text you see in that window should blink on and off. + If it never disappears, then the test has failed. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("GdiLockTest") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(5) + .columns(45) + .testUI(GdiLockTest::createUI) + .build() + .awaitAndCheck(); + } + + private static Frame createUI() { + Frame f = new Frame("GdiLockTest"); + f.setSize(300, 300); + GdiLockTestComponent test = new GdiLockTestComponent(); + Thread t = new Thread(test); + f.add(test); + t.start(); + return f; + } +} + +class GdiLockTestComponent extends Component implements Runnable { + + boolean textVisible = true; + + public void paint(Graphics g) { + g.setColor(Color.white); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.black); + if (!textVisible) { + g.setClip(200, 200, 300, 300); + } + g.drawString("This text should be blinking", 10, 30); + if (!textVisible) { + g.setClip(0, 0, getWidth(), getHeight()); + } + } + + public void run() { + while (true) { + repaint(); + textVisible = !textVisible; + try { + Thread.sleep(500); + } catch (Exception e) {} + } + } +} diff --git a/test/jdk/sun/java2d/SunGraphics2D/DrawRoundRect0Bug.java b/test/jdk/sun/java2d/SunGraphics2D/DrawRoundRect0Bug.java new file mode 100644 index 0000000000000..09b6b3e0b3fe3 --- /dev/null +++ b/test/jdk/sun/java2d/SunGraphics2D/DrawRoundRect0Bug.java @@ -0,0 +1,69 @@ +/* + * 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 4515761 + * @summary verify that drawRoundRect produces correct output for 0 w/h + */ + +import java.awt.Color; +import static java.awt.Color.*; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +public class DrawRoundRect0Bug { + + public static void main(String argv[]) { + BufferedImage img = new BufferedImage(250, 250, BufferedImage.TYPE_INT_RGB); + Graphics2D g = img.createGraphics(); + + g.setColor(white); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + + g.setColor(green); + g.drawLine(150, 90, 150, 110); + if (img.getRGB(150, 100) != green.getRGB()) { + throw new RuntimeException("Vertical line not green"); + } + + g.setColor(blue); + g.drawRoundRect(160, 90, 0, 20, 4, 4); + if (img.getRGB(160, 100) != blue.getRGB()) { + throw new RuntimeException("Vertical (ie zero width) round rect not blue"); + } + + g.setColor(green); + g.drawLine(150, 140, 170, 140); + if (img.getRGB(160, 140) != green.getRGB()) { + throw new RuntimeException("Horizontal line not green"); + } + + g.setColor(blue); + g.drawRoundRect(150, 150, 20, 0, 4, 4); + if (img.getRGB(160, 150) != blue.getRGB()) { + throw new RuntimeException("Horizontal (ie zero height) round rect not blue"); + } + } + +} diff --git a/test/jdk/sun/java2d/SunGraphics2D/RevalidateBug.java b/test/jdk/sun/java2d/SunGraphics2D/RevalidateBug.java new file mode 100644 index 0000000000000..068dd78284b4c --- /dev/null +++ b/test/jdk/sun/java2d/SunGraphics2D/RevalidateBug.java @@ -0,0 +1,104 @@ +/* + * 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 + * 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 4652373 + * @summary verify that SunGraphics2D survives surface revalidation + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual RevalidateBug + * @requires (os.family == "windows") + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; +import javax.swing.JComponent; +import javax.swing.JFrame; + +public class RevalidateBug { + + private static final String INSTRUCTIONS = """ + This bug only reproduces on Windows systems with a task manager that can lock the computer. + + This test draws a grayscale gradient in a window. + + After the gradient becomes visible above, use ctrl-alt-del to bring up + the task manager and lock the computer. + Then unlock the computer and the gradient should be repainted to pass. + + If the gradient does not appear after unlocking (or if the test gets + an error on its own after unlocking the computer) then it fails. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("RevalidateBug") + .instructions(INSTRUCTIONS) + .testTimeOut(5) + .rows(12) + .columns(50) + .testUI(RevalidateBug::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + + JComponent comp = new JComponent() { + + protected void paintComponent(Graphics g) { + super.paintComponent(g); + System.out.println("paintComponent"); + Graphics2D g2d = (Graphics2D) g; + + Insets insets = getInsets(); + Rectangle rect = + new Rectangle(insets.left, insets.top, + getWidth() - insets.right - insets.left, + getHeight() - insets.top - insets.bottom); + g2d.setPaint(new GradientPaint(rect.x, rect.y, Color.white, + rect.x + rect.width, rect.y, Color.black)); + + System.out.println(rect + " w:" + getWidth() + " h:"+getHeight()); + + g2d.fillRect(0, 0, getWidth(), getHeight()); + } + + public Dimension getPreferredSize() { + return new Dimension(500, 500); + } + }; + + JFrame f = new JFrame("RevalidateTest"); + f.add(comp); + f.pack(); + return f; + } + +} diff --git a/test/jdk/sun/java2d/SunGraphics2D/ScaledPolyTest.java b/test/jdk/sun/java2d/SunGraphics2D/ScaledPolyTest.java new file mode 100644 index 0000000000000..65bb912785ba9 --- /dev/null +++ b/test/jdk/sun/java2d/SunGraphics2D/ScaledPolyTest.java @@ -0,0 +1,95 @@ +/* + * 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 4516037 + * @summary verify that scaled Polygons honor the transform + */ + +import java.awt.Color; +import static java.awt.Color.*; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.image.BufferedImage; + +public class ScaledPolyTest { + + public static void main(String[] args) { + + Polygon poly = new Polygon(); + poly.addPoint(20, 10); + poly.addPoint(30, 30); + poly.addPoint(10, 30); + poly.addPoint(20, 10); + + int height = 300; + int width = 300; + BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bi.createGraphics(); + g2d.setColor(Color.white); + g2d.fillRect(0, 0, bi.getWidth(), bi.getHeight()); + + g2d.translate(10, 10); + g2d.scale(2, 2); + g2d.setColor(Color.yellow); + g2d.fill(poly); + g2d.setColor(Color.blue); + g2d.draw(poly); + + /* + * Examine each row of the image. + * If the stroked polygon is correctly aligned on the filled polygon, + * if there is anything except white on the line, + * the transition will always be white+->blue+->yellow*->blue*->white+ + */ + int bluePix = blue.getRGB(); + int yellowPix = yellow.getRGB(); + int whitePix = white.getRGB(); + for (int y = 0; y < height; y++ ) { + int x = 0; + int pix = whitePix; + + while (pix == whitePix && x < width) pix = bi.getRGB(x++, y); + if (pix == whitePix && x == width) continue; // all white row. + + if (pix != bluePix) throw new RuntimeException("Expected blue"); + + while (pix == bluePix) pix = bi.getRGB(x++, y); + + if (pix == yellowPix) { + while (pix == yellowPix) pix = bi.getRGB(x++, y); + if (pix != bluePix) throw new RuntimeException("Expected blue"); + while (pix == bluePix) pix = bi.getRGB(x++, y); + if (pix != whitePix) throw new RuntimeException("Expected white"); + } + + while (pix == whitePix && x < width) pix = bi.getRGB(x++, y); + if (pix == whitePix && x == width) { + continue; + } else { + throw new RuntimeException("Expected white to finish the row"); + } + } + } +} diff --git a/test/jdk/sun/java2d/loops/ARGBBgToRGB.java b/test/jdk/sun/java2d/loops/ARGBBgToRGB.java new file mode 100644 index 0000000000000..55764af2c1354 --- /dev/null +++ b/test/jdk/sun/java2d/loops/ARGBBgToRGB.java @@ -0,0 +1,57 @@ +/* + * 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 4238978 + * @summary This test verifies that the correct blitting loop is being used. + * The correct output should have a yellow border on the top and + * left sides of a red box. The incorrect output would have only + * a red box -- no yellow border." + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +public class ARGBBgToRGB { + + public static void main(String[] argv) { + BufferedImage bi = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB); + Graphics2D big = bi.createGraphics(); + big.setColor(Color.red); + big.fillRect(30, 30, 150, 150); + + BufferedImage bi2 = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + Graphics2D big2 = bi2.createGraphics(); + big2.drawImage(bi, 0, 0, Color.yellow, null); + + int expectYellowPix = bi2.getRGB(0, 0); + int expectRedPix = bi2.getRGB(50, 50); + if ((expectYellowPix != Color.yellow.getRGB()) || + (expectRedPix != Color.red.getRGB())) + { + throw new RuntimeException("Unexpected colors " + expectYellowPix + " " + expectRedPix); + } + } +} diff --git a/test/jdk/sun/java2d/loops/CopyNegative.java b/test/jdk/sun/java2d/loops/CopyNegative.java new file mode 100644 index 0000000000000..0b8296918ac63 --- /dev/null +++ b/test/jdk/sun/java2d/loops/CopyNegative.java @@ -0,0 +1,103 @@ +/* + * 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 4188744 + * @summary This test verifies that copyArea performs correctly for negative offset values. + * The correct output shows that the text area is moved to the left and down, + * leaving some garbage on the right and the top. + * The incorrect copy would show the text area garbled and no text is legible. + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual CopyNegative + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Panel; + +public class CopyNegative extends Panel { + + private static final String INSTRUCTIONS = """ + This test verifies that copyArea performs correctly for negative offset values. + The test draws text in an image, then copies the contents repeatedly. + The correct output shows that the text is moved to the left and down, + leaving some garbage on the top / right and some legible text at the bottom left. + The incorrect copy would show the whole text area garbled and no text is legible. + """; + + public static void main(String[] argv) throws Exception { + PassFailJFrame.builder() + .title("CopyNegativeTest") + .instructions(INSTRUCTIONS) + .testUI(CopyNegative::createUI) + .testTimeOut(5) + .rows(10) + .columns(50) + .build() + .awaitAndCheck(); + } + + Image img; + + static final int W = 200, H = 200; + + static Frame createUI() { + Frame f = new Frame("CopyNegative"); + f.add(new CopyNegative()); + f.pack(); + return f; + } + + public Dimension getPreferredSize() { + return new Dimension(W, H); + } + + private void doCopy() { + Graphics g = img.getGraphics(); + g.setColor(Color.white); + g.fillRect(0, 0, W, H); + g.setColor(Color.black); + String text = "Some Text To Display, it is long enough to fill the entire display line."; + StringBuffer sb = new StringBuffer(text); + + for (int i = 1; i < 50; i++) { + g.drawString(sb.toString(), 5,20 * i - 10); + sb.insert(0, Integer.toString(i)); + } + for (int i = 0 ; i < 20 ; i++ ) { + g.copyArea(0, 0, W, H, -3, 3); + } + } + + public void paint(Graphics g) { + img = createImage(W, H); + doCopy(); + g.drawImage(img, 0, 0, this); + } + +} diff --git a/test/jdk/sun/java2d/loops/DitheredSolidFill.java b/test/jdk/sun/java2d/loops/DitheredSolidFill.java new file mode 100644 index 0000000000000..509b0bbe3b234 --- /dev/null +++ b/test/jdk/sun/java2d/loops/DitheredSolidFill.java @@ -0,0 +1,73 @@ +/* + * 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 4181172 + * @summary Confirm that solid white fill is not dithered on an 8-bit indexed surface. + * The test draws two areas filled with white solid color. + * The upper left square is filled in aliasing mode and + * the lower right square is filled in anti-aliasing mode. + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; + +public class DitheredSolidFill { + + public static void main(String args[]) { + BufferedImage bi = new BufferedImage(120, 120, BufferedImage.TYPE_BYTE_INDEXED); + Graphics2D g2D = bi.createGraphics(); + + g2D.setColor(Color.black); + g2D.fillRect(0, 0, 100, 100); + + g2D.setColor(Color.white); + g2D.fillRect(5, 5, 40, 40); + checkPixels(bi, 5, 5, 40, 40); + + g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2D.fillRect(55, 55, 40, 40); + checkPixels(bi, 55, 55, 40, 40); + } + + static void checkPixels(BufferedImage bi, int x, int y, int w, int h) { + // pixel can be off white, but must be the same in all cases. + int expectedPix = bi.getRGB(x, y); + for (int x0 = x; x0 < x + w; x0++) { + for (int y0 = y; y0 < y + h; y0++) { + if (bi.getRGB(x0, y0) != expectedPix) { + try { + javax.imageio.ImageIO.write(bi, "png", new java.io.File("failed.png")); + } catch (Exception e) { + } + throw new RuntimeException("Not expected pix : " + + Integer.toHexString(bi.getRGB(x0, y0)) + + " at " + x0 + "," + y0); + } + } + } + } +} diff --git a/test/jdk/sun/java2d/loops/OffsetCalculationTest.java b/test/jdk/sun/java2d/loops/OffsetCalculationTest.java new file mode 100644 index 0000000000000..fe30107f22a9f --- /dev/null +++ b/test/jdk/sun/java2d/loops/OffsetCalculationTest.java @@ -0,0 +1,68 @@ +/* + * 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 4236576 + @summary tests that a BufferedImage in TYPE_3BYTE_BGR format is correctly + drawn when there is an offset between the Graphics clip bounds + and the clip box of the underlying device context. + @run main OffsetCalculationTest +*/ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; + +public class OffsetCalculationTest { + + public static void main(String[] args) { + BufferedImage srcImage = new BufferedImage(500, 500, BufferedImage.TYPE_3BYTE_BGR); + + DataBuffer buffer = srcImage.getRaster().getDataBuffer(); + for (int i = 2; i < buffer.getSize(); i+=3) { + // setting each pixel to blue via the data buffer elements. + buffer.setElem(i - 2, 0xff); + buffer.setElem(i - 1, 0); + buffer.setElem(i, 0); + } + + int w = 200, h = 200; + BufferedImage destImage = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = destImage.createGraphics(); + Rectangle r = new Rectangle(0, 0, w, h); + g.setClip(r.x - 1, r.y, r.width + 1, r.height); + g.drawImage(srcImage, 0, 0, null); + + int bluepix = Color.blue.getRGB(); + for (int y = 0; y < w; y++) { + for (int x = 0; x < h; x++) { + if (destImage.getRGB(x, y) != bluepix) { + throw new RuntimeException("Not Blue"); + } + } + } + } +} diff --git a/test/jdk/sun/java2d/loops/XORClearRect.java b/test/jdk/sun/java2d/loops/XORClearRect.java new file mode 100644 index 0000000000000..f5c30581cf1f3 --- /dev/null +++ b/test/jdk/sun/java2d/loops/XORClearRect.java @@ -0,0 +1,93 @@ +/* + * 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 4088173 + * @summary This interactive test verifies that the XOR mode is not affecting + * the clearRect() call. The correct output looks like: + * + * \ / + * \ / + * The backgound is blue. + * The lines outside the central rectangle are green. + * The central rectangle is also blue (the result of clearRect()) + * / \ + * / \ + * + * @key headful + * @run main XORClearRect + */ + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Graphics; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; + +public class XORClearRect extends Panel { + + public static void main(String args[]) throws Exception { + EventQueue.invokeAndWait(XORClearRect::createUI); + try { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(2000); + Point p = frame.getLocationOnScreen(); + int pix = robot.getPixelColor(p.x + 100, p.y + 100).getRGB(); + if (pix != Color.blue.getRGB()) { + throw new RuntimeException("Not blue"); + } + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } + } + } + + static volatile Frame frame; + + static void createUI() { + frame = new Frame("XORClearRect"); + frame.setBackground(Color.blue); + XORClearRect xor = new XORClearRect(); + frame.add(xor); + frame.setSize(200,200); + frame.setVisible(true); + } + + public XORClearRect() { + setBackground(Color.blue); + } + + public void paint(Graphics g) { + g.setColor(Color.green); + g.drawLine(0,0,200,200); + g.drawLine(0,200,200,0); + g.setXORMode(Color.blue); + g.clearRect(50,50,100,100); //expecting the rectangle to be filled + // with the background color (blue) + } +} diff --git a/test/jdk/sun/net/www/ParseUtil_6380332.java b/test/jdk/sun/net/www/ParseUtil_6380332.java index a517742f2c4bf..bd0331d52bd53 100644 --- a/test/jdk/sun/net/www/ParseUtil_6380332.java +++ b/test/jdk/sun/net/www/ParseUtil_6380332.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 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 @@ -22,12 +22,13 @@ */ /* @test - * @summary SunTea applet fails to load under Mustang + * @summary Ensure ParseUtil.toURI does not fail when port number is -1 * @bug 6380332 * @modules java.base/sun.net.www */ import sun.net.www.ParseUtil; + import java.net.URI; import java.net.URL; diff --git a/test/jdk/sun/net/www/protocol/http/B6296310.java b/test/jdk/sun/net/www/protocol/http/B6296310.java index dbb3ead18c556..867b4edaf3280 100644 --- a/test/jdk/sun/net/www/protocol/http/B6296310.java +++ b/test/jdk/sun/net/www/protocol/http/B6296310.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 @@ -27,7 +27,8 @@ * @library /test/lib * @run main/othervm B6296310 * @run main/othervm -Djava.net.preferIPv6Addresses=true B6296310 - * @summary REGRESSION: AppletClassLoader.getResourceAsStream() behaviour is wrong in some cases + * @summary Prevent NPE in HttpURLConnection.getInputStream0() when + * content length is 0 */ import java.io.IOException; diff --git a/test/jdk/sun/net/www/protocol/http/ResponseCacheStream.java b/test/jdk/sun/net/www/protocol/http/ResponseCacheStream.java index 841279fa76d5e..2fba6d8ab7548 100644 --- a/test/jdk/sun/net/www/protocol/http/ResponseCacheStream.java +++ b/test/jdk/sun/net/www/protocol/http/ResponseCacheStream.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 @@ -26,7 +26,7 @@ * @bug 6262486 * @library /test/lib * @run main/othervm -Dhttp.keepAlive=false ResponseCacheStream - * @summary COMPATIBILITY: jagex_com - Monkey Puzzle applet fails to load + * @summary Ensure HttpInputStream resets properly when cache is in use */ import java.io.ByteArrayOutputStream; diff --git a/test/jdk/sun/rmi/transport/tcp/disableMultiplexing/DisableMultiplexing.java b/test/jdk/sun/rmi/transport/tcp/disableMultiplexing/DisableMultiplexing.java index 4a8748ab25320..253f166cae4dd 100644 --- a/test/jdk/sun/rmi/transport/tcp/disableMultiplexing/DisableMultiplexing.java +++ b/test/jdk/sun/rmi/transport/tcp/disableMultiplexing/DisableMultiplexing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, 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 @@ -24,7 +24,7 @@ /* @test * @bug 4183204 * @summary The RMI runtime should fail to export a remote object on a TCP - * port for an applet or application that does not have permission to listen + * port for an application that does not have permission to listen * on that port, rather than engage in the deprecated "multiplexing protocol". * @author Peter Jones * diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index 83427f1a33aba..d64c5d7c52b0b 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 - = "BD:80:65:81:68:E5:6C:51:64:ED:B9:08:53:9F:BB:2F:D9:6C:5D:D4:06:D4:16:59:39:10:8E:F8:24:81:8B:78"; + = "21:68:E7:16:5B:94:23:D2:60:5C:BB:F2:AF:C1:66:5C:EC:36:BC:20:FF:5C:54:AF:91:D1:2C:38:AE:55:D3:27"; // 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/provider/NamedEdDSA.java b/test/jdk/sun/security/provider/NamedEdDSA.java new file mode 100644 index 0000000000000..4d0e3e9228aac --- /dev/null +++ b/test/jdk/sun/security/provider/NamedEdDSA.java @@ -0,0 +1,239 @@ +/* + * 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 8340327 + * @modules java.base/sun.security.ec.ed + * java.base/sun.security.ec.point + * java.base/sun.security.jca + * java.base/sun.security.provider + * @library /test/lib + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import sun.security.ec.ed.EdDSAOperations; +import sun.security.ec.ed.EdDSAParameters; +import sun.security.ec.point.AffinePoint; +import sun.security.jca.JCAUtil; +import sun.security.provider.NamedKeyFactory; +import sun.security.provider.NamedKeyPairGenerator; +import sun.security.provider.NamedSignature; + +import java.security.*; +import java.security.spec.EdDSAParameterSpec; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; +import java.util.List; + +public class NamedEdDSA { + + public static class ProviderImpl extends Provider { + public ProviderImpl() { + super("Named", "0", ""); + put("KeyPairGenerator.EdDSA", EdDSAKeyPairGenerator.class.getName()); + put("KeyPairGenerator.Ed25519", EdDSAKeyPairGenerator.Ed25519.class.getName()); + put("KeyPairGenerator.Ed448", EdDSAKeyPairGenerator.Ed448.class.getName()); + put("KeyFactory.EdDSA", EdDSAKeyFactory.class.getName()); + put("KeyFactory.Ed25519", EdDSAKeyFactory.Ed25519.class.getName()); + put("KeyFactory.Ed448", EdDSAKeyFactory.Ed448.class.getName()); + put("Signature.EdDSA", EdDSASignature.class.getName()); + put("Signature.Ed25519", EdDSASignature.Ed25519.class.getName()); + put("Signature.Ed448", EdDSASignature.Ed448.class.getName()); + } + } + + public static class EdDSASignature extends NamedSignature { + public EdDSASignature() { + super("EdDSA", "Ed25519", "Ed448"); + } + + protected EdDSASignature(String pname) { + super("EdDSA", pname); + } + + public static class Ed25519 extends EdDSASignature { + public Ed25519() { + super("Ed25519"); + } + } + + public static class Ed448 extends EdDSASignature { + public Ed448() { + super("Ed448"); + } + } + + @Override + public byte[] implSign(String name, byte[] sk, Object sk2, byte[] msg, SecureRandom sr) throws SignatureException { + return getOps(name).sign(plain, sk, msg); + } + + @Override + public boolean implVerify(String name, byte[] pk, Object pk2, byte[] msg, byte[] sig) throws SignatureException { + return getOps(name).verify(plain, (AffinePoint) pk2, pk, msg, sig); + } + + @Override + public Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException { + return getOps(name).decodeAffinePoint(InvalidKeyException::new, pk); + } + } + + public static class EdDSAKeyFactory extends NamedKeyFactory { + public EdDSAKeyFactory() { + super("EdDSA", "Ed25519", "Ed448"); + } + + protected EdDSAKeyFactory(String pname) { + super("EdDSA", pname); + } + + public static class Ed25519 extends EdDSAKeyFactory { + public Ed25519() { + super("Ed25519"); + } + } + + public static class Ed448 extends EdDSAKeyFactory { + public Ed448() { + super("Ed448"); + } + } + } + + public static class EdDSAKeyPairGenerator extends NamedKeyPairGenerator { + public EdDSAKeyPairGenerator() { + super("EdDSA", "Ed25519", "Ed448"); + } + + protected EdDSAKeyPairGenerator(String pname) { + super("EdDSA", pname); + } + + public static class Ed25519 extends EdDSAKeyPairGenerator { + public Ed25519() { + super("Ed25519"); + } + } + + public static class Ed448 extends EdDSAKeyPairGenerator { + public Ed448() { + super("Ed448"); + } + } + + @Override + public byte[][] implGenerateKeyPair(String pname, SecureRandom sr) { + sr = sr == null ? JCAUtil.getDefSecureRandom() : sr; + var op = getOps(pname); + var sk = op.generatePrivate(sr); + var point = op.computePublic(sk); + byte[] encodedPoint = point.getY().toByteArray(); + reverse(encodedPoint); + // array may be too large or too small, depending on the value + encodedPoint = Arrays.copyOf(encodedPoint, op.getParameters().getKeyLength()); + // set the high-order bit of the encoded point + byte msb = (byte) (point.isXOdd() ? 0x80 : 0); + encodedPoint[encodedPoint.length - 1] |= msb; + return new byte[][] { encodedPoint, sk }; + } + + private static void swap(byte[] arr, int i, int j) { + byte tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + private static void reverse(byte [] arr) { + int i = 0; + int j = arr.length - 1; + + while (i < j) { + swap(arr, i, j); + i++; + j--; + } + } + } + + private static EdDSAOperations getOps(String pname) { + var op = switch (pname) { + case "Ed25519" -> e2; + case "Ed448" -> e4; + default -> throw new AssertionError("unknown pname " + pname); + }; + return op; + } + + static final EdDSAParameterSpec plain = new EdDSAParameterSpec(false); + static final EdDSAOperations e2, e4; + static { + try { + e2 = new EdDSAOperations(EdDSAParameters.getBySize(AssertionError::new, 255)); + e4 = new EdDSAOperations(EdDSAParameters.getBySize(AssertionError::new, 448)); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + public static void main(String[] args) throws Exception { + var ps = List.of(new ProviderImpl(), Security.getProvider("SunEC")); + for (var p1 : ps) { + for (var p2 : ps) { + for (var p3 : ps) { + test(p1, p2, p3); + } + } + } + } + + static void test(Provider p1, Provider p2, Provider p3) throws Exception { + System.out.println(p1.getName() + " " + p2.getName() + " " + p3.getName()); + var g = KeyPairGenerator.getInstance("EdDSA", p1); + g.initialize(NamedParameterSpec.ED448); + var kp = g.generateKeyPair(); + var s1 = Signature.getInstance("EdDSA", p2); + var s2 = Signature.getInstance("EdDSA", p3); + var f1 = KeyFactory.getInstance("EdDSA", p2); + var f2 = KeyFactory.getInstance("EdDSA", p3); + var sk = (PrivateKey) f1.translateKey(kp.getPrivate()); + var pk = (PublicKey) f2.translateKey(kp.getPublic()); + // sign and verify twice to make sure the key is intact + s1.initSign(sk); + var sig1 = s1.sign(); + s1.initSign(sk); + var sig2 = s1.sign(); + // EdDSA signing is deterministic + Asserts.assertEqualsByteArray(sig1, sig2); + s2.initVerify(pk); + Asserts.assertTrue(s2.verify(sig1)); + s2.initVerify(pk); + Asserts.assertTrue(s2.verify(sig2)); + // No parameters defined + s1.setParameter(null); + Utils.runAndCheckException(() -> s1.setParameter(NamedParameterSpec.ED448), + InvalidAlgorithmParameterException.class); + } +} diff --git a/test/jdk/sun/security/provider/NamedKeyFactoryTest.java b/test/jdk/sun/security/provider/NamedKeyFactoryTest.java new file mode 100644 index 0000000000000..1ca179bc04690 --- /dev/null +++ b/test/jdk/sun/security/provider/NamedKeyFactoryTest.java @@ -0,0 +1,261 @@ +/* + * 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 8340327 + * @modules java.base/sun.security.x509 + * java.base/sun.security.pkcs + * java.base/sun.security.provider + * java.base/sun.security.util + * @library /test/lib + */ +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import jdk.test.lib.security.SeededSecureRandom; +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.provider.NamedKeyFactory; +import sun.security.provider.NamedKeyPairGenerator; +import sun.security.util.RawKeySpec; +import sun.security.x509.NamedX509Key; + +import java.security.*; +import java.security.spec.*; + +public class NamedKeyFactoryTest { + + private static final SeededSecureRandom RAND = SeededSecureRandom.one(); + + public static void main(String[] args) throws Exception { + Security.addProvider(new ProviderImpl()); + + var g = KeyPairGenerator.getInstance("sHA"); + var g2 = KeyPairGenerator.getInstance("ShA-256"); + var g5 = KeyPairGenerator.getInstance("SHa-512"); + var kf = KeyFactory.getInstance("ShA"); + var kf2 = KeyFactory.getInstance("Sha-256"); + var kf5 = KeyFactory.getInstance("Sha-512"); + + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g2.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g5.generateKeyPair(), "SHA", "SHA-512"); + + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g2.generateKeyPair(), "SHA", "SHA-256"); + checkKeyPair(g5.generateKeyPair(), "SHA", "SHA-512"); + + Utils.runAndCheckException(() -> g.initialize(NamedParameterSpec.ED448), + InvalidAlgorithmParameterException.class); // wrong pname + Utils.runAndCheckException(() -> g.initialize(new NamedParameterSpec("SHA-384")), + InvalidAlgorithmParameterException.class); // wrong pname + + Utils.runAndCheckException(() -> g5.initialize(new NamedParameterSpec("SHA-256")), + InvalidAlgorithmParameterException.class); // diff pname + g5.initialize(new NamedParameterSpec("SHA-512")); + + g.initialize(new NamedParameterSpec("sHA-512")); + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-512"); + g.initialize(new NamedParameterSpec("ShA-256")); + checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256"); + + var pk = new NamedX509Key("sHa", "ShA-256", RAND.nBytes(2)); + var sk = new NamedPKCS8Key("sHa", "SHa-256", RAND.nBytes(2)); + checkKey(pk, "sHa", "ShA-256"); + checkKey(sk, "sHa", "SHa-256"); + + Asserts.assertEquals("X.509", pk.getFormat()); + Asserts.assertEquals("PKCS#8", sk.getFormat()); + + var pkSpec = kf.getKeySpec(pk, X509EncodedKeySpec.class); + var skSpec = kf.getKeySpec(sk, PKCS8EncodedKeySpec.class); + + kf2.getKeySpec(pk, X509EncodedKeySpec.class); + kf2.getKeySpec(sk, PKCS8EncodedKeySpec.class); + Utils.runAndCheckException(() -> kf5.getKeySpec(pk, X509EncodedKeySpec.class), + InvalidKeySpecException.class); // wrong KF + Utils.runAndCheckException(() -> kf5.getKeySpec(sk, PKCS8EncodedKeySpec.class), + InvalidKeySpecException.class); + Utils.runAndCheckException(() -> kf.getKeySpec(pk, PKCS8EncodedKeySpec.class), + InvalidKeySpecException.class); // wrong KeySpec + Utils.runAndCheckException(() -> kf.getKeySpec(sk, X509EncodedKeySpec.class), + InvalidKeySpecException.class); + + checkKey(kf.generatePublic(pkSpec), "SHA", "SHA-256"); + Utils.runAndCheckException(() -> kf.generatePrivate(pkSpec), + InvalidKeySpecException.class); + + checkKey(kf.generatePrivate(skSpec), "SHA", "SHA-256"); + Utils.runAndCheckException(() -> kf.generatePublic(skSpec), + InvalidKeySpecException.class); + + checkKey(kf2.generatePrivate(skSpec), "SHA", "SHA-256"); + checkKey(kf2.generatePublic(pkSpec), "SHA", "SHA-256"); + + Utils.runAndCheckException(() -> kf5.generatePublic(pkSpec), + InvalidKeySpecException.class); // wrong KF + Utils.runAndCheckException(() -> kf5.generatePublic(skSpec), + InvalidKeySpecException.class); + + // The private RawKeySpec and unnamed RAW EncodedKeySpec + var prk = kf.getKeySpec(pk, RawKeySpec.class); + Asserts.assertEqualsByteArray(prk.getKeyArr(), pk.getRawBytes()); + var prk2 = kf.getKeySpec(pk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", prk2.getFormat()); + Asserts.assertEqualsByteArray(prk.getKeyArr(), prk2.getEncoded()); + + Asserts.assertEqualsByteArray(kf2.generatePublic(prk).getEncoded(), pk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePublic(prk), InvalidKeySpecException.class); // no pname + Asserts.assertEqualsByteArray(kf2.generatePublic(prk2).getEncoded(), pk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePublic(prk2), InvalidKeySpecException.class); // no pname + + var srk = kf.getKeySpec(sk, RawKeySpec.class); + Asserts.assertEqualsByteArray(srk.getKeyArr(), sk.getRawBytes()); + var srk2 = kf.getKeySpec(sk, EncodedKeySpec.class); + Asserts.assertEquals("RAW", srk2.getFormat()); + Asserts.assertEqualsByteArray(srk2.getEncoded(), sk.getRawBytes()); + + Asserts.assertEqualsByteArray(kf2.generatePrivate(srk).getEncoded(), sk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePrivate(srk), InvalidKeySpecException.class); // no pname + Asserts.assertEqualsByteArray(kf2.generatePrivate(srk2).getEncoded(), sk.getEncoded()); + Utils.runAndCheckException(() -> kf.generatePrivate(srk2), InvalidKeySpecException.class); // no pname + + var pk1 = new PublicKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var pk2 = new PublicKey() { + public String getAlgorithm() { return "sHA-256"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var pk3 = new PublicKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); } + }; + + checkKey(kf2.translateKey(pk1), "SHA", "SHA-256"); + checkKey(kf.translateKey(pk2), "SHA", "SHA-256"); + checkKey(kf.translateKey(pk3), "SHA", "SHA-256"); + + Utils.runAndCheckException(() -> kf.translateKey(pk1), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(pk2), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(pk3), InvalidKeyException.class); + + var sk1 = new PrivateKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var sk2 = new PrivateKey() { + public String getAlgorithm() { return "sHA-256"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + }; + var sk3 = new PrivateKey() { + public String getAlgorithm() { return "SHA"; } + public String getFormat() { return "RAW"; } + public byte[] getEncoded() { return RAND.nBytes(2); } + public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); } + }; + + checkKey(kf2.translateKey(sk1), "SHA", "SHA-256"); + checkKey(kf.translateKey(sk2), "SHA", "SHA-256"); + checkKey(kf.translateKey(sk3), "SHA", "SHA-256"); + + Utils.runAndCheckException(() -> kf.translateKey(sk1), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(sk2), InvalidKeyException.class); + Utils.runAndCheckException(() -> kf5.translateKey(sk3), InvalidKeyException.class); + } + + static void checkKeyPair(KeyPair kp, String algName, String toString) { + checkKey(kp.getPrivate(), algName, toString); + checkKey(kp.getPublic(), algName, toString); + } + + static void checkKey(Key k, String algName, String pname) { + Asserts.assertEquals(algName, k.getAlgorithm()); + Asserts.assertTrue(k.toString().contains(pname)); + if (k instanceof AsymmetricKey ak && ak.getParams() instanceof NamedParameterSpec nps) { + Asserts.assertEquals(pname, nps.getName()); + } + } + + // Provider + + public static class ProviderImpl extends Provider { + public ProviderImpl() { + super("P", "1", "..."); + put("KeyFactory.SHA", KF.class.getName()); + put("KeyFactory.SHA-256", KF1.class.getName()); + put("KeyFactory.SHA-512", KF2.class.getName()); + put("KeyPairGenerator.SHA", KPG.class.getName()); + put("KeyPairGenerator.SHA-256", KPG1.class.getName()); + put("KeyPairGenerator.SHA-512", KPG2.class.getName()); + } + } + public static class KF extends NamedKeyFactory { + public KF() { + super("SHA", "SHA-256", "SHA-512"); + } + } + public static class KF1 extends NamedKeyFactory { + public KF1() { + super("SHA", "SHA-256"); + } + } + public static class KF2 extends NamedKeyFactory { + public KF2() { + super("SHA", "SHA-512"); + } + } + public static class KPG extends NamedKeyPairGenerator { + public KPG() { + super("SHA", "SHA-256", "SHA-512"); + } + + public KPG(String pname) { + super("SHA", pname); + } + + @Override + public byte[][] implGenerateKeyPair(String name, SecureRandom sr) { + var out = new byte[2][]; + out[0] = RAND.nBytes(name.endsWith("256") ? 2 : 4); + out[1] = RAND.nBytes(name.endsWith("256") ? 2 : 4); + return out; + } + } + public static class KPG1 extends KPG { + public KPG1() { + super("SHA-256"); + } + } + public static class KPG2 extends KPG { + public KPG2() { + super("SHA-512"); + } + } +} diff --git a/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java b/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java index 58ebb5b876af8..5555363374ca9 100644 --- a/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java +++ b/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, 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,7 +23,7 @@ /* * @test - * @bug 8046321 + * @bug 8046321 8339403 * @library ../../../../java/security/testlibrary * @build CertificateBuilder SimpleOCSPServer * @run main/othervm -Djavax.net.debug=ssl:respmgr java.base/sun.security.ssl.StatusResponseManagerTests diff --git a/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java index f6b0d1f10e20f..b210cfb26eaed 100644 --- a/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java +++ b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.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 @@ -82,6 +82,7 @@ public static void main(String[] args) throws Exception { put("Clear StatusResponseManager cache", testClearSRM); put("Basic OCSP_MULTI fetch test", testOcspMultiFetch); put("Test Cache Expiration", testCacheExpiry); + put("Test Interrupt while fetching", forceInterruptMainThread); }}; // Create the CAs and OCSP responders @@ -262,6 +263,38 @@ public Map.Entry runTest() { } }; + public static final TestCase forceInterruptMainThread = new TestCase() { + @Override + public Map.Entry runTest() { + StatusResponseManager srm = new StatusResponseManager(); + Boolean pass = Boolean.FALSE; + String message = null; + CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP; + + try { + // Force the interrupt flag to be set on the thread that + // performs the invokeAll in the SRM. + Thread.currentThread().interrupt(); + + // Get OCSP responses for non-root certs in the chain + Map responseMap = srm.get( + CertStatusRequestType.OCSP, oReq, chain, 5000, + TimeUnit.MILLISECONDS); + if (Thread.currentThread().isInterrupted()) { + pass = Boolean.TRUE; + message = "Thread is in expected interrupted state."; + } else { + message = "Missing expectedInterruptedException."; + } + message += " Number of SRM entries: " + responseMap.size(); + } catch (Exception exc) { + message = "Unexpected exception: " + exc; + } + + return new AbstractMap.SimpleEntry<>(pass, message); + } + }; + /** * Creates the PKI components necessary for this test, including * Root CA, Intermediate CA and SSL server certificates, the keystores diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java deleted file mode 100644 index ba008c68cdc10..0000000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/Distrust.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (c) 2018, 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.*; -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 8207258 8216280 - * @summary Check that TLS Server certificates chaining back to distrusted - * Symantec 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[] { - "geotrustprimarycag2", "geotrustprimarycag3", - "geotrustuniversalca", "thawteprimaryrootca", "thawteprimaryrootcag2", - "thawteprimaryrootcag3", "verisignclass3g3ca", "verisignclass3g4ca", - "verisignclass3g5ca", "verisignuniversalrootca" }; - - // Each of the subCAs with a delayed distrust date have a test certificate - // chain stored in a file named "-chain.pem". - private static String[] subCAsToTest = new String[]{"appleistca8g1"}; - - // A date that is after the restrictions take affect - private static final Date APRIL_17_2019 = - Date.from(LocalDate.of(2019, 4, 17) - .atStartOfDay(ZoneOffset.UTC) - .toInstant()); - - // A date that is a second before the restrictions take affect - private static final Date BEFORE_APRIL_17_2019 = - Date.from(LocalDate.of(2019, 4, 17) - .atStartOfDay(ZoneOffset.UTC) - .minusSeconds(1) - .toInstant()); - - // A date that is after the subCA restrictions take affect - private static final Date JANUARY_1_2020 = - Date.from(LocalDate.of(2020, 1, 1) - .atStartOfDay(ZoneOffset.UTC) - .toInstant()); - - // A date that is a second before the subCA restrictions take affect - private static final Date BEFORE_JANUARY_1_2020 = - Date.from(LocalDate.of(2020, 1, 1) - .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_APRIL_17_2019 : APRIL_17_2019; - - 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); - } - - // test chain if params are passed to TrustManager - System.err.println("Testing verisignuniversalrootca with params"); - testTM(getTMF("PKIX", getParams()), - loadCertificateChain("verisignuniversalrootca"), - notBefore, isValid); - - // test code-signing chain (should be valid as restrictions don't apply) - System.err.println("Testing verisignclass3g5ca code-signing chain"); - Validator v = Validator.getInstance(Validator.TYPE_PKIX, - Validator.VAR_CODE_SIGNING, - getParams()); - // set validation date so this will still pass when cert expires - v.setValidationDate(new Date(1544197375493l)); - v.validate(loadCertificateChain("verisignclass3g5ca-codesigning")); - - // test chains issued through subCAs - notBefore = before ? BEFORE_JANUARY_1_2020 : JANUARY_1_2020; - for (String test : subCAsToTest) { - 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/Distrust.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Distrust.java similarity index 54% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/Distrust.java rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Distrust.java index a9a47bf834cc1..18178f65ec116 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/Distrust.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Distrust.java @@ -25,7 +25,7 @@ import java.math.BigInteger; import java.security.*; import java.security.cert.*; -import java.time.*; +import java.time.ZonedDateTime; import java.util.*; import javax.net.ssl.*; import sun.security.validator.Validator; @@ -34,95 +34,80 @@ import jdk.test.lib.security.SecurityUtils; /** - * @test - * @bug 8337664 - * @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 + * Helper class that provides methods to facilitate testing of distrusted roots. */ - -public class Distrust { +public final 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_1_2024 = - Date.from(LocalDate.of(2024, 11, 1) - .atStartOfDay(ZoneOffset.UTC) - .toInstant()); - - // A date that is a second before the restrictions take effect - private static final Date BEFORE_NOVEMBER_1_2024 = - Date.from(LocalDate.of(2024, 11, 1) - .atStartOfDay(ZoneOffset.UTC) - .minusSeconds(1) - .toInstant()); - - public static void main(String[] args) throws Exception { + private final boolean before; + private final boolean policyOn; + private final boolean isValid; - cf = CertificateFactory.getInstance("X.509"); - - boolean before = args[0].equals("before"); - boolean policyOn = args[1].equals("policyOn"); - boolean isValid = args[2].equals("valid"); + public Distrust(String[] args) { + before = args[0].equals("before"); + policyOn = args[1].equals("policyOn"); + isValid = args[2].equals("valid"); if (!policyOn) { // disable policy (default is on) Security.setProperty("jdk.security.caDistrustPolicies", ""); } + } + + public Date getNotBefore(ZonedDateTime distrustDate) { + ZonedDateTime notBefore = before ? distrustDate.minusSeconds(1) : distrustDate; + return Date.from(notBefore.toInstant()); + } - Date notBefore = before ? BEFORE_NOVEMBER_1_2024 : NOVEMBER_1_2024; + public void testCodeSigningChain(String certPath, String name, Date validationDate) + throws Exception { + System.err.println("Testing " + name + " code-signing chain"); + Validator v = Validator.getInstance(Validator.TYPE_PKIX, + Validator.VAR_CODE_SIGNING, + getParams()); + // set validation date so this will still pass when cert expires + v.setValidationDate(validationDate); + v.validate(loadCertificateChain(certPath, name)); + } - X509TrustManager pkixTM = getTMF("PKIX", null); - X509TrustManager sunX509TM = getTMF("SunX509", null); - for (String test : rootsToTest) { + public void testCertificateChain(String certPath, Date notBefore, X509TrustManager[] tms, + String... tests) throws Exception { + for (String test : tests) { System.err.println("Testing " + test); - X509Certificate[] chain = loadCertificateChain(test); + X509Certificate[] chain = loadCertificateChain(certPath, test); - testTM(sunX509TM, chain, notBefore, isValid); - testTM(pkixTM, chain, notBefore, isValid); + for (X509TrustManager tm : tms) { + testTM(tm, chain, notBefore, isValid); + } } } - private static X509TrustManager getTMF(String type, - PKIXBuilderParameters params) throws Exception { + public X509TrustManager getTMF(String type, PKIXBuilderParameters params) throws Exception { TrustManagerFactory tmf = TrustManagerFactory.getInstance(type); if (params == null) { - tmf.init((KeyStore)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; + return (X509TrustManager) tm; } - throw new Exception("No TrustManager for " + type); + throw new RuntimeException("No TrustManager for " + type); } - private static PKIXBuilderParameters getParams() throws Exception { + public PKIXBuilderParameters getParams() throws Exception { PKIXBuilderParameters pbp = - new PKIXBuilderParameters(SecurityUtils.getCacertsKeyStore(), - new X509CertSelector()); + 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 { + public void testTM(X509TrustManager xtm, X509Certificate[] chain, + Date notBefore, boolean valid) { // 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 @@ -130,67 +115,54 @@ private static void testTM(X509TrustManager xtm, X509Certificate[] chain, // after then. chain[0] = new DistrustedTLSServerCert(chain[0], notBefore); + // Wrap the intermediate and root CA certs in NonExpiringTLSServerCert + // so it will never throw a CertificateExpiredException + for (int i = 1; i < chain.length; i++) { + chain[i] = new NonExpiringTLSServerCert(chain[i]); + } + try { xtm.checkServerTrusted(chain, "ECDHE_RSA"); if (!valid) { - throw new Exception("chain should be invalid"); + throw new RuntimeException("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); + throw new RuntimeException("Unexpected exception, chain " + + "should be valid", ce); } if (ce instanceof ValidatorException) { - ValidatorException ve = (ValidatorException)ce; + ValidatorException ve = (ValidatorException) ce; if (ve.getErrorType() != ValidatorException.T_UNTRUSTED_CERT) { ce.printStackTrace(System.err); - throw new Exception("Unexpected exception: " + ce); + throw new RuntimeException("Unexpected exception: " + ce); } } else { - throw new Exception("Unexpected exception: " + ce); + throw new RuntimeException(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) + private X509Certificate[] loadCertificateChain(String certPath, String name) throws Exception { - try (InputStream in = new FileInputStream(TEST_SRC + File.separator + - name + "-chain.pem")) { + if (cf == null) { + cf = CertificateFactory.getInstance("X.509"); + } + try (InputStream in = new FileInputStream(TEST_SRC + File.separator + certPath + + File.separator + name + "-chain.pem")) { Collection certs = - (Collection)cf.generateCertificates(in); + (Collection) cf.generateCertificates(in); return certs.toArray(new X509Certificate[0]); } } - private static class DistrustedTLSServerCert extends X509Certificate { + private static class NonExpiringTLSServerCert extends X509Certificate { private final X509Certificate cert; - private final Date notBefore; - DistrustedTLSServerCert(X509Certificate cert, Date notBefore) { + NonExpiringTLSServerCert(X509Certificate cert) { this.cert = cert; - this.notBefore = notBefore; } public Set getCriticalExtensionOIDs() { - return cert.getCriticalExtensionOIDs(); + return cert.getCriticalExtensionOIDs(); } public byte[] getExtensionValue(String oid) { return cert.getExtensionValue(oid); @@ -201,19 +173,17 @@ public Set getNonCriticalExtensionOIDs() { public boolean hasUnsupportedCriticalExtension() { return cert.hasUnsupportedCriticalExtension(); } - public void checkValidity() throws CertificateExpiredException, - CertificateNotYetValidException { + public void checkValidity() { // always pass } - public void checkValidity(Date date) throws CertificateExpiredException, - CertificateNotYetValidException { + public void checkValidity(Date date) { // 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 getNotBefore() { return cert.getNotBefore(); } public Date getNotAfter() { return cert.getNotAfter(); } public byte[] getTBSCertificate() throws CertificateEncodingException { return cert.getTBSCertificate(); @@ -234,16 +204,25 @@ public byte[] getEncoded() throws CertificateEncodingException { return cert.getEncoded(); } public void verify(PublicKey key) throws CertificateException, - InvalidKeyException, NoSuchAlgorithmException, - NoSuchProviderException, SignatureException { + InvalidKeyException, NoSuchAlgorithmException, + NoSuchProviderException, SignatureException { cert.verify(key); } public void verify(PublicKey key, String sigProvider) throws - CertificateException, InvalidKeyException, NoSuchAlgorithmException, - NoSuchProviderException, SignatureException { + CertificateException, InvalidKeyException, NoSuchAlgorithmException, + NoSuchProviderException, SignatureException { cert.verify(key, sigProvider); } public PublicKey getPublicKey() { return cert.getPublicKey(); } public String toString() { return cert.toString(); } } + + private static class DistrustedTLSServerCert extends NonExpiringTLSServerCert { + private final Date notBefore; + DistrustedTLSServerCert(X509Certificate cert, Date notBefore) { + super(cert); + this.notBefore = notBefore; + } + public Date getNotBefore() { return notBefore; } + } } diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java new file mode 100644 index 0000000000000..809674e8f209c --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java @@ -0,0 +1,68 @@ +/* + * 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.time.*; +import java.util.*; +import javax.net.ssl.*; + +/** + * @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 Entrust after policyOn invalid + * @run main/othervm Entrust after policyOff valid + * @run main/othervm Entrust before policyOn valid + * @run main/othervm Entrust before policyOff valid + */ + +public class Entrust { + + private static final String certPath = "chains" + File.separator + "entrust"; + + // 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"}; + + // Date when the restrictions take effect + private static final ZonedDateTime DISTRUST_DATE = + LocalDate.of(2024, 11, 12).atStartOfDay(ZoneOffset.UTC); + + public static void main(String[] args) throws Exception { + Distrust distrust = new Distrust(args); + + X509TrustManager[] tms = new X509TrustManager[]{ + distrust.getTMF("PKIX", null), + distrust.getTMF("SunX509", null) + }; + + Date notBefore = distrust.getNotBefore(DISTRUST_DATE); + distrust.testCertificateChain(certPath, notBefore, tms, rootsToTest); + } +} diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Symantec.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Symantec.java new file mode 100644 index 0000000000000..4e099a8de8de6 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Symantec.java @@ -0,0 +1,95 @@ +/* + * 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 + * 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.net.ssl.X509TrustManager; +import java.io.File; +import java.time.*; +import java.util.*; + + +/** + * @test + * @bug 8207258 8216280 + * @summary Check that TLS Server certificates chaining back to distrusted + * Symantec roots are invalid + * @library /test/lib + * @modules java.base/sun.security.validator + * @run main/othervm Symantec after policyOn invalid + * @run main/othervm Symantec after policyOff valid + * @run main/othervm Symantec before policyOn valid + * @run main/othervm Symantec before policyOff valid + */ + +public class Symantec { + + private static final String certPath = "chains" + File.separator + "symantec"; + + // Each of the roots have a test certificate chain stored in a file + // named "-chain.pem". + private static final String[] rootsToTest = new String[]{ + "geotrustprimarycag2", "geotrustprimarycag3", "geotrustuniversalca", + "thawteprimaryrootca", "thawteprimaryrootcag2", "thawteprimaryrootcag3", + "verisignclass3g3ca", "verisignclass3g4ca", "verisignclass3g5ca", + "verisignuniversalrootca" + }; + + // Each of the subCAs with a delayed distrust date have a test certificate + // chain stored in a file named "-chain.pem". + private static String[] subCAsToTest = new String[]{"appleistca8g1"}; + + // Date when the restrictions take effect + private static final ZonedDateTime ROOTS_DISTRUST_DATE = + LocalDate.of(2019, 4, 17).atStartOfDay(ZoneOffset.UTC); + + // Date when the subCA restrictions take effect + private static final ZonedDateTime SUBCA_DISTRUST_DATE = + LocalDate.of(2020, 1, 1).atStartOfDay(ZoneOffset.UTC); + + public static void main(String[] args) throws Exception { + Distrust distrust = new Distrust(args); + X509TrustManager[] tms = new X509TrustManager[]{ + distrust.getTMF("PKIX", null), + distrust.getTMF("SunX509", null) + }; + + // test chains issued through roots + Date notBefore = distrust.getNotBefore(ROOTS_DISTRUST_DATE); + distrust.testCertificateChain(certPath, notBefore, tms, rootsToTest); + + // test chain if params are passed to TrustManager + System.err.println("Testing verisignuniversalrootca with params"); + X509TrustManager[] tmsParams = new X509TrustManager[]{ + distrust.getTMF("PKIX", distrust.getParams()) + }; + distrust.testCertificateChain(certPath, notBefore, tmsParams, + "verisignuniversalrootca"); + + // test code-signing chain (should be valid as restrictions don't apply) + Date validationDate = new Date(1544197375493L); + distrust.testCodeSigningChain(certPath, "verisignclass3g5ca-codesigning", validationDate); + + // test chains issued through subCAs + notBefore = distrust.getNotBefore(SUBCA_DISTRUST_DATE); + distrust.testCertificateChain(certPath, notBefore, tms, subCAsToTest); + } +} diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustcommercialca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustcommercialca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustnetworkingca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustnetworkingca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumeccca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/affirmtrustpremiumeccca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrust2048ca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrust2048ca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrust2048ca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrust2048ca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustevca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustevca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustevca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustevca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcaec1-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcaec1-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcaec1-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcaec1-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag2-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag2-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag2-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag2-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag4-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag4-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Entrust/entrustrootcag4-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/entrustrootcag4-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca8g1-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/appleistca8g1-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/appleistca8g1-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/appleistca8g1-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustprimarycag2-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/geotrustprimarycag2-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustprimarycag2-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/geotrustprimarycag2-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustprimarycag3-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/geotrustprimarycag3-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustprimarycag3-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/geotrustprimarycag3-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustuniversalca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/geotrustuniversalca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/geotrustuniversalca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/geotrustuniversalca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/thawteprimaryrootca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/thawteprimaryrootca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/thawteprimaryrootca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/thawteprimaryrootca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/thawteprimaryrootcag2-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/thawteprimaryrootcag2-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/thawteprimaryrootcag2-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/thawteprimaryrootcag2-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/thawteprimaryrootcag3-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/thawteprimaryrootcag3-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/thawteprimaryrootcag3-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/thawteprimaryrootcag3-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g3ca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g3ca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g3ca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g3ca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g4ca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g4ca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g4ca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g4ca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g5ca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g5ca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g5ca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g5ca-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g5ca-codesigning-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g5ca-codesigning-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignclass3g5ca-codesigning-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignclass3g5ca-codesigning-chain.pem diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignuniversalrootca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignuniversalrootca-chain.pem similarity index 100% rename from test/jdk/sun/security/ssl/X509TrustManagerImpl/Symantec/verisignuniversalrootca-chain.pem rename to test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/symantec/verisignuniversalrootca-chain.pem diff --git a/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java b/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java new file mode 100644 index 0000000000000..598e25a9a709b --- /dev/null +++ b/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java @@ -0,0 +1,94 @@ +/* + * 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 8309841 + * @summary Jarsigner should print a warning if an entry is removed + * @library /test/lib + */ + +import jdk.test.lib.SecurityTools; +import jdk.test.lib.util.JarUtils; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +public class RemovedFiles { + + private static final String NONEXISTENT_ENTRIES_FOUND + = "This jar contains signed entries for files that do not exist. See the -verbose output for more details."; + + public static void main(String[] args) throws Exception { + JarUtils.createJarFile( + Path.of("a.jar"), + Path.of("."), + Files.writeString(Path.of("a"), "a"), + Files.writeString(Path.of("b"), "b")); + SecurityTools.keytool("-genkeypair -storepass changeit -keystore ks -alias x -dname CN=x -keyalg ed25519"); + SecurityTools.jarsigner("-storepass changeit -keystore ks a.jar x"); + + // All is fine at the beginning. + SecurityTools.jarsigner("-verify a.jar") + .shouldNotContain(NONEXISTENT_ENTRIES_FOUND); + + // Remove an entry after signing. There will be a warning. + JarUtils.deleteEntries(Path.of("a.jar"), "a"); + SecurityTools.jarsigner("-verify a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + SecurityTools.jarsigner("-verify -verbose a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND) + .shouldContain("Warning: nonexistent signed entries: [a]"); + + // Remove one more entry. + JarUtils.deleteEntries(Path.of("a.jar"), "b"); + SecurityTools.jarsigner("-verify a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + SecurityTools.jarsigner("-verify -verbose a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND) + .shouldContain("Warning: nonexistent signed entries: [a, b]"); + + // Re-sign will not clear the warning. + SecurityTools.jarsigner("-storepass changeit -keystore ks a.jar x"); + SecurityTools.jarsigner("-verify a.jar") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + + // Unfortunately, if there is a non-file entry in manifest, there will be + // a false alarm. See https://bugs.openjdk.org/browse/JDK-8334261. + var man = new Manifest(); + man.getMainAttributes().putValue("Manifest-Version", "1.0"); + man.getEntries().computeIfAbsent("Hello", _ -> new Attributes()) + .putValue("Foo", "Bar"); + JarUtils.createJarFile(Path.of("b.jar"), + man, + Path.of("."), + Path.of("a")); + SecurityTools.jarsigner("-storepass changeit -keystore ks b.jar x"); + SecurityTools.jarsigner("-verbose -verify b.jar") + .shouldContain("Warning: nonexistent signed entries: [Hello]") + .shouldContain(NONEXISTENT_ENTRIES_FOUND); + + } +} diff --git a/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java b/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java index 52ca1ead82c5a..84cfcd7cb17de 100644 --- a/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java +++ b/test/jdk/sun/security/tools/keytool/GenKeyPairSigner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -84,7 +84,7 @@ static void testSignerPKCS12() throws Exception { System.out.println("Generating an XDH cert with -signer option"); SecurityTools.keytool("-keystore ks -storepass changeit " + "-genkeypair -keyalg XDH -alias e1 -dname CN=E1 -signer ca") - .shouldContain("Generating 255 bit XDH key pair and a certificate (Ed25519) issued by with a validity of 90 days") + .shouldContain("Generating 255 bit X25519 key pair and a certificate (Ed25519) issued by with a validity of 90 days") .shouldContain("for: CN=E1") .shouldHaveExitValue(0); @@ -118,7 +118,7 @@ static void testSignerPKCS12() throws Exception { .shouldContain("Alias name: e1") .shouldContain("Certificate chain length: 2") .shouldContain("Signature algorithm name: Ed25519") - .shouldContain("Subject Public Key Algorithm: 255-bit XDH key") + .shouldContain("Subject Public Key Algorithm: 255-bit X25519 key") .shouldHaveExitValue(0); // check to make sure that cert's AKID is created from the SKID of the signing cert @@ -150,7 +150,7 @@ static void testSignerPKCS12() throws Exception { System.out.println("Generating an X448 cert with -signer option"); SecurityTools.keytool("-keystore ks -storepass changeit " + "-genkeypair -keyalg X448 -alias e2 -dname CN=E2 -sigalg SHA384withRSA -signer ca2") - .shouldContain("Generating 448 bit XDH key pair and a certificate (SHA384withRSA) issued by with a validity of 90 days") + .shouldContain("Generating 448 bit X448 key pair and a certificate (SHA384withRSA) issued by with a validity of 90 days") .shouldContain("for: CN=E2") .shouldHaveExitValue(0); @@ -177,7 +177,7 @@ static void testSignerPKCS12() throws Exception { "-list -v") .shouldContain("Alias name: e2") .shouldContain("Signature algorithm name: SHA384withRSA") - .shouldContain("Subject Public Key Algorithm: 448-bit XDH key") + .shouldContain("Subject Public Key Algorithm: 448-bit X448 key") .shouldHaveExitValue(0); kt("-genkeypair -keyalg DSA -alias ca3 -dname CN=CA3 -ext bc:c ", @@ -249,7 +249,7 @@ static void testSignerJKS() throws Exception { SecurityTools.keytool("-keystore ksjks -storepass changeit -storetype jks " + "-genkeypair -keyalg XDH -alias e1 -dname CN=E1 " + "-keypass e1keypass -signer ca1 -signerkeypass ca1keypass") - .shouldContain("Generating 255 bit XDH key pair and a certificate (SHA256withDSA) issued by with a validity of 90 days") + .shouldContain("Generating 255 bit X25519 key pair and a certificate (SHA256withDSA) issued by with a validity of 90 days") .shouldContain("for: CN=E1") .shouldContain("The generated certificate #2 of 3 uses a 1024-bit DSA key which is considered a security risk") .shouldContain("The generated certificate #3 of 3 uses a 1024-bit RSA key which is considered a security risk") @@ -285,7 +285,7 @@ static void testSignerJKS() throws Exception { .shouldContain("Alias name: e1") .shouldContain("Certificate chain length: 3") .shouldContain("Signature algorithm name: SHA256withDSA") - .shouldContain("Subject Public Key Algorithm: 255-bit XDH key") + .shouldContain("Subject Public Key Algorithm: 255-bit X25519 key") .shouldHaveExitValue(0); } diff --git a/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java b/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java index 4af5bf1ff6755..f6b9718a28bf4 100644 --- a/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java +++ b/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java @@ -51,6 +51,8 @@ public static void main(String[] args) throws Exception { verifyOutputFilenames("GC.heap_dump", FILENAME); if (Platform.isLinux()) { verifyOutputFilenames("Compiler.perfmap", FILENAME); + } + if (Platform.isLinux() || Platform.isWindows()) { verifyOutputFilenames("System.dump_map", "-F=%s".formatted(FILENAME)); } } diff --git a/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java b/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java index 1ac4b01f1456c..0b6570b74dc73 100644 --- a/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java +++ b/test/jdk/sun/util/calendar/zi/TestZoneInfo310.java @@ -280,5 +280,4 @@ private static ZoneInfoOld toZoneInfoOld(TimeZone tz) throws Exception { willGMTOffsetChange.getBoolean(tz)); } - } diff --git a/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java b/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java index d2289d4b1b53e..300b7f13dddd7 100644 --- a/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java +++ b/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java @@ -86,9 +86,9 @@ public class ZoneInfoOld extends TimeZone { private static final int TRANSITION_NSHIFT = 12; // IDs having conflicting data between Olson and JDK 1.1 - static final String[] conflictingIDs = { - "EST", "MST", "HST" - }; + static final Map conflictingIDs = Map.of( + "EST", "America/Panama", + "MST", "America/Phoenix"); private static final CalendarSystem gcal = CalendarSystem.getGregorianCalendar(); @@ -823,10 +823,8 @@ public synchronized static Map getAliasTable() { if (aliases == null) { aliases = ZoneInfoFile.getZoneAliases(); if (aliases != null) { - // Remove the conflicting IDs from the alias table. - for (String key : conflictingIDs) { - aliases.remove(key); - } + // Replace old mappings from `jdk11_backward` + aliases.putAll(conflictingIDs); aliasTable = new SoftReference>(aliases); } } diff --git a/test/jdk/sun/util/resources/TimeZone/Bug4848242.java b/test/jdk/sun/util/resources/TimeZone/Bug4848242.java index 99f66340316d9..68962f135d0d3 100644 --- a/test/jdk/sun/util/resources/TimeZone/Bug4848242.java +++ b/test/jdk/sun/util/resources/TimeZone/Bug4848242.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2022, 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,7 +35,6 @@ public class Bug4848242 { public static void main(String[] args) { - getTzInfo("de", "DE"); getTzInfo("es", "ES"); getTzInfo("fr", "FR"); getTzInfo("it", "IT"); @@ -46,7 +45,6 @@ static void getTzInfo(String langName, String locName) { Locale tzLocale = Locale.of(langName, locName); TimeZone euroTz = TimeZone.getTimeZone("MET"); - System.out.println("Locale is " + langName + "_" + locName); if ( euroTz.getID().equalsIgnoreCase("GMT") ) { @@ -56,13 +54,13 @@ static void getTzInfo(String langName, String locName) // get the timezone info System.out.println(euroTz.getDisplayName(false, TimeZone.SHORT, tzLocale)); - if(!euroTz.getDisplayName(false, TimeZone.SHORT, tzLocale).equals("MET")) - throw new RuntimeException("Timezone name is incorrect (should be MET)\n"); + if (!euroTz.getDisplayName(false, TimeZone.SHORT, tzLocale).equals("CET")) + throw new RuntimeException("Timezone name is incorrect (should be CET)\n"); System.out.println(euroTz.getDisplayName(false, TimeZone.LONG, tzLocale)); System.out.println(euroTz.getDisplayName(true, TimeZone.SHORT, tzLocale)); - if(!euroTz.getDisplayName(true, TimeZone.SHORT, tzLocale).equals("MEST")) - throw new RuntimeException("Summer timezone name is incorrect (should be MEST)\n"); + if (!euroTz.getDisplayName(true, TimeZone.SHORT, tzLocale).equals("CEST")) + throw new RuntimeException("Summer timezone name is incorrect (should be CEST)\n"); System.out.println(euroTz.getDisplayName(true, TimeZone.LONG, tzLocale) + "\n"); } diff --git a/test/jdk/tools/jpackage/macosx/SigningOptionsTest.java b/test/jdk/tools/jpackage/macosx/SigningOptionsTest.java index 9db836af99238..fee874da2e8bc 100644 --- a/test/jdk/tools/jpackage/macosx/SigningOptionsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningOptionsTest.java @@ -91,8 +91,17 @@ public static Collection input() { new String[]{"--type"}, "Option [--mac-installer-sign-identity] is not valid with type"}, // --app-content and --type app-image + // JDK-8340802: "codesign" may or may not fail if additional + // content is specified based on macOS version. For example on + // macOS 15 aarch64 "codesign" will not fail with additional content. + // Since we only need to check that warning is displayed when + // "codesign" fails and "--app-content" is provided, lets fail + // "codesign" for some better reason like identity which does not + // exists. {"Hello", - new String[]{"--app-content", TEST_DUKE}, + new String[]{"--app-content", TEST_DUKE, + "--mac-sign", + "--mac-app-image-sign-identity", "test-identity"}, null, "\"codesign\" failed and additional application content" + " was supplied via the \"--app-content\" parameter."}, diff --git a/test/jdk/tools/jpackage/share/AppContentTest.java b/test/jdk/tools/jpackage/share/AppContentTest.java index b31a6e637b292..a343e20d8a748 100644 --- a/test/jdk/tools/jpackage/share/AppContentTest.java +++ b/test/jdk/tools/jpackage/share/AppContentTest.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 @@ -34,6 +34,7 @@ import java.util.Collection; import java.util.List; +import jdk.internal.util.OSVersion; /** * Tests generation of packages with input folder containing empty folders. @@ -48,6 +49,7 @@ * @build jdk.jpackage.test.* * @build AppContentTest * @modules jdk.jpackage/jdk.jpackage.internal + * @modules java.base/jdk.internal.util * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=AppContentTest */ @@ -81,6 +83,17 @@ public AppContentTest(String... testPathArgs) { @Test public void test() throws Exception { + // On macOS signing may or may not work for modified app bundles. + // It works on macOS 15 and up, but fails on macOS below 15. + final int expectedJPackageExitCode; + final boolean isMacOS15 = (OSVersion.current().compareTo( + new OSVersion(15, 0, 0)) > 0); + if (testPathArgs.contains(TEST_BAD) || (TKit.isOSX() && !isMacOS15)) { + expectedJPackageExitCode = 1; + } else { + expectedJPackageExitCode = 0; + } + new PackageTest().configureHelloApp() .addInitializer(cmd -> { for (String arg : testPathArgs) { @@ -99,9 +112,7 @@ public void test() throws Exception { } }) - // On macOS we always signing app image and signing will fail, since - // test produces invalid app bundle. - .setExpectedExitCode(testPathArgs.contains(TEST_BAD) || TKit.isOSX() ? 1 : 0) + .setExpectedExitCode(expectedJPackageExitCode) .run(); } } diff --git a/test/jdk/tools/launcher/HelpFlagsTest.java b/test/jdk/tools/launcher/HelpFlagsTest.java index 15c6c101dd0c1..0e8bc345ddc77 100644 --- a/test/jdk/tools/launcher/HelpFlagsTest.java +++ b/test/jdk/tools/launcher/HelpFlagsTest.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. * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -45,7 +45,6 @@ public class HelpFlagsTest extends TestHelper { // Tools that should not be tested because a usage message is pointless. static final String[] TOOLS_NOT_TO_TEST = { - "appletviewer", // deprecated, don't test "jaccessinspector", // gui, don't test, win only "jaccessinspector-32", // gui, don't test, win-32 only "jaccesswalker", // gui, don't test, win only diff --git a/test/jdk/tools/launcher/MultipleJRERemoved.java b/test/jdk/tools/launcher/MultipleJRERemoved.java deleted file mode 100644 index 551ffc885f275..0000000000000 --- a/test/jdk/tools/launcher/MultipleJRERemoved.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @bug 8067437 - * @summary Verify Multiple JRE version support has been removed. - * @modules jdk.compiler - * jdk.zipfs - * @build TestHelper - * @run main MultipleJRERemoved - */ - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.util.*; -import java.util.jar.Attributes; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.zip.ZipEntry; - -public class MultipleJRERemoved extends TestHelper { - - public static final String VERSION_JAR = "version.jar"; - public static final String PRINT_VERSION_CLASS = "PrintVersion"; - private final File javaFile = new File(PRINT_VERSION_CLASS + ".java"); - private final File clsFile = new File(PRINT_VERSION_CLASS + ".class"); - - private MultipleJRERemoved() { - } - - /** - * @param args the command line arguments - * @throws java.io.FileNotFoundException - */ - public static void main(String[] args) throws Exception { - MultipleJRERemoved a = new MultipleJRERemoved(); - a.run(args); - } - - /** - * Check all combinations of flags: "-version:", "-jre-restrict-search", "-jre-no-restrict-search". Test expects to see errors. - */ - @Test - public void allFlagCombinations() throws IOException { - final Pattern newLine = Pattern.compile("\n"); - createJar(Collections.emptyMap()); - - for (Flag flag1 : Flag.values()) { - for (Flag flag2 : Flag.values()) { - for (Flag flag3 : Flag.values()) { - List flags = Stream.of(flag1, flag2, flag3) - .filter(f -> !Flag.EMPTY.equals(f)) - .collect(Collectors.toList()); - - if (flags.size() == 0) continue; - - List flagValues = flags.stream() - .map(Flag::value) - .collect(Collectors.toList()); - - List errorMessages = flags.stream() - .map(Flag::errorMessage) - .flatMap(newLine::splitAsStream) - .collect(Collectors.toList()); - - List jarCmd = new ArrayList<>(); - jarCmd.add(javaCmd); - jarCmd.addAll(flagValues); - jarCmd.add("-jar"); - jarCmd.add("version.jar"); - - check(jarCmd, errorMessages); - - List cmd = new ArrayList<>(); - cmd.add(javaCmd); - cmd.addAll(flagValues); - cmd.add(PRINT_VERSION_CLASS); - - check(cmd, errorMessages); - } - } - } - } - - private void check(List cmd, List errorMessages) { - TestResult tr = doExec(cmd.toArray(new String[cmd.size()])); - tr.checkNegative(); - tr.isNotZeroOutput(); - errorMessages.forEach(tr::contains); - - if (!tr.testStatus) { - System.out.println(tr); - throw new RuntimeException("test case: failed\n" + cmd); - } - } - - /** - * Verifies that java -help output doesn't contain information about "mJRE" flags. - */ - @Test - public void javaHelp() { - TestResult tr = doExec(javaCmd, "-help"); - tr.checkPositive(); - tr.isNotZeroOutput(); - tr.notContains("-version:"); - tr.notContains("-jre-restrict-search"); - tr.notContains("-jre-no-restrict-search"); - tr.notContains("-no-jre-restrict-search"); //it's not a typo in flag name. - if (!tr.testStatus) { - System.out.println(tr); - throw new RuntimeException("Failed. java -help output contains obsolete flags.\n"); - } - } - - /** - * Verifies that java -jar version.jar output ignores "mJRE" manifest directives. - */ - @Test - public void manifestDirectives() throws IOException { - Map manifest = new TreeMap<>(); - manifest.put("JRE-Version", "1.8"); - manifest.put("JRE-Restrict-Search", "1.8"); - createJar(manifest); - - TestResult tr = doExec(javaCmd, "-jar", VERSION_JAR); - tr.checkPositive(); - tr.contains(System.getProperty("java.version")); - if (!tr.testStatus) { - System.out.println(tr); - throw new RuntimeException("Failed.\n"); - } - } - - private void emitFile() throws IOException { - List scr = new ArrayList<>(); - scr.add("public class PrintVersion {"); - scr.add(" public static void main(String... args) {"); - scr.add(" System.out.println(System.getProperty(\"java.version\"));"); - scr.add(" }"); - scr.add("}"); - createFile(javaFile, scr); - compile(javaFile.getName()); - } - - private void createJar(Map manifestAttributes) throws IOException { - emitFile(); - - Manifest manifest = new Manifest(); - final Attributes mainAttributes = manifest.getMainAttributes(); - mainAttributes.putValue("Manifest-Version", "1.0"); - mainAttributes.putValue("Main-Class", PRINT_VERSION_CLASS); - manifestAttributes.forEach(mainAttributes::putValue); - - try (JarOutputStream jar = new JarOutputStream(new FileOutputStream(VERSION_JAR), manifest)) { - jar.putNextEntry(new ZipEntry(PRINT_VERSION_CLASS + ".class")); - jar.write(Files.readAllBytes(clsFile.toPath())); - jar.closeEntry(); - } finally { - javaFile.delete(); - } - } - - private enum Flag { - EMPTY("", ""), - VERSION("-version:1.9", "Error: Specifying an alternate JDK/JRE version is no longer supported.\n" + - "The use of the flag '-version:' is no longer valid.\n" + - "Please download and execute the appropriate version."), - JRE_RESTRICT_SEARCH("-jre-restrict-search", "Error: Specifying an alternate JDK/JRE is no longer supported.\n" + - "The related flags -jre-restrict-search | -jre-no-restrict-search are also no longer valid."), - JRE_NO_RESTRICT_SEARCH("-jre-no-restrict-search", "Error: Specifying an alternate JDK/JRE is no longer supported.\n" + - "The related flags -jre-restrict-search | -jre-no-restrict-search are also no longer valid."); - private final String flag; - private final String errorMessage; - - Flag(String flag, String errorMessage) { - this.flag = flag; - this.errorMessage = errorMessage; - } - - String value() { - return flag; - } - - String errorMessage() { - return errorMessage; - } - } -} diff --git a/test/jdk/tools/launcher/VersionCheck.java b/test/jdk/tools/launcher/VersionCheck.java index 8ce636c4af7cf..539af8698d473 100644 --- a/test/jdk/tools/launcher/VersionCheck.java +++ b/test/jdk/tools/launcher/VersionCheck.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,6 @@ public class VersionCheck extends TestHelper { // tools that do not accept -version static final String[] BLACKLIST_VERSION = { - "appletviewer", "controlpanel", "jaccessinspector", "jaccessinspector-32", diff --git a/test/jdk/tools/sincechecker/SinceChecker.java b/test/jdk/tools/sincechecker/SinceChecker.java new file mode 100644 index 0000000000000..860db6a2798fd --- /dev/null +++ b/test/jdk/tools/sincechecker/SinceChecker.java @@ -0,0 +1,948 @@ +/* + * 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.io.IOException; +import java.io.UncheckedIOException; +import java.lang.Runtime.Version; +import java.net.URI; +import java.nio.file.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.lang.model.element.*; +import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.*; +import javax.tools.JavaFileManager.Location; +import com.sun.source.tree.*; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.util.Pair; +import jtreg.SkippedException; + +/* +This checker checks the values of the `@since` tag found in the documentation comment for an element against +the release in which the element first appeared. +The source code containing the documentation comments is read from `src.zip` in the release of JDK used to run the test. +The releases used to determine the expected value of `@since` tags are taken from the historical data built into `javac`. + +The `@since` checker works as a two-step process: +In the first step, we process JDKs 9-current, only classfiles, + producing a map ` => ``. + - "version(s)", because we handle versioning of Preview API, so there may be two versions + (we use a class with two fields for preview and stable), + one when it was introduced as a preview, and one when it went out of preview. More on that below. + - For each Element, we compute the unique ID, look into the map, and if there's nothing, + record the current version as the originating version. + - At the end of this step we have a map of the Real since values + +In the second step, we look at "effective" `@since` tags in the mainline sources, from `src.zip` + (if the test run doesn't have it, we throw a `jtreg.SkippedException`) + - We only check the specific MODULE whose name was passed as an argument in the test. + In that module, we look for unqualified exports and test those packages. + - The `@since` checker verifies that for every API element, the real since value and + the effective since value are the same, and reports an error if they are not. + +Important note : We only check code written since JDK 9 as the releases used to determine the expected value + of @since tags are taken from the historical data built into javac which only goes back that far + +note on rules for Real and effective `@since: + +Real since value of an API element is computed as the oldest release in which the given API element was introduced. +That is: +- for modules, packages, classes and interfaces, the release in which the element with the given qualified name was introduced +- for constructors, the release in which the constructor with the given VM descriptor was introduced +- for methods and fields, the release in which the given method or field with the given VM descriptor became a member + of its enclosing class or interface, whether direct or inherited + +Effective since value of an API element is computed as follows: +- if the given element has a @since tag in its javadoc, it is used +- in all other cases, return the effective since value of the enclosing element + + +Special Handling for preview method, as per JEP 12: +- When an element is still marked as preview, the `@since` should be the first JDK release where the element was added. +- If the element is no longer marked as preview, the `@since` should be the first JDK release where it was no longer preview. + +note on legacy preview: Until JDK 14, the preview APIs were not marked in any machine-understandable way. + It was deprecated, and had a comment in the javadoc. + and the use of `@PreviewFeature` only became standard in JDK 17. + So the checker has an explicit knowledge of these preview elements. + +note: The `` for methods looks like + `method: .()`. +it is somewhat inspired from the VM Method Descriptors. But we use the erased return so that methods +that were later generified remain the same. + +usage: the checker is run from a module specific test + `@run main SinceChecker [--exclude package1,package2 | --exclude package1 package2]` +*/ + +public class SinceChecker { + private final Map> LEGACY_PREVIEW_METHODS = new HashMap<>(); + private final Map classDictionary = new HashMap<>(); + private final JavaCompiler tool; + private int errorCount = 0; + + // packages to skip during the test + private static final Set EXCLUDE_LIST = new HashSet<>(); + + public static class IntroducedIn { + public String introducedPreview; + public String introducedStable; + } + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new IllegalArgumentException("Test module not specified"); + } + String moduleName = args[0]; + boolean excludeFlag = false; + + for (int i = 1; i < args.length; i++) { + if ("--exclude".equals(args[i])) { + excludeFlag = true; + continue; + } + + if (excludeFlag) { + if (args[i].contains(",")) { + EXCLUDE_LIST.addAll(Arrays.asList(args[i].split(","))); + } else { + EXCLUDE_LIST.add(args[i]); + } + } + } + + SinceChecker sinceCheckerTestHelper = new SinceChecker(moduleName); + sinceCheckerTestHelper.checkModule(moduleName); + } + + private void error(String message) { + System.err.println(message); + errorCount++; + } + + private SinceChecker(String moduleName) throws IOException { + tool = ToolProvider.getSystemJavaCompiler(); + for (int i = 9; i <= Runtime.version().feature(); i++) { + DiagnosticListener noErrors = d -> { + if (!d.getCode().equals("compiler.err.module.not.found")) { + error(d.getMessage(null)); + } + }; + JavacTask ct = (JavacTask) tool.getTask(null, + null, + noErrors, + List.of("--add-modules", moduleName, "--release", String.valueOf(i)), + null, + Collections.singletonList(SimpleJavaFileObject.forSource(URI.create("myfo:/Test.java"), ""))); + ct.analyze(); + + String version = String.valueOf(i); + Elements elements = ct.getElements(); + elements.getModuleElement("java.base"); // forces module graph to be instantiated + elements.getAllModuleElements().forEach(me -> + processModuleElement(me, version, ct)); + } + } + + private void processModuleElement(ModuleElement moduleElement, String releaseVersion, JavacTask ct) { + processElement(moduleElement, moduleElement, ct.getTypes(), releaseVersion); + for (ModuleElement.ExportsDirective ed : ElementFilter.exportsIn(moduleElement.getDirectives())) { + if (ed.getTargetModules() == null) { + processPackageElement(ed.getPackage(), releaseVersion, ct); + } + } + } + + private void processPackageElement(PackageElement pe, String releaseVersion, JavacTask ct) { + processElement(pe, pe, ct.getTypes(), releaseVersion); + List typeElements = ElementFilter.typesIn(pe.getEnclosedElements()); + for (TypeElement te : typeElements) { + processClassElement(te, releaseVersion, ct.getTypes(), ct.getElements()); + } + } + + /// JDK documentation only contains public and protected declarations + private boolean isDocumented(Element te) { + Set mod = te.getModifiers(); + return mod.contains(Modifier.PUBLIC) || mod.contains(Modifier.PROTECTED); + } + + private boolean isMember(Element e) { + var kind = e.getKind(); + return kind.isField() || switch (kind) { + case METHOD, CONSTRUCTOR -> true; + default -> false; + }; + } + + private void processClassElement(TypeElement te, String version, Types types, Elements elements) { + if (!isDocumented(te)) { + return; + } + processElement(te.getEnclosingElement(), te, types, version); + elements.getAllMembers(te).stream() + .filter(this::isDocumented) + .filter(this::isMember) + .forEach(element -> processElement(te, element, types, version)); + te.getEnclosedElements().stream() + .filter(element -> element.getKind().isDeclaredType()) + .map(TypeElement.class::cast) + .forEach(nestedClass -> processClassElement(nestedClass, version, types, elements)); + } + + private void processElement(Element explicitOwner, Element element, Types types, String version) { + String uniqueId = getElementName(explicitOwner, element, types); + IntroducedIn introduced = classDictionary.computeIfAbsent(uniqueId, _ -> new IntroducedIn()); + if (isPreview(element, uniqueId, version)) { + if (introduced.introducedPreview == null) { + introduced.introducedPreview = version; + } + } else { + if (introduced.introducedStable == null) { + introduced.introducedStable = version; + } + } + } + + private boolean isPreview(Element el, String uniqueId, String currentVersion) { + while (el != null) { + Symbol s = (Symbol) el; + if ((s.flags() & Flags.PREVIEW_API) != 0) { + return true; + } + el = el.getEnclosingElement(); + } + + return LEGACY_PREVIEW_METHODS.getOrDefault(currentVersion, Set.of()) + .contains(uniqueId); + } + + private void checkModule(String moduleName) throws Exception { + Path home = Paths.get(System.getProperty("java.home")); + Path srcZip = home.resolve("lib").resolve("src.zip"); + if (Files.notExists(srcZip)) { + //possibly running over an exploded JDK build, attempt to find a + //co-located full JDK image with src.zip: + Path testJdk = Paths.get(System.getProperty("test.jdk")); + srcZip = testJdk.getParent().resolve("images").resolve("jdk").resolve("lib").resolve("src.zip"); + } + if (!Files.isReadable(srcZip)) { + throw new SkippedException("Skipping Test because src.zip wasn't found or couldn't be read"); + } + URI uri = URI.create("jar:" + srcZip.toUri()); + try (FileSystem zipFO = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Path root = zipFO.getRootDirectories().iterator().next(); + Path moduleDirectory = root.resolve(moduleName); + try (StandardJavaFileManager fm = + tool.getStandardFileManager(null, null, null)) { + JavacTask ct = (JavacTask) tool.getTask(null, + fm, + null, + List.of("--add-modules", moduleName, "-d", "."), + null, + Collections.singletonList(SimpleJavaFileObject.forSource(URI.create("myfo:/Test.java"), ""))); + ct.analyze(); + Elements elements = ct.getElements(); + elements.getModuleElement("java.base"); + try (EffectiveSourceSinceHelper javadocHelper = EffectiveSourceSinceHelper.create(ct, List.of(root), this)) { + processModuleCheck(elements.getModuleElement(moduleName), ct, moduleDirectory, javadocHelper); + } catch (Exception e) { + e.printStackTrace(); + error("Initiating javadocHelper Failed " + e); + } + if (errorCount > 0) { + throw new Exception("The `@since` checker found " + errorCount + " problems"); + } + } + } + } + + private boolean isExcluded(ModuleElement.ExportsDirective ed ){ + return EXCLUDE_LIST.stream().anyMatch(excludePackage -> + ed.getPackage().toString().equals(excludePackage) || + ed.getPackage().toString().startsWith(excludePackage + ".")); + } + + private void processModuleCheck(ModuleElement moduleElement, JavacTask ct, Path moduleDirectory, EffectiveSourceSinceHelper javadocHelper) { + if (moduleElement == null) { + error("Module element: was null because `elements.getModuleElement(moduleName)` returns null." + + "fixes are needed for this Module"); + } + String moduleVersion = getModuleVersionFromFile(moduleDirectory); + checkModuleOrPackage(javadocHelper, moduleVersion, moduleElement, ct, "Module: "); + for (ModuleElement.ExportsDirective ed : ElementFilter.exportsIn(moduleElement.getDirectives())) { + if (ed.getTargetModules() == null) { + String packageVersion = getPackageVersionFromFile(moduleDirectory, ed); + if (packageVersion != null && !isExcluded(ed)) { + checkModuleOrPackage(javadocHelper, packageVersion, ed.getPackage(), ct, "Package: "); + analyzePackageCheck(ed.getPackage(), ct, javadocHelper); + } // Skip the package if packageVersion is null + } + } + } + + private void checkModuleOrPackage(EffectiveSourceSinceHelper javadocHelper, String moduleVersion, Element moduleElement, JavacTask ct, String elementCategory) { + String id = getElementName(moduleElement, moduleElement, ct.getTypes()); + var elementInfo = classDictionary.get(id); + if (elementInfo == null) { + error("Element :" + id + " was not mapped"); + return; + } + String version = elementInfo.introducedStable; + if (moduleVersion == null) { + error("Unable to retrieve `@since` for " + elementCategory + id); + } else { + String position = javadocHelper.getElementPosition(id); + checkEquals(position, moduleVersion, version, id); + } + } + + private String getModuleVersionFromFile(Path moduleDirectory) { + Path moduleInfoFile = moduleDirectory.resolve("module-info.java"); + String version = null; + if (Files.exists(moduleInfoFile)) { + try { + String moduleInfoContent = Files.readString(moduleInfoFile); + var extractedVersion = extractSinceVersionFromText(moduleInfoContent); + if (extractedVersion != null) { + version = extractedVersion.toString(); + } + } catch (IOException e) { + error("module-info.java not found or couldn't be opened AND this module has no unqualified exports"); + } + } + return version; + } + + private String getPackageVersionFromFile(Path moduleDirectory, ModuleElement.ExportsDirective ed) { + Path pkgInfo = moduleDirectory.resolve(ed.getPackage() + .getQualifiedName() + .toString() + .replace(".", File.separator) + ) + .resolve("package-info.java"); + + if (!Files.exists(pkgInfo)) { + return null; // Skip if the file does not exist + } + + String packageTopVersion = null; + try { + String packageContent = Files.readString(pkgInfo); + var extractedVersion = extractSinceVersionFromText(packageContent); + if (extractedVersion != null) { + packageTopVersion = extractedVersion.toString(); + } else { + error(ed.getPackage().getQualifiedName() + ": package-info.java exists but doesn't contain @since"); + } + } catch (IOException e) { + error(ed.getPackage().getQualifiedName() + ": package-info.java couldn't be opened"); + } + return packageTopVersion; + } + + private void analyzePackageCheck(PackageElement pe, JavacTask ct, EffectiveSourceSinceHelper javadocHelper) { + List typeElements = ElementFilter.typesIn(pe.getEnclosedElements()); + for (TypeElement te : typeElements) { + analyzeClassCheck(te, null, javadocHelper, ct.getTypes(), ct.getElements()); + } + } + + private boolean isNotCommonRecordMethod(TypeElement te, Element element, Types types) { + var isRecord = te.getKind() == ElementKind.RECORD; + if (!isRecord) { + return true; + } + String uniqueId = getElementName(te, element, types); + boolean isCommonMethod = uniqueId.endsWith(".toString()") || + uniqueId.endsWith(".hashCode()") || + uniqueId.endsWith(".equals(java.lang.Object)"); + if (isCommonMethod) { + return false; + } + for (var parameter : te.getEnclosedElements()) { + if (parameter.getKind() == ElementKind.RECORD_COMPONENT) { + if (uniqueId.endsWith(String.format("%s.%s()", te.getSimpleName(), parameter.getSimpleName().toString()))) { + return false; + } + } + } + return true; + } + + private void analyzeClassCheck(TypeElement te, String version, EffectiveSourceSinceHelper javadocHelper, + Types types, Elements elementUtils) { + String currentjdkVersion = String.valueOf(Runtime.version().feature()); + if (!isDocumented(te)) { + return; + } + checkElement(te.getEnclosingElement(), te, types, javadocHelper, version, elementUtils); + te.getEnclosedElements().stream().filter(this::isDocumented) + .filter(this::isMember) + .filter(element -> isNotCommonRecordMethod(te, element, types)) + .forEach(element -> checkElement(te, element, types, javadocHelper, version, elementUtils)); + te.getEnclosedElements().stream() + .filter(element -> element.getKind().isDeclaredType()) + .map(TypeElement.class::cast) + .forEach(nestedClass -> analyzeClassCheck(nestedClass, currentjdkVersion, javadocHelper, types, elementUtils)); + } + + private void checkElement(Element explicitOwner, Element element, Types types, + EffectiveSourceSinceHelper javadocHelper, String currentVersion, Elements elementUtils) { + String uniqueId = getElementName(explicitOwner, element, types); + + if (element.getKind() == ElementKind.METHOD && + element.getEnclosingElement().getKind() == ElementKind.ENUM && + (uniqueId.contains(".values()") || uniqueId.contains(".valueOf(java.lang.String)"))) { + //mandated enum type methods + return; + } + String sinceVersion = null; + var effectiveSince = javadocHelper.effectiveSinceVersion(explicitOwner, element, types, elementUtils); + if (effectiveSince == null) { + // Skip the element if the java file doesn't exist in src.zip + return; + } + sinceVersion = effectiveSince.toString(); + IntroducedIn mappedVersion = classDictionary.get(uniqueId); + if (mappedVersion == null) { + error("Element: " + uniqueId + " was not mapped"); + return; + } + String realMappedVersion = null; + try { + realMappedVersion = isPreview(element, uniqueId, currentVersion) ? + mappedVersion.introducedPreview : + mappedVersion.introducedStable; + } catch (Exception e) { + error("For element " + element + "mappedVersion" + mappedVersion + " is null " + e); + } + String position = javadocHelper.getElementPosition(uniqueId); + checkEquals(position, sinceVersion, realMappedVersion, uniqueId); + } + + private Version extractSinceVersionFromText(String documentation) { + Pattern pattern = Pattern.compile("@since\\s+(\\d+(?:\\.\\d+)?)"); + Matcher matcher = pattern.matcher(documentation); + if (matcher.find()) { + String versionString = matcher.group(1); + try { + if (versionString.equals("1.0")) { + versionString = "1"; //ended up being necessary + } else if (versionString.startsWith("1.")) { + versionString = versionString.substring(2); + } + return Version.parse(versionString); + } catch (NumberFormatException ex) { + error("`@since` value that cannot be parsed: " + versionString); + return null; + } + } else { + return null; + } + } + + private void checkEquals(String prefix, String sinceVersion, String mappedVersion, String name) { + if (sinceVersion == null || mappedVersion == null) { + error(name + ": NULL value for either real or effective `@since` . real/mapped version is=" + + mappedVersion + " while the `@since` in the source code is= " + sinceVersion); + return; + } + if (Integer.parseInt(sinceVersion) < 9) { + sinceVersion = "9"; + } + if (!sinceVersion.equals(mappedVersion)) { + String message = getWrongSinceMessage(prefix, sinceVersion, mappedVersion, name); + error(message); + } + } + private static String getWrongSinceMessage(String prefix, String sinceVersion, String mappedVersion, String elementSimpleName) { + String message; + if (mappedVersion.equals("9")) { + message = elementSimpleName + ": `@since` version is " + sinceVersion + " but the element exists before JDK 10"; + } else { + message = elementSimpleName + ": `@since` version: " + sinceVersion + "; should be: " + mappedVersion; + } + return prefix + message; + } + + private static String getElementName(Element owner, Element element, Types types) { + String prefix = ""; + String suffix = ""; + ElementKind kind = element.getKind(); + if (kind.isField()) { + TypeElement te = (TypeElement) owner; + prefix = "field"; + suffix = ": " + te.getQualifiedName() + ":" + element.getSimpleName(); + } else if (kind == ElementKind.METHOD || kind == ElementKind.CONSTRUCTOR) { + prefix = "method"; + TypeElement te = (TypeElement) owner; + ExecutableElement executableElement = (ExecutableElement) element; + String returnType = types.erasure(executableElement.getReturnType()).toString(); + String methodName = executableElement.getSimpleName().toString(); + String descriptor = executableElement.getParameters().stream() + .map(p -> types.erasure(p.asType()).toString()) + .collect(Collectors.joining(",", "(", ")")); + suffix = ": " + returnType + " " + te.getQualifiedName() + "." + methodName + descriptor; + } else if (kind.isDeclaredType()) { + if (kind.isClass()) { + prefix = "class"; + } else if (kind.isInterface()) { + prefix = "interface"; + } + suffix = ": " + ((TypeElement) element).getQualifiedName(); + } else if (kind == ElementKind.PACKAGE) { + prefix = "package"; + suffix = ": " + ((PackageElement) element).getQualifiedName(); + } else if (kind == ElementKind.MODULE) { + prefix = "module"; + suffix = ": " + ((ModuleElement) element).getQualifiedName(); + } + return prefix + suffix; + } + + //these were preview in before the introduction of the @PreviewFeature + { + LEGACY_PREVIEW_METHODS.put("9", Set.of( + "module: jdk.nio.mapmode", + "module: java.transaction.xa", + "module: jdk.unsupported.desktop", + "module: jdk.jpackage", + "module: java.net.http" + )); + LEGACY_PREVIEW_METHODS.put("10", Set.of( + "module: jdk.nio.mapmode", + "module: java.transaction.xa", + "module: java.net.http", + "module: jdk.unsupported.desktop", + "module: jdk.jpackage" + )); + LEGACY_PREVIEW_METHODS.put("11", Set.of( + "module: jdk.nio.mapmode", + "module: jdk.jpackage" + )); + LEGACY_PREVIEW_METHODS.put("12", Set.of( + "module: jdk.nio.mapmode", + "module: jdk.jpackage", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.BreakTree.getValue()", + "method: java.util.List com.sun.source.tree.CaseTree.getExpressions()", + "method: com.sun.source.tree.Tree com.sun.source.tree.CaseTree.getBody()", + "method: com.sun.source.tree.CaseTree.CaseKind com.sun.source.tree.CaseTree.getCaseKind()", + "class: com.sun.source.tree.CaseTree.CaseKind", + "field: com.sun.source.tree.CaseTree.CaseKind:STATEMENT", + "field: com.sun.source.tree.CaseTree.CaseKind:RULE", + "field: com.sun.source.tree.Tree.Kind:SWITCH_EXPRESSION", + "interface: com.sun.source.tree.SwitchExpressionTree", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.SwitchExpressionTree.getExpression()", + "method: java.util.List com.sun.source.tree.SwitchExpressionTree.getCases()", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.TreeScanner.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.SimpleTreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("13", Set.of( + "module: jdk.nio.mapmode", + "module: jdk.jpackage", + "method: java.util.List com.sun.source.tree.CaseTree.getExpressions()", + "method: com.sun.source.tree.Tree com.sun.source.tree.CaseTree.getBody()", + "method: com.sun.source.tree.CaseTree.CaseKind com.sun.source.tree.CaseTree.getCaseKind()", + "class: com.sun.source.tree.CaseTree.CaseKind", + "field: com.sun.source.tree.CaseTree.CaseKind:STATEMENT", + "field: com.sun.source.tree.CaseTree.CaseKind:RULE", + "field: com.sun.source.tree.Tree.Kind:SWITCH_EXPRESSION", + "interface: com.sun.source.tree.SwitchExpressionTree", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.SwitchExpressionTree.getExpression()", + "method: java.util.List com.sun.source.tree.SwitchExpressionTree.getCases()", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.TreeScanner.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.SimpleTreeVisitor.visitSwitchExpression(com.sun.source.tree.SwitchExpressionTree,java.lang.Object)", + "method: java.lang.String java.lang.String.stripIndent()", + "method: java.lang.String java.lang.String.translateEscapes()", + "method: java.lang.String java.lang.String.formatted(java.lang.Object[])", + "class: javax.swing.plaf.basic.motif.MotifLookAndFeel", + "field: com.sun.source.tree.Tree.Kind:YIELD", + "interface: com.sun.source.tree.YieldTree", + "method: com.sun.source.tree.ExpressionTree com.sun.source.tree.YieldTree.getValue()", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitYield(com.sun.source.tree.YieldTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.SimpleTreeVisitor.visitYield(com.sun.source.tree.YieldTree,java.lang.Object)", + "method: java.lang.Object com.sun.source.util.TreeScanner.visitYield(com.sun.source.tree.YieldTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("14", Set.of( + "module: jdk.jpackage", + "class: javax.swing.plaf.basic.motif.MotifLookAndFeel", + "field: jdk.jshell.Snippet.SubKind:RECORD_SUBKIND", + "class: javax.lang.model.element.RecordComponentElement", + "method: javax.lang.model.type.TypeMirror javax.lang.model.element.RecordComponentElement.asType()", + "method: java.lang.Object javax.lang.model.element.ElementVisitor.visitRecordComponent(javax.lang.model.element.RecordComponentElement,java.lang.Object)", + "class: javax.lang.model.util.ElementScanner14", + "class: javax.lang.model.util.AbstractElementVisitor14", + "class: javax.lang.model.util.SimpleElementVisitor14", + "method: java.lang.Object javax.lang.model.util.ElementKindVisitor6.visitTypeAsRecord(javax.lang.model.element.TypeElement,java.lang.Object)", + "class: javax.lang.model.util.ElementKindVisitor14", + "method: javax.lang.model.element.RecordComponentElement javax.lang.model.util.Elements.recordComponentFor(javax.lang.model.element.ExecutableElement)", + "method: java.util.List javax.lang.model.util.ElementFilter.recordComponentsIn(java.lang.Iterable)", + "method: java.util.Set javax.lang.model.util.ElementFilter.recordComponentsIn(java.util.Set)", + "method: java.util.List javax.lang.model.element.TypeElement.getRecordComponents()", + "field: javax.lang.model.element.ElementKind:RECORD", + "field: javax.lang.model.element.ElementKind:RECORD_COMPONENT", + "field: javax.lang.model.element.ElementKind:BINDING_VARIABLE", + "field: com.sun.source.tree.Tree.Kind:RECORD", + "field: sun.reflect.annotation.TypeAnnotation.TypeAnnotationTarget:RECORD_COMPONENT", + "class: java.lang.reflect.RecordComponent", + "class: java.lang.runtime.ObjectMethods", + "field: java.lang.annotation.ElementType:RECORD_COMPONENT", + "method: boolean java.lang.Class.isRecord()", + "method: java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents()", + "class: java.lang.Record", + "interface: com.sun.source.tree.PatternTree", + "field: com.sun.source.tree.Tree.Kind:BINDING_PATTERN", + "method: com.sun.source.tree.PatternTree com.sun.source.tree.InstanceOfTree.getPattern()", + "interface: com.sun.source.tree.BindingPatternTree", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitBindingPattern(com.sun.source.tree.BindingPatternTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("15", Set.of( + "module: jdk.jpackage", + "field: jdk.jshell.Snippet.SubKind:RECORD_SUBKIND", + "class: javax.lang.model.element.RecordComponentElement", + "method: javax.lang.model.type.TypeMirror javax.lang.model.element.RecordComponentElement.asType()", + "method: java.lang.Object javax.lang.model.element.ElementVisitor.visitRecordComponent(javax.lang.model.element.RecordComponentElement,java.lang.Object)", + "class: javax.lang.model.util.ElementScanner14", + "class: javax.lang.model.util.AbstractElementVisitor14", + "class: javax.lang.model.util.SimpleElementVisitor14", + "method: java.lang.Object javax.lang.model.util.ElementKindVisitor6.visitTypeAsRecord(javax.lang.model.element.TypeElement,java.lang.Object)", + "class: javax.lang.model.util.ElementKindVisitor14", + "method: javax.lang.model.element.RecordComponentElement javax.lang.model.util.Elements.recordComponentFor(javax.lang.model.element.ExecutableElement)", + "method: java.util.List javax.lang.model.util.ElementFilter.recordComponentsIn(java.lang.Iterable)", + "method: java.util.Set javax.lang.model.util.ElementFilter.recordComponentsIn(java.util.Set)", + "method: java.util.List javax.lang.model.element.TypeElement.getRecordComponents()", + "field: javax.lang.model.element.ElementKind:RECORD", + "field: javax.lang.model.element.ElementKind:RECORD_COMPONENT", + "field: javax.lang.model.element.ElementKind:BINDING_VARIABLE", + "field: com.sun.source.tree.Tree.Kind:RECORD", + "field: sun.reflect.annotation.TypeAnnotation.TypeAnnotationTarget:RECORD_COMPONENT", + "class: java.lang.reflect.RecordComponent", + "class: java.lang.runtime.ObjectMethods", + "field: java.lang.annotation.ElementType:RECORD_COMPONENT", + "class: java.lang.Record", + "method: boolean java.lang.Class.isRecord()", + "method: java.lang.reflect.RecordComponent[] java.lang.Class.getRecordComponents()", + "field: javax.lang.model.element.Modifier:SEALED", + "field: javax.lang.model.element.Modifier:NON_SEALED", + "method: javax.lang.model.element.TypeElement:getPermittedSubclasses:()", + "method: java.util.List com.sun.source.tree.ClassTree.getPermitsClause()", + "method: boolean java.lang.Class.isSealed()", + "method: java.lang.constant.ClassDesc[] java.lang.Class.permittedSubclasses()", + "interface: com.sun.source.tree.PatternTree", + "field: com.sun.source.tree.Tree.Kind:BINDING_PATTERN", + "method: com.sun.source.tree.PatternTree com.sun.source.tree.InstanceOfTree.getPattern()", + "interface: com.sun.source.tree.BindingPatternTree", + "method: java.lang.Object com.sun.source.tree.TreeVisitor.visitBindingPattern(com.sun.source.tree.BindingPatternTree,java.lang.Object)" + )); + + LEGACY_PREVIEW_METHODS.put("16", Set.of( + "field: jdk.jshell.Snippet.SubKind:RECORD_SUBKIND", + "field: javax.lang.model.element.Modifier:SEALED", + "field: javax.lang.model.element.Modifier:NON_SEALED", + "method: javax.lang.model.element.TypeElement:getPermittedSubclasses:()", + "method: java.util.List com.sun.source.tree.ClassTree.getPermitsClause()", + "method: boolean java.lang.Class.isSealed()", + "method: java.lang.constant.ClassDesc[] java.lang.Class.permittedSubclasses()" + )); + + // java.lang.foreign existed since JDK 19 and wasn't annotated - went out of preview in JDK 22 + LEGACY_PREVIEW_METHODS.put("19", Set.of( + "package: java.lang.foreign" + )); + LEGACY_PREVIEW_METHODS.put("20", Set.of( + "package: java.lang.foreign" + )); + LEGACY_PREVIEW_METHODS.put("21", Set.of( + "package: java.lang.foreign" + )); + } + + /** + * Helper to find javadoc and resolve @inheritDoc and the effective since version. + */ + + private final class EffectiveSourceSinceHelper implements AutoCloseable { + private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + private final JavaFileManager baseFileManager; + private final StandardJavaFileManager fm; + private final Set seenLookupElements = new HashSet<>(); + private final Map signature2Source = new HashMap<>(); + private final Map signature2Location = new HashMap<>(); + + /** + * Create the helper. + * + * @param mainTask JavacTask from which the further Elements originate + * @param sourceLocations paths where source files should be searched + * @param validator enclosing class of the helper, typically the object invoking this method + * @return a EffectiveSourceSinceHelper + */ + + public static EffectiveSourceSinceHelper create(JavacTask mainTask, Collection sourceLocations, SinceChecker validator) { + StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null); + try { + fm.setLocationFromPaths(StandardLocation.MODULE_SOURCE_PATH, sourceLocations); + return validator.new EffectiveSourceSinceHelper(mainTask, fm); + } catch (IOException ex) { + try { + fm.close(); + } catch (IOException closeEx) { + ex.addSuppressed(closeEx); + } + throw new UncheckedIOException(ex); + } + } + + private EffectiveSourceSinceHelper(JavacTask mainTask, StandardJavaFileManager fm) { + this.baseFileManager = ((JavacTaskImpl) mainTask).getContext().get(JavaFileManager.class); + this.fm = fm; + } + + public Version effectiveSinceVersion(Element owner, Element element, Types typeUtils, Elements elementUtils) { + String handle = getElementName(owner, element, typeUtils); + Version since = signature2Source.get(handle); + + if (since == null) { + try { + Element lookupElement = switch (element.getKind()) { + case MODULE, PACKAGE -> element; + default -> elementUtils.getOutermostTypeElement(element); + }; + + if (lookupElement == null) + return null; + + String lookupHandle = getElementName(owner, element, typeUtils); + + if (!seenLookupElements.add(lookupHandle)) { + //we've already processed this top-level, don't try to compute + //the values again: + return null; + } + + Pair source = findSource(lookupElement, elementUtils); + + if (source == null) + return null; + + fillElementCache(source.fst, source.snd, source.fst.getTypes(), source.fst.getElements()); + since = signature2Source.get(handle); + + } catch (IOException ex) { + error("JavadocHelper failed for " + element); + } + } + + return since; + } + + private String getElementPosition(String signature) { + return signature2Location.getOrDefault(signature, ""); + } + + //where: + private void fillElementCache(JavacTask task, CompilationUnitTree cut, Types typeUtils, Elements elementUtils) { + Trees trees = Trees.instance(task); + String fileName = cut.getSourceFile().getName(); + + new TreePathScanner() { + @Override + public Void visitMethod(MethodTree node, Void p) { + handleDeclaration(node, fileName); + return null; + } + + @Override + public Void visitClass(ClassTree node, Void p) { + handleDeclaration(node, fileName); + return super.visitClass(node, p); + } + + @Override + public Void visitVariable(VariableTree node, Void p) { + handleDeclaration(node, fileName); + return null; + } + + @Override + public Void visitModule(ModuleTree node, Void p) { + handleDeclaration(node, fileName); + return null; + } + + @Override + public Void visitBlock(BlockTree node, Void p) { + return null; + } + + @Override + public Void visitPackage(PackageTree node, Void p) { + if (cut.getSourceFile().isNameCompatible("package-info", JavaFileObject.Kind.SOURCE)) { + handleDeclaration(node, fileName); + } + return super.visitPackage(node, p); + } + + private void handleDeclaration(Tree node, String fileName) { + Element currentElement = trees.getElement(getCurrentPath()); + + if (currentElement != null) { + long startPosition = trees.getSourcePositions().getStartPosition(cut, node); + long lineNumber = cut.getLineMap().getLineNumber(startPosition); + String filePathWithLineNumber = String.format("src%s:%s ", fileName, lineNumber); + + signature2Source.put(getElementName(currentElement.getEnclosingElement(), currentElement, typeUtils), computeSinceVersion(currentElement, typeUtils, elementUtils)); + signature2Location.put(getElementName(currentElement.getEnclosingElement(), currentElement, typeUtils), filePathWithLineNumber); + } + } + }.scan(cut, null); + } + + private Version computeSinceVersion(Element element, Types types, + Elements elementUtils) { + String docComment = elementUtils.getDocComment(element); + Version version = null; + if (docComment != null) { + version = extractSinceVersionFromText(docComment); + } + + if (version != null) { + return version; //explicit @since has an absolute priority + } + + if (element.getKind() != ElementKind.MODULE) { + version = effectiveSinceVersion(element.getEnclosingElement().getEnclosingElement(), element.getEnclosingElement(), types, elementUtils); + } + + return version; + } + + private Pair findSource(Element forElement, Elements elementUtils) throws IOException { + String moduleName = elementUtils.getModuleOf(forElement).getQualifiedName().toString(); + String binaryName = switch (forElement.getKind()) { + case MODULE -> "module-info"; + case PACKAGE -> ((QualifiedNameable) forElement).getQualifiedName() + ".package-info"; + default -> elementUtils.getBinaryName((TypeElement) forElement).toString(); + }; + Location packageLocationForModule = fm.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH, moduleName); + JavaFileObject jfo = fm.getJavaFileForInput(packageLocationForModule, + binaryName, + JavaFileObject.Kind.SOURCE); + + if (jfo == null) + return null; + + List jfos = Arrays.asList(jfo); + JavaFileManager patchFM = moduleName != null + ? new PatchModuleFileManager(baseFileManager, jfo, moduleName) + : baseFileManager; + JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, patchFM, d -> { + }, null, null, jfos); + Iterable cuts = task.parse(); + + task.enter(); + + return Pair.of(task, cuts.iterator().next()); + } + + @Override + public void close() throws IOException { + fm.close(); + } + + /** + * Manages files within a patch module. + * Provides custom behavior for handling file locations within a patch module. + * Includes methods to specify module locations, infer module names and determine + * if a location belongs to the patch module path. + */ + private static final class PatchModuleFileManager + extends ForwardingJavaFileManager { + + private final JavaFileObject file; + private final String moduleName; + + public PatchModuleFileManager(JavaFileManager fileManager, + JavaFileObject file, + String moduleName) { + super(fileManager); + this.file = file; + this.moduleName = moduleName; + } + + @Override + public Location getLocationForModule(Location location, + JavaFileObject fo) throws IOException { + return fo == file + ? PATCH_LOCATION + : super.getLocationForModule(location, fo); + } + + @Override + public String inferModuleName(Location location) throws IOException { + return location == PATCH_LOCATION + ? moduleName + : super.inferModuleName(location); + } + + @Override + public boolean hasLocation(Location location) { + return location == StandardLocation.PATCH_MODULE_PATH || + super.hasLocation(location); + } + + private static final Location PATCH_LOCATION = new Location() { + @Override + public String getName() { + return "PATCH_LOCATION"; + } + + @Override + public boolean isOutputLocation() { + return false; + } + + @Override + public boolean isModuleOrientedLocation() { + return false; + } + }; + } + } +} diff --git a/test/jdk/tools/sincechecker/modules/java_base/CheckSince_javaBase.java b/test/jdk/tools/sincechecker/modules/java_base/CheckSince_javaBase.java new file mode 100644 index 0000000000000..6d0b9d0e932e8 --- /dev/null +++ b/test/jdk/tools/sincechecker/modules/java_base/CheckSince_javaBase.java @@ -0,0 +1,34 @@ +/* + * 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 8331051 + * @summary Test for `@since` for java.base module + * @library /test/lib + * /test/jdk/tools/sincechecker + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.util + * jdk.compiler/com.sun.tools.javac.code + * @run main SinceChecker java.base --exclude java.lang.classfile + */ diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 795a658afa591..465c641d44204 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -128,11 +128,10 @@ public Map call() { map.put("vm.graal.enabled", this::isGraalEnabled); // jdk.hasLibgraal is true if the libgraal shared library file is present map.put("jdk.hasLibgraal", this::hasLibgraal); - // vm.libgraal.enabled is true if libgraal is used as JIT - map.put("vm.libgraal.enabled", this::isLibgraalEnabled); + map.put("vm.libgraal.jit", this::isLibgraalJIT); map.put("vm.compiler1.enabled", this::isCompiler1Enabled); map.put("vm.compiler2.enabled", this::isCompiler2Enabled); - map.put("docker.support", this::dockerSupport); + map.put("container.support", this::containerSupport); map.put("systemd.support", this::systemdSupport); map.put("vm.musl", this::isMusl); map.put("release.implementor", this::implementor); @@ -385,7 +384,9 @@ protected void vmOptFinalFlags(SafeMap map) { vmOptFinalFlag(map, "CriticalJNINatives"); vmOptFinalFlag(map, "EnableJVMCI"); vmOptFinalFlag(map, "EliminateAllocations"); + vmOptFinalFlag(map, "UnlockExperimentalVMOptions"); vmOptFinalFlag(map, "UseCompressedOops"); + vmOptFinalFlag(map, "UseLargePages"); vmOptFinalFlag(map, "UseVectorizedMismatchIntrinsic"); vmOptFinalFlag(map, "ZGenerational"); } @@ -478,12 +479,14 @@ protected boolean isCDSRuntimeOptionsCompatible() { } String CCP_DISABLED = "-XX:-UseCompressedClassPointers"; String G1GC_ENABLED = "-XX:+UseG1GC"; + String PARALLELGC_ENABLED = "-XX:+UseParallelGC"; + String SERIALGC_ENABLED = "-XX:+UseSerialGC"; for (String opt : jtropts.split(",")) { if (opt.equals(CCP_DISABLED)) { return false; } if (opt.startsWith(GC_PREFIX) && opt.endsWith(GC_SUFFIX) && - !opt.equals(G1GC_ENABLED)) { + !opt.equals(G1GC_ENABLED) && !opt.equals(PARALLELGC_ENABLED) && !opt.equals(SERIALGC_ENABLED)) { return false; } } @@ -559,8 +562,8 @@ protected String hasLibgraal() { * * @return true if libgraal is used as JIT compiler. */ - protected String isLibgraalEnabled() { - return "" + Compiler.isLibgraalEnabled(); + protected String isLibgraalJIT() { + return "" + Compiler.isLibgraalJIT(); } /** @@ -582,16 +585,16 @@ protected String isCompiler2Enabled() { } /** - * A simple check for docker support + * A simple check for container support * - * @return true if docker is supported in a given environment + * @return true if container is supported in a given environment */ - protected String dockerSupport() { - log("Entering dockerSupport()"); + protected String containerSupport() { + log("Entering containerSupport()"); boolean isSupported = false; if (Platform.isLinux()) { - // currently docker testing is only supported for Linux, + // currently container testing is only supported for Linux, // on certain platforms String arch = System.getProperty("os.arch"); @@ -607,17 +610,17 @@ protected String dockerSupport() { } } - log("dockerSupport(): platform check: isSupported = " + isSupported); + log("containerSupport(): platform check: isSupported = " + isSupported); if (isSupported) { try { - isSupported = checkProgramSupport("checkDockerSupport()", Container.ENGINE_COMMAND); + isSupported = checkProgramSupport("checkContainerSupport()", Container.ENGINE_COMMAND); } catch (Exception e) { isSupported = false; } } - log("dockerSupport(): returning isSupported = " + isSupported); + log("containerSupport(): returning isSupported = " + isSupported); return "" + isSupported; } diff --git a/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownCodeBlocks.java b/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownCodeBlocks.java index 8f1c76b9e474d..dda8a76868e12 100644 --- a/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownCodeBlocks.java +++ b/test/langtools/jdk/javadoc/doclet/testMarkdown/TestMarkdownCodeBlocks.java @@ -486,4 +486,107 @@ public int hashCode() {

            NullPointerException - if other is null
            """); } + + @Test + public void testLeadingCodeBlock(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + """ + package p; + /// Leading code block + /// Lorum ipsum. + public class C { } + """); + + javadoc("-d", base.resolve("api").toString(), + "--no-platform-links", + "--source-path", src.toString(), + "p"); + checkExit(Exit.OK); + + // check first sentence is empty in package summary file + checkOutput("p/package-summary.html", true, + """ + +
             
            """); + + checkOutput("p/C.html", true, + """ +
            Leading code block
            +                    
            +

            Lorum ipsum.

            """); + + } + + @Test + public void testTrailingCodeBlock(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + """ + package p; + /// Lorum ipsum. + /// + /// Trailing code block + public class C { } + """); + + javadoc("-d", base.resolve("api").toString(), + "--no-platform-links", + "--source-path", src.toString(), + "p"); + checkExit(Exit.OK); + + checkOutput("p/C.html", true, + """ +

            Lorum ipsum.

            +
            Trailing code block
            +                    
            +
            """); + } + + // this example is derived from the test case in JDK-8338525 + @Test + public void testLeadingTrailingCodeBlockWithAnnotations(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + """ + package p; + public class C { + /// @Override + /// void m() {} + /// + /// Plain text + /// + /// @Override + /// void m() {} + public void m() {} + }"""); + + javadoc("-d", base.resolve("api").toString(), + "--no-platform-links", + "--source-path", src.toString(), + "p"); + checkExit(Exit.OK); + + checkOutput("p/C.html", true, + """ +
            void
            +
            m()
            +
             
            """, + """ +
            public \ + void m()
            +
            @Override
            +                    void m() {}
            +                    
            +

            Plain text

            +
            @Override
            +                    void m() {}
            +                    
            +
            """); + + } } diff --git a/test/langtools/jdk/javadoc/tool/subpackageNoModules/SubpackageNoModules.java b/test/langtools/jdk/javadoc/tool/subpackageNoModules/SubpackageNoModules.java new file mode 100644 index 0000000000000..164ec1f19e494 --- /dev/null +++ b/test/langtools/jdk/javadoc/tool/subpackageNoModules/SubpackageNoModules.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. + */ + +/* + * @test + * @bug 8325090 + * @summary javadoc fails when -subpackages option is used with non-modular -source + * @modules jdk.javadoc/jdk.javadoc.internal.api + * jdk.javadoc/jdk.javadoc.internal.tool + * @library /tools/lib + * @build toolbox.TestRunner toolbox.ToolBox + * @run main SubpackageNoModules + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import toolbox.*; +import toolbox.Task.Expect; + +public class SubpackageNoModules extends TestRunner { + + final ToolBox tb = new ToolBox(); + + public SubpackageNoModules() { + super(System.err); + } + + public static void main(String[] args) throws Exception { + SubpackageNoModules t = new SubpackageNoModules(); + t.runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testSubpackageNoModules(Path base) throws Exception { + Files.createDirectories(base); + tb.writeFile(base.resolve("pkg/A.java"), "package pkg;\npublic class A {}\n"); + + Path outDir = base.resolve("out"); + Files.createDirectory(outDir); + // Combine -subpackages option with -source release that doesn't support modules + new JavadocTask(tb) + .outdir(outDir) + .sourcepath(base) + .options("-source", "8", + "-subpackages", "pkg") + .run(Expect.SUCCESS); + // Check for presence of generated docs + if (!Files.isRegularFile(outDir.resolve("pkg/A.html"))) { + error("File not found: " + outDir.resolve("pkg/A.html")); + } + } +} diff --git a/test/langtools/tools/javac/ImportModule.java b/test/langtools/tools/javac/ImportModule.java index f38b77062e224..dc1ed9bd0c12d 100644 --- a/test/langtools/tools/javac/ImportModule.java +++ b/test/langtools/tools/javac/ImportModule.java @@ -797,4 +797,50 @@ public class C {} } } + + @Test + public void testImportModuleNoModules(Path base) throws Exception { + Path current = base.resolve("."); + Path src = current.resolve("src"); + Path classes = current.resolve("classes"); + tb.writeJavaFiles(src, + """ + package test; + import module java.base; + public class Test { + List l; + } + """); + + Files.createDirectories(classes); + + List actualErrors = new JavacTask(tb) + .options("--release", "8", + "-XDshould-stop.at=FLOW", + "-XDdev", + "-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expectedErrors = List.of( + "- compiler.warn.option.obsolete.source: 8", + "- compiler.warn.option.obsolete.target: 8", + "- compiler.warn.option.obsolete.suppression", + "Test.java:2:8: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.module.imports)", + "Test.java:2:1: compiler.err.import.module.not.found: java.base", + "Test.java:4:5: compiler.err.cant.resolve.location: kindname.class, List, , , (compiler.misc.location: kindname.class, test.Test, null)", + "3 errors", + "3 warnings" + ); + + if (!Objects.equals(expectedErrors, actualErrors)) { + throw new AssertionError("Incorrect Output, expected: " + expectedErrors + + ", actual: " + out); + + } + } + } diff --git a/test/langtools/tools/javac/doctree/DocCommentTester.java b/test/langtools/tools/javac/doctree/DocCommentTester.java index f14dde2e567cd..d418005745a0d 100644 --- a/test/langtools/tools/javac/doctree/DocCommentTester.java +++ b/test/langtools/tools/javac/doctree/DocCommentTester.java @@ -1043,8 +1043,9 @@ String normalize(String s, boolean isLineComment, boolean normalizeTags) { .replaceAll("(\\{@value\\s+[^}]+)\\s+(})", "$1$2"); } + // See comment in MarkdownTest for explanation of dummy and Override String normalizeFragment(String s) { - return s.replaceAll("\n[ \t]+@(?!([@*]|dummy))", "\n@"); + return s.replaceAll("\n[ \t]+@(?!([@*]|(dummy|Override)))", "\n@"); } int copyLiteral(String s, int start, StringBuilder sb) { diff --git a/test/langtools/tools/javac/doctree/MarkdownTest.java b/test/langtools/tools/javac/doctree/MarkdownTest.java index 8da9e6ed44c2e..3ec23076db9a8 100644 --- a/test/langtools/tools/javac/doctree/MarkdownTest.java +++ b/test/langtools/tools/javac/doctree/MarkdownTest.java @@ -40,6 +40,7 @@ * In the tests for code spans and code blocks, "@dummy" is used as a dummy inline * or block tag to verify that it is skipped as part of the code span or code block. * In other words, "@dummy" should appear as a literal part of the Markdown content. + * ("@Override" is also treated the same way, as a commonly found annotation.) * Conversely, standard tags are used to verify that a fragment of text is not being * skipped as a code span or code block. In other words, they should be recognized as tags * and not skipped as part of any Markdown content. @@ -409,6 +410,32 @@ void indentedCodeBlock_afterFencedCodeBlock() { } RawText[MARKDOWN, pos:85, .] block tags: empty ] +*/ + + /// Indented Code Block + /// Lorum ipsum. + void indentedCodeBlock_leading() { } +/* +DocComment[DOC_COMMENT, pos:0 + firstSentence: empty + body: 1 + RawText[MARKDOWN, pos:0, ____Indented_Code_Block|Lorum_ipsum.] + block tags: empty +] +*/ + + /// Lorum ipsum. + /// + /// Indented Code Block + void indentedCodeBlock_trailing() { } +/* +DocComment[DOC_COMMENT, pos:0 + firstSentence: 1 + RawText[MARKDOWN, pos:0, Lorum_ipsum.] + body: 1 + RawText[MARKDOWN, pos:18, Indented_Code_Block] + block tags: empty +] */ ///123. @@ -613,5 +640,24 @@ void indent8() { } ] */ +// The following test case is derived from the test case in JDK-8338525. + + /// @Override + /// void m() { } + /// + /// Plain text + /// + /// @Override + /// void m() { } + void leadingTrailingCodeBlocksWithAnnos() { } +/* +DocComment[DOC_COMMENT, pos:0 + firstSentence: empty + body: 1 + RawText[MARKDOWN, pos:0, ____@Override|____void_m()_{_}||...||____@Override|____void_m()_{_}] + block tags: empty +] +*/ + } diff --git a/test/langtools/tools/javac/generics/ParametricException.java b/test/langtools/tools/javac/generics/parametricException/ParametricException.java similarity index 100% rename from test/langtools/tools/javac/generics/ParametricException.java rename to test/langtools/tools/javac/generics/parametricException/ParametricException.java diff --git a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java index acd91eac8c785..904e4e78cad1f 100644 --- a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java +++ b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java @@ -41,6 +41,8 @@ import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.ArrayList; @@ -93,13 +95,15 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor { private static final Map> nameToAnnotation = Map.ofEntries(new NameToAnnotationEntry("java.lang.Override", Override.class), new NameToAnnotationEntry("java.lang.annotation.Repeatable", Repeatable.class), + new NameToAnnotationEntry("java.lang.annotation.Retention", Target.class), new NameToAnnotationEntry("java.lang.annotation.Target", Target.class), new NameToAnnotationEntry("BasicAnnoTests.Test", BasicAnnoTests.Test.class), new NameToAnnotationEntry("BasicAnnoTests.Tests",BasicAnnoTests.Tests.class), new NameToAnnotationEntry("BasicAnnoTests.TA", BasicAnnoTests.TA.class), new NameToAnnotationEntry("BasicAnnoTests.TB", BasicAnnoTests.TB.class), new NameToAnnotationEntry("BasicAnnoTests.TC", BasicAnnoTests.TC.class), - new NameToAnnotationEntry("BasicAnnoTests.TCs", BasicAnnoTests.TCs.class)); + new NameToAnnotationEntry("BasicAnnoTests.TCs", BasicAnnoTests.TCs.class), + new NameToAnnotationEntry("BasicAnnoTests.TD", BasicAnnoTests.TD.class)); static class NameToAnnotationEntry extends AbstractMap.SimpleEntry> { public NameToAnnotationEntry(String key, Class entry) { @@ -520,6 +524,12 @@ R scan(Iterable iter, P p) { TC[] value(); } + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + public @interface TD { + int value(); + } + // Test cases // TODO: add more cases for arrays @@ -657,6 +667,10 @@ public class Inner6 {} @Test(posn=1, annoType=TA.class, expect="23") public Set<@TA(23) ? super Object> f9; + @Test(posn=0, annoType=TA.class, expect="1") + @Test(posn=0, annoType=TD.class, expect="2") + public @TA(1) @TD(2) int f10; + // Test type use annotations on uses of type variables @Test(posn=6, annoType = TA.class, expect = "25") @Test(posn=6, annoType = TB.class, expect = "26") diff --git a/test/langtools/tools/javac/processing/model/util/types/TestInvalidInputs.java b/test/langtools/tools/javac/processing/model/util/types/TestInvalidInputs.java new file mode 100644 index 0000000000000..47b11bbbba84e --- /dev/null +++ b/test/langtools/tools/javac/processing/model/util/types/TestInvalidInputs.java @@ -0,0 +1,230 @@ +/* + * 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 8340721 8341483 + * @summary Test invalid inputs to javax.lang.model.util.Types methods + * @library /tools/javac/lib + * @modules java.compiler + * @build JavacTestingAbstractProcessor TestInvalidInputs + * @compile -processor TestInvalidInputs -proc:only TestInvalidInputs.java + */ + +import java.util.*; +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; + +/** + * Test if exceptions are thrown for invalid arguments as expected. + */ +public class TestInvalidInputs extends JavacTestingAbstractProcessor { + + // Reference types are ArrayType, DeclaredType, ErrorType, NullType, and TypeVariable + + private TypeMirror objectType; // Notable DeclaredType + private TypeMirror stringType; // Another notable DeclaredType + private ArrayType arrayType; + // private ErrorType errorType; // skip for now + private ExecutableType executableType; + private IntersectionType intersectionType; + + private NoType noTypeVoid; + private NoType noTypeNone; + private NoType noTypePackage; + private NoType noTypeModule; + + private NullType nullType; + private PrimitiveType primitiveType; + private UnionType unionType; + private WildcardType wildcardType; + + /** + * Check expected behavior on classes and packages. + */ + public boolean process(Set annotations, + RoundEnvironment roundEnv) { + if (!roundEnv.processingOver()) { + initializeTypes(); + + // isSubType + // isAssignable + // contains + // directSupertypes + testUnboxedType(); + // capture + // getPrimitiveType + // getNoType + testGetArrayType(); + testGetWildcardType(); + // getDeclaredType + // getDeclaredType (overload) + // asMemberOf + } + return true; + } + + void initializeTypes() { + objectType = elements.getTypeElement("java.lang.Object").asType(); + stringType = elements.getTypeElement("java.lang.String").asType(); + + arrayType = types.getArrayType(objectType); // Object[] + executableType = extractExecutableType(); + intersectionType = extractIntersectionType(); + + noTypeVoid = types.getNoType(TypeKind.VOID); + noTypeNone = types.getNoType(TypeKind.NONE); + noTypePackage = (NoType)(elements.getPackageElement("java.lang").asType()); + noTypeModule = (NoType)(elements.getModuleElement("java.base").asType()); + + nullType = types.getNullType(); + primitiveType = types.getPrimitiveType(TypeKind.DOUBLE); + // unionType; // more work here + wildcardType = types.getWildcardType(objectType, null); + + return; + } + + ExecutableType extractExecutableType() { + var typeElement = elements.getTypeElement("TestInvalidInputs.InvalidInputsHost"); + for (var method : ElementFilter.methodsIn(typeElement.getEnclosedElements())) { + if ("foo7".equals(method.getSimpleName().toString())) { + return (ExecutableType)method.asType(); + } + } + throw new RuntimeException("Expected method not found"); + } + + IntersectionType extractIntersectionType() { + var typeElement = elements.getTypeElement("TestInvalidInputs.InvalidInputsHost"); + for (var method : ElementFilter.methodsIn(typeElement.getEnclosedElements())) { + if ("foo9".equals(method.getSimpleName().toString())) { + return (IntersectionType) ((TypeVariable)method.getReturnType()).getUpperBound(); + } + } + throw new RuntimeException("Expected method not found"); + } + + /* + * Class to host inputs for testing. + */ + class InvalidInputsHost { + // Use a method to get an ExecutableType + public static String foo7(int arg) {return null;} + + // Type variable with intersection type + public static S foo9() {return null;} + } + + /** + * @throws IllegalArgumentException if the given type has no + * unboxing conversion, including for types that are not + * {@linkplain ReferenceType reference types} + */ + void testUnboxedType() { + // Only DeclaredType's for wrapper classes should have unboxing conversions defined. + + // Reference types are ArrayType, DeclaredType, ErrorType, NullType, TypeVariable + // non-reference: ExecutableType, IntersectionType, NoType, PrimitiveType, UnionType, WildcardType + var invalidInputs = + List.of(primitiveType, executableType, + objectType, stringType, arrayType, + intersectionType, /*unionType, */ + noTypeVoid, noTypeNone, noTypePackage, noTypeModule, + nullType, + wildcardType); + + for (TypeMirror tm : invalidInputs) { + try { + PrimitiveType pt = types.unboxedType(tm); + shouldNotReach(tm); + } catch(IllegalArgumentException iae) { + ; // Expected + } + } + return; + } + + private void shouldNotReach(TypeMirror tm) { + throw new RuntimeException("Should not reach " + tm + + " " + tm.getKind()); + } + + /** + * @throws IllegalArgumentException if bounds are not valid, + * including for types that are not {@linkplain ReferenceType + * reference types} + */ + void testGetWildcardType() { + // Reference types are ArrayType, DeclaredType, ErrorType, NullType, TypeVariable + // non-reference: ExecutableType, IntersectionType, NoType, PrimitiveType, UnionType, WildcardType + var invalidInputs = + List.of(primitiveType, executableType, + intersectionType, /*unionType, */ + noTypeVoid, noTypeNone, noTypePackage, noTypeModule, + nullType, + wildcardType); + + for (TypeMirror tm : invalidInputs) { + try { + WildcardType wc1 = types.getWildcardType(tm, null); + shouldNotReach(tm); + } catch(IllegalArgumentException iae) { + ; // Expected + } + + try { + WildcardType wc2 = types.getWildcardType(null, tm); + shouldNotReach(tm); + } catch(IllegalArgumentException iae) { + ; // Expected + } + } + return; + } + + /** + * @throws IllegalArgumentException if the component type is not valid for + * an array. All valid types are {@linkplain ReferenceType + * reference types} or {@linkplain PrimitiveType primitive types}. + * Invalid types include null, executable, package, module, and wildcard types. + */ + void testGetArrayType() { + var invalidInputs = + List.of(executableType, + noTypeVoid, noTypeNone, noTypePackage, noTypeModule, + nullType, + /*unionType, */ wildcardType); + + for (TypeMirror tm : invalidInputs) { + try { + ArrayType arrayType = types.getArrayType(tm); + shouldNotReach(tm); + } catch(IllegalArgumentException iae) { + ; // Expected + } + } + } +} diff --git a/test/langtools/tools/javac/resolve/MethodAmbiguityCrash1.java b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash1.java new file mode 100644 index 0000000000000..9e2b7e10080ff --- /dev/null +++ b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash1.java @@ -0,0 +1,23 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8337980 + * @summary Test compiler crash due to failure to resolve method ambiguity + * @compile/fail/ref=MethodAmbiguityCrash1.out -XDrawDiagnostics MethodAmbiguityCrash1.java + */ +public class MethodAmbiguityCrash1 { + + public interface A { + int op(); + } + + public abstract static class B { + abstract int op(); + } + + public abstract static class C extends B implements A { + + public static int test() { + return op(); // compile should fail here + } + } +} diff --git a/test/langtools/tools/javac/resolve/MethodAmbiguityCrash1.out b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash1.out new file mode 100644 index 0000000000000..ab94e965d598e --- /dev/null +++ b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash1.out @@ -0,0 +1,2 @@ +MethodAmbiguityCrash1.java:20:20: compiler.err.non-static.cant.be.ref: kindname.method, op() +1 error diff --git a/test/langtools/tools/javac/resolve/MethodAmbiguityCrash2.java b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash2.java new file mode 100644 index 0000000000000..9886e91a7f3d4 --- /dev/null +++ b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash2.java @@ -0,0 +1,26 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8337980 + * @summary Test compiler crash due to failure to resolve method ambiguity + * @compile/fail/ref=MethodAmbiguityCrash2.out -XDrawDiagnostics MethodAmbiguityCrash2.java + */ +public class MethodAmbiguityCrash2 { + + public interface A { + int op(); + } + + public abstract static class B { + public abstract int op(); + } + + public abstract static class C extends B implements A { + + public C(int x) { + } + + public C() { + this(op()); // compile should fail here + } + } +} diff --git a/test/langtools/tools/javac/resolve/MethodAmbiguityCrash2.out b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash2.out new file mode 100644 index 0000000000000..ace3ffce9cdd7 --- /dev/null +++ b/test/langtools/tools/javac/resolve/MethodAmbiguityCrash2.out @@ -0,0 +1,2 @@ +MethodAmbiguityCrash2.java:23:18: compiler.err.cant.ref.before.ctor.called: op() +1 error diff --git a/test/langtools/tools/javac/tree/PrettyCharLiteral.java b/test/langtools/tools/javac/tree/PrettyCharLiteral.java new file mode 100644 index 0000000000000..eced3209607ce --- /dev/null +++ b/test/langtools/tools/javac/tree/PrettyCharLiteral.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8340568 + * @summary Incorrect escaping of single quotes when pretty-printing character literals + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.file + * jdk.compiler/com.sun.tools.javac.tree + * jdk.compiler/com.sun.tools.javac.util + */ + +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.tree.Pretty; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.Context; + +import java.io.IOException; +import java.io.StringWriter; + +public class PrettyCharLiteral { + public static void main(String... args) throws Exception { + new PrettyCharLiteral().run(); + } + + private final TreeMaker make; + + private PrettyCharLiteral() { + Context ctx = new Context(); + JavacFileManager.preRegister(ctx); + this.make = TreeMaker.instance(ctx); + } + + void run() throws Exception { + assertEquals( + prettyPrintLiteral('\''), + """ + '\\'' + """.trim()); + assertEquals( + prettyPrintLiteral('"'), + """ + '"' + """.trim()); + assertEquals( + prettyPrintLiteral("'"), + """ + "'" + """.trim()); + assertEquals( + prettyPrintLiteral("\""), + """ + "\\"" + """.trim()); + } + + private void assertEquals(String actual, String expected) { + if (!actual.equals(expected)) { + throw new AssertionError("expected: " + expected + ", actual: " + actual); + } + } + + private String prettyPrintLiteral(Object value) throws IOException { + StringWriter sw = new StringWriter(); + new Pretty(sw, true).printExpr(make.Literal(value)); + return sw.toString(); + } +} diff --git a/test/langtools/tools/javac/warnings/Serial.java b/test/langtools/tools/javac/warnings/Serial/Serial.java similarity index 100% rename from test/langtools/tools/javac/warnings/Serial.java rename to test/langtools/tools/javac/warnings/Serial/Serial.java diff --git a/test/langtools/tools/javac/warnings/Serial.out b/test/langtools/tools/javac/warnings/Serial/Serial.out similarity index 100% rename from test/langtools/tools/javac/warnings/Serial.out rename to test/langtools/tools/javac/warnings/Serial/Serial.out diff --git a/test/lib-test/jdk/test/lib/util/JarUtilsTest.java b/test/lib-test/jdk/test/lib/util/JarUtilsTest.java new file mode 100644 index 0000000000000..eb9dced32569a --- /dev/null +++ b/test/lib-test/jdk/test/lib/util/JarUtilsTest.java @@ -0,0 +1,77 @@ +/* + * 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 8309841 + * @summary Unit Test for a common Test API in jdk.test.lib.util.JarUtils + * @library /test/lib + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.util.JarUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; + +public class JarUtilsTest { + public static void main(String[] args) throws Exception { + Files.createDirectory(Path.of("bx")); + JarUtils.createJarFile(Path.of("a.jar"), + Path.of("."), + Files.writeString(Path.of("a"), ""), + Files.writeString(Path.of("b1"), ""), + Files.writeString(Path.of("b2"), ""), + Files.writeString(Path.of("bx/x"), ""), + Files.writeString(Path.of("c"), ""), + Files.writeString(Path.of("e1"), ""), + Files.writeString(Path.of("e2"), "")); + checkContent("a", "b1", "b2", "bx/x", "c", "e1", "e2"); + + JarUtils.deleteEntries(Path.of("a.jar"), "a"); + checkContent("b1", "b2", "bx/x", "c", "e1", "e2"); + + // Note: b* covers everything starting with b, even bx/x + JarUtils.deleteEntries(Path.of("a.jar"), "b*"); + checkContent("c", "e1", "e2"); + + // d* does not match + JarUtils.deleteEntries(Path.of("a.jar"), "d*"); + checkContent("c", "e1", "e2"); + + // multiple patterns + JarUtils.deleteEntries(Path.of("a.jar"), "d*", "e*"); + checkContent("c"); + } + + static void checkContent(String... expected) throws IOException { + try (var jf = new JarFile("a.jar")) { + Asserts.assertEquals(Set.of(expected), + jf.stream().map(JarEntry::getName).collect(Collectors.toSet())); + } + } +} diff --git a/test/lib/jdk/test/lib/Container.java b/test/lib/jdk/test/lib/Container.java index e0ca4851e144b..83fd265980f26 100644 --- a/test/lib/jdk/test/lib/Container.java +++ b/test/lib/jdk/test/lib/Container.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2019, Red Hat Inc. + * 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 @@ -23,9 +24,9 @@ package jdk.test.lib; public class Container { - // Use this property to specify docker location on your system. + // Use this property to specify container runtime location (e.g. docker) on your system. // E.g.: "/usr/local/bin/docker". We define this constant here so - // that it can be used in VMProps as well which checks docker support + // that it can be used in VMProps as well which checks container support // via this command public static final String ENGINE_COMMAND = System.getProperty("jdk.test.container.command", "docker"); diff --git a/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java b/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java index 341b24c3e051a..9acff93aaca23 100644 --- a/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java +++ b/test/lib/jdk/test/lib/containers/systemd/SystemdTestUtils.java @@ -28,6 +28,7 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -92,19 +93,23 @@ public static OutputAnalyzer buildAndRunSystemdJava(SystemdRunOptions opts) thro try { return SystemdTestUtils.systemdRunJava(opts); } finally { - try { - if (files.memory() != null) { - Files.delete(files.memory()); - } - if (files.cpu() != null) { - Files.delete(files.cpu()); - } - if (files.sliceDotDDir() != null) { - FileUtils.deleteFileTreeUnchecked(files.sliceDotDDir()); - } - } catch (NoSuchFileException e) { - // ignore + cleanupFiles(files); + } + } + + private static void cleanupFiles(ResultFiles files) throws IOException { + try { + if (files.memory() != null) { + Files.delete(files.memory()); } + if (files.cpu() != null) { + Files.delete(files.cpu()); + } + if (files.sliceDotDDir() != null) { + FileUtils.deleteFileTreeUnchecked(files.sliceDotDDir()); + } + } catch (NoSuchFileException e) { + // ignore } } @@ -135,15 +140,23 @@ private static ResultFiles buildSystemdSlices(SystemdRunOptions runOpts) throws if (runOpts.hasSliceDLimit()) { String dirName = String.format("%s.slice.d", SLICE_NAMESPACE_PREFIX); sliceDotDDir = SYSTEMD_CONFIG_HOME.resolve(Path.of(dirName)); - Files.createDirectory(sliceDotDDir); + // Using createDirectories since we only need to ensure the directory + // exists. Ignore it if already existent. + Files.createDirectories(sliceDotDDir); if (runOpts.sliceDMemoryLimit != null) { Path memoryConfig = sliceDotDDir.resolve(Path.of(SLICE_D_MEM_CONFIG_FILE)); - Files.writeString(memoryConfig, getMemoryDSliceContent(runOpts)); + Files.writeString(memoryConfig, + getMemoryDSliceContent(runOpts), + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE); } if (runOpts.sliceDCpuLimit != null) { Path cpuConfig = sliceDotDDir.resolve(Path.of(SLICE_D_CPU_CONFIG_FILE)); - Files.writeString(cpuConfig, getCPUDSliceContent(runOpts)); + Files.writeString(cpuConfig, + getCPUDSliceContent(runOpts), + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE); } } @@ -159,7 +172,7 @@ private static ResultFiles buildSystemdSlices(SystemdRunOptions runOpts) throws throw new AssertionError("Failed to write systemd slice files"); } - systemdDaemonReload(cpu); + systemdDaemonReload(cpu, memory, sliceDotDDir); return new ResultFiles(memory, cpu, sliceDotDDir); } @@ -175,12 +188,25 @@ private static String sliceNameCpu(SystemdRunOptions runOpts) { return String.format("%s-cpu", slice); } - private static void systemdDaemonReload(Path cpu) throws Exception { + private static void systemdDaemonReload(Path cpu, Path memory, Path sliceDdir) throws Exception { List daemonReload = systemCtl(); daemonReload.add("daemon-reload"); if (execute(daemonReload).getExitValue() != 0) { - throw new AssertionError("Failed to reload systemd daemon"); + if (RUN_AS_USER) { + cleanupFiles(new ResultFiles(cpu, memory, sliceDdir)); + // When run as user the systemd user manager needs to be + // accessible and working. This is usually the case when + // connected via SSH or user login, but may not work for + // sessions set up via 'su ' or similar. + // In that case, 'systemctl --user status' usually doesn't + // work. There is no other option than skip the test. + String msg = "Service user@.service not properly configured. " + + "Skipping the test!"; + throw new SkippedException(msg); + } else { + throw new AssertionError("Failed to reload systemd daemon"); + } } } diff --git a/test/lib/jdk/test/lib/util/JarUtils.java b/test/lib/jdk/test/lib/util/JarUtils.java index e1b3ccac19fc5..3aa4ada5197ad 100644 --- a/test/lib/jdk/test/lib/util/JarUtils.java +++ b/test/lib/jdk/test/lib/util/JarUtils.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 @@ -320,6 +320,57 @@ public static void updateManifest(String src, String dest, Manifest man) updateJar(src, dest, Map.of(JarFile.MANIFEST_NAME, bout.toByteArray())); } + /** + * Remove entries from a ZIP file. + * + * Each entry can be a name or a name ending with "*". + * + * @return number of removed entries + * @throws IOException if there is any I/O error + */ + public static int deleteEntries(Path jarfile, String... patterns) + throws IOException { + Path tmpfile = Files.createTempFile("jar", "jar"); + int count = 0; + + try (OutputStream out = Files.newOutputStream(tmpfile); + JarOutputStream jos = new JarOutputStream(out)) { + try (JarFile jf = new JarFile(jarfile.toString())) { + Enumeration jentries = jf.entries(); + top: while (jentries.hasMoreElements()) { + JarEntry jentry = jentries.nextElement(); + String name = jentry.getName(); + for (String pattern : patterns) { + if (pattern.endsWith("*")) { + if (name.startsWith(pattern.substring( + 0, pattern.length() - 1))) { + // Go directly to next entry. This + // one is not written into `jos` and + // therefore removed. + count++; + continue top; + } + } else { + if (name.equals(pattern)) { + // Same as above + count++; + continue top; + } + } + } + // No pattern matched, file retained + jos.putNextEntry(copyEntry(jentry)); + jf.getInputStream(jentry).transferTo(jos); + } + } + } + + // replace the original JAR file + Files.move(tmpfile, jarfile, StandardCopyOption.REPLACE_EXISTING); + + return count; + } + private static void updateEntry(JarOutputStream jos, String name, Object content) throws IOException { if (content instanceof Boolean) { diff --git a/test/lib/jdk/test/whitebox/code/Compiler.java b/test/lib/jdk/test/whitebox/code/Compiler.java index 2f8447700d57d..69d79ab9d7104 100644 --- a/test/lib/jdk/test/whitebox/code/Compiler.java +++ b/test/lib/jdk/test/whitebox/code/Compiler.java @@ -91,12 +91,12 @@ public static boolean isGraalEnabled() { /** * Check if libgraal is used as JIT compiler. * - * libraal is enabled if isGraalEnabled is true and: + * libraal JIT is enabled if isGraalEnabled is true and: * - UseJVMCINativeLibrary flag is true * * @return true if libgraal is used as JIT compiler. */ - public static boolean isLibgraalEnabled() { + public static boolean isLibgraalJIT() { if (!isGraalEnabled()) { return false; } diff --git a/test/micro/org/openjdk/bench/java/io/DataOutputStreamBench.java b/test/micro/org/openjdk/bench/java/io/DataOutputStreamBench.java new file mode 100644 index 0000000000000..34568110dde53 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/io/DataOutputStreamBench.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.io; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.HexFormat; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Fork(2) +@Measurement(iterations = 6, time = 1) +@Warmup(iterations = 4, time = 2) +@State(Scope.Thread) +public class DataOutputStreamBench { + + @Param({"ascii", "utf8_2_bytes", "utf8_3_bytes", "emoji"}) + public String charType; + + ByteArrayOutputStream bytesOutput; + DataOutputStream dataOutput; + ObjectOutputStream objectOutput; + String[] strings; + + @Setup(Level.Trial) + public void setup() throws Exception { + byte[] bytes = HexFormat.of().parseHex( + switch (charType) { + case "ascii" -> "78"; + case "utf8_2_bytes" -> "c2a9"; + case "utf8_3_bytes" -> "e6b8a9"; + case "emoji" -> "e29da3efb88f"; + default -> throw new IllegalArgumentException("bad charType: " + charType); + } + ); + String s = new String(bytes, 0, bytes.length, StandardCharsets.UTF_8); + strings = new String[128]; + for (int i = 0; i < strings.length; i++) { + strings[i] = "A".repeat(i).concat(s.repeat(i)); + } + + bytesOutput = new ByteArrayOutputStream(1024 * 64); + dataOutput = new DataOutputStream(bytesOutput); + objectOutput = new ObjectOutputStream(bytesOutput); + } + + @Benchmark + public void dataOutwriteUTF(Blackhole bh) throws Exception { + bytesOutput.reset(); + for (var s : strings) { + dataOutput.writeUTF(s); + } + dataOutput.flush(); + bh.consume(bytesOutput.size()); + } + + @Benchmark + public void objectWriteUTF(Blackhole bh) throws Exception { + bytesOutput.reset(); + for (var s : strings) { + objectOutput.writeUTF(s); + } + objectOutput.flush(); + bh.consume(bytesOutput.size()); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantAsType.java b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantAsType.java new file mode 100644 index 0000000000000..1fbe431b2f23a --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/LoopOverNonConstantAsType.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. + */ +package org.openjdk.bench.java.lang.foreign; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +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.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import sun.misc.Unsafe; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Proxy; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import static java.lang.foreign.ValueLayout.*; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 3, jvmArgsAppend = { "-XX:-TieredCompilation" }) +public class LoopOverNonConstantAsType extends JavaLayouts { + + static final Unsafe unsafe = Utils.unsafe; + + static final int ELEM_SIZE = 1_000_000; + static final int CARRIER_SIZE = (int)JAVA_LONG.byteSize(); + static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; + + @Param({"false", "true"}) + public boolean asTypeCompiled; + + Arena arena; + MemorySegment segment; + long unsafe_addr; + + @Setup + public void setup() { + unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE); + for (int i = 0; i < ELEM_SIZE; i++) { + unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i); + } + arena = Arena.ofConfined(); + segment = arena.allocate(ALLOC_SIZE, 1); + for (int i = 0; i < ELEM_SIZE; i++) { + VH_INT.set(segment, (long) i, i); + } + if (asTypeCompiled) { + compileAsType(); + } + } + + public interface T { } + + static final int TYPE_SIZE = 100; + static final Class[] types; + + static { + types = new Class[TYPE_SIZE]; + ClassLoader customLoader = new URLClassLoader(new URL[0], LoopOverNonConstantAsType.class.getClassLoader()); + for (int i = 0 ; i < TYPE_SIZE ; i++) { + types[i] = Proxy.newProxyInstance(customLoader, + new Class[] { T.class }, (_, _, _) -> null).getClass(); + } + } + + void compileAsType() { + for (Class type : types) { + MethodHandle handle = MethodHandles.zero(Object.class); + Class[] args = new Class[254]; + Arrays.fill(args, Object.class); + handle = MethodHandles.dropArguments(handle, 0, args); + for (int j = 0; j < args.length ; j++) { + handle = handle.asType(handle.type().changeParameterType(j, type)); + } + } + } + + @TearDown + public void tearDown() { + arena.close(); + unsafe.freeMemory(unsafe_addr); + } + + @Benchmark + public long unsafe_loop() { + long res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += unsafe.getLong(unsafe_addr + (i * CARRIER_SIZE)); + } + return res; + } + + @Benchmark + public long segment_loop() { + long sum = 0; + for (int i = 0; i < ELEM_SIZE; i++) { + sum += segment.get(JAVA_LONG, i * CARRIER_SIZE); + } + return sum; + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java b/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java new file mode 100644 index 0000000000000..6a7b4d6997056 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.ref; + +import java.lang.ref.*; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +public class ReferenceClear { + + final Reference soft = new SoftReference<>(new Object()); + final Reference weak = new WeakReference<>(new Object()); + final Reference phantom = new PhantomReference<>(new Object(), null); + + @Benchmark + public void soft() { + soft.clear(); + } + + @Benchmark + public void soft_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference soft = new SoftReference<>(ref); + soft.clear(); + } + + @Benchmark + public void weak() { + weak.clear(); + } + + @Benchmark + public void weak_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference weak = new WeakReference<>(ref); + weak.clear(); + } + + @Benchmark + public void phantom() { + phantom.clear(); + } + + @Benchmark + public void phantom_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference phantom = new PhantomReference<>(ref, null); + phantom.clear(); + } + +} diff --git a/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyBench.java b/test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyBench.java similarity index 100% rename from test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyBench.java rename to test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyBench.java diff --git a/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java b/test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyGeneratorBench.java similarity index 57% rename from test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java rename to test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyGeneratorBench.java index 22fe4d3ef28db..3393431730080 100644 --- a/test/micro/org/openjdk/bench/java/lang/reflect/Proxy/ProxyPerf.java +++ b/test/micro/org/openjdk/bench/java/lang/reflect/proxy/ProxyGeneratorBench.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, 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 @@ -26,7 +26,6 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.CompilerControl; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; @@ -36,37 +35,29 @@ import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.infra.Blackhole; - import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.concurrent.TimeUnit; /** * Benchmark measuring java.lang.reflect.ProxyGenerator.generateProxyClass. * It bypasses the cache of proxies to measure the time to construct a proxy. */ -@Warmup(iterations = 5) -@Measurement(iterations = 10) -@Fork(value = 1) +@Warmup(iterations = 5, time = 2) +@Measurement(iterations = 5, time = 2) +@Fork(value = 1, jvmArgsPrepend = "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED") @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) -public class ProxyPerf { +public class ProxyGeneratorBench { /** * Sample results from a Dell T7610. * Benchmark Mode Cnt Score Error Units * ProxyPerf.genIntf_1 avgt 10 35325.428 +/- 780.459 ns/op - * ProxyPerf.genIntf_1_V49 avgt 10 34309.423 +/- 727.188 ns/op * ProxyPerf.genStringsIntf_3 avgt 10 46600.366 +/- 663.812 ns/op - * ProxyPerf.genStringsIntf_3_V49 avgt 10 45911.817 +/- 1598.536 ns/op * ProxyPerf.genZeroParams avgt 10 33245.048 +/- 437.988 ns/op - * ProxyPerf.genZeroParams_V49 avgt 10 32954.254 +/- 1041.932 ns/op - * ProxyPerf.getPrimsIntf_2 avgt 10 43987.819 +/- 837.443 ns/op - * ProxyPerf.getPrimsIntf_2_V49 avgt 10 42863.462 +/- 1193.480 ns/op + * ProxyPerf.genPrimsIntf_2 avgt 10 43987.819 +/- 837.443 ns/op */ public interface Intf_1 { @@ -84,7 +75,6 @@ public interface Intf_3 { public String m2String(String s1, String s2); } - private InvocationHandler handler; private ClassLoader classloader; private Method proxyGen; private Method proxyGenV49; @@ -92,19 +82,11 @@ public interface Intf_3 { @Setup public void setup() { try { - handler = (Object proxy, Method method, Object[] args) -> null; classloader = ClassLoader.getSystemClassLoader(); Class proxyGenClass = Class.forName("java.lang.reflect.ProxyGenerator"); proxyGen = proxyGenClass.getDeclaredMethod("generateProxyClass", ClassLoader.class, String.class, java.util.List.class, int.class); proxyGen.setAccessible(true); - - // Init access to the old Proxy generator - Class proxyGenClassV49 = Class.forName("java.lang.reflect.ProxyGenerator_v49"); - proxyGenV49 = proxyGenClassV49.getDeclaredMethod("generateProxyClass", - String.class, java.util.List.class, int.class); - proxyGenV49.setAccessible(true); - } catch (Exception ex) { ex.printStackTrace(); throw new RuntimeException("ProxyClass setup fails", ex); @@ -112,51 +94,35 @@ public void setup() { } @Benchmark - public void genZeroParams(Blackhole bh) throws Exception { + public Object genZeroParams() throws Exception { List> interfaces = List.of(Runnable.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } @Benchmark - public void genIntf_1(Blackhole bh) throws Exception { + public Object genIntf_1() throws Exception { List> interfaces = List.of(Intf_1.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } @Benchmark - public void getPrimsIntf_2(Blackhole bh) throws Exception { + public Object genPrimsIntf_2() throws Exception { List> interfaces = List.of(Intf_2.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); - } - @Benchmark - public void genStringsIntf_3(Blackhole bh) throws Exception { - List> interfaces = List.of(Intf_3.class); - bh.consume(proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } - // Generate using the V49inal generator for comparison - @Benchmark - public void genZeroParams_V49(Blackhole bh) throws Exception { - List> interfaces = List.of(Runnable.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); - } - - @Benchmark - public void genIntf_1_V49(Blackhole bh) throws Exception { - List> interfaces = List.of(Intf_1.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); - } - - @Benchmark - public void getPrimsIntf_2_V49(Blackhole bh) throws Exception { - List> interfaces = List.of(Intf_2.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); - } - @Benchmark - public void genStringsIntf_3_V49(Blackhole bh) throws Exception { + public Object genStringsIntf_3() throws Exception { List> interfaces = List.of(Intf_3.class); - bh.consume(proxyGenV49.invoke(null, "ProxyImpl", interfaces, 1)); + return proxyGen.invoke(null, classloader, "ProxyImpl", interfaces, 1); } + public static void main(String... args) throws Exception { + var benchmark = new ProxyGeneratorBench(); + benchmark.setup(); + benchmark.genZeroParams(); + benchmark.genIntf_1(); + benchmark.genPrimsIntf_2(); + benchmark.genStringsIntf_3(); + } } diff --git a/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java b/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java index e450fcfe9a0ae..4f6ae6373ec46 100644 --- a/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java +++ b/test/micro/org/openjdk/bench/java/util/zip/ZipFileOpen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -107,4 +107,13 @@ public void openCloseZipFilex2() throws Exception { zf.close(); zf2.close(); } + + // Provide a simple one-off run without JMH dependencies enable simple debugging, + // diagnostics and dual-purposing this micro as a startup test. + public static void main(String... args) throws Exception { + var bench = new ZipFileOpen(); + bench.size = 1024*4; + bench.beforeRun(); + bench.openCloseZipFile(); + } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java index e46f50678ef81..8355e4aed728e 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/AESGCMBench.java @@ -35,7 +35,7 @@ public class AESGCMBench extends BenchBase { - @Param({"128"}) + @Param({"128", "192", "256"}) int keyLength; public static final int IV_MODULO = 16; diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java b/test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java index 0c5df20d9cb64..94c8ef30ea553 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/BenchBase.java @@ -45,7 +45,7 @@ public abstract class BenchBase extends CryptoBase { int keyLength = 256; // Default data sizes for full tests - @Param({"1024", "1500", "4096", "16384"}) + @Param({"128", "256", "512", "1024", "1500", "4096", "16384"}) int dataSize; static final int IV_BUFFER_SIZE = 36; diff --git a/test/micro/org/openjdk/bench/jdk/classfile/ConstantPoolBuildingClassEntry.java b/test/micro/org/openjdk/bench/jdk/classfile/ConstantPoolBuildingClassEntry.java new file mode 100644 index 0000000000000..0f8bf0449af0b --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/classfile/ConstantPoolBuildingClassEntry.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.jdk.classfile; + +import java.lang.classfile.constantpool.ConstantPoolBuilder; +import java.lang.constant.ClassDesc; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import static java.lang.constant.ConstantDescs.*; + +import static org.openjdk.bench.jdk.classfile.TestConstants.*; + +/** + * Tests constant pool builder lookup performance for ClassEntry. + * Note that ClassEntry is available only for reference types. + */ +@Warmup(iterations = 3) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@BenchmarkMode(Mode.Throughput) +@Fork(value = 1, jvmArgsAppend = {"--enable-preview"}) +@State(Scope.Benchmark) +public class ConstantPoolBuildingClassEntry { + // JDK-8338546 + ConstantPoolBuilder builder; + List classDescs; + List nonIdenticalClassDescs; + List internalNames; + List nonDuplicateClassDescs; + List nonDuplicateInternalNames; + int size; + + @Setup(Level.Iteration) + public void setup() { + builder = ConstantPoolBuilder.of(); + // Note these can only be reference types, no primitives + classDescs = List.of( + CD_Byte, CD_Object, CD_Long.arrayType(), CD_String, CD_String, CD_Object, CD_Short, + CD_MethodHandle, CD_MethodHandle, CD_Object, CD_Character, CD_List, CD_ArrayList, + CD_List, CD_Set, CD_Integer, CD_Object.arrayType(), CD_Enum, CD_Object, CD_MethodHandles_Lookup, + CD_Long, CD_Set, CD_Object, CD_Character, CD_Integer, CD_System, CD_String, CD_String, + CD_CallSite, CD_Collection, CD_List, CD_Collection, CD_String, CD_int.arrayType() + ); + size = classDescs.size(); + nonIdenticalClassDescs = classDescs.stream().map(cd -> { + var ret = ClassDesc.ofDescriptor(new String(cd.descriptorString())); + ret.hashCode(); // pre-compute hash code for cd + return ret; + }).toList(); + internalNames = classDescs.stream().map(cd -> { + // also sets up builder + cd.hashCode(); // pre-computes hash code for cd + var ce = builder.classEntry(cd); + var ret = ce.name().stringValue(); + ret.hashCode(); // pre-computes hash code for stringValue + return ret; + }).toList(); + nonDuplicateClassDescs = List.copyOf(new LinkedHashSet<>(classDescs)); + nonDuplicateInternalNames = nonDuplicateClassDescs.stream().map(cd -> + builder.classEntry(cd).asInternalName()).toList(); + } + + // Copied from jdk.internal.classfile.impl.Util::toInternalName + // to reduce internal dependencies + public static String toInternalName(ClassDesc cd) { + var desc = cd.descriptorString(); + if (desc.charAt(0) == 'L') + return desc.substring(1, desc.length() - 1); + throw new IllegalArgumentException(desc); + } + + /** + * Looking up with identical ClassDesc objects. Happens in bytecode generators reusing + * constant CD_Xxx. + */ + @Benchmark + public void identicalLookup(Blackhole bh) { + for (var cd : classDescs) { + bh.consume(builder.classEntry(cd)); + } + } + + /** + * Looking up with non-identical ClassDesc objects. Happens in bytecode generators + * using ad-hoc Class.describeConstable().orElseThrow() or other parsed ClassDesc. + * Cannot use identity fast path compared to {@link #identicalLookup}. + */ + @Benchmark + public void nonIdenticalLookup(Blackhole bh) { + for (var cd : nonIdenticalClassDescs) { + bh.consume(builder.classEntry(cd)); + } + } + + /** + * Looking up with internal names. Closest to ASM behavior. + * Baseline for {@link #identicalLookup}. + */ + @Benchmark + public void internalNameLookup(Blackhole bh) { + for (var name : internalNames) { + bh.consume(builder.classEntry(builder.utf8Entry(name))); + } + } + + /** + * The default implementation provided by {@link ConstantPoolBuilder#classEntry(ClassDesc)}. + * Does substring so needs to rehash and has no caching, should be very slow. + */ + @Benchmark + public void oldStyleLookup(Blackhole bh) { + for (var cd : classDescs) { + var s = cd.isClassOrInterface() ? toInternalName(cd) : cd.descriptorString(); + bh.consume(builder.classEntry(builder.utf8Entry(s))); + } + } + + /** + * Measures performance of creating new class entries in new constant pools with symbols. + */ + @Benchmark + public void freshCreationWithDescs(Blackhole bh) { + var cp = ConstantPoolBuilder.of(); + for (var cd : nonDuplicateClassDescs) { + bh.consume(cp.classEntry(cd)); + } + } + + /** + * Measures performance of creating new class entries in new constant pools with internal names. + */ + @Benchmark + public void freshCreationWithInternalNames(Blackhole bh) { + var cp = ConstantPoolBuilder.of(); + for (var name : nonDuplicateInternalNames) { + bh.consume(cp.classEntry(cp.utf8Entry(name))); + } + } +} diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Write.java b/test/micro/org/openjdk/bench/jdk/classfile/Write.java index b8bc9605559d9..6e041cf64361e 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Write.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Write.java @@ -62,6 +62,15 @@ "--enable-preview", "--add-exports", "java.base/jdk.internal.classfile.impl=ALL-UNNAMED"}) public class Write { + static final int REPEATS = 40; + static final String[] METHOD_NAMES; + static { + var names = new String[REPEATS]; + for (int xi = 0; xi < REPEATS; ++xi) { + names[xi] = "main" + ((xi == 0) ? "" : "" + xi); + } + METHOD_NAMES = names; + } static String checkFileAsm = "/tmp/asw/MyClass.class"; static String checkFileBc = "/tmp/byw/MyClass.class"; static boolean writeClassAsm = Files.exists(Paths.get(checkFileAsm).getParent()); @@ -90,8 +99,8 @@ public byte[] asmStream() { mv.visitEnd(); } - for (int xi = 0; xi < 40; ++xi) { - MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC, "main"+ ((xi==0)? "" : ""+xi), "([Ljava/lang/String;)V", null, null); + for (int xi = 0; xi < REPEATS; ++xi) { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC, METHOD_NAMES[xi], "([Ljava/lang/String;)V", null, null); mv.visitCode(); Label loopTop = new Label(); Label loopEnd = new Label(); @@ -141,13 +150,13 @@ public byte[] jdkTree() { cb.withVersion(52, 0); cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) .withMethod(INIT_NAME, MTD_void, 0, mb -> mb - .withCode(codeb -> codeb.loadLocal(REFERENCE, 0) - .invoke(INVOKESPECIAL, CD_Object, INIT_NAME, MTD_void, false) - .return_(VOID) + .withCode(codeb -> codeb.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void) + .return_() ) ); - for (int xi = 0; xi < 40; ++xi) { - cb.withMethod("main" + ((xi == 0) ? "" : "" + xi), MTD_void_StringArray, + for (int xi = 0; xi < REPEATS; ++xi) { + cb.withMethod(METHOD_NAMES[xi], MTD_void_StringArray, ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { java.lang.classfile.Label loopTop = c0.newLabel(); @@ -180,54 +189,6 @@ public byte[] jdkTree() { return bytes; } - @Benchmark - @BenchmarkMode(Mode.Throughput) - public byte[] jdkTreePrimitive() { - - byte[] bytes = ClassFile.of().build(CD_MyClass, cb -> { - cb.withFlags(AccessFlag.PUBLIC); - cb.withVersion(52, 0); - cb.with(SourceFileAttribute.of(cb.constantPool().utf8Entry(("MyClass.java")))) - .withMethod(INIT_NAME, MTD_void, 0, - mb -> mb.withCode(codeb -> codeb.loadLocal(REFERENCE, 0) - .invokespecial(CD_Object, INIT_NAME, MTD_void, false) - .return_() - ) - ); - for (int xi = 0; xi < 40; ++xi) { - cb.withMethod("main" + ((xi == 0) ? "" : "" + xi), MTD_void_StringArray, - ACC_PUBLIC | ACC_STATIC, - mb -> mb.withCode(c0 -> { - java.lang.classfile.Label loopTop = c0.newLabel(); - java.lang.classfile.Label loopEnd = c0.newLabel(); - int vFac = 1; - int vI = 2; - c0.iconst_1() // 0 - .istore(1) // 1 - .iconst_1() // 2 - .istore(2) // 3 - .labelBinding(loopTop) - .iload(2) // 4 - .bipush(10) // 5 - .if_icmpge(loopEnd) // 6 - .iload(1) // 7 - .iload(2) // 8 - .imul() // 9 - .istore(1) // 10 - .iinc(2, 1) // 11 - .goto_(loopTop) // 12 - .labelBinding(loopEnd) - .getstatic(CD_System, "out", CD_PrintStream) // 13 - .iload(1) - .invokevirtual(CD_PrintStream, "println", MTD_void_int) // 15 - .return_(); - })); - } - }); - if (writeClassBc) writeClass(bytes, checkFileBc); - return bytes; - } - private void writeClass(byte[] bytes, String fn) { try { FileOutputStream out = new FileOutputStream(fn); diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorReduction2.java b/test/micro/org/openjdk/bench/vm/compiler/VectorReduction2.java new file mode 100644 index 0000000000000..5ec34a0423f62 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorReduction2.java @@ -0,0 +1,1454 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.*; + +import java.util.concurrent.TimeUnit; +import java.util.Random; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +public abstract class VectorReduction2 { + @Param({"2048"}) + public int SIZE; + + private byte[] in1B; + private byte[] in2B; + private byte[] in3B; + private char[] in1C; + private char[] in2C; + private char[] in3C; + private short[] in1S; + private short[] in2S; + private short[] in3S; + + private int[] in1I; + private int[] in2I; + private int[] in3I; + private long[] in1L; + private long[] in2L; + private long[] in3L; + + private float[] in1F; + private float[] in2F; + private float[] in3F; + private double[] in1D; + private double[] in2D; + private double[] in3D; + + @Param("0") + private int seed; + private Random r = new Random(seed); + + private static int globalResI; + + @Setup + public void init() { + in1B = new byte[SIZE]; + in2B = new byte[SIZE]; + in3B = new byte[SIZE]; + in1C = new char[SIZE]; + in2C = new char[SIZE]; + in3C = new char[SIZE]; + in1S = new short[SIZE]; + in2S = new short[SIZE]; + in3S = new short[SIZE]; + + in1I = new int[SIZE]; + in2I = new int[SIZE]; + in3I = new int[SIZE]; + in1L = new long[SIZE]; + in2L = new long[SIZE]; + in3L = new long[SIZE]; + + in1F = new float[SIZE]; + in2F = new float[SIZE]; + in3F = new float[SIZE]; + in1D = new double[SIZE]; + in2D = new double[SIZE]; + in3D = new double[SIZE]; + + for (int i = 0; i < SIZE; i++) { + in1B[i] = (byte)r.nextInt(); + in2B[i] = (byte)r.nextInt(); + in3B[i] = (byte)r.nextInt(); + in1C[i] = (char)r.nextInt(); + in2C[i] = (char)r.nextInt(); + in3C[i] = (char)r.nextInt(); + in1S[i] = (short)r.nextInt(); + in2S[i] = (short)r.nextInt(); + in3S[i] = (short)r.nextInt(); + + in1I[i] = r.nextInt(); + in2I[i] = r.nextInt(); + in3I[i] = r.nextInt(); + in1L[i] = r.nextLong(); + in2L[i] = r.nextLong(); + in3L[i] = r.nextLong(); + + in1F[i] = r.nextFloat(); + in2F[i] = r.nextFloat(); + in3F[i] = r.nextFloat(); + in1D[i] = r.nextDouble(); + in2D[i] = r.nextDouble(); + in3D[i] = r.nextDouble(); + } + } + + // Naming convention: + // How much work? + // - simple: val = a[i] + // - dotprod: val = a[i] * b[i] + // - big: val = (a[i] * b[i]) + (a[i] * c[i]) + (b[i] * c[i]) + // Reduction operator: + // - and: acc &= val + // - or: acc |= val + // - xor: acc ^= val + // - add: acc += val + // - mul: acc *= val + // - min: acc = min(acc, val) + // - max: acc = max(acc, val) + + // ---------byte***Simple ------------------------------------------------------------ + @Benchmark + public void byteAndSimple(Blackhole bh) { + byte acc = (byte)0xFF; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = in1B[i]; + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteOrSimple(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = in1B[i]; + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteXorSimple(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = in1B[i]; + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteAddSimple(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = in1B[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void byteMulSimple(Blackhole bh) { + byte acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = in1B[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteMinSimple(Blackhole bh) { + byte acc = Byte.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = in1B[i]; + acc = (byte)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void byteMaxSimple(Blackhole bh) { + byte acc = Byte.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = in1B[i]; + acc = (byte)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------byte***DotProduct ------------------------------------------------------------ + @Benchmark + public void byteAndDotProduct(Blackhole bh) { + byte acc = (byte)0xFF; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)(in1B[i] * in2B[i]); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteOrDotProduct(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)(in1B[i] * in2B[i]); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteXorDotProduct(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)(in1B[i] * in2B[i]); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteAddDotProduct(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)(in1B[i] * in2B[i]); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void byteMulDotProduct(Blackhole bh) { + byte acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)(in1B[i] * in2B[i]); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteMinDotProduct(Blackhole bh) { + byte acc = Byte.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)(in1B[i] * in2B[i]); + acc = (byte)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void byteMaxDotProduct(Blackhole bh) { + byte acc = Byte.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)(in1B[i] * in2B[i]); + acc = (byte)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------byte***Big ------------------------------------------------------------ + @Benchmark + public void byteAndBig(Blackhole bh) { + byte acc = (byte)0xFF; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)((in1B[i] * in2B[i]) + (in1B[i] * in3B[i]) + (in2B[i] * in3B[i])); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteOrBig(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)((in1B[i] * in2B[i]) + (in1B[i] * in3B[i]) + (in2B[i] * in3B[i])); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteXorBig(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)((in1B[i] * in2B[i]) + (in1B[i] * in3B[i]) + (in2B[i] * in3B[i])); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteAddBig(Blackhole bh) { + byte acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)((in1B[i] * in2B[i]) + (in1B[i] * in3B[i]) + (in2B[i] * in3B[i])); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void byteMulBig(Blackhole bh) { + byte acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)((in1B[i] * in2B[i]) + (in1B[i] * in3B[i]) + (in2B[i] * in3B[i])); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void byteMinBig(Blackhole bh) { + byte acc = Byte.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)((in1B[i] * in2B[i]) + (in1B[i] * in3B[i]) + (in2B[i] * in3B[i])); + acc = (byte)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void byteMaxBig(Blackhole bh) { + byte acc = Byte.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + byte val = (byte)((in1B[i] * in2B[i]) + (in1B[i] * in3B[i]) + (in2B[i] * in3B[i])); + acc = (byte)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------char***Simple ------------------------------------------------------------ + @Benchmark + public void charAndSimple(Blackhole bh) { + char acc = (char)0xFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = in1C[i]; + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void charOrSimple(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = in1C[i]; + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void charXorSimple(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = in1C[i]; + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void charAddSimple(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = in1C[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void charMulSimple(Blackhole bh) { + char acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = in1C[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void charMinSimple(Blackhole bh) { + char acc = Character.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = in1C[i]; + acc = (char)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void charMaxSimple(Blackhole bh) { + char acc = Character.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = in1C[i]; + acc = (char)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------char***DotProduct ------------------------------------------------------------ + @Benchmark + public void charAndDotProduct(Blackhole bh) { + char acc = (char)0xFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)(in1C[i] * in2C[i]); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void charOrDotProduct(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)(in1C[i] * in2C[i]); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void charXorDotProduct(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)(in1C[i] * in2C[i]); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void charAddDotProduct(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)(in1C[i] * in2C[i]); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void charMulDotProduct(Blackhole bh) { + char acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)(in1C[i] * in2C[i]); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void charMinDotProduct(Blackhole bh) { + char acc = Character.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)(in1C[i] * in2C[i]); + acc = (char)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void charMaxDotProduct(Blackhole bh) { + char acc = Character.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)(in1C[i] * in2C[i]); + acc = (char)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------char***Big ------------------------------------------------------------ + @Benchmark + public void charAndBig(Blackhole bh) { + char acc = (char)0xFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)((in1C[i] * in2C[i]) + (in1C[i] * in3C[i]) + (in2C[i] * in3C[i])); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void charOrBig(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)((in1C[i] * in2C[i]) + (in1C[i] * in3C[i]) + (in2C[i] * in3C[i])); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void charXorBig(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)((in1C[i] * in2C[i]) + (in1C[i] * in3C[i]) + (in2C[i] * in3C[i])); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void charAddBig(Blackhole bh) { + char acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)((in1C[i] * in2C[i]) + (in1C[i] * in3C[i]) + (in2C[i] * in3C[i])); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void charMulBig(Blackhole bh) { + char acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)((in1C[i] * in2C[i]) + (in1C[i] * in3C[i]) + (in2C[i] * in3C[i])); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void charMinBig(Blackhole bh) { + char acc = Character.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)((in1C[i] * in2C[i]) + (in1C[i] * in3C[i]) + (in2C[i] * in3C[i])); + acc = (char)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void charMaxBig(Blackhole bh) { + char acc = Character.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + char val = (char)((in1C[i] * in2C[i]) + (in1C[i] * in3C[i]) + (in2C[i] * in3C[i])); + acc = (char)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------short***Simple ------------------------------------------------------------ + @Benchmark + public void shortAndSimple(Blackhole bh) { + short acc = (short)0xFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = in1S[i]; + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortOrSimple(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = in1S[i]; + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortXorSimple(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = in1S[i]; + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortAddSimple(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = in1S[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void shortMulSimple(Blackhole bh) { + short acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = in1S[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortMinSimple(Blackhole bh) { + short acc = Short.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = in1S[i]; + acc = (short)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void shortMaxSimple(Blackhole bh) { + short acc = Short.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = in1S[i]; + acc = (short)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------short***DotProduct ------------------------------------------------------------ + @Benchmark + public void shortAndDotProduct(Blackhole bh) { + short acc = (short)0xFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)(in1S[i] * in2S[i]); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortOrDotProduct(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)(in1S[i] * in2S[i]); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortXorDotProduct(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)(in1S[i] * in2S[i]); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortAddDotProduct(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)(in1S[i] * in2S[i]); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void shortMulDotProduct(Blackhole bh) { + short acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)(in1S[i] * in2S[i]); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortMinDotProduct(Blackhole bh) { + short acc = Short.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)(in1S[i] * in2S[i]); + acc = (short)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void shortMaxDotProduct(Blackhole bh) { + short acc = Short.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)(in1S[i] * in2S[i]); + acc = (short)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------short***Big ------------------------------------------------------------ + @Benchmark + public void shortAndBig(Blackhole bh) { + short acc = (short)0xFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)((in1S[i] * in2S[i]) + (in1S[i] * in3S[i]) + (in2S[i] * in3S[i])); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortOrBig(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)((in1S[i] * in2S[i]) + (in1S[i] * in3S[i]) + (in2S[i] * in3S[i])); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortXorBig(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)((in1S[i] * in2S[i]) + (in1S[i] * in3S[i]) + (in2S[i] * in3S[i])); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortAddBig(Blackhole bh) { + short acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)((in1S[i] * in2S[i]) + (in1S[i] * in3S[i]) + (in2S[i] * in3S[i])); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void shortMulBig(Blackhole bh) { + short acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)((in1S[i] * in2S[i]) + (in1S[i] * in3S[i]) + (in2S[i] * in3S[i])); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void shortMinBig(Blackhole bh) { + short acc = Short.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)((in1S[i] * in2S[i]) + (in1S[i] * in3S[i]) + (in2S[i] * in3S[i])); + acc = (short)Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void shortMaxBig(Blackhole bh) { + short acc = Short.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + short val = (short)((in1S[i] * in2S[i]) + (in1S[i] * in3S[i]) + (in2S[i] * in3S[i])); + acc = (short)Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------int***Simple ------------------------------------------------------------ + @Benchmark + public void intAndSimple(Blackhole bh) { + int acc = 0xFFFFFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i]; + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void intOrSimple(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i]; + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void intXorSimple(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i]; + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void intAddSimple(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void intMulSimple(Blackhole bh) { + int acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void intMinSimple(Blackhole bh) { + int acc = Integer.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void intMaxSimple(Blackhole bh) { + int acc = Integer.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------int***DotProduct ------------------------------------------------------------ + @Benchmark + public void intAndDotProduct(Blackhole bh) { + int acc = 0xFFFFFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i] * in2I[i]; + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void intOrDotProduct(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i] * in2I[i]; + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void intXorDotProduct(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i] * in2I[i]; + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void intAddDotProduct(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i] * in2I[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void intMulDotProduct(Blackhole bh) { + int acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i] * in2I[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void intMinDotProduct(Blackhole bh) { + int acc = Integer.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i] * in2I[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void intMaxDotProduct(Blackhole bh) { + int acc = Integer.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = in1I[i] * in2I[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------int***Big ------------------------------------------------------------ + @Benchmark + public void intAndBig(Blackhole bh) { + int acc = 0xFFFFFFFF; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = (in1I[i] * in2I[i]) + (in1I[i] * in3I[i]) + (in2I[i] * in3I[i]); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void intOrBig(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = (in1I[i] * in2I[i]) + (in1I[i] * in3I[i]) + (in2I[i] * in3I[i]); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void intXorBig(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = (in1I[i] * in2I[i]) + (in1I[i] * in3I[i]) + (in2I[i] * in3I[i]); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void intAddBig(Blackhole bh) { + int acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = (in1I[i] * in2I[i]) + (in1I[i] * in3I[i]) + (in2I[i] * in3I[i]); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void intMulBig(Blackhole bh) { + int acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = (in1I[i] * in2I[i]) + (in1I[i] * in3I[i]) + (in2I[i] * in3I[i]); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void intMinBig(Blackhole bh) { + int acc = Integer.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = (in1I[i] * in2I[i]) + (in1I[i] * in3I[i]) + (in2I[i] * in3I[i]); + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void intMaxBig(Blackhole bh) { + int acc = Integer.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + int val = (in1I[i] * in2I[i]) + (in1I[i] * in3I[i]) + (in2I[i] * in3I[i]); + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------long***Simple ------------------------------------------------------------ + @Benchmark + public void longAndSimple(Blackhole bh) { + long acc = 0xFFFFFFFFFFFFFFFFL; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i]; + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void longOrSimple(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i]; + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void longXorSimple(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i]; + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void longAddSimple(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void longMulSimple(Blackhole bh) { + long acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void longMinSimple(Blackhole bh) { + long acc = Long.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void longMaxSimple(Blackhole bh) { + long acc = Long.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------long***DotProduct ------------------------------------------------------------ + @Benchmark + public void longAndDotProduct(Blackhole bh) { + long acc = 0xFFFFFFFFFFFFFFFFL; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i] * in2L[i]; + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void longOrDotProduct(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i] * in2L[i]; + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void longXorDotProduct(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i] * in2L[i]; + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void longAddDotProduct(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i] * in2L[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void longMulDotProduct(Blackhole bh) { + long acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i] * in2L[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void longMinDotProduct(Blackhole bh) { + long acc = Long.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i] * in2L[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void longMaxDotProduct(Blackhole bh) { + long acc = Long.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = in1L[i] * in2L[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------long***Big ------------------------------------------------------------ + @Benchmark + public void longAndBig(Blackhole bh) { + long acc = 0xFFFFFFFFFFFFFFFFL; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = (in1L[i] * in2L[i]) + (in1L[i] * in3L[i]) + (in2L[i] * in3L[i]); + acc &= val; + } + bh.consume(acc); + } + + @Benchmark + public void longOrBig(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = (in1L[i] * in2L[i]) + (in1L[i] * in3L[i]) + (in2L[i] * in3L[i]); + acc |= val; + } + bh.consume(acc); + } + + @Benchmark + public void longXorBig(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = (in1L[i] * in2L[i]) + (in1L[i] * in3L[i]) + (in2L[i] * in3L[i]); + acc ^= val; + } + bh.consume(acc); + } + + @Benchmark + public void longAddBig(Blackhole bh) { + long acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = (in1L[i] * in2L[i]) + (in1L[i] * in3L[i]) + (in2L[i] * in3L[i]); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void longMulBig(Blackhole bh) { + long acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = (in1L[i] * in2L[i]) + (in1L[i] * in3L[i]) + (in2L[i] * in3L[i]); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void longMinBig(Blackhole bh) { + long acc = Long.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = (in1L[i] * in2L[i]) + (in1L[i] * in3L[i]) + (in2L[i] * in3L[i]); + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void longMaxBig(Blackhole bh) { + long acc = Long.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + long val = (in1L[i] * in2L[i]) + (in1L[i] * in3L[i]) + (in2L[i] * in3L[i]); + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------float***Simple ------------------------------------------------------------ + @Benchmark + public void floatAddSimple(Blackhole bh) { + float acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void floatMulSimple(Blackhole bh) { + float acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void floatMinSimple(Blackhole bh) { + float acc = Float.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void floatMaxSimple(Blackhole bh) { + float acc = Float.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------float***DotProduct ------------------------------------------------------------ + @Benchmark + public void floatAddDotProduct(Blackhole bh) { + float acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i] * in2F[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void floatMulDotProduct(Blackhole bh) { + float acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i] * in2F[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void floatMinDotProduct(Blackhole bh) { + float acc = Float.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i] * in2F[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void floatMaxDotProduct(Blackhole bh) { + float acc = Float.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = in1F[i] * in2F[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------float***Big ------------------------------------------------------------ + @Benchmark + public void floatAddBig(Blackhole bh) { + float acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = (in1F[i] * in2F[i]) + (in1F[i] * in3F[i]) + (in2F[i] * in3F[i]); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void floatMulBig(Blackhole bh) { + float acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = (in1F[i] * in2F[i]) + (in1F[i] * in3F[i]) + (in2F[i] * in3F[i]); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void floatMinBig(Blackhole bh) { + float acc = Float.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = (in1F[i] * in2F[i]) + (in1F[i] * in3F[i]) + (in2F[i] * in3F[i]); + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void floatMaxBig(Blackhole bh) { + float acc = Float.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + float val = (in1F[i] * in2F[i]) + (in1F[i] * in3F[i]) + (in2F[i] * in3F[i]); + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------double***Simple ------------------------------------------------------------ + @Benchmark + public void doubleAddSimple(Blackhole bh) { + double acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void doubleMulSimple(Blackhole bh) { + double acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void doubleMinSimple(Blackhole bh) { + double acc = Double.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void doubleMaxSimple(Blackhole bh) { + double acc = Double.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------double***DotProduct ------------------------------------------------------------ + @Benchmark + public void doubleAddDotProduct(Blackhole bh) { + double acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i] * in2D[i]; + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void doubleMulDotProduct(Blackhole bh) { + double acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i] * in2D[i]; + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void doubleMinDotProduct(Blackhole bh) { + double acc = Double.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i] * in2D[i]; + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void doubleMaxDotProduct(Blackhole bh) { + double acc = Double.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = in1D[i] * in2D[i]; + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + // ---------double***Big ------------------------------------------------------------ + @Benchmark + public void doubleAddBig(Blackhole bh) { + double acc = 0; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = (in1D[i] * in2D[i]) + (in1D[i] * in3D[i]) + (in2D[i] * in3D[i]); + acc += val; + } + bh.consume(acc); + } + + @Benchmark + public void doubleMulBig(Blackhole bh) { + double acc = 1; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = (in1D[i] * in2D[i]) + (in1D[i] * in3D[i]) + (in2D[i] * in3D[i]); + acc *= val; + } + bh.consume(acc); + } + + @Benchmark + public void doubleMinBig(Blackhole bh) { + double acc = Double.MAX_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = (in1D[i] * in2D[i]) + (in1D[i] * in3D[i]) + (in2D[i] * in3D[i]); + acc = Math.min(acc, val); + } + bh.consume(acc); + } + + @Benchmark + public void doubleMaxBig(Blackhole bh) { + double acc = Double.MIN_VALUE; // neutral element + for (int i = 0; i < SIZE; i++) { + double val = (in1D[i] * in2D[i]) + (in1D[i] * in3D[i]) + (in2D[i] * in3D[i]); + acc = Math.max(acc, val); + } + bh.consume(acc); + } + + @Fork(value = 1, jvmArgsPrepend = {"-XX:+UseSuperWord"}) + public static class WithSuperword extends VectorReduction2 {} + + @Fork(value = 1, jvmArgsPrepend = {"-XX:-UseSuperWord"}) + public static class NoSuperword extends VectorReduction2 {} +} + diff --git a/test/micro/org/openjdk/bench/vm/lang/LockUnlock.java b/test/micro/org/openjdk/bench/vm/lang/LockUnlock.java index 3ed862e8218cd..39c8569532ec4 100644 --- a/test/micro/org/openjdk/bench/vm/lang/LockUnlock.java +++ b/test/micro/org/openjdk/bench/vm/lang/LockUnlock.java @@ -309,10 +309,11 @@ private synchronized int fact(int n) { } /** - * With two threads lockObject1 will be contended so should be - * inflated. + * With three threads lockObject1 will be contended so should be + * inflated. Three threads is also needed to ensure a high level + * of code coverage in the locking code. */ - @Threads(2) + @Threads(3) @Benchmark public void testContendedLock() { synchronized (lockObject1) {