diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index 5aadaab05c9..351d087ef7d 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -64,26 +64,26 @@ jobs: gnu-arch: aarch64 debian-arch: arm64 debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: bookworm tolerate-sysroot-errors: false - target-cpu: arm gnu-arch: arm debian-arch: armhf debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: bookworm tolerate-sysroot-errors: false gnu-abi: eabihf - target-cpu: s390x gnu-arch: s390x debian-arch: s390x debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: bookworm tolerate-sysroot-errors: false - target-cpu: ppc64le gnu-arch: powerpc64le debian-arch: ppc64el debian-repository: https://httpredir.debian.org/debian/ - debian-version: bullseye + debian-version: bookworm tolerate-sysroot-errors: false - target-cpu: riscv64 gnu-arch: riscv64 diff --git a/doc/building.html b/doc/building.html index da8465bc532..f7af2648592 100644 --- a/doc/building.html +++ b/doc/building.html @@ -1491,12 +1491,12 @@ following targets are known to work:

-

BASE_OS must be one of "OEL6" for Oracle Enterprise -Linux 6 or "Fedora" (if not specified "OEL6" will be the default). If -the base OS is "Fedora" the corresponding Fedora release can be -specified with the help of the BASE_OS_VERSION option (with -"27" as default version). If the build is successful, the new devkits -can be found in the build/devkit/result subdirectory:

+

BASE_OS must be one of OL for Oracle +Enterprise Linux or Fedora. If the base OS is +Fedora the corresponding Fedora release can be specified +with the help of the BASE_OS_VERSION option. If the build +is successful, the new devkits can be found in the +build/devkit/result subdirectory:

cd make/devkit
 make TARGETS="ppc64le-linux-gnu aarch64-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=21
 ls -1 ../../build/devkit/result/
diff --git a/doc/building.md b/doc/building.md
index 1a9fe6b2e78..32c6ae46540 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -1285,12 +1285,10 @@ at least the following targets are known to work:
 | ppc64le-linux-gnu        |
 | s390x-linux-gnu          |
 
-`BASE_OS` must be one of "OEL6" for Oracle Enterprise Linux 6 or "Fedora" (if
-not specified "OEL6" will be the default). If the base OS is "Fedora" the
-corresponding Fedora release can be specified with the help of the
-`BASE_OS_VERSION` option (with "27" as default version). If the build is
-successful, the new devkits can be found in the `build/devkit/result`
-subdirectory:
+`BASE_OS` must be one of `OL` for Oracle Enterprise Linux or `Fedora`. If the
+base OS is `Fedora` the corresponding Fedora release can be specified with the
+help of the `BASE_OS_VERSION` option. If the build is successful, the new
+devkits can be found in the `build/devkit/result` subdirectory:
 
 ```
 cd make/devkit
diff --git a/make/CompileJavaModules.gmk b/make/CompileJavaModules.gmk
index 1e26fb2b529..c6e8fab3038 100644
--- a/make/CompileJavaModules.gmk
+++ b/make/CompileJavaModules.gmk
@@ -85,7 +85,7 @@ CreateHkTargets = \
 ################################################################################
 # Include module specific build settings
 
-THIS_SNIPPET := modules/$(MODULE)/Java.gmk
+THIS_SNIPPET := $(call GetModuleSnippetName, Java)
 
 ifneq ($(wildcard $(THIS_SNIPPET)), )
   include MakeSnippetStart.gmk
diff --git a/make/CreateJmods.gmk b/make/CreateJmods.gmk
index 40bceda69a9..a26042fb0d5 100644
--- a/make/CreateJmods.gmk
+++ b/make/CreateJmods.gmk
@@ -184,7 +184,7 @@ endif
 ################################################################################
 # Include module specific build settings
 
-THIS_SNIPPET := modules/$(MODULE)/Jmod.gmk
+THIS_SNIPPET := $(call GetModuleSnippetName, Jmod)
 
 ifneq ($(wildcard $(THIS_SNIPPET)), )
   include MakeSnippetStart.gmk
diff --git a/make/Docs.gmk b/make/Docs.gmk
index 1fcc8575d2c..8b20572cb03 100644
--- a/make/Docs.gmk
+++ b/make/Docs.gmk
@@ -236,8 +236,8 @@ define create_overview_file
       #
   ifneq ($$($1_GROUPS), )
     $1_OVERVIEW_TEXT += \
-      

This document is divided into \ - $$(subst 2,two,$$(subst 3,three,$$(words $$($1_GROUPS)))) sections:

\ +

This document has \ + $$(subst 2,two,$$(subst 3,three,$$(words $$($1_GROUPS)))) major sections:

\
\ # $1_OVERVIEW_TEXT += $$(foreach g, $$($1_GROUPS), \ @@ -246,7 +246,10 @@ define create_overview_file ) $1_OVERVIEW_TEXT += \
\ - # +

Related documents specify the Java \ + programming language, the Java Virtual Machine, various protocols and file \ + formats pertaining to the Java platform, and tools included in the JDK.

\ + # endif $1_OVERVIEW_TEXT += \ \ diff --git a/make/Images.gmk b/make/Images.gmk index 22e3e43cb1f..34d81081d29 100644 --- a/make/Images.gmk +++ b/make/Images.gmk @@ -270,6 +270,7 @@ endif # Since debug symbols are not included in the jmod files, they need to be copied # in manually after generating the images. +# These variables are read by SetupCopyDebuginfo ALL_JDK_MODULES := $(JDK_MODULES) ALL_JRE_MODULES := $(sort $(JRE_MODULES), $(foreach m, $(JRE_MODULES), \ $(call FindTransitiveDepsForModule, $m))) diff --git a/make/Main.gmk b/make/Main.gmk index d9433e722f0..e9f0b5bb9eb 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -1407,7 +1407,7 @@ CLEAN_SUPPORT_DIRS += demos CLEAN_SUPPORT_DIR_TARGETS := $(addprefix clean-, $(CLEAN_SUPPORT_DIRS)) CLEAN_TESTS += hotspot-jtreg-native jdk-jtreg-native lib CLEAN_TEST_TARGETS += $(addprefix clean-test-, $(CLEAN_TESTS)) -CLEAN_PHASES := gensrc java native include +CLEAN_PHASES += gensrc java native include CLEAN_PHASE_TARGETS := $(addprefix clean-, $(CLEAN_PHASES)) CLEAN_MODULE_TARGETS := $(addprefix clean-, $(ALL_MODULES)) # Construct targets of the form clean-$module-$phase diff --git a/make/MainSupport.gmk b/make/MainSupport.gmk index d8dc894c1e9..ee5bb324f8f 100644 --- a/make/MainSupport.gmk +++ b/make/MainSupport.gmk @@ -149,7 +149,7 @@ endef ################################################################################ -PHASE_MAKEDIRS := $(TOPDIR)/make +PHASE_MAKEDIRS += $(TOPDIR)/make # Helper macro for DeclareRecipesForPhase # Declare a recipe for calling the module and phase specific makefile. diff --git a/make/ModuleWrapper.gmk b/make/ModuleWrapper.gmk index b3ddf940e00..2db77b9ea32 100644 --- a/make/ModuleWrapper.gmk +++ b/make/ModuleWrapper.gmk @@ -34,18 +34,23 @@ include MakeFileStart.gmk ################################################################################ include CopyFiles.gmk +include Modules.gmk MODULE_SRC := $(TOPDIR)/src/$(MODULE) -# Define the snippet for MakeSnippetStart/End -THIS_SNIPPET := modules/$(MODULE)/$(MAKEFILE_PREFIX).gmk +################################################################################ +# Include module specific build settings -include MakeSnippetStart.gmk +THIS_SNIPPET := $(call GetModuleSnippetName, $(MAKEFILE_PREFIX)) -# Include the file being wrapped. -include $(THIS_SNIPPET) +ifneq ($(wildcard $(THIS_SNIPPET)), ) + include MakeSnippetStart.gmk -include MakeSnippetEnd.gmk + # Include the file being wrapped. + include $(THIS_SNIPPET) + + include MakeSnippetEnd.gmk +endif ifeq ($(MAKEFILE_PREFIX), Lib) # We need to keep track of what libraries are generated/needed by this diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index e80d9a98957..056334e99c4 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -736,8 +736,15 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP], $1_CFLAGS_CPU_JVM="${$1_CFLAGS_CPU_JVM} -mminimal-toc" elif test "x$FLAGS_CPU" = xppc64le; then # Little endian machine uses ELFv2 ABI. - # Use Power8, this is the first CPU to support PPC64 LE with ELFv2 ABI. - $1_CFLAGS_CPU="-mcpu=power8 -mtune=power10" + # Use Power8 for target cpu, this is the first CPU to support PPC64 LE with ELFv2 ABI. + # Use Power10 for tuning target, this is supported by gcc >= 10 + POWER_TUNE_VERSION="-mtune=power10" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${POWER_TUNE_VERSION}], + IF_FALSE: [ + POWER_TUNE_VERSION="-mtune=power8" + ] + ) + $1_CFLAGS_CPU="-mcpu=power8 ${POWER_TUNE_VERSION}" $1_CFLAGS_CPU_JVM="${$1_CFLAGS_CPU_JVM} -DABI_ELFv2" fi elif test "x$FLAGS_CPU" = xs390x; then diff --git a/make/common/JavaCompilation.gmk b/make/common/JavaCompilation.gmk index c5a74413de1..99672d59884 100644 --- a/make/common/JavaCompilation.gmk +++ b/make/common/JavaCompilation.gmk @@ -178,6 +178,10 @@ define SetupJavaCompilationBody $1_SAFE_NAME := $$(strip $$(subst /,_, $1)) + ifeq ($$($1_LOG_ACTION), ) + $1_LOG_ACTION := Compiling + endif + ifeq ($$($1_SMALL_JAVA), ) # If unspecified, default to true $1_SMALL_JAVA := true @@ -472,7 +476,7 @@ define SetupJavaCompilationBody # list of files. $$($1_FILELIST): $$($1_SRCS) $$($1_VARDEPS_FILE) $$(call MakeDir, $$(@D)) - $$(call LogWarn, Compiling up to $$(words $$($1_SRCS)) files for $1) + $$(call LogWarn, $$($1_LOG_ACTION) up to $$(words $$($1_SRCS)) files for $1) $$(eval $$(call ListPathsSafely, $1_SRCS, $$($1_FILELIST))) # Create a $$($1_MODFILELIST) file with significant modified dependencies diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk index 725424d7618..2880504676a 100644 --- a/make/common/Modules.gmk +++ b/make/common/Modules.gmk @@ -33,7 +33,7 @@ include $(TOPDIR)/make/conf/module-loader-map.conf # Append platform-specific and upgradeable modules PLATFORM_MODULES += $(PLATFORM_MODULES_$(OPENJDK_TARGET_OS)) \ - $(UPGRADEABLE_PLATFORM_MODULES) + $(UPGRADEABLE_PLATFORM_MODULES) $(CUSTOM_UPGRADEABLE_PLATFORM_MODULES) ################################################################################ # Setup module sets for docs @@ -216,7 +216,7 @@ endif # Find dependencies ("requires") for a given module. # Param 1: Module to find dependencies for. FindDepsForModule = \ - $(DEPS_$(strip $1)) + $(filter-out $(IMPORT_MODULES), $(DEPS_$(strip $1))) # Find dependencies ("requires") transitively in 3 levels for a given module. # Param 1: Module to find dependencies for. @@ -254,7 +254,8 @@ FindTransitiveIndirectDepsForModules = \ # Upgradeable modules are those that are either defined as upgradeable or that # require an upradeable module. FindAllUpgradeableModules = \ - $(sort $(filter-out $(MODULES_FILTER), $(UPGRADEABLE_PLATFORM_MODULES))) + $(sort $(filter-out $(MODULES_FILTER), \ + $(UPGRADEABLE_PLATFORM_MODULES) $(CUSTOM_UPGRADEABLE_PLATFORM_MODULES))) ################################################################################ @@ -316,6 +317,19 @@ define ReadImportMetaData $$(eval $$(call ReadSingleImportMetaData, $$m))) endef +################################################################################ +# Get a full snippet path for the current module and a given base name. +# +# Param 1 - The base name of the snippet file to include +GetModuleSnippetName = \ + $(if $(CUSTOM_MODULE_MAKE_ROOT), \ + $(if $(wildcard $(CUSTOM_MODULE_MAKE_ROOT)/$(MODULE)/$(strip $1).gmk), \ + $(CUSTOM_MODULE_MAKE_ROOT)/$(MODULE)/$(strip $1).gmk, \ + $(wildcard modules/$(MODULE)/$(strip $1).gmk) \ + ), \ + $(wildcard modules/$(MODULE)/$(strip $1).gmk) \ + ) + ################################################################################ endif # include guard diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk index dcb41defba3..a69b65180d7 100644 --- a/make/modules/java.desktop/lib/ClientLibraries.gmk +++ b/make/modules/java.desktop/lib/ClientLibraries.gmk @@ -177,7 +177,8 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false) endif LIBSPLASHSCREEN_CFLAGS += -DSPLASHSCREEN -DPNG_NO_MMX_CODE \ - -DPNG_ARM_NEON_OPT=0 -DPNG_ARM_NEON_IMPLEMENTATION=0 + -DPNG_ARM_NEON_OPT=0 -DPNG_ARM_NEON_IMPLEMENTATION=0 \ + -DPNG_LOONGARCH_LSX_OPT=0 ifeq ($(call isTargetOs, linux)+$(call isTargetCpuArch, ppc), true+true) LIBSPLASHSCREEN_CFLAGS += -DPNG_POWERPC_VSX_OPT=0 diff --git a/make/modules/jdk.jpackage/Lib.gmk b/make/modules/jdk.jpackage/Lib.gmk index e9c55548b0d..d2dd9d92a03 100644 --- a/make/modules/jdk.jpackage/Lib.gmk +++ b/make/modules/jdk.jpackage/Lib.gmk @@ -121,15 +121,15 @@ ifeq ($(call isTargetOs, windows), true) TARGETS += $(BUILD_LIBJPACKAGE) ############################################################################## - ## Build libwixhelper + ## Build libmsica ############################################################################## - # Build Wix custom action helper + # Build MSI custom action library # Output library in resources dir, and symbols in the object dir - $(eval $(call SetupJdkLibrary, BUILD_LIBWIXHELPER, \ - NAME := wixhelper, \ + $(eval $(call SetupJdkLibrary, BUILD_LIBMSICA, \ + NAME := msica, \ OUTPUT_DIR := $(JPACKAGE_OUTPUT_DIR), \ - SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libwixhelper, \ + SYMBOLS_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libmsica, \ ONLY_EXPORTED := true, \ OPTIMIZATION := LOW, \ EXTRA_SRC := common, \ @@ -139,7 +139,7 @@ ifeq ($(call isTargetOs, windows), true) LIBS_windows := msi.lib ole32.lib shell32.lib shlwapi.lib user32.lib, \ )) - TARGETS += $(BUILD_LIBWIXHELPER) + TARGETS += $(BUILD_LIBMSICA) ############################################################################## ## Build msiwrapper diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 404ab8d9ba4..517da8066de 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -881,6 +881,46 @@ reg_class vectorx_reg( V31, V31_H, V31_J, V31_K ); +// Class for vector register V10 +reg_class v10_veca_reg( + V10, V10_H, V10_J, V10_K +); + +// Class for vector register V11 +reg_class v11_veca_reg( + V11, V11_H, V11_J, V11_K +); + +// Class for vector register V12 +reg_class v12_veca_reg( + V12, V12_H, V12_J, V12_K +); + +// Class for vector register V13 +reg_class v13_veca_reg( + V13, V13_H, V13_J, V13_K +); + +// Class for vector register V17 +reg_class v17_veca_reg( + V17, V17_H, V17_J, V17_K +); + +// Class for vector register V18 +reg_class v18_veca_reg( + V18, V18_H, V18_J, V18_K +); + +// Class for vector register V23 +reg_class v23_veca_reg( + V23, V23_H, V23_J, V23_K +); + +// Class for vector register V24 +reg_class v24_veca_reg( + V24, V24_H, V24_J, V24_K +); + // Class for 128 bit register v0 reg_class v0_reg( V0, V0_H @@ -4969,6 +5009,86 @@ operand vReg() interface(REG_INTER); %} +operand vReg_V10() +%{ + constraint(ALLOC_IN_RC(v10_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vReg_V11() +%{ + constraint(ALLOC_IN_RC(v11_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vReg_V12() +%{ + constraint(ALLOC_IN_RC(v12_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vReg_V13() +%{ + constraint(ALLOC_IN_RC(v13_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vReg_V17() +%{ + constraint(ALLOC_IN_RC(v17_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vReg_V18() +%{ + constraint(ALLOC_IN_RC(v18_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vReg_V23() +%{ + constraint(ALLOC_IN_RC(v23_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + +operand vReg_V24() +%{ + constraint(ALLOC_IN_RC(v24_veca_reg)); + match(vReg); + + op_cost(0); + format %{ %} + interface(REG_INTER); +%} + operand vecA() %{ constraint(ALLOC_IN_RC(vectora_reg)); diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index 1b6296ddd8b..58300992c2a 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -257,6 +257,28 @@ source %{ return false; } break; + case Op_SelectFromTwoVector: + // The "tbl" instruction for two vector table is supported only in Neon and SVE2. Return + // false if vector length > 16B but supported SVE version < 2. + // For vector length of 16B, generate SVE2 "tbl" instruction if SVE2 is supported, else + // generate Neon "tbl" instruction to select from two vectors. + // This operation is disabled for doubles and longs on machines with SVE < 2 and instead + // the default VectorRearrange + VectorBlend is generated because the performance of the default + // implementation was better than or equal to the implementation for SelectFromTwoVector. + if (UseSVE < 2 && (type2aelembytes(bt) == 8 || length_in_bytes > 16)) { + return false; + } + + // Because the SVE2 "tbl" instruction is unpredicated and partial operations cannot be generated + // using masks, we disable this operation on machines where length_in_bytes < MaxVectorSize + // on that machine with the only exception of 8B vector length. This is because at the time of + // writing this, there is no SVE2 machine available with length_in_bytes > 8 and + // length_in_bytes < MaxVectorSize to test this operation on (for example - there isn't an + // SVE2 machine available with MaxVectorSize = 32 to test a case with length_in_bytes = 16). + if (UseSVE == 2 && length_in_bytes > 8 && length_in_bytes < MaxVectorSize) { + return false; + } + break; default: break; } @@ -7172,3 +7194,71 @@ instruct vexpandBits(vReg dst, vReg src1, vReg src2) %{ %} ins_pipe(pipe_slow); %} + +// ------------------------------------- SelectFromTwoVector ------------------------------------ +// The Neon and SVE2 tbl instruction for two vector lookup requires both the source vectors to be +// consecutive. The match rules for SelectFromTwoVector reserve two consecutive vector registers +// for src1 and src2. +// Four combinations of vector registers for vselect_from_two_vectors are chosen at random +// (two from volatile and two from non-volatile set) which gives more freedom to the register +// allocator to choose the best pair of source registers at that point. + +instruct vselect_from_two_vectors_10_11(vReg dst, vReg_V10 src1, vReg_V11 src2, + vReg index, vReg tmp) %{ + effect(TEMP_DEF dst, TEMP tmp); + match(Set dst (SelectFromTwoVector (Binary index src1) src2)); + format %{ "vselect_from_two_vectors_10_11 $dst, $src1, $src2, $index\t# KILL $tmp" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + __ select_from_two_vectors($dst$$FloatRegister, $src1$$FloatRegister, + $src2$$FloatRegister, $index$$FloatRegister, + $tmp$$FloatRegister, bt, length_in_bytes); + %} + ins_pipe(pipe_slow); +%} + +instruct vselect_from_two_vectors_12_13(vReg dst, vReg_V12 src1, vReg_V13 src2, + vReg index, vReg tmp) %{ + effect(TEMP_DEF dst, TEMP tmp); + match(Set dst (SelectFromTwoVector (Binary index src1) src2)); + format %{ "vselect_from_two_vectors_12_13 $dst, $src1, $src2, $index\t# KILL $tmp" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + __ select_from_two_vectors($dst$$FloatRegister, $src1$$FloatRegister, + $src2$$FloatRegister, $index$$FloatRegister, + $tmp$$FloatRegister, bt, length_in_bytes); + %} + ins_pipe(pipe_slow); +%} + +instruct vselect_from_two_vectors_17_18(vReg dst, vReg_V17 src1, vReg_V18 src2, + vReg index, vReg tmp) %{ + effect(TEMP_DEF dst, TEMP tmp); + match(Set dst (SelectFromTwoVector (Binary index src1) src2)); + format %{ "vselect_from_two_vectors_17_18 $dst, $src1, $src2, $index\t# KILL $tmp" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + __ select_from_two_vectors($dst$$FloatRegister, $src1$$FloatRegister, + $src2$$FloatRegister, $index$$FloatRegister, + $tmp$$FloatRegister, bt, length_in_bytes); + %} + ins_pipe(pipe_slow); +%} + +instruct vselect_from_two_vectors_23_24(vReg dst, vReg_V23 src1, vReg_V24 src2, + vReg index, vReg tmp) %{ + effect(TEMP_DEF dst, TEMP tmp); + match(Set dst (SelectFromTwoVector (Binary index src1) src2)); + format %{ "vselect_from_two_vectors_23_24 $dst, $src1, $src2, $index\t# KILL $tmp" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + __ select_from_two_vectors($dst$$FloatRegister, $src1$$FloatRegister, + $src2$$FloatRegister, $index$$FloatRegister, + $tmp$$FloatRegister, bt, length_in_bytes); + %} + ins_pipe(pipe_slow); +%} diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index efefbf692bd..4d91e04dc21 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -247,6 +247,28 @@ source %{ return false; } break; + case Op_SelectFromTwoVector: + // The "tbl" instruction for two vector table is supported only in Neon and SVE2. Return + // false if vector length > 16B but supported SVE version < 2. + // For vector length of 16B, generate SVE2 "tbl" instruction if SVE2 is supported, else + // generate Neon "tbl" instruction to select from two vectors. + // This operation is disabled for doubles and longs on machines with SVE < 2 and instead + // the default VectorRearrange + VectorBlend is generated because the performance of the default + // implementation was better than or equal to the implementation for SelectFromTwoVector. + if (UseSVE < 2 && (type2aelembytes(bt) == 8 || length_in_bytes > 16)) { + return false; + } + + // Because the SVE2 "tbl" instruction is unpredicated and partial operations cannot be generated + // using masks, we disable this operation on machines where length_in_bytes < MaxVectorSize + // on that machine with the only exception of 8B vector length. This is because at the time of + // writing this, there is no SVE2 machine available with length_in_bytes > 8 and + // length_in_bytes < MaxVectorSize to test this operation on (for example - there isn't an + // SVE2 machine available with MaxVectorSize = 32 to test a case with length_in_bytes = 16). + if (UseSVE == 2 && length_in_bytes > 8 && length_in_bytes < MaxVectorSize) { + return false; + } + break; default: break; } @@ -5154,3 +5176,34 @@ BITPERM(vcompressBits, CompressBitsV, sve_bext) // ----------------------------------- ExpandBitsV --------------------------------- BITPERM(vexpandBits, ExpandBitsV, sve_bdep) + +// ------------------------------------- SelectFromTwoVector ------------------------------------ +// The Neon and SVE2 tbl instruction for two vector lookup requires both the source vectors to be +// consecutive. The match rules for SelectFromTwoVector reserve two consecutive vector registers +// for src1 and src2. +// Four combinations of vector registers for vselect_from_two_vectors are chosen at random +// (two from volatile and two from non-volatile set) which gives more freedom to the register +// allocator to choose the best pair of source registers at that point. +dnl +dnl SELECT_FROM_TWO_VECTORS($1, $2 ) +dnl SELECT_FROM_TWO_VECTORS(first_reg, second_reg) +define(`SELECT_FROM_TWO_VECTORS', ` +instruct vselect_from_two_vectors_$1_$2(vReg dst, vReg_V$1 src1, vReg_V$2 src2, + vReg index, vReg tmp) %{ + effect(TEMP_DEF dst, TEMP tmp); + match(Set dst (SelectFromTwoVector (Binary index src1) src2)); + format %{ "vselect_from_two_vectors_$1_$2 $dst, $src1, $src2, $index\t# KILL $tmp" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + uint length_in_bytes = Matcher::vector_length_in_bytes(this); + __ select_from_two_vectors($dst$$FloatRegister, $src1$$FloatRegister, + $src2$$FloatRegister, $index$$FloatRegister, + $tmp$$FloatRegister, bt, length_in_bytes); + %} + ins_pipe(pipe_slow); +%}')dnl +dnl +SELECT_FROM_TWO_VECTORS(10, 11) +SELECT_FROM_TWO_VECTORS(12, 13) +SELECT_FROM_TWO_VECTORS(17, 18) +SELECT_FROM_TWO_VECTORS(23, 24) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 2e35763aa43..11d302e9026 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -4231,12 +4231,29 @@ public: sf(imm1, 9, 5), rf(Zd, 0); } - // SVE programmable table lookup/permute using vector of element indices - void sve_tbl(FloatRegister Zd, SIMD_RegVariant T, FloatRegister Zn, FloatRegister Zm) { +private: + void _sve_tbl(FloatRegister Zd, SIMD_RegVariant T, FloatRegister Zn, unsigned reg_count, FloatRegister Zm) { starti; assert(T != Q, "invalid size"); + // Only supports one or two vector lookup. One vector lookup was introduced in SVE1 + // and two vector lookup in SVE2 + assert(0 < reg_count && reg_count <= 2, "invalid number of registers"); + + int op11 = (reg_count == 1) ? 0b10 : 0b01; + f(0b00000101, 31, 24), f(T, 23, 22), f(0b1, 21), rf(Zm, 16); - f(0b001100, 15, 10), rf(Zn, 5), rf(Zd, 0); + f(0b001, 15, 13), f(op11, 12, 11), f(0b0, 10), rf(Zn, 5), rf(Zd, 0); + } + +public: + // SVE/SVE2 Programmable table lookup in one or two vector table (zeroing) + void sve_tbl(FloatRegister Zd, SIMD_RegVariant T, FloatRegister Zn, FloatRegister Zm) { + _sve_tbl(Zd, T, Zn, 1, Zm); + } + + void sve_tbl(FloatRegister Zd, SIMD_RegVariant T, FloatRegister Zn1, FloatRegister Zn2, FloatRegister Zm) { + assert(Zn1->successor() == Zn2, "invalid order of registers"); + _sve_tbl(Zd, T, Zn1, 2, Zm); } // Shuffle active elements of vector to the right and fill with zero diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index a4ecd56af08..e87cb478c8f 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -2858,3 +2858,124 @@ void C2_MacroAssembler::reconstruct_frame_pointer(Register rtmp) { add(rfp, sp, framesize - 2 * wordSize); } } + +// Selects elements from two source vectors (src1, src2) based on index values in the index register +// using Neon instructions and places it in the destination vector element corresponding to the +// index vector element. Each index in the index register must be in the range - [0, 2 * NUM_ELEM), +// where NUM_ELEM is the number of BasicType elements per vector. +// If idx < NUM_ELEM --> selects src1[idx] (idx is an element of the index register) +// Otherwise, selects src2[idx – NUM_ELEM] +void C2_MacroAssembler::select_from_two_vectors_neon(FloatRegister dst, FloatRegister src1, + FloatRegister src2, FloatRegister index, + FloatRegister tmp, unsigned vector_length_in_bytes) { + assert_different_registers(dst, src1, src2, tmp); + SIMD_Arrangement size = vector_length_in_bytes == 16 ? T16B : T8B; + + if (vector_length_in_bytes == 16) { + assert(UseSVE <= 1, "sve must be <= 1"); + assert(src1->successor() == src2, "Source registers must be ordered"); + // If the vector length is 16B, then use the Neon "tbl" instruction with two vector table + tbl(dst, size, src1, 2, index); + } else { // vector length == 8 + assert(UseSVE == 0, "must be Neon only"); + // We need to fit both the source vectors (src1, src2) in a 128-bit register because the + // Neon "tbl" instruction supports only looking up 16B vectors. We then use the Neon "tbl" + // instruction with one vector lookup + ins(tmp, D, src1, 0, 0); + ins(tmp, D, src2, 1, 0); + tbl(dst, size, tmp, 1, index); + } +} + +// Selects elements from two source vectors (src1, src2) based on index values in the index register +// using SVE/SVE2 instructions and places it in the destination vector element corresponding to the +// index vector element. Each index in the index register must be in the range - [0, 2 * NUM_ELEM), +// where NUM_ELEM is the number of BasicType elements per vector. +// If idx < NUM_ELEM --> selects src1[idx] (idx is an element of the index register) +// Otherwise, selects src2[idx – NUM_ELEM] +void C2_MacroAssembler::select_from_two_vectors_sve(FloatRegister dst, FloatRegister src1, + FloatRegister src2, FloatRegister index, + FloatRegister tmp, SIMD_RegVariant T, + unsigned vector_length_in_bytes) { + assert_different_registers(dst, src1, src2, index, tmp); + + if (vector_length_in_bytes == 8) { + // We need to fit both the source vectors (src1, src2) in a single vector register because the + // SVE "tbl" instruction is unpredicated and works on the entire vector which can lead to + // incorrect results if each source vector is only partially filled. We then use the SVE "tbl" + // instruction with one vector lookup + assert(UseSVE >= 1, "sve must be >= 1"); + ins(tmp, D, src1, 0, 0); + ins(tmp, D, src2, 1, 0); + sve_tbl(dst, T, tmp, index); + } else { // UseSVE == 2 and vector_length_in_bytes > 8 + // If the vector length is > 8, then use the SVE2 "tbl" instruction with the two vector table. + // The assertion - vector_length_in_bytes == MaxVectorSize ensures that this operation + // is not executed on machines where vector_length_in_bytes < MaxVectorSize + // with the only exception of 8B vector length. + assert(UseSVE == 2 && vector_length_in_bytes == MaxVectorSize, "must be"); + assert(src1->successor() == src2, "Source registers must be ordered"); + sve_tbl(dst, T, src1, src2, index); + } +} + +void C2_MacroAssembler::select_from_two_vectors(FloatRegister dst, FloatRegister src1, + FloatRegister src2, FloatRegister index, + FloatRegister tmp, BasicType bt, + unsigned vector_length_in_bytes) { + + assert_different_registers(dst, src1, src2, index, tmp); + + // The cases that can reach this method are - + // - UseSVE = 0, vector_length_in_bytes = 8 or 16 + // - UseSVE = 1, vector_length_in_bytes = 8 or 16 + // - UseSVE = 2, vector_length_in_bytes >= 8 + // + // SVE/SVE2 tbl instructions are generated when UseSVE = 1 with vector_length_in_bytes = 8 + // and UseSVE = 2 with vector_length_in_bytes >= 8 + // + // Neon instructions are generated when UseSVE = 0 with vector_length_in_bytes = 8 or 16 and + // UseSVE = 1 with vector_length_in_bytes = 16 + + if ((UseSVE == 1 && vector_length_in_bytes == 8) || UseSVE == 2) { + SIMD_RegVariant T = elemType_to_regVariant(bt); + select_from_two_vectors_sve(dst, src1, src2, index, tmp, T, vector_length_in_bytes); + return; + } + + // The only BasicTypes that can reach here are T_SHORT, T_BYTE, T_INT and T_FLOAT + assert(bt != T_DOUBLE && bt != T_LONG, "unsupported basic type"); + assert(vector_length_in_bytes <= 16, "length_in_bytes must be <= 16"); + + bool isQ = vector_length_in_bytes == 16; + + SIMD_Arrangement size1 = isQ ? T16B : T8B; + SIMD_Arrangement size2 = esize2arrangement((uint)type2aelembytes(bt), isQ); + + // Neon "tbl" instruction only supports byte tables, so we need to look at chunks of + // 2B for selecting shorts or chunks of 4B for selecting ints/floats from the table. + // The index values in "index" register are in the range of [0, 2 * NUM_ELEM) where NUM_ELEM + // is the number of elements that can fit in a vector. For ex. for T_SHORT with 64-bit vector length, + // the indices can range from [0, 8). + // As an example with 64-bit vector length and T_SHORT type - let index = [2, 5, 1, 0] + // Move a constant 0x02 in every byte of tmp - tmp = [0x0202, 0x0202, 0x0202, 0x0202] + // Multiply index vector with tmp to yield - dst = [0x0404, 0x0a0a, 0x0202, 0x0000] + // Move a constant 0x0100 in every 2B of tmp - tmp = [0x0100, 0x0100, 0x0100, 0x0100] + // Add the multiplied result to the vector in tmp to obtain the byte level + // offsets - dst = [0x0504, 0x0b0a, 0x0302, 0x0100] + // Use these offsets in the "tbl" instruction to select chunks of 2B. + + if (bt == T_BYTE) { + select_from_two_vectors_neon(dst, src1, src2, index, tmp, vector_length_in_bytes); + } else { + int elem_size = (bt == T_SHORT) ? 2 : 4; + uint64_t tbl_offset = (bt == T_SHORT) ? 0x0100u : 0x03020100u; + + mov(tmp, size1, elem_size); + mulv(dst, size2, index, tmp); + mov(tmp, size2, tbl_offset); + addv(dst, size1, dst, tmp); // "dst" now contains the processed index elements + // to select a set of 2B/4B + select_from_two_vectors_neon(dst, src1, src2, dst, tmp, vector_length_in_bytes); + } +} diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 70e4265c7cc..233f600cb14 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -34,6 +34,15 @@ void neon_reduce_logical_helper(int opc, bool sf, Register Rd, Register Rn, Register Rm, enum shift_kind kind = Assembler::LSL, unsigned shift = 0); + void select_from_two_vectors_neon(FloatRegister dst, FloatRegister src1, + FloatRegister src2, FloatRegister index, + FloatRegister tmp, unsigned vector_length_in_bytes); + + void select_from_two_vectors_sve(FloatRegister dst, FloatRegister src1, + FloatRegister src2, FloatRegister index, + FloatRegister tmp, SIMD_RegVariant T, + unsigned vector_length_in_bytes); + public: // jdk.internal.util.ArraysSupport.vectorizedHashCode address arrays_hashcode(Register ary, Register cnt, Register result, FloatRegister vdata0, @@ -193,4 +202,9 @@ void reconstruct_frame_pointer(Register rtmp); + // Select from a table of two vectors + void select_from_two_vectors(FloatRegister dst, FloatRegister src1, FloatRegister src2, + FloatRegister index, FloatRegister tmp, BasicType bt, + unsigned vector_length_in_bytes); + #endif // CPU_AARCH64_C2_MACROASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index a2b3f44c68b..ed321ca4759 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -292,7 +292,8 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, } else { assert(is_phantom, "only remaining strength"); assert(!is_narrow, "phantom access cannot be narrow"); - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom)); + // AOT saved adapters need relocation for this call. + __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom))); } __ blr(lr); __ mov(rscratch1, r0); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 0a7d9af9bff..fa7329f4942 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2732,8 +2732,11 @@ class StubGenerator: public StubCodeGenerator { address entry_jlong_arraycopy; address entry_checkcast_arraycopy; - address ucm_common_error_exit = generate_unsafecopy_common_error_exit(); - UnsafeMemoryAccess::set_common_exit_stub_pc(ucm_common_error_exit); + // generate the common exit first so later stubs can rely on it if + // they want an UnsafeMemoryAccess exit non-local to the stub + StubRoutines::_unsafecopy_common_exit = generate_unsafecopy_common_error_exit(); + // register the stub as the default exit with class UnsafeMemoryAccess + UnsafeMemoryAccess::set_common_exit_stub_pc(StubRoutines::_unsafecopy_common_exit); generate_copy_longs(StubId::stubgen_copy_byte_f_id, IN_HEAP | IS_ARRAY, copy_f, r0, r1, r15); generate_copy_longs(StubId::stubgen_copy_byte_b_id, IN_HEAP | IS_ARRAY, copy_b, r0, r1, r15); @@ -11680,8 +11683,6 @@ class StubGenerator: public StubCodeGenerator { } if (UseCRC32Intrinsics) { - // set table address before stub generation which use it - StubRoutines::_crc_table_adr = (address)StubRoutines::aarch64::_crc_table; StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32(); } diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp index fab76c41303..88993818b47 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp @@ -71,6 +71,10 @@ ATTRIBUTE_ALIGNED(64) uint32_t StubRoutines::aarch64::_dilithiumConsts[] = /** * crc_table[] from jdk/src/share/native/java/util/zip/zlib-1.2.5/crc32.h */ + +address StubRoutines::crc_table_addr() { return (address)StubRoutines::aarch64::_crc_table; } +address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } + ATTRIBUTE_ALIGNED(4096) juint StubRoutines::aarch64::_crc_table[] = { // Table 0 diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp index 4c942b9f8d8..c35371e1083 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp @@ -47,6 +47,7 @@ enum platform_dependent_constants { class aarch64 { friend class StubGenerator; + friend class StubRoutines; #if INCLUDE_JVMCI friend class JVMCIVMStructs; #endif diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 9321dd0542e..24c77174711 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -32,6 +32,7 @@ #include "runtime/vm_version.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/macros.hpp" +#include "utilities/ostream.hpp" int VM_Version::_cpu; int VM_Version::_model; @@ -50,6 +51,8 @@ uintptr_t VM_Version::_pac_mask; SpinWait VM_Version::_spin_wait; +const char* VM_Version::_features_names[MAX_CPU_FEATURES] = { nullptr }; + static SpinWait get_spin_wait_desc() { SpinWait spin_wait(OnSpinWaitInst, OnSpinWaitInstCount); if (spin_wait.inst() == SpinWait::SB && !VM_Version::supports_sb()) { @@ -60,6 +63,11 @@ static SpinWait get_spin_wait_desc() { } void VM_Version::initialize() { +#define SET_CPU_FEATURE_NAME(id, name, bit) \ + _features_names[bit] = XSTR(name); + CPU_FEATURE_FLAGS(SET_CPU_FEATURE_NAME) +#undef SET_CPU_FEATURE_NAME + _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; _supports_atomic_getset8 = true; @@ -194,7 +202,7 @@ void VM_Version::initialize() { // Cortex A53 if (_cpu == CPU_ARM && model_is(0xd03)) { - _features |= CPU_A53MAC; + set_feature(CPU_A53MAC); if (FLAG_IS_DEFAULT(UseSIMDForArrayEquals)) { FLAG_SET_DEFAULT(UseSIMDForArrayEquals, false); } @@ -234,7 +242,7 @@ void VM_Version::initialize() { } } - if (_features & (CPU_FP | CPU_ASIMD)) { + if (supports_feature(CPU_FP) || supports_feature(CPU_ASIMD)) { if (FLAG_IS_DEFAULT(UseSignumIntrinsic)) { FLAG_SET_DEFAULT(UseSignumIntrinsic, true); } @@ -397,7 +405,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); } - if (_features & CPU_ASIMD) { + if (supports_feature(CPU_ASIMD)) { if (FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) { UseChaCha20Intrinsics = true; } @@ -408,7 +416,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false); } - if (_features & CPU_ASIMD) { + if (supports_feature(CPU_ASIMD)) { if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { UseKyberIntrinsics = true; } @@ -419,7 +427,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseKyberIntrinsics, false); } - if (_features & CPU_ASIMD) { + if (supports_feature(CPU_ASIMD)) { if (FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) { UseDilithiumIntrinsics = true; } @@ -620,32 +628,38 @@ void VM_Version::initialize() { // Sync SVE related CPU features with flags if (UseSVE < 2) { - _features &= ~CPU_SVE2; - _features &= ~CPU_SVEBITPERM; + clear_feature(CPU_SVE2); + clear_feature(CPU_SVEBITPERM); } if (UseSVE < 1) { - _features &= ~CPU_SVE; + clear_feature(CPU_SVE); } // Construct the "features" string - char buf[512]; - int buf_used_len = os::snprintf_checked(buf, sizeof(buf), "0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); + stringStream ss(512); + ss.print("0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); if (_model2) { - os::snprintf_checked(buf + buf_used_len, sizeof(buf) - buf_used_len, "(0x%03x)", _model2); + ss.print("(0x%03x)", _model2); } - size_t features_offset = strnlen(buf, sizeof(buf)); -#define ADD_FEATURE_IF_SUPPORTED(id, name, bit) \ - do { \ - if (VM_Version::supports_##name()) strcat(buf, ", " #name); \ - } while(0); - CPU_FEATURE_FLAGS(ADD_FEATURE_IF_SUPPORTED) -#undef ADD_FEATURE_IF_SUPPORTED + ss.print(", "); + int features_offset = (int)ss.size(); + insert_features_names(_features, ss); - _cpu_info_string = os::strdup(buf); + _cpu_info_string = ss.as_string(true); + _features_string = _cpu_info_string + features_offset; +} - _features_string = extract_features_string(_cpu_info_string, - strnlen(_cpu_info_string, sizeof(buf)), - features_offset); +void VM_Version::insert_features_names(uint64_t features, stringStream& ss) { + int i = 0; + ss.join([&]() { + while (i < MAX_CPU_FEATURES) { + if (supports_feature((VM_Version::Feature_Flag)i)) { + return _features_names[i++]; + } + i += 1; + } + return (const char*)nullptr; + }, ", "); } #if defined(LINUX) diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 99450d3dde1..5a8642a285a 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -30,6 +30,10 @@ #include "runtime/abstract_vm_version.hpp" #include "utilities/sizes.hpp" +class stringStream; + +#define BIT_MASK(flag) (1ULL<<(flag)) + class VM_Version : public Abstract_VM_Version { friend class VMStructs; friend class JVMCIVMStructs; @@ -66,6 +70,8 @@ public: static void initialize(); static void check_virtualizations(); + static void insert_features_names(uint64_t features, stringStream& ss); + static void print_platform_virtualization_info(outputStream*); // Asserts @@ -139,17 +145,32 @@ enum Ampere_CPU_Model { decl(A53MAC, a53mac, 31) enum Feature_Flag { -#define DECLARE_CPU_FEATURE_FLAG(id, name, bit) CPU_##id = (1 << bit), +#define DECLARE_CPU_FEATURE_FLAG(id, name, bit) CPU_##id = bit, CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_FLAG) #undef DECLARE_CPU_FEATURE_FLAG + MAX_CPU_FEATURES }; + STATIC_ASSERT(sizeof(_features) * BitsPerByte >= MAX_CPU_FEATURES); + + static const char* _features_names[MAX_CPU_FEATURES]; + // Feature identification #define CPU_FEATURE_DETECTION(id, name, bit) \ - static bool supports_##name() { return (_features & CPU_##id) != 0; }; + static bool supports_##name() { return supports_feature(CPU_##id); } CPU_FEATURE_FLAGS(CPU_FEATURE_DETECTION) #undef CPU_FEATURE_DETECTION + static void set_feature(Feature_Flag flag) { + _features |= BIT_MASK(flag); + } + static void clear_feature(Feature_Flag flag) { + _features &= (~BIT_MASK(flag)); + } + static bool supports_feature(Feature_Flag flag) { + return (_features & BIT_MASK(flag)) != 0; + } + static int cpu_family() { return _cpu; } static int cpu_model() { return _model; } static int cpu_model2() { return _model2; } diff --git a/src/hotspot/cpu/arm/c1_FrameMap_arm.hpp b/src/hotspot/cpu/arm/c1_FrameMap_arm.hpp index 9c9877544ec..beb0699a430 100644 --- a/src/hotspot/cpu/arm/c1_FrameMap_arm.hpp +++ b/src/hotspot/cpu/arm/c1_FrameMap_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,8 +95,6 @@ } static int adjust_reg_range(int range) { - // Reduce the number of available regs (to free Rheap_base) in case of compressed oops - if (UseCompressedOops || UseCompressedClassPointers) return range - 1; return range; } diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index 0367f349d65..6a859a5875b 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -2229,16 +2229,9 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // We don't know the array types are compatible if (basic_type != T_OBJECT) { // Simple test for basic type arrays - if (UseCompressedClassPointers) { - // We don't need decode because we just need to compare - __ ldr_u32(tmp, Address(src, oopDesc::klass_offset_in_bytes())); - __ ldr_u32(tmp2, Address(dst, oopDesc::klass_offset_in_bytes())); - __ cmp_32(tmp, tmp2); - } else { - __ load_klass(tmp, src); - __ load_klass(tmp2, dst); - __ cmp(tmp, tmp2); - } + __ load_klass(tmp, src); + __ load_klass(tmp2, dst); + __ cmp(tmp, tmp2); __ b(*stub->entry(), ne); } else { // For object arrays, if src is a sub class of dst then we can @@ -2461,12 +2454,7 @@ void LIR_Assembler::emit_load_klass(LIR_OpLoadKlass* op) { if (info != nullptr) { add_debug_info_for_null_check_here(info); } - - if (UseCompressedClassPointers) { // On 32 bit arm?? - __ ldr_u32(result, Address(obj, oopDesc::klass_offset_in_bytes())); - } else { - __ ldr(result, Address(obj, oopDesc::klass_offset_in_bytes())); - } + __ ldr(result, Address(obj, oopDesc::klass_offset_in_bytes())); } void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { diff --git a/src/hotspot/cpu/arm/stubGenerator_arm.cpp b/src/hotspot/cpu/arm/stubGenerator_arm.cpp index de5ce221495..b81400ae877 100644 --- a/src/hotspot/cpu/arm/stubGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/stubGenerator_arm.cpp @@ -3001,12 +3001,15 @@ class StubGenerator: public StubCodeGenerator { void generate_arraycopy_stubs() { + // generate the common exit first so later stubs can rely on it if + // they want an UnsafeMemoryAccess exit non-local to the stub + StubRoutines::_unsafecopy_common_exit = generate_unsafecopy_common_error_exit(); + // register the stub as the default exit with class UnsafeMemoryAccess + UnsafeMemoryAccess::set_common_exit_stub_pc(StubRoutines::_unsafecopy_common_exit); + // Note: the disjoint stubs must be generated first, some of // the conjoint stubs use them. - address ucm_common_error_exit = generate_unsafecopy_common_error_exit(); - UnsafeMemoryAccess::set_common_exit_stub_pc(ucm_common_error_exit); - // these need always status in case they are called from generic_arraycopy StubRoutines::_jbyte_disjoint_arraycopy = generate_primitive_copy(StubId::stubgen_jbyte_disjoint_arraycopy_id); StubRoutines::_jshort_disjoint_arraycopy = generate_primitive_copy(StubId::stubgen_jshort_disjoint_arraycopy_id); diff --git a/src/hotspot/cpu/arm/stubRoutines_arm.cpp b/src/hotspot/cpu/arm/stubRoutines_arm.cpp index d843d89186e..a4f2b5e1bd9 100644 --- a/src/hotspot/cpu/arm/stubRoutines_arm.cpp +++ b/src/hotspot/cpu/arm/stubRoutines_arm.cpp @@ -36,3 +36,6 @@ STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY + +address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; } +address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } diff --git a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp index db4a5c8625c..8abefe39b2d 100644 --- a/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/templateInterpreterGenerator_arm.cpp @@ -174,6 +174,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_sinh: case Interpreter::java_lang_math_tanh: case Interpreter::java_lang_math_cbrt: // TODO: Implement intrinsic diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index c2f290212bd..f9f43ade501 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -3271,12 +3271,15 @@ class StubGenerator: public StubCodeGenerator { } void generate_arraycopy_stubs() { + // generate the common exit first so later stubs can rely on it if + // they want an UnsafeMemoryAccess exit non-local to the stub + StubRoutines::_unsafecopy_common_exit = generate_unsafecopy_common_error_exit(); + // register the stub as the default exit with class UnsafeMemoryAccess + UnsafeMemoryAccess::set_common_exit_stub_pc(StubRoutines::_unsafecopy_common_exit); + // Note: the disjoint stubs must be generated first, some of // the conjoint stubs use them. - address ucm_common_error_exit = generate_unsafecopy_common_error_exit(); - UnsafeMemoryAccess::set_common_exit_stub_pc(ucm_common_error_exit); - // non-aligned disjoint versions StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(StubId::stubgen_jbyte_disjoint_arraycopy_id); StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(StubId::stubgen_jshort_disjoint_arraycopy_id); @@ -4982,13 +4985,11 @@ void generate_lookup_secondary_supers_table_stub() { // CRC32 Intrinsics. if (UseCRC32Intrinsics) { - StubRoutines::_crc_table_adr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32_POLY); StubRoutines::_updateBytesCRC32 = generate_CRC32_updateBytes(StubId::stubgen_updateBytesCRC32_id); } // CRC32C Intrinsics. if (UseCRC32CIntrinsics) { - StubRoutines::_crc32c_table_addr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32C_POLY); StubRoutines::_updateBytesCRC32C = generate_CRC32_updateBytes(StubId::stubgen_updateBytesCRC32C_id); } diff --git a/src/hotspot/cpu/ppc/stubRoutines_ppc.hpp b/src/hotspot/cpu/ppc/stubRoutines_ppc.hpp index a542d7947f8..f8909ad5fa1 100644 --- a/src/hotspot/cpu/ppc/stubRoutines_ppc.hpp +++ b/src/hotspot/cpu/ppc/stubRoutines_ppc.hpp @@ -54,6 +54,7 @@ enum platform_dependent_constants { class ppc { friend class StubGenerator; + friend class StubRoutines; private: public: diff --git a/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp b/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp index fed3f208f06..914c5a17a19 100644 --- a/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp @@ -74,6 +74,22 @@ static julong compute_inverse_poly(julong long_poly) { return div; } +static address _crc_table_addr = nullptr; +static address _crc32c_table_addr = nullptr; + +address StubRoutines::crc_table_addr() { + if (_crc_table_addr == nullptr) { + _crc_table_addr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32_POLY); + } + return _crc_table_addr; +} +address StubRoutines::crc32c_table_addr() { + if (_crc32c_table_addr == nullptr) { + _crc32c_table_addr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32C_POLY); + } + return _crc32c_table_addr; +} + // Constants to fold n words as needed by macroAssembler. address StubRoutines::ppc::generate_crc_constants(juint reverse_poly) { // Layout of constant table: diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index ab4f35f4d8c..b5f1c76c5da 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -1089,6 +1089,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_sinh : /* run interpreted */ break; case Interpreter::java_lang_math_tanh : /* run interpreted */ break; case Interpreter::java_lang_math_cbrt : /* run interpreted */ break; case Interpreter::java_lang_math_abs : /* run interpreted */ break; diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index ce13ebde74f..bf71d2c68f1 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -1952,16 +1952,15 @@ void C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register res mv(pow31_3, 29791); // [31^^3] mv(pow31_2, 961); // [31^^2] - slli(chunks_end, chunks, chunks_end_shift); - add(chunks_end, ary, chunks_end); + shadd(chunks_end, chunks, ary, t0, chunks_end_shift); andi(cnt, cnt, stride - 1); // don't forget about tail! bind(WIDE_LOOP); - mulw(result, result, pow31_4); // 31^^4 * h arrays_hashcode_elload(t0, Address(ary, 0 * elsize), eltype); arrays_hashcode_elload(t1, Address(ary, 1 * elsize), eltype); arrays_hashcode_elload(tmp5, Address(ary, 2 * elsize), eltype); arrays_hashcode_elload(tmp6, Address(ary, 3 * elsize), eltype); + mulw(result, result, pow31_4); // 31^^4 * h mulw(t0, t0, pow31_3); // 31^^3 * ary[i+0] addw(result, result, t0); mulw(t1, t1, pow31_2); // 31^^2 * ary[i+1] @@ -1976,8 +1975,7 @@ void C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register res beqz(cnt, DONE); bind(TAIL); - slli(chunks_end, cnt, chunks_end_shift); - add(chunks_end, ary, chunks_end); + shadd(chunks_end, cnt, ary, t0, chunks_end_shift); bind(TAIL_LOOP); arrays_hashcode_elload(t0, Address(ary), eltype); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index a72a2a50fd7..a4c44b0b1d1 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -97,52 +97,52 @@ bool MacroAssembler::is_pc_relative_at(address instr) { // auipc + load // auipc + fload_load return (is_auipc_at(instr)) && - (is_addi_at(instr + instruction_size) || - is_jalr_at(instr + instruction_size) || - is_load_at(instr + instruction_size) || - is_float_load_at(instr + instruction_size)) && + (is_addi_at(instr + MacroAssembler::instruction_size) || + is_jalr_at(instr + MacroAssembler::instruction_size) || + is_load_at(instr + MacroAssembler::instruction_size) || + is_float_load_at(instr + MacroAssembler::instruction_size)) && check_pc_relative_data_dependency(instr); } // ie:ld(Rd, Label) bool MacroAssembler::is_load_pc_relative_at(address instr) { return is_auipc_at(instr) && // auipc - is_ld_at(instr + instruction_size) && // ld + is_ld_at(instr + MacroAssembler::instruction_size) && // ld check_load_pc_relative_data_dependency(instr); } bool MacroAssembler::is_movptr1_at(address instr) { return is_lui_at(instr) && // Lui - is_addi_at(instr + instruction_size) && // Addi - is_slli_shift_at(instr + instruction_size * 2, 11) && // Slli Rd, Rs, 11 - is_addi_at(instr + instruction_size * 3) && // Addi - is_slli_shift_at(instr + instruction_size * 4, 6) && // Slli Rd, Rs, 6 - (is_addi_at(instr + instruction_size * 5) || - is_jalr_at(instr + instruction_size * 5) || - is_load_at(instr + instruction_size * 5)) && // Addi/Jalr/Load + is_addi_at(instr + MacroAssembler::instruction_size) && // Addi + is_slli_shift_at(instr + MacroAssembler::instruction_size * 2, 11) && // Slli Rd, Rs, 11 + is_addi_at(instr + MacroAssembler::instruction_size * 3) && // Addi + is_slli_shift_at(instr + MacroAssembler::instruction_size * 4, 6) && // Slli Rd, Rs, 6 + (is_addi_at(instr + MacroAssembler::instruction_size * 5) || + is_jalr_at(instr + MacroAssembler::instruction_size * 5) || + is_load_at(instr + MacroAssembler::instruction_size * 5)) && // Addi/Jalr/Load check_movptr1_data_dependency(instr); } bool MacroAssembler::is_movptr2_at(address instr) { return is_lui_at(instr) && // lui - is_lui_at(instr + instruction_size) && // lui - is_slli_shift_at(instr + instruction_size * 2, 18) && // slli Rd, Rs, 18 - is_add_at(instr + instruction_size * 3) && - (is_addi_at(instr + instruction_size * 4) || - is_jalr_at(instr + instruction_size * 4) || - is_load_at(instr + instruction_size * 4)) && // Addi/Jalr/Load + is_lui_at(instr + MacroAssembler::instruction_size) && // lui + is_slli_shift_at(instr + MacroAssembler::instruction_size * 2, 18) && // slli Rd, Rs, 18 + is_add_at(instr + MacroAssembler::instruction_size * 3) && + (is_addi_at(instr + MacroAssembler::instruction_size * 4) || + is_jalr_at(instr + MacroAssembler::instruction_size * 4) || + is_load_at(instr + MacroAssembler::instruction_size * 4)) && // Addi/Jalr/Load check_movptr2_data_dependency(instr); } bool MacroAssembler::is_li16u_at(address instr) { return is_lui_at(instr) && // lui - is_srli_at(instr + instruction_size) && // srli + is_srli_at(instr + MacroAssembler::instruction_size) && // srli check_li16u_data_dependency(instr); } bool MacroAssembler::is_li32_at(address instr) { return is_lui_at(instr) && // lui - is_addiw_at(instr + instruction_size) && // addiw + is_addiw_at(instr + MacroAssembler::instruction_size) && // addiw check_li32_data_dependency(instr); } @@ -5110,7 +5110,7 @@ address MacroAssembler::emit_reloc_call_address_stub(int insts_call_instruction_ int MacroAssembler::max_reloc_call_address_stub_size() { // Max stub size: alignment nop, target address. - return 1 * instruction_size + wordSize; + return 1 * MacroAssembler::instruction_size + wordSize; } int MacroAssembler::static_call_stub_size() { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 8968f3858af..17f113cc819 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -1240,7 +1240,7 @@ public: void far_jump(const Address &entry, Register tmp = t1); static int far_branch_size() { - return 2 * 4; // auipc + jalr, see far_call() & far_jump() + return 2 * MacroAssembler::instruction_size; // auipc + jalr, see far_call() & far_jump() } void load_byte_map_base(Register reg); @@ -1644,9 +1644,9 @@ public: public: enum { // movptr - movptr1_instruction_size = 6 * instruction_size, // lui, addi, slli, addi, slli, addi. See movptr1(). - movptr2_instruction_size = 5 * instruction_size, // lui, lui, slli, add, addi. See movptr2(). - load_pc_relative_instruction_size = 2 * instruction_size // auipc, ld + movptr1_instruction_size = 6 * MacroAssembler::instruction_size, // lui, addi, slli, addi, slli, addi. See movptr1(). + movptr2_instruction_size = 5 * MacroAssembler::instruction_size, // lui, lui, slli, add, addi. See movptr2(). + load_pc_relative_instruction_size = 2 * MacroAssembler::instruction_size // auipc, ld }; static bool is_load_pc_relative_at(address branch); @@ -1701,11 +1701,11 @@ public: // addi/jalr/load static bool check_movptr1_data_dependency(address instr) { address lui = instr; - address addi1 = lui + instruction_size; - address slli1 = addi1 + instruction_size; - address addi2 = slli1 + instruction_size; - address slli2 = addi2 + instruction_size; - address last_instr = slli2 + instruction_size; + address addi1 = lui + MacroAssembler::instruction_size; + address slli1 = addi1 + MacroAssembler::instruction_size; + address addi2 = slli1 + MacroAssembler::instruction_size; + address slli2 = addi2 + MacroAssembler::instruction_size; + address last_instr = slli2 + MacroAssembler::instruction_size; return extract_rs1(addi1) == extract_rd(lui) && extract_rs1(addi1) == extract_rd(addi1) && extract_rs1(slli1) == extract_rd(addi1) && @@ -1725,10 +1725,10 @@ public: // addi/jalr/load static bool check_movptr2_data_dependency(address instr) { address lui1 = instr; - address lui2 = lui1 + instruction_size; - address slli = lui2 + instruction_size; - address add = slli + instruction_size; - address last_instr = add + instruction_size; + address lui2 = lui1 + MacroAssembler::instruction_size; + address slli = lui2 + MacroAssembler::instruction_size; + address add = slli + MacroAssembler::instruction_size; + address last_instr = add + MacroAssembler::instruction_size; return extract_rd(add) == extract_rd(lui2) && extract_rs1(add) == extract_rd(lui2) && extract_rs2(add) == extract_rd(slli) && @@ -1742,7 +1742,7 @@ public: // srli static bool check_li16u_data_dependency(address instr) { address lui = instr; - address srli = lui + instruction_size; + address srli = lui + MacroAssembler::instruction_size; return extract_rs1(srli) == extract_rd(lui) && extract_rs1(srli) == extract_rd(srli); @@ -1753,7 +1753,7 @@ public: // addiw static bool check_li32_data_dependency(address instr) { address lui = instr; - address addiw = lui + instruction_size; + address addiw = lui + MacroAssembler::instruction_size; return extract_rs1(addiw) == extract_rd(lui) && extract_rs1(addiw) == extract_rd(addiw); @@ -1764,7 +1764,7 @@ public: // jalr/addi/load/float_load static bool check_pc_relative_data_dependency(address instr) { address auipc = instr; - address last_instr = auipc + instruction_size; + address last_instr = auipc + MacroAssembler::instruction_size; return extract_rs1(last_instr) == extract_rd(auipc); } @@ -1774,7 +1774,7 @@ public: // load static bool check_load_pc_relative_data_dependency(address instr) { address auipc = instr; - address load = auipc + instruction_size; + address load = auipc + MacroAssembler::instruction_size; return extract_rd(load) == extract_rd(auipc) && extract_rs1(load) == extract_rd(load); diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp index 9237749f9d9..eb8ef6349b0 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp @@ -46,44 +46,11 @@ bool NativeInstruction::is_call_at(address addr) { } //----------------------------------------------------------------------------- -// NativeFarCall -// -// Implements direct far calling loading an address from the stub section version of reloc call. +// NativeCall -class NativeFarCall: public NativeInstruction { - public: - enum RISCV_specific_constants { - return_address_offset = 3 * NativeInstruction::instruction_size, // auipc + ld + jalr - }; - - address instruction_address() const { return addr_at(0); } - address next_instruction_address() const { return addr_at(return_address_offset); } - address return_address() const { return addr_at(return_address_offset); } - address destination() const; - address reloc_destination(); - - void set_destination(address dest); - void verify(); - void print(); - - bool set_destination_mt_safe(address dest); - bool reloc_set_destination(address dest); - - private: - address stub_address(); - - static void set_stub_address_destination_at(address dest, address value); - static address stub_address_destination_at(address src); - public: - - static NativeFarCall* at(address addr); - static bool is_at(address addr); - static bool is_call_before(address return_address); -}; - -address NativeFarCall::destination() const { +address NativeCall::destination() const { address addr = instruction_address(); - assert(NativeFarCall::is_at(addr), "unexpected code at call site"); + assert(NativeCall::is_at(addr), "unexpected code at call site"); address destination = MacroAssembler::target_addr_for_insn(addr); @@ -96,44 +63,36 @@ address NativeFarCall::destination() const { return stub_address_destination_at(destination); } -address NativeFarCall::reloc_destination() { +address NativeCall::reloc_destination() { address call_addr = instruction_address(); - assert(NativeFarCall::is_at(call_addr), "unexpected code at call site"); + assert(NativeCall::is_at(call_addr), "unexpected code at call site"); CodeBlob *code = CodeCache::find_blob(call_addr); assert(code != nullptr, "Could not find the containing code blob"); address stub_addr = nullptr; if (code->is_nmethod()) { + // TODO: Need to revisit this when porting the AOT features. stub_addr = trampoline_stub_Relocation::get_trampoline_for(call_addr, code->as_nmethod()); - } - - if (stub_addr != nullptr) { - stub_addr = MacroAssembler::target_addr_for_insn(call_addr); + assert(stub_addr != nullptr, "Sanity"); } return stub_addr; } -void NativeFarCall::set_destination(address dest) { - address addr = instruction_address(); - assert(NativeFarCall::is_at(addr), "unexpected code at call site"); - Unimplemented(); +void NativeCall::verify() { + assert(NativeCall::is_at(instruction_address()), "unexpected code at call site"); } -void NativeFarCall::verify() { - assert(NativeFarCall::is_at(instruction_address()), "unexpected code at call site"); +void NativeCall::print() { + assert(NativeCall::is_at(instruction_address()), "unexpected code at call site"); + tty->print_cr(PTR_FORMAT ": auipc,ld,jalr x1, offset/reg, ", p2i(instruction_address())); } -void NativeFarCall::print() { - assert(NativeFarCall::is_at(instruction_address()), "unexpected code at call site"); - tty->print_cr(PTR_FORMAT ": auipc,ld,jalr x1, offset/reg, ", p2i(addr_at(0))); -} - -bool NativeFarCall::set_destination_mt_safe(address dest) { - assert(NativeFarCall::is_at(addr_at(0)), "unexpected code at call site"); +bool NativeCall::set_destination_mt_safe(address dest) { + assert(NativeCall::is_at(instruction_address()), "unexpected code at call site"); assert((CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) || - CompiledICLocker::is_safe(addr_at(0)), + CompiledICLocker::is_safe(instruction_address()), "concurrent code patching"); address stub_addr = stub_address(); @@ -145,25 +104,25 @@ bool NativeFarCall::set_destination_mt_safe(address dest) { return false; } -bool NativeFarCall::reloc_set_destination(address dest) { - address call_addr = addr_at(0); - assert(NativeFarCall::is_at(call_addr), "unexpected code at call site"); +bool NativeCall::reloc_set_destination(address dest) { + address call_addr = instruction_address(); + assert(NativeCall::is_at(call_addr), "unexpected code at call site"); CodeBlob *code = CodeCache::find_blob(call_addr); assert(code != nullptr, "Could not find the containing code blob"); - address stub_addr = nullptr; if (code->is_nmethod()) { - stub_addr = trampoline_stub_Relocation::get_trampoline_for(call_addr, code->as_nmethod()); - } - if (stub_addr != nullptr) { - MacroAssembler::pd_patch_instruction_size(call_addr, stub_addr); + // TODO: Need to revisit this when porting the AOT features. + assert(dest != nullptr, "Sanity"); + assert(dest == trampoline_stub_Relocation::get_trampoline_for(call_addr, + code->as_nmethod()), "Sanity"); + MacroAssembler::pd_patch_instruction_size(call_addr, dest); } return true; } -void NativeFarCall::set_stub_address_destination_at(address dest, address value) { +void NativeCall::set_stub_address_destination_at(address dest, address value) { assert_cond(dest != nullptr); assert_cond(value != nullptr); @@ -171,31 +130,24 @@ void NativeFarCall::set_stub_address_destination_at(address dest, address value) OrderAccess::release(); } -address NativeFarCall::stub_address_destination_at(address src) { +address NativeCall::stub_address_destination_at(address src) { assert_cond(src != nullptr); address dest = (address)get_data64_at(src); return dest; } -address NativeFarCall::stub_address() { - address call_addr = addr_at(0); +address NativeCall::stub_address() { + address call_addr = instruction_address(); CodeBlob *code = CodeCache::find_blob(call_addr); assert(code != nullptr, "Could not find the containing code blob"); - address dest = MacroAssembler::pd_call_destination(call_addr); + address dest = MacroAssembler::target_addr_for_insn(call_addr); assert(code->contains(dest), "Sanity"); return dest; } -NativeFarCall* NativeFarCall::at(address addr) { - assert_cond(addr != nullptr); - assert(NativeFarCall::is_at(addr), "unexpected code at call site: %p", addr); - NativeFarCall* call = (NativeFarCall*)(addr); - return call; -} - -bool NativeFarCall::is_at(address addr) { +bool NativeCall::is_at(address addr) { assert_cond(addr != nullptr); const int instr_size = NativeInstruction::instruction_size; if (MacroAssembler::is_auipc_at(addr) && @@ -211,59 +163,8 @@ bool NativeFarCall::is_at(address addr) { return false; } -bool NativeFarCall::is_call_before(address return_address) { - return NativeFarCall::is_at(return_address - return_address_offset); -} - -//----------------------------------------------------------------------------- -// NativeCall - -address NativeCall::instruction_address() const { - return NativeFarCall::at(addr_at(0))->instruction_address(); -} - -address NativeCall::next_instruction_address() const { - return NativeFarCall::at(addr_at(0))->next_instruction_address(); -} - -address NativeCall::return_address() const { - return NativeFarCall::at(addr_at(0))->return_address(); -} - -address NativeCall::destination() const { - return NativeFarCall::at(addr_at(0))->destination(); -} - -address NativeCall::reloc_destination() { - return NativeFarCall::at(addr_at(0))->reloc_destination(); -} - -void NativeCall::set_destination(address dest) { - NativeFarCall::at(addr_at(0))->set_destination(dest); -} - -void NativeCall::verify() { - NativeFarCall::at(addr_at(0))->verify();; -} - -void NativeCall::print() { - NativeFarCall::at(addr_at(0))->print();; -} - -bool NativeCall::set_destination_mt_safe(address dest) { - return NativeFarCall::at(addr_at(0))->set_destination_mt_safe(dest); -} - -bool NativeCall::reloc_set_destination(address dest) { - return NativeFarCall::at(addr_at(0))->reloc_set_destination(dest); -} - -bool NativeCall::is_at(address addr) { - return NativeFarCall::is_at(addr); -} - bool NativeCall::is_call_before(address return_address) { - return NativeFarCall::is_call_before(return_address); + return NativeCall::is_at(return_address - NativeCall::instruction_size); } NativeCall* nativeCall_at(address addr) { @@ -276,7 +177,7 @@ NativeCall* nativeCall_at(address addr) { NativeCall* nativeCall_before(address return_address) { assert_cond(return_address != nullptr); NativeCall* call = nullptr; - call = (NativeCall*)(return_address - NativeFarCall::return_address_offset); + call = (NativeCall*)(return_address - NativeCall::instruction_size); DEBUG_ONLY(call->verify()); return call; } diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp index ba2270a5f78..8e6632419e2 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp @@ -93,7 +93,6 @@ class NativeInstruction { static uint64_t get_data64_at(address src) { return Bytes::get_native_u8(src); } public: - inline friend NativeInstruction* nativeInstruction_at(address addr); static bool maybe_cpool_ref(address instr) { @@ -111,6 +110,7 @@ NativeCall* nativeCall_before(address return_address); // The NativeCall is an abstraction for accessing/manipulating native // call instructions (used to manipulate inline caches, primitive & // DSO calls, etc.). +// NativeCall is reloc call on RISC-V. See MacroAssembler::reloc_call. class NativeCall: private NativeInstruction { // private: when common code is using byte_size() private: @@ -118,21 +118,21 @@ class NativeCall: private NativeInstruction { // Use byte_size() as it can be changed in runtime // Since instruction_size exists on NativeInstruction we need // to overload and hide it. - instruction_size = 3 * Assembler::instruction_size // auipc + ld + jalr + instruction_size = 3 * NativeInstruction::instruction_size // auipc + ld + jalr }; - public: + public: static int byte_size() { - return 3 * NativeInstruction::instruction_size; // auipc + ld + jalr + return NativeCall::instruction_size; // auipc + ld + jalr } // Creation friend NativeCall* nativeCall_at(address addr); friend NativeCall* nativeCall_before(address return_address); - address instruction_address() const; - address next_instruction_address() const; - address return_address() const; + address instruction_address() const { return addr_at(0); } + address next_instruction_address() const { return addr_at(NativeCall::instruction_size); } + address return_address() const { return addr_at(NativeCall::instruction_size); } address destination() const; address reloc_destination(); @@ -140,12 +140,22 @@ class NativeCall: private NativeInstruction { void verify(); void print(); - void set_destination(address dest); + void set_destination(address dest) { Unimplemented(); } + // patch stub to target address of the reloc call bool set_destination_mt_safe(address dest); + // patch reloc call to stub address bool reloc_set_destination(address dest); static bool is_at(address addr); static bool is_call_before(address return_address); + + private: + // return stub address, without checking stub address in locs + address stub_address(); + // set target address at stub + static void set_stub_address_destination_at(address dest, address value); + // return target address at stub + static address stub_address_destination_at(address src); }; // An interface for accessing/manipulating native mov reg, imm instructions. diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index a44fe39917c..fbcb939f59d 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -2301,12 +2301,15 @@ class StubGenerator: public StubCodeGenerator { address entry_jlong_arraycopy = nullptr; address entry_checkcast_arraycopy = nullptr; + // generate the common exit first so later stubs can rely on it if + // they want an UnsafeMemoryAccess exit non-local to the stub + StubRoutines::_unsafecopy_common_exit = generate_unsafecopy_common_error_exit(); + // register the stub as the default exit with class UnsafeMemoryAccess + UnsafeMemoryAccess::set_common_exit_stub_pc(StubRoutines::_unsafecopy_common_exit); + generate_copy_longs(StubId::stubgen_copy_byte_f_id, copy_f, c_rarg0, c_rarg1, t1); generate_copy_longs(StubId::stubgen_copy_byte_b_id, copy_b, c_rarg0, c_rarg1, t1); - address ucm_common_error_exit = generate_unsafecopy_common_error_exit(); - UnsafeMemoryAccess::set_common_exit_stub_pc(ucm_common_error_exit); - StubRoutines::riscv::_zero_blocks = generate_zero_blocks(); //*** jbyte @@ -6686,8 +6689,6 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_catch_exception_entry = generate_catch_exception(); if (UseCRC32Intrinsics) { - // set table address before stub generation which use it - StubRoutines::_crc_table_adr = (address)StubRoutines::riscv::_crc_table; StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32(); } diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp index 2a1150276c1..2aac95d71fa 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp @@ -52,6 +52,10 @@ bool StubRoutines::riscv::_completed = false; /** * crc_table[] from jdk/src/java.base/share/native/libzip/zlib/crc32.h */ + +address StubRoutines::crc_table_addr() { return (address)StubRoutines::riscv::_crc_table; } +address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } + ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] = { // Table 0 diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp index 1cd10b996db..0f90777ce64 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp @@ -48,6 +48,7 @@ enum platform_dependent_constants { class riscv { friend class StubGenerator; + friend class StubRoutines; #if INCLUDE_JVMCI friend class JVMCIVMStructs; #endif diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index aaed67fd269..34696f18848 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -1576,12 +1576,14 @@ class StubGenerator: public StubCodeGenerator { void generate_arraycopy_stubs() { + // they want an UnsafeMemoryAccess exit non-local to the stub + StubRoutines::_unsafecopy_common_exit = generate_unsafecopy_common_error_exit(); + // register the stub as the default exit with class UnsafeMemoryAccess + UnsafeMemoryAccess::set_common_exit_stub_pc(StubRoutines::_unsafecopy_common_exit); + // Note: the disjoint stubs must be generated first, some of // the conjoint stubs use them. - address ucm_common_error_exit = generate_unsafecopy_common_error_exit(); - UnsafeMemoryAccess::set_common_exit_stub_pc(ucm_common_error_exit); - StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_nonoop_copy (StubId::stubgen_jbyte_disjoint_arraycopy_id); StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_nonoop_copy(StubId::stubgen_jshort_disjoint_arraycopy_id); StubRoutines::_jint_disjoint_arraycopy = generate_disjoint_nonoop_copy (StubId::stubgen_jint_disjoint_arraycopy_id); @@ -3308,12 +3310,10 @@ class StubGenerator: public StubCodeGenerator { } if (UseCRC32Intrinsics) { - StubRoutines::_crc_table_adr = (address)StubRoutines::zarch::_crc_table; StubRoutines::_updateBytesCRC32 = generate_CRC32_updateBytes(); } if (UseCRC32CIntrinsics) { - StubRoutines::_crc32c_table_addr = (address)StubRoutines::zarch::_crc32c_table; StubRoutines::_updateBytesCRC32C = generate_CRC32C_updateBytes(); } diff --git a/src/hotspot/cpu/s390/stubRoutines_s390.cpp b/src/hotspot/cpu/s390/stubRoutines_s390.cpp index e75928ad00e..6feb20f9604 100644 --- a/src/hotspot/cpu/s390/stubRoutines_s390.cpp +++ b/src/hotspot/cpu/s390/stubRoutines_s390.cpp @@ -78,14 +78,17 @@ void StubRoutines::zarch::generate_load_absolute_address(MacroAssembler* masm, R #endif } +address StubRoutines::crc_table_addr() { return (address)StubRoutines::zarch::_crc_table; } +address StubRoutines::crc32c_table_addr() { return (address)StubRoutines::zarch::_crc32c_table; } + void StubRoutines::zarch::generate_load_crc_table_addr(MacroAssembler* masm, Register table) { const uint64_t table_contents = 0x77073096UL; // required contents of table[1] - generate_load_absolute_address(masm, table, StubRoutines::_crc_table_adr, table_contents); + generate_load_absolute_address(masm, table, StubRoutines::crc_table_addr(), table_contents); } void StubRoutines::zarch::generate_load_crc32c_table_addr(MacroAssembler* masm, Register table) { const uint64_t table_contents = 0xf26b8303UL; // required contents of table[1] - generate_load_absolute_address(masm, table, StubRoutines::_crc32c_table_addr, table_contents); + generate_load_absolute_address(masm, table, StubRoutines::crc32c_table_addr(), table_contents); } diff --git a/src/hotspot/cpu/s390/stubRoutines_s390.hpp b/src/hotspot/cpu/s390/stubRoutines_s390.hpp index 7a4bc18eb7d..494d90cd85a 100644 --- a/src/hotspot/cpu/s390/stubRoutines_s390.hpp +++ b/src/hotspot/cpu/s390/stubRoutines_s390.hpp @@ -62,6 +62,7 @@ enum method_handles_platform_dependent_constants { class zarch { friend class StubGenerator; + friend class StubRoutines; public: enum { nof_instance_allocators = 10 }; diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index e03d891496a..3f568572966 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -1239,6 +1239,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_sinh : /* run interpreted */ break; case Interpreter::java_lang_math_tanh : /* run interpreted */ break; case Interpreter::java_lang_math_cbrt : /* run interpreted */ break; case Interpreter::java_lang_math_abs : /* run interpreted */ break; diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 04c90c43b6e..3bd86d9b7e5 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -720,8 +720,8 @@ 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::_dtanh || - x->id() == vmIntrinsics::_dcbrt + x->id() == vmIntrinsics::_dlog10 || x->id() == vmIntrinsics::_dsinh || + x->id() == vmIntrinsics::_dtanh || x->id() == vmIntrinsics::_dcbrt ) { do_LibmIntrinsic(x); return; @@ -835,6 +835,12 @@ void LIRGenerator::do_LibmIntrinsic(Intrinsic* x) { __ call_runtime_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtan), getThreadTemp(), result_reg, cc->args()); } break; + case vmIntrinsics::_dsinh: + assert(StubRoutines::dsinh() != nullptr, "sinh intrinsic not found"); + if (StubRoutines::dsinh() != nullptr) { + __ call_runtime_leaf(StubRoutines::dsinh(), getThreadTemp(), result_reg, cc->args()); + } + break; case vmIntrinsics::_dtanh: assert(StubRoutines::dtanh() != nullptr, "tanh intrinsic not found"); if (StubRoutines::dtanh() != nullptr) { diff --git a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp index ae947135f78..30a93fa4917 100644 --- a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp +++ b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp @@ -37,7 +37,7 @@ do_arch_blob, \ do_arch_entry, \ do_arch_entry_init) \ - do_arch_blob(initial, 20000 WINDOWS_ONLY(+1000)) \ + do_arch_blob(initial, PRODUCT_ONLY(20000) NOT_PRODUCT(21000) WINDOWS_ONLY(+1000)) \ do_stub(initial, verify_mxcsr) \ do_arch_entry(x86, initial, verify_mxcsr, verify_mxcsr_entry, \ verify_mxcsr_entry) \ diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index e1c84d9528f..058ea06ab4e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -3689,6 +3689,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::_dsinh)) { + StubRoutines::_dsinh = generate_libmSinh(); // from stubGenerator_x86_64_sinh.cpp + } if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_dtanh)) { StubRoutines::_dtanh = generate_libmTanh(); // from stubGenerator_x86_64_tanh.cpp } @@ -4095,15 +4098,11 @@ void StubGenerator::generate_initial_stubs() { StubRoutines::x86::_double_sign_flip = generate_fp_mask(StubId::stubgen_double_sign_flip_id, 0x8000000000000000); if (UseCRC32Intrinsics) { - // set table address before stub generation which use it - StubRoutines::_crc_table_adr = (address)StubRoutines::x86::_crc_table; StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32(); } if (UseCRC32CIntrinsics) { bool supports_clmul = VM_Version::supports_clmul(); - StubRoutines::x86::generate_CRC32C_table(supports_clmul); - StubRoutines::_crc32c_table_addr = (address)StubRoutines::x86::_crc32c_table; StubRoutines::_updateBytesCRC32C = generate_updateBytesCRC32C(supports_clmul); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index de2b0ad506d..44d13bbbf31 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -555,6 +555,7 @@ class StubGenerator: public StubCodeGenerator { address generate_libmSin(); address generate_libmCos(); address generate_libmTan(); + address generate_libmSinh(); address generate_libmTanh(); address generate_libmCbrt(); address generate_libmExp(); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp index 299a37b88fd..5130fd2c9d2 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2025, Intel Corporation. All rights reserved. -* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +* Copyright (C) 2021, Tencent. All rights reserved. * Intel Math Library (LIBM) Source Code * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp index 8665aec5903..6b5b4d704e3 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2025, Intel Corporation. All rights reserved. -* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +* Copyright (C) 2021, Tencent. All rights reserved. * Intel Math Library (LIBM) Source Code * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp index 22ac8059458..3c3df7e6ac4 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2025, Intel Corporation. All rights reserved. -* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +* Copyright (C) 2021, Tencent. All rights reserved. * Intel Math Library (LIBM) Source Code * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp new file mode 100644 index 00000000000..86e4ac20176 --- /dev/null +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2025, 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 "macroAssembler_x86.hpp" +#include "stubGenerator_x86_64.hpp" + +/******************************************************************************/ +// ALGORITHM DESCRIPTION +// --------------------- +// +// sinh(x)=(exp(x)-exp(-x))/2 +// +// 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^7=int(xH*L2EH*2^7), +// f=0.b1 b2 ... b7, k integer +// 2^f is approximated as Tp[f]+Dp[f], and 2^{-f} as Tn[f]+Dn[f] +// Tp stores the high 53 bits, Dp stores (2^f-Tp[f]) rounded to double precision +// +// e^|x|=2^{k+f}*2^r, r=r`+xL*L2EH+|x|*L2EL, |r|<2^{-8}+2^{-14}, +// for |x| in [23/64,3*2^7) +// e^{-|x|}=2^{-k-f}*2^{-r} +// +// e^|x| is approximated as 2^k*Tp+2^k*Tp*c1*r(1+c2*r+..+c5*r^4)+2^k*Dp= +// =2^k*Tp+2^k*Tp*P15+2^k*Dp +// e^{-|x|} approximated as 2^{-k}*Tn-2^{-k}*Tn*c1*r(1-c2*r+..+c5*r^4)+2^{-k}*Dn +// +// For |x| in [1/8, 3*2^7), sinh(x) is formed as +// RN(2^k*Tp-2^{-k}*Tn)+2^k*Tp*P15-2^{-k}*Tn*P`15-2^{-k}*TnL-2^{-k}*Dn+2^k*Dp +// +// For x in (3*2^7, 3*2^8), sign(x)*(e^|x|)/2 is returned, and +// the result is checked for overflow. +// +// For |x|<23/64, a Taylor polynomial expansion is used (degree 13) +// To reduce rounding errors, the p3*x^3 term is computed as +// (p3*xh^3)_high+[(p3*xl*(3*x*xh+xl^2))+(p3*xh^3)_low], +// where x=xh+xl, (xh are the leading 17 bits of x), and +// (p3*xh^3)_high=RN(x+p3*xh^3)-x +// +// Error bound: +// 0.51 ulp +// +// Special cases: +// sinh(NaN) = quiet NaN, and raise invalid exception +// sinh(+/-INF) = +/-INF +// sinh(+/-0) = +/-0 +/******************************************************************************/ + +ATTRIBUTE_ALIGNED(8) static const juint _HALFMASK[] = +{ + 0xF8000000UL, 0x7FFFFFFFUL +}; + +ATTRIBUTE_ALIGNED(16) static const juint _MASK3[] = +{ + 0x00000000UL, 0xFFFFFFF0UL, 0x00000000UL, 0xFFFFFFF0UL +}; + +ATTRIBUTE_ALIGNED(16) static const juint _L2E[] = +{ + 0x60000000UL, 0x40671547UL, 0xF85DDF44UL, 0x3EC4AE0BUL +}; + +ATTRIBUTE_ALIGNED(16) static const juint _Shifter[] = +{ + 0x00000000UL, 0x43380000UL, 0x00000000UL, 0xC3380000UL +}; + +ATTRIBUTE_ALIGNED(16) static const juint _cv[] = +{ + 0xD704A0C0UL, 0x3E3C6B08UL, 0xD704A0C0UL, 0xBE3C6B08UL, 0xFEFA39EFUL, + 0x3F662E42UL, 0xFEFA39EFUL, 0xBF662E42UL, 0x7F907D8BUL, 0x3D9F8445UL, + 0x7F907D8BUL, 0x3D9F8445UL, 0xFFAC83B4UL, 0x3ED47FD3UL, 0xFFAC83B4UL, + 0x3ED47FD3UL, 0xFEFA39EFUL, 0x3F762E42UL, 0xFEFA39EFUL, 0x3F762E42UL +}; + +ATTRIBUTE_ALIGNED(16) static const juint _pv[] = +{ + 0x13A86D08UL, 0x3DE61246UL, 0xA556C732UL, 0x3EC71DE3UL, 0x11111111UL, + 0x3F811111UL, 0x55555555UL, 0x3FC55555UL, 0x67F544E1UL, 0x3E5AE645UL, + 0x1A01A019UL, 0x3F2A01A0UL +}; + +ATTRIBUTE_ALIGNED(16) static const juint _T2f[] = +{ + 0x00000000UL, 0x3FF00000UL, 0x00000000UL, 0x00000000UL, 0xA9FB3335UL, 0x3FF0163DUL, + 0x9AB8CDB7UL, 0x3C9B6129UL, 0x3E778061UL, 0x3FF02C9AUL, 0x535B085DUL, 0xBC719083UL, + 0xE86E7F85UL, 0x3FF04315UL, 0x1977C96EUL, 0xBC90A31CUL, 0xD3158574UL, 0x3FF059B0UL, + 0xA475B465UL, 0x3C8D73E2UL, 0x29DDF6DEUL, 0x3FF0706BUL, 0xE2B13C27UL, 0xBC8C91DFUL, + 0x18759BC8UL, 0x3FF08745UL, 0x4BB284FFUL, 0x3C6186BEUL, 0xCAC6F383UL, 0x3FF09E3EUL, + 0x18316136UL, 0x3C914878UL, 0x6CF9890FUL, 0x3FF0B558UL, 0x4ADC610BUL, 0x3C98A62EUL, + 0x2B7247F7UL, 0x3FF0CC92UL, 0x16E24F71UL, 0x3C901EDCUL, 0x32D3D1A2UL, 0x3FF0E3ECUL, + 0x27C57B52UL, 0x3C403A17UL, 0xAFFED31BUL, 0x3FF0FB66UL, 0xC44EBD7BUL, 0xBC6B9BEDUL, + 0xD0125B51UL, 0x3FF11301UL, 0x39449B3AUL, 0xBC96C510UL, 0xC06C31CCUL, 0x3FF12ABDUL, + 0xB36CA5C7UL, 0xBC51B514UL, 0xAEA92DE0UL, 0x3FF1429AUL, 0x9AF1369EUL, 0xBC932FBFUL, + 0xC8A58E51UL, 0x3FF15A98UL, 0xB9EEAB0AUL, 0x3C82406AUL, 0x3C7D517BUL, 0x3FF172B8UL, + 0xB9D78A76UL, 0xBC819041UL, 0x388C8DEAUL, 0x3FF18AF9UL, 0xD1970F6CUL, 0xBC911023UL, + 0xEB6FCB75UL, 0x3FF1A35BUL, 0x7B4968E4UL, 0x3C8E5B4CUL, 0x84045CD4UL, 0x3FF1BBE0UL, + 0x352EF607UL, 0xBC995386UL, 0x3168B9AAUL, 0x3FF1D487UL, 0x00A2643CUL, 0x3C9E016EUL, + 0x22FCD91DUL, 0x3FF1ED50UL, 0x027BB78CUL, 0xBC91DF98UL, 0x88628CD6UL, 0x3FF2063BUL, + 0x814A8495UL, 0x3C8DC775UL, 0x917DDC96UL, 0x3FF21F49UL, 0x9494A5EEUL, 0x3C82A97EUL, + 0x6E756238UL, 0x3FF2387AUL, 0xB6C70573UL, 0x3C99B07EUL, 0x4FB2A63FUL, 0x3FF251CEUL, + 0xBEF4F4A4UL, 0x3C8AC155UL, 0x65E27CDDUL, 0x3FF26B45UL, 0x9940E9D9UL, 0x3C82BD33UL, + 0xE1F56381UL, 0x3FF284DFUL, 0x8C3F0D7EUL, 0xBC9A4C3AUL, 0xF51FDEE1UL, 0x3FF29E9DUL, + 0xAFAD1255UL, 0x3C8612E8UL, 0xD0DAD990UL, 0x3FF2B87FUL, 0xD6381AA4UL, 0xBC410ADCUL, + 0xA6E4030BUL, 0x3FF2D285UL, 0x54DB41D5UL, 0x3C900247UL, 0xA93E2F56UL, 0x3FF2ECAFUL, + 0x45D52383UL, 0x3C71CA0FUL, 0x0A31B715UL, 0x3FF306FEUL, 0xD23182E4UL, 0x3C86F46AUL, + 0xFC4CD831UL, 0x3FF32170UL, 0x8E18047CUL, 0x3C8A9CE7UL, 0xB26416FFUL, 0x3FF33C08UL, + 0x843659A6UL, 0x3C932721UL, 0x5F929FF1UL, 0x3FF356C5UL, 0x5C4E4628UL, 0xBC8B5CEEUL, + 0x373AA9CBUL, 0x3FF371A7UL, 0xBF42EAE2UL, 0xBC963AEAUL, 0x6D05D866UL, 0x3FF38CAEUL, + 0x3C9904BDUL, 0xBC9E958DUL, 0x34E59FF7UL, 0x3FF3A7DBUL, 0xD661F5E3UL, 0xBC75E436UL, + 0xC313A8E5UL, 0x3FF3C32DUL, 0x375D29C3UL, 0xBC9EFFF8UL, 0x4C123422UL, 0x3FF3DEA6UL, + 0x11F09EBCUL, 0x3C8ADA09UL, 0x04AC801CUL, 0x3FF3FA45UL, 0xF956F9F3UL, 0xBC97D023UL, + 0x21F72E2AUL, 0x3FF4160AUL, 0x1C309278UL, 0xBC5EF369UL, 0xD950A897UL, 0x3FF431F5UL, + 0xE35F7999UL, 0xBC81C7DDUL, 0x6061892DUL, 0x3FF44E08UL, 0x04EF80D0UL, 0x3C489B7AUL, + 0xED1D0057UL, 0x3FF46A41UL, 0xD1648A76UL, 0x3C9C944BUL, 0xB5C13CD0UL, 0x3FF486A2UL, + 0xB69062F0UL, 0x3C73C1A3UL, 0xF0D7D3DEUL, 0x3FF4A32AUL, 0xF3D1BE56UL, 0x3C99CB62UL, + 0xD5362A27UL, 0x3FF4BFDAUL, 0xAFEC42E2UL, 0x3C7D4397UL, 0x99FDDD0DUL, 0x3FF4DCB2UL, + 0xBC6A7833UL, 0x3C98ECDBUL, 0x769D2CA7UL, 0x3FF4F9B2UL, 0xD25957E3UL, 0xBC94B309UL, + 0xA2CF6642UL, 0x3FF516DAUL, 0x69BD93EFUL, 0xBC8F7685UL, 0x569D4F82UL, 0x3FF5342BUL, + 0x1DB13CADUL, 0xBC807ABEUL, 0xCA5D920FUL, 0x3FF551A4UL, 0xEFEDE59BUL, 0xBC8D689CUL, + 0x36B527DAUL, 0x3FF56F47UL, 0x011D93ADUL, 0x3C99BB2CUL, 0xD497C7FDUL, 0x3FF58D12UL, + 0x5B9A1DE8UL, 0x3C8295E1UL, 0xDD485429UL, 0x3FF5AB07UL, 0x054647ADUL, 0x3C96324CUL, + 0x8A5946B7UL, 0x3FF5C926UL, 0x816986A2UL, 0x3C3C4B1BUL, 0x15AD2148UL, 0x3FF5E76FUL, + 0x3080E65EUL, 0x3C9BA6F9UL, 0xB976DC09UL, 0x3FF605E1UL, 0x9B56DE47UL, 0xBC93E242UL, + 0xB03A5585UL, 0x3FF6247EUL, 0x7E40B497UL, 0xBC9383C1UL, 0x34CCC320UL, 0x3FF64346UL, + 0x759D8933UL, 0xBC8C483CUL, 0x82552225UL, 0x3FF66238UL, 0x87591C34UL, 0xBC9BB609UL, + 0xD44CA973UL, 0x3FF68155UL, 0x44F73E65UL, 0x3C6038AEUL, 0x667F3BCDUL, 0x3FF6A09EUL, + 0x13B26456UL, 0xBC9BDD34UL, 0x750BDABFUL, 0x3FF6C012UL, 0x67FF0B0DUL, 0xBC728956UL, + 0x3C651A2FUL, 0x3FF6DFB2UL, 0x683C88ABUL, 0xBC6BBE3AUL, 0xF9519484UL, 0x3FF6FF7DUL, + 0x25860EF6UL, 0xBC883C0FUL, 0xE8EC5F74UL, 0x3FF71F75UL, 0x86887A99UL, 0xBC816E47UL, + 0x48A58174UL, 0x3FF73F9AUL, 0x6C65D53CUL, 0xBC90A8D9UL, 0x564267C9UL, 0x3FF75FEBUL, + 0x57316DD3UL, 0xBC902459UL, 0x4FDE5D3FUL, 0x3FF78069UL, 0x0A02162DUL, 0x3C9866B8UL, + 0x73EB0187UL, 0x3FF7A114UL, 0xEE04992FUL, 0xBC841577UL, 0x0130C132UL, 0x3FF7C1EDUL, + 0xD1164DD6UL, 0x3C9F124CUL, 0x36CF4E62UL, 0x3FF7E2F3UL, 0xBA15797EUL, 0x3C705D02UL, + 0x543E1A12UL, 0x3FF80427UL, 0x626D972BUL, 0xBC927C86UL, 0x994CCE13UL, 0x3FF82589UL, + 0xD41532D8UL, 0xBC9D4C1DUL, 0x4623C7ADUL, 0x3FF8471AUL, 0xA341CDFBUL, 0xBC88D684UL, + 0x9B4492EDUL, 0x3FF868D9UL, 0x9BD4F6BAUL, 0xBC9FC6F8UL, 0xD98A6699UL, 0x3FF88AC7UL, + 0xF37CB53AUL, 0x3C9994C2UL, 0x422AA0DBUL, 0x3FF8ACE5UL, 0x56864B27UL, 0x3C96E9F1UL, + 0x16B5448CUL, 0x3FF8CF32UL, 0x32E9E3AAUL, 0xBC70D55EUL, 0x99157736UL, 0x3FF8F1AEUL, + 0xA2E3976CUL, 0x3C85CC13UL, 0x0B91FFC6UL, 0x3FF9145BUL, 0x2E582524UL, 0xBC9DD679UL, + 0xB0CDC5E5UL, 0x3FF93737UL, 0x81B57EBCUL, 0xBC675FC7UL, 0xCBC8520FUL, 0x3FF95A44UL, + 0x96A5F039UL, 0xBC764B7CUL, 0x9FDE4E50UL, 0x3FF97D82UL, 0x7C1B85D1UL, 0xBC9D185BUL, + 0x70CA07BAUL, 0x3FF9A0F1UL, 0x91CEE632UL, 0xBC9173BDUL, 0x82A3F090UL, 0x3FF9C491UL, + 0xB071F2BEUL, 0x3C7C7C46UL, 0x19E32323UL, 0x3FF9E863UL, 0x78E64C6EUL, 0x3C7824CAUL, + 0x7B5DE565UL, 0x3FFA0C66UL, 0x5D1CD533UL, 0xBC935949UL, 0xEC4A2D33UL, 0x3FFA309BUL, + 0x7DDC36ABUL, 0x3C96305CUL, 0xB23E255DUL, 0x3FFA5503UL, 0xDB8D41E1UL, 0xBC9D2F6EUL, + 0x1330B358UL, 0x3FFA799EUL, 0xCAC563C7UL, 0x3C9BCB7EUL, 0x5579FDBFUL, 0x3FFA9E6BUL, + 0x0EF7FD31UL, 0x3C90FAC9UL, 0xBFD3F37AUL, 0x3FFAC36BUL, 0xCAE76CD0UL, 0xBC8F9234UL, + 0x995AD3ADUL, 0x3FFAE89FUL, 0x345DCC81UL, 0x3C97A1CDUL, 0x298DB666UL, 0x3FFB0E07UL, + 0x4C80E425UL, 0xBC9BDEF5UL, 0xB84F15FBUL, 0x3FFB33A2UL, 0x3084D708UL, 0xBC62805EUL, + 0x8DE5593AUL, 0x3FFB5972UL, 0xBBBA6DE3UL, 0xBC9C71DFUL, 0xF2FB5E47UL, 0x3FFB7F76UL, + 0x7E54AC3BUL, 0xBC75584FUL, 0x30A1064AUL, 0x3FFBA5B0UL, 0x0E54292EUL, 0xBC9EFCD3UL, + 0x904BC1D2UL, 0x3FFBCC1EUL, 0x7A2D9E84UL, 0x3C823DD0UL, 0x5BD71E09UL, 0x3FFBF2C2UL, + 0x3F6B9C73UL, 0xBC9EFDCAUL, 0xDD85529CUL, 0x3FFC199BUL, 0x895048DDUL, 0x3C811065UL, + 0x5FFFD07AUL, 0x3FFC40ABUL, 0xE083C60AUL, 0x3C9B4537UL, 0x2E57D14BUL, 0x3FFC67F1UL, + 0xFF483CADUL, 0x3C92884DUL, 0x9406E7B5UL, 0x3FFC8F6DUL, 0x48805C44UL, 0x3C71ACBCUL, + 0xDCEF9069UL, 0x3FFCB720UL, 0xD1E949DCUL, 0x3C7503CBUL, 0x555DC3FAUL, 0x3FFCDF0BUL, + 0x53829D72UL, 0xBC8DD83BUL, 0x4A07897CUL, 0x3FFD072DUL, 0x43797A9CUL, 0xBC9CBC37UL, + 0x080D89F2UL, 0x3FFD2F87UL, 0x719D8578UL, 0xBC9D487BUL, 0xDCFBA487UL, 0x3FFD5818UL, + 0xD75B3707UL, 0x3C82ED02UL, 0x16C98398UL, 0x3FFD80E3UL, 0x8BEDDFE8UL, 0xBC911EC1UL, + 0x03DB3285UL, 0x3FFDA9E6UL, 0x696DB532UL, 0x3C9C2300UL, 0xF301B460UL, 0x3FFDD321UL, + 0x78F018C3UL, 0x3C92DA57UL, 0x337B9B5FUL, 0x3FFDFC97UL, 0x4F184B5CUL, 0xBC91A5CDUL, + 0x14F5A129UL, 0x3FFE2646UL, 0x817A1496UL, 0xBC97B627UL, 0xE78B3FF6UL, 0x3FFE502EUL, + 0x80A9CC8FUL, 0x3C839E89UL, 0xFBC74C83UL, 0x3FFE7A51UL, 0xCA0C8DE2UL, 0x3C92D522UL, + 0xA2A490DAUL, 0x3FFEA4AFUL, 0x179C2893UL, 0xBC9E9C23UL, 0x2D8E67F1UL, 0x3FFECF48UL, + 0xB411AD8CUL, 0xBC9C93F3UL, 0xEE615A27UL, 0x3FFEFA1BUL, 0x86A4B6B0UL, 0x3C9DC7F4UL, + 0x376BBA97UL, 0x3FFF252BUL, 0xBF0D8E43UL, 0x3C93A1A5UL, 0x5B6E4540UL, 0x3FFF5076UL, + 0x2DD8A18BUL, 0x3C99D3E1UL, 0xAD9CBE14UL, 0x3FFF7BFDUL, 0xD006350AUL, 0xBC9DBB12UL, + 0x819E90D8UL, 0x3FFFA7C1UL, 0xF3A5931EUL, 0x3C874853UL, 0x2B8F71F1UL, 0x3FFFD3C2UL, + 0x966579E7UL, 0x3C62EB74UL +}; + +ATTRIBUTE_ALIGNED(16) static const juint _T2_neg_f[] = +{ + 0x00000000UL, 0x3FF00000UL, 0x00000000UL, 0x00000000UL, 0x2B8F71F1UL, 0x3FEFD3C2UL, + 0x966579E7UL, 0x3C52EB74UL, 0x819E90D8UL, 0x3FEFA7C1UL, 0xF3A5931EUL, 0x3C774853UL, + 0xAD9CBE14UL, 0x3FEF7BFDUL, 0xD006350AUL, 0xBC8DBB12UL, 0x5B6E4540UL, 0x3FEF5076UL, + 0x2DD8A18BUL, 0x3C89D3E1UL, 0x376BBA97UL, 0x3FEF252BUL, 0xBF0D8E43UL, 0x3C83A1A5UL, + 0xEE615A27UL, 0x3FEEFA1BUL, 0x86A4B6B0UL, 0x3C8DC7F4UL, 0x2D8E67F1UL, 0x3FEECF48UL, + 0xB411AD8CUL, 0xBC8C93F3UL, 0xA2A490DAUL, 0x3FEEA4AFUL, 0x179C2893UL, 0xBC8E9C23UL, + 0xFBC74C83UL, 0x3FEE7A51UL, 0xCA0C8DE2UL, 0x3C82D522UL, 0xE78B3FF6UL, 0x3FEE502EUL, + 0x80A9CC8FUL, 0x3C739E89UL, 0x14F5A129UL, 0x3FEE2646UL, 0x817A1496UL, 0xBC87B627UL, + 0x337B9B5FUL, 0x3FEDFC97UL, 0x4F184B5CUL, 0xBC81A5CDUL, 0xF301B460UL, 0x3FEDD321UL, + 0x78F018C3UL, 0x3C82DA57UL, 0x03DB3285UL, 0x3FEDA9E6UL, 0x696DB532UL, 0x3C8C2300UL, + 0x16C98398UL, 0x3FED80E3UL, 0x8BEDDFE8UL, 0xBC811EC1UL, 0xDCFBA487UL, 0x3FED5818UL, + 0xD75B3707UL, 0x3C72ED02UL, 0x080D89F2UL, 0x3FED2F87UL, 0x719D8578UL, 0xBC8D487BUL, + 0x4A07897CUL, 0x3FED072DUL, 0x43797A9CUL, 0xBC8CBC37UL, 0x555DC3FAUL, 0x3FECDF0BUL, + 0x53829D72UL, 0xBC7DD83BUL, 0xDCEF9069UL, 0x3FECB720UL, 0xD1E949DCUL, 0x3C6503CBUL, + 0x9406E7B5UL, 0x3FEC8F6DUL, 0x48805C44UL, 0x3C61ACBCUL, 0x2E57D14BUL, 0x3FEC67F1UL, + 0xFF483CADUL, 0x3C82884DUL, 0x5FFFD07AUL, 0x3FEC40ABUL, 0xE083C60AUL, 0x3C8B4537UL, + 0xDD85529CUL, 0x3FEC199BUL, 0x895048DDUL, 0x3C711065UL, 0x5BD71E09UL, 0x3FEBF2C2UL, + 0x3F6B9C73UL, 0xBC8EFDCAUL, 0x904BC1D2UL, 0x3FEBCC1EUL, 0x7A2D9E84UL, 0x3C723DD0UL, + 0x30A1064AUL, 0x3FEBA5B0UL, 0x0E54292EUL, 0xBC8EFCD3UL, 0xF2FB5E47UL, 0x3FEB7F76UL, + 0x7E54AC3BUL, 0xBC65584FUL, 0x8DE5593AUL, 0x3FEB5972UL, 0xBBBA6DE3UL, 0xBC8C71DFUL, + 0xB84F15FBUL, 0x3FEB33A2UL, 0x3084D708UL, 0xBC52805EUL, 0x298DB666UL, 0x3FEB0E07UL, + 0x4C80E425UL, 0xBC8BDEF5UL, 0x995AD3ADUL, 0x3FEAE89FUL, 0x345DCC81UL, 0x3C87A1CDUL, + 0xBFD3F37AUL, 0x3FEAC36BUL, 0xCAE76CD0UL, 0xBC7F9234UL, 0x5579FDBFUL, 0x3FEA9E6BUL, + 0x0EF7FD31UL, 0x3C80FAC9UL, 0x1330B358UL, 0x3FEA799EUL, 0xCAC563C7UL, 0x3C8BCB7EUL, + 0xB23E255DUL, 0x3FEA5503UL, 0xDB8D41E1UL, 0xBC8D2F6EUL, 0xEC4A2D33UL, 0x3FEA309BUL, + 0x7DDC36ABUL, 0x3C86305CUL, 0x7B5DE565UL, 0x3FEA0C66UL, 0x5D1CD533UL, 0xBC835949UL, + 0x19E32323UL, 0x3FE9E863UL, 0x78E64C6EUL, 0x3C6824CAUL, 0x82A3F090UL, 0x3FE9C491UL, + 0xB071F2BEUL, 0x3C6C7C46UL, 0x70CA07BAUL, 0x3FE9A0F1UL, 0x91CEE632UL, 0xBC8173BDUL, + 0x9FDE4E50UL, 0x3FE97D82UL, 0x7C1B85D1UL, 0xBC8D185BUL, 0xCBC8520FUL, 0x3FE95A44UL, + 0x96A5F039UL, 0xBC664B7CUL, 0xB0CDC5E5UL, 0x3FE93737UL, 0x81B57EBCUL, 0xBC575FC7UL, + 0x0B91FFC6UL, 0x3FE9145BUL, 0x2E582524UL, 0xBC8DD679UL, 0x99157736UL, 0x3FE8F1AEUL, + 0xA2E3976CUL, 0x3C75CC13UL, 0x16B5448CUL, 0x3FE8CF32UL, 0x32E9E3AAUL, 0xBC60D55EUL, + 0x422AA0DBUL, 0x3FE8ACE5UL, 0x56864B27UL, 0x3C86E9F1UL, 0xD98A6699UL, 0x3FE88AC7UL, + 0xF37CB53AUL, 0x3C8994C2UL, 0x9B4492EDUL, 0x3FE868D9UL, 0x9BD4F6BAUL, 0xBC8FC6F8UL, + 0x4623C7ADUL, 0x3FE8471AUL, 0xA341CDFBUL, 0xBC78D684UL, 0x994CCE13UL, 0x3FE82589UL, + 0xD41532D8UL, 0xBC8D4C1DUL, 0x543E1A12UL, 0x3FE80427UL, 0x626D972BUL, 0xBC827C86UL, + 0x36CF4E62UL, 0x3FE7E2F3UL, 0xBA15797EUL, 0x3C605D02UL, 0x0130C132UL, 0x3FE7C1EDUL, + 0xD1164DD6UL, 0x3C8F124CUL, 0x73EB0187UL, 0x3FE7A114UL, 0xEE04992FUL, 0xBC741577UL, + 0x4FDE5D3FUL, 0x3FE78069UL, 0x0A02162DUL, 0x3C8866B8UL, 0x564267C9UL, 0x3FE75FEBUL, + 0x57316DD3UL, 0xBC802459UL, 0x48A58174UL, 0x3FE73F9AUL, 0x6C65D53CUL, 0xBC80A8D9UL, + 0xE8EC5F74UL, 0x3FE71F75UL, 0x86887A99UL, 0xBC716E47UL, 0xF9519484UL, 0x3FE6FF7DUL, + 0x25860EF6UL, 0xBC783C0FUL, 0x3C651A2FUL, 0x3FE6DFB2UL, 0x683C88ABUL, 0xBC5BBE3AUL, + 0x750BDABFUL, 0x3FE6C012UL, 0x67FF0B0DUL, 0xBC628956UL, 0x667F3BCDUL, 0x3FE6A09EUL, + 0x13B26456UL, 0xBC8BDD34UL, 0xD44CA973UL, 0x3FE68155UL, 0x44F73E65UL, 0x3C5038AEUL, + 0x82552225UL, 0x3FE66238UL, 0x87591C34UL, 0xBC8BB609UL, 0x34CCC320UL, 0x3FE64346UL, + 0x759D8933UL, 0xBC7C483CUL, 0xB03A5585UL, 0x3FE6247EUL, 0x7E40B497UL, 0xBC8383C1UL, + 0xB976DC09UL, 0x3FE605E1UL, 0x9B56DE47UL, 0xBC83E242UL, 0x15AD2148UL, 0x3FE5E76FUL, + 0x3080E65EUL, 0x3C8BA6F9UL, 0x8A5946B7UL, 0x3FE5C926UL, 0x816986A2UL, 0x3C2C4B1BUL, + 0xDD485429UL, 0x3FE5AB07UL, 0x054647ADUL, 0x3C86324CUL, 0xD497C7FDUL, 0x3FE58D12UL, + 0x5B9A1DE8UL, 0x3C7295E1UL, 0x36B527DAUL, 0x3FE56F47UL, 0x011D93ADUL, 0x3C89BB2CUL, + 0xCA5D920FUL, 0x3FE551A4UL, 0xEFEDE59BUL, 0xBC7D689CUL, 0x569D4F82UL, 0x3FE5342BUL, + 0x1DB13CADUL, 0xBC707ABEUL, 0xA2CF6642UL, 0x3FE516DAUL, 0x69BD93EFUL, 0xBC7F7685UL, + 0x769D2CA7UL, 0x3FE4F9B2UL, 0xD25957E3UL, 0xBC84B309UL, 0x99FDDD0DUL, 0x3FE4DCB2UL, + 0xBC6A7833UL, 0x3C88ECDBUL, 0xD5362A27UL, 0x3FE4BFDAUL, 0xAFEC42E2UL, 0x3C6D4397UL, + 0xF0D7D3DEUL, 0x3FE4A32AUL, 0xF3D1BE56UL, 0x3C89CB62UL, 0xB5C13CD0UL, 0x3FE486A2UL, + 0xB69062F0UL, 0x3C63C1A3UL, 0xED1D0057UL, 0x3FE46A41UL, 0xD1648A76UL, 0x3C8C944BUL, + 0x6061892DUL, 0x3FE44E08UL, 0x04EF80D0UL, 0x3C389B7AUL, 0xD950A897UL, 0x3FE431F5UL, + 0xE35F7999UL, 0xBC71C7DDUL, 0x21F72E2AUL, 0x3FE4160AUL, 0x1C309278UL, 0xBC4EF369UL, + 0x04AC801CUL, 0x3FE3FA45UL, 0xF956F9F3UL, 0xBC87D023UL, 0x4C123422UL, 0x3FE3DEA6UL, + 0x11F09EBCUL, 0x3C7ADA09UL, 0xC313A8E5UL, 0x3FE3C32DUL, 0x375D29C3UL, 0xBC8EFFF8UL, + 0x34E59FF7UL, 0x3FE3A7DBUL, 0xD661F5E3UL, 0xBC65E436UL, 0x6D05D866UL, 0x3FE38CAEUL, + 0x3C9904BDUL, 0xBC8E958DUL, 0x373AA9CBUL, 0x3FE371A7UL, 0xBF42EAE2UL, 0xBC863AEAUL, + 0x5F929FF1UL, 0x3FE356C5UL, 0x5C4E4628UL, 0xBC7B5CEEUL, 0xB26416FFUL, 0x3FE33C08UL, + 0x843659A6UL, 0x3C832721UL, 0xFC4CD831UL, 0x3FE32170UL, 0x8E18047CUL, 0x3C7A9CE7UL, + 0x0A31B715UL, 0x3FE306FEUL, 0xD23182E4UL, 0x3C76F46AUL, 0xA93E2F56UL, 0x3FE2ECAFUL, + 0x45D52383UL, 0x3C61CA0FUL, 0xA6E4030BUL, 0x3FE2D285UL, 0x54DB41D5UL, 0x3C800247UL, + 0xD0DAD990UL, 0x3FE2B87FUL, 0xD6381AA4UL, 0xBC310ADCUL, 0xF51FDEE1UL, 0x3FE29E9DUL, + 0xAFAD1255UL, 0x3C7612E8UL, 0xE1F56381UL, 0x3FE284DFUL, 0x8C3F0D7EUL, 0xBC8A4C3AUL, + 0x65E27CDDUL, 0x3FE26B45UL, 0x9940E9D9UL, 0x3C72BD33UL, 0x4FB2A63FUL, 0x3FE251CEUL, + 0xBEF4F4A4UL, 0x3C7AC155UL, 0x6E756238UL, 0x3FE2387AUL, 0xB6C70573UL, 0x3C89B07EUL, + 0x917DDC96UL, 0x3FE21F49UL, 0x9494A5EEUL, 0x3C72A97EUL, 0x88628CD6UL, 0x3FE2063BUL, + 0x814A8495UL, 0x3C7DC775UL, 0x22FCD91DUL, 0x3FE1ED50UL, 0x027BB78CUL, 0xBC81DF98UL, + 0x3168B9AAUL, 0x3FE1D487UL, 0x00A2643CUL, 0x3C8E016EUL, 0x84045CD4UL, 0x3FE1BBE0UL, + 0x352EF607UL, 0xBC895386UL, 0xEB6FCB75UL, 0x3FE1A35BUL, 0x7B4968E4UL, 0x3C7E5B4CUL, + 0x388C8DEAUL, 0x3FE18AF9UL, 0xD1970F6CUL, 0xBC811023UL, 0x3C7D517BUL, 0x3FE172B8UL, + 0xB9D78A76UL, 0xBC719041UL, 0xC8A58E51UL, 0x3FE15A98UL, 0xB9EEAB0AUL, 0x3C72406AUL, + 0xAEA92DE0UL, 0x3FE1429AUL, 0x9AF1369EUL, 0xBC832FBFUL, 0xC06C31CCUL, 0x3FE12ABDUL, + 0xB36CA5C7UL, 0xBC41B514UL, 0xD0125B51UL, 0x3FE11301UL, 0x39449B3AUL, 0xBC86C510UL, + 0xAFFED31BUL, 0x3FE0FB66UL, 0xC44EBD7BUL, 0xBC5B9BEDUL, 0x32D3D1A2UL, 0x3FE0E3ECUL, + 0x27C57B52UL, 0x3C303A17UL, 0x2B7247F7UL, 0x3FE0CC92UL, 0x16E24F71UL, 0x3C801EDCUL, + 0x6CF9890FUL, 0x3FE0B558UL, 0x4ADC610BUL, 0x3C88A62EUL, 0xCAC6F383UL, 0x3FE09E3EUL, + 0x18316136UL, 0x3C814878UL, 0x18759BC8UL, 0x3FE08745UL, 0x4BB284FFUL, 0x3C5186BEUL, + 0x29DDF6DEUL, 0x3FE0706BUL, 0xE2B13C27UL, 0xBC7C91DFUL, 0xD3158574UL, 0x3FE059B0UL, + 0xA475B465UL, 0x3C7D73E2UL, 0xE86E7F85UL, 0x3FE04315UL, 0x1977C96EUL, 0xBC80A31CUL, + 0x3E778061UL, 0x3FE02C9AUL, 0x535B085DUL, 0xBC619083UL, 0xA9FB3335UL, 0x3FE0163DUL, + 0x9AB8CDB7UL, 0x3C8B6129UL +}; + +#define __ _masm-> + +address StubGenerator::generate_libmSinh() { + StubId stub_id = StubId::stubgen_dsinh_id; + StubCodeMark mark(this, stub_id); + address start = __ pc(); + + Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_3_0_2, L_2TAG_PACKET_4_0_2; + Label L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2; + Label B1_2, B1_5; + + address HALFMASK = (address)_HALFMASK; + address MASK3 = (address)_MASK3; + address L2E = (address)_L2E; + address Shifter = (address)_Shifter; + address cv = (address)_cv; + address pv = (address)_pv; + address T2f = (address) _T2f; + address T2_neg_f = (address) _T2_neg_f; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + __ bind(B1_2); + __ xorpd(xmm4, xmm4); + __ movsd(xmm1, ExternalAddress(L2E), r11 /*rscratch*/); + __ movl(rax, 32768); + __ pinsrw(xmm4, rax, 3); + __ pextrw(rcx, xmm0, 3); + __ andnpd(xmm4, xmm0); + __ pshufd(xmm5, xmm4, 68); + __ movl(rdx, 32768); + __ andl(rdx, rcx); + __ andl(rcx, 32767); + __ subl(rcx, 16343); + __ cmpl(rcx, 177); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_2); // Branch only if |x| is not in [23/64, 3*2^8) + __ movsd(xmm3, ExternalAddress(HALFMASK), r11 /*rscratch*/); + __ movsd(xmm2, ExternalAddress(L2E + 8), r11 /*rscratch*/); + __ movsd(xmm6, ExternalAddress(Shifter), r11 /*rscratch*/); + __ andpd(xmm3, xmm0); + __ subsd(xmm4, xmm3); + __ mulsd(xmm3, xmm1); + __ mulsd(xmm2, xmm5); + __ cvtsd2siq(rax, xmm3); + __ shll(rdx, 3); + __ orl(rax, rdx); + __ movq(xmm7, xmm3); + __ addsd(xmm3, xmm6); + __ mulsd(xmm1, xmm4); + __ xorpd(xmm5, xmm5); + __ subsd(xmm3, xmm6); + __ movapd(xmm4, ExternalAddress(cv), r11 /*rscratch*/); + __ addsd(xmm2, xmm1); + __ movapd(xmm6, ExternalAddress(cv + 16), r11 /*rscratch*/); + __ subsd(xmm7, xmm3); + __ movl(rdx, 32704); + __ pinsrw(xmm5, rdx, 3); + __ movapd(xmm1, ExternalAddress(cv + 32), r11 /*rscratch*/); + __ addsd(xmm2, xmm7); + __ movl(rdx, 127); + __ andl(rdx, rax); + __ addl(rdx, rdx); + __ shrl(rax, 3); + __ andl(rax, 65520); + __ addl(rax, 16352); + __ xorpd(xmm0, xmm0); + __ cmpl(rcx, 161); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_1_0_2); // Branch only if |x| is not in [23/64, 3*2^7) + __ pshufd(xmm5, xmm5, 68); + __ pinsrw(xmm0, rax, 3); + __ pshufd(xmm0, xmm0, 68); + __ psubw(xmm5, xmm0); + __ lea(r8, ExternalAddress(T2f)); + __ mulpd(xmm0, Address(r8, rdx, Address::times_8)); + __ lea(r8, ExternalAddress(T2_neg_f)); + __ mulpd(xmm5, Address(r8, rdx, Address::times_8)); + __ pshufd(xmm3, xmm2, 68); + __ movapd(xmm7, ExternalAddress(cv + 48), r11 /*rscratch*/); + __ pshufd(xmm2, xmm2, 68); + __ mulpd(xmm3, xmm3); + __ mulpd(xmm4, xmm2); + __ mulpd(xmm6, xmm2); + __ mulpd(xmm2, ExternalAddress(cv + 64), r11 /*rscratch*/); + __ mulpd(xmm1, xmm3); + __ mulpd(xmm7, xmm3); + __ mulpd(xmm4, xmm3); + __ mulpd(xmm1, xmm3); + __ addpd(xmm6, xmm7); + __ movq(xmm7, xmm0); + __ addpd(xmm4, xmm1); + __ shufpd(xmm7, xmm5, 0); + __ subpd(xmm0, xmm5); + __ mulpd(xmm2, xmm7); + __ addpd(xmm4, xmm6); + __ subsd(xmm7, xmm0); + __ mulpd(xmm4, xmm2); + __ pshufd(xmm6, xmm0, 238); + __ subsd(xmm7, xmm5); + __ addpd(xmm4, xmm2); + __ addsd(xmm7, xmm6); + __ pshufd(xmm2, xmm4, 238); + __ addsd(xmm2, xmm7); + __ addsd(xmm2, xmm4); + __ addsd(xmm0, xmm2); + __ jmp(B1_5); + + __ bind(L_2TAG_PACKET_1_0_2); + __ subl(rax, 16352); + __ movl(rcx, rax); + __ andl(rax, 32752); + __ shrl(rax, 1); + __ andl(rax, 65520); + __ subl(rcx, rax); + __ addl(rax, 16352); + __ pinsrw(xmm0, rax, 3); + __ pshufd(xmm0, xmm0, 68); + __ lea(r8, ExternalAddress(T2f)); + __ mulpd(xmm0, Address(r8, rdx, Address::times_8)); + __ pshufd(xmm3, xmm2, 68); + __ movsd(xmm7, ExternalAddress(cv + 48), r11 /*rscratch*/); + __ mulsd(xmm3, xmm3); + __ mulsd(xmm4, xmm2); + __ mulsd(xmm6, xmm2); + __ mulsd(xmm2, ExternalAddress(cv + 64), r11 /*rscratch*/); + __ mulsd(xmm1, xmm3); + __ mulsd(xmm7, xmm3); + __ mulsd(xmm4, xmm3); + __ addl(rcx, 16368); + __ pinsrw(xmm5, rcx, 3); + __ mulsd(xmm1, xmm3); + __ addsd(xmm6, xmm7); + __ addsd(xmm4, xmm1); + __ mulsd(xmm2, xmm0); + __ addsd(xmm4, xmm6); + __ mulsd(xmm4, xmm2); + __ pshufd(xmm6, xmm0, 238); + __ addsd(xmm4, xmm6); + __ addsd(xmm2, xmm4); + __ addsd(xmm0, xmm2); + __ mulsd(xmm0, xmm5); + __ jmp(B1_5); + + __ bind(L_2TAG_PACKET_0_0_2); + __ addl(rcx, 16343); + __ cmpl(rcx, 16343); + __ jcc(Assembler::above, L_2TAG_PACKET_3_0_2); // Branch only if |x| > 23/64 + __ cmpl(rcx, 15856); + __ jcc(Assembler::below, L_2TAG_PACKET_4_0_2); // Branch only if |x| < 2^-32 + __ movapd(xmm1, ExternalAddress(pv), r11 /*rscratch*/); + __ pshufd(xmm6, xmm0, 68); + __ mulpd(xmm5, xmm5); + __ movapd(xmm2, ExternalAddress(pv + 16), r11 /*rscratch*/); + __ pshufd(xmm7, xmm0, 68); + __ movapd(xmm3, ExternalAddress(pv + 32), r11 /*rscratch*/); + __ pshufd(xmm4, xmm0, 68); + __ andpd(xmm6, ExternalAddress(MASK3), r11 /*rscratch*/); + __ mulpd(xmm1, xmm5); + __ mulsd(xmm2, xmm5); + __ subpd(xmm4, xmm6); + __ mulpd(xmm7, xmm5); + __ addpd(xmm1, xmm3); + __ pshufd(xmm3, xmm6, 68); + __ mulpd(xmm5, xmm5); + __ mulsd(xmm2, xmm7); + __ mulpd(xmm1, xmm7); + __ pshufd(xmm7, xmm0, 68); + __ mulsd(xmm6, xmm6); + __ addsd(xmm7, xmm7); + __ mulsd(xmm4, xmm4); + __ mulpd(xmm1, xmm5); + __ addsd(xmm7, xmm0); + __ mulsd(xmm6, xmm3); + __ mulsd(xmm7, xmm3); + __ pshufd(xmm3, xmm1, 238); + __ mulsd(xmm1, xmm5); + __ pshufd(xmm5, xmm4, 238); + __ addsd(xmm3, xmm2); + __ pshufd(xmm2, xmm2, 238); + __ addsd(xmm7, xmm4); + __ movq(xmm4, xmm0); + __ mulsd(xmm6, xmm2); + __ mulsd(xmm7, xmm5); + __ addsd(xmm0, xmm6); + __ mulsd(xmm7, xmm2); + __ subsd(xmm4, xmm0); + __ addsd(xmm1, xmm7); + __ addsd(xmm6, xmm4); + __ addsd(xmm1, xmm3); + __ addsd(xmm1, xmm6); + __ addsd(xmm0, xmm1); + __ jmp(B1_5); + + __ bind(L_2TAG_PACKET_4_0_2); + __ cmpl(rcx, 16); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_5_0_2); // Branch only if |x| is not denormalized + __ movq(xmm1, xmm0); + __ mulsd(xmm1, xmm1); + __ jmp(B1_5); + + __ bind(L_2TAG_PACKET_5_0_2); + __ xorpd(xmm2, xmm2); + __ movl(rcx, 17392); + __ pinsrw(xmm2, rcx, 3); + __ xorpd(xmm3, xmm3); + __ movl(rdx, 15344); + __ pinsrw(xmm3, rdx, 3); + __ mulsd(xmm2, xmm0); + __ addsd(xmm0, xmm2); + __ mulsd(xmm0, xmm3); + __ jmp(B1_5); + + __ bind(L_2TAG_PACKET_3_0_2); + __ cmpl(rcx, 32752); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_6_0_2); // Branch only if |x| is INF or NaN + __ xorpd(xmm0, xmm0); + __ movl(rax, 32736); + __ pinsrw(xmm0, rax, 3); + __ orl(rax, rdx); + __ pinsrw(xmm1, rax, 3); + __ mulsd(xmm0, xmm1); + __ jmp(B1_5); + + __ bind(L_2TAG_PACKET_6_0_2); + __ xorpd(xmm1, xmm1); + __ movl(rax, 32768); + __ pinsrw(xmm1, rax, 3); + __ andnpd(xmm1, xmm0); + __ mulsd(xmm0, xmm1); + + __ bind(B1_5); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; +} + +#undef __ diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index 9b524ae94cf..ee9cea08e64 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -45,6 +45,17 @@ STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY +address StubRoutines::crc_table_addr() { + return (address)StubRoutines::x86::_crc_table; +} +address StubRoutines::crc32c_table_addr() { + if (StubRoutines::x86::_crc32c_table == nullptr) { + bool supports_clmul = VM_Version::supports_clmul(); + StubRoutines::x86::generate_CRC32C_table(supports_clmul); + } + return (address)StubRoutines::x86::_crc32c_table; +} + address StubRoutines::x86::_k256_adr = nullptr; address StubRoutines::x86::_k256_W_adr = nullptr; address StubRoutines::x86::_k512_W_addr = nullptr; @@ -291,7 +302,7 @@ static uint32_t crc32c_f_pow_n(uint32_t n) { return result; } -juint *StubRoutines::x86::_crc32c_table; +juint* StubRoutines::x86::_crc32c_table = nullptr; void StubRoutines::x86::generate_CRC32C_table(bool is_pclmulqdq_table_supported) { diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index c4930e1593c..7d13c4f6e7a 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -44,6 +44,7 @@ enum platform_dependent_constants { class x86 { friend class StubGenerator; + friend class StubRoutines; friend class VMStructs; // declare fields for arch-specific entries diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp index 46af93e9760..c716e10a1b2 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp @@ -464,6 +464,13 @@ 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_sinh) { + if (StubRoutines::dsinh() != nullptr) { + __ movdbl(xmm0, Address(rsp, wordSize)); + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dsinh()))); + } else { + return nullptr; // Fallback to default implementation + } } else if (kind == Interpreter::java_lang_math_tanh) { if (StubRoutines::dtanh() != nullptr) { __ movdbl(xmm0, Address(rsp, wordSize)); diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 42661bd7a2b..b81c200c440 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -38,6 +38,7 @@ #include "runtime/stubCodeGenerator.hpp" #include "runtime/vm_version.hpp" #include "utilities/checkedCast.hpp" +#include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/virtualizationSupport.hpp" @@ -1069,6 +1070,7 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(IntelJccErratumMitigation)) { _has_intel_jcc_erratum = compute_has_intel_jcc_erratum(); + FLAG_SET_ERGO(IntelJccErratumMitigation, _has_intel_jcc_erratum); } else { _has_intel_jcc_erratum = IntelJccErratumMitigation; } @@ -1096,21 +1098,16 @@ void VM_Version::get_processor_features() { } } - char buf[2048]; - size_t cpu_info_size = jio_snprintf( - buf, sizeof(buf), - "(%u cores per cpu, %u threads per core) family %d model %d stepping %d microcode 0x%x", - cores_per_cpu(), threads_per_core(), - cpu_family(), _model, _stepping, os::cpu_microcode_revision()); - assert(cpu_info_size > 0, "not enough temporary space allocated"); + stringStream ss(2048); + ss.print("(%u cores per cpu, %u threads per core) family %d model %d stepping %d microcode 0x%x", + cores_per_cpu(), threads_per_core(), + cpu_family(), _model, _stepping, os::cpu_microcode_revision()); + ss.print(", "); + int features_offset = (int)ss.size(); + insert_features_names(_features, ss); - insert_features_names(_features, buf + cpu_info_size, sizeof(buf) - cpu_info_size); - - _cpu_info_string = os::strdup(buf); - - _features_string = extract_features_string(_cpu_info_string, - strnlen(_cpu_info_string, sizeof(buf)), - cpu_info_size); + _cpu_info_string = ss.as_string(true); + _features_string = _cpu_info_string + features_offset; // Use AES instructions if available. if (supports_aes()) { @@ -3265,13 +3262,15 @@ bool VM_Version::is_intrinsic_supported(vmIntrinsicID id) { return true; } -void VM_Version::insert_features_names(VM_Version::VM_Features features, char* buf, size_t buflen) { - for (int i = 0; i < MAX_CPU_FEATURES; i++) { - if (features.supports_feature((VM_Version::Feature_Flag)i)) { - int res = jio_snprintf(buf, buflen, ", %s", _features_names[i]); - assert(res > 0, "not enough temporary space allocated"); - buf += res; - buflen -= res; +void VM_Version::insert_features_names(VM_Version::VM_Features features, stringStream& ss) { + int i = 0; + ss.join([&]() { + while (i < MAX_CPU_FEATURES) { + if (_features.supports_feature((VM_Version::Feature_Flag)i)) { + return _features_names[i++]; + } + i += 1; } - } + return (const char*)nullptr; + }, ", "); } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 3c8971e474b..6ee9f95fdf5 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -30,6 +30,8 @@ #include "utilities/macros.hpp" #include "utilities/sizes.hpp" +class stringStream; + class VM_Version : public Abstract_VM_Version { friend class VMStructs; friend class JVMCIVMStructs; @@ -922,7 +924,7 @@ public: static bool is_intel_tsc_synched_at_init(); - static void insert_features_names(VM_Version::VM_Features features, char* buf, size_t buflen); + static void insert_features_names(VM_Version::VM_Features features, stringStream& ss); // This checks if the JVM is potentially affected by an erratum on Intel CPUs (SKX102) // that causes unpredictable behaviour when jcc crosses 64 byte boundaries. Its microcode diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 933be1667c2..2eb748e350c 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1831,7 +1831,10 @@ bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType bt) { } break; case Op_SelectFromTwoVector: - if (size_in_bits < 128 || (size_in_bits < 512 && !VM_Version::supports_avx512vl())) { + if (size_in_bits < 128) { + return false; + } + if ((size_in_bits < 512 && !VM_Version::supports_avx512vl())) { return false; } if (bt == T_SHORT && !VM_Version::supports_avx512bw()) { diff --git a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp index 2357bbb5169..3126cf71460 100644 --- a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp +++ b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp @@ -37,7 +37,7 @@ do_arch_blob, \ do_arch_entry, \ do_arch_entry_init) \ - do_arch_blob(initial, 0) \ + do_arch_blob(initial, 32) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ @@ -58,7 +58,7 @@ do_arch_blob, \ do_arch_entry, \ do_arch_entry_init) \ - do_arch_blob(final, 0) \ + do_arch_blob(final, 32) \ #endif // CPU_ZERO_STUBDECLARATIONS_HPP diff --git a/src/hotspot/cpu/zero/stubRoutines_zero.cpp b/src/hotspot/cpu/zero/stubRoutines_zero.cpp index 47d2c27eefd..9b53f09be5d 100644 --- a/src/hotspot/cpu/zero/stubRoutines_zero.cpp +++ b/src/hotspot/cpu/zero/stubRoutines_zero.cpp @@ -28,4 +28,5 @@ #include "runtime/javaThread.hpp" #include "runtime/stubRoutines.hpp" -// zero has no arch-specific stubs nor any associated entries +address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; } +address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 17186fb9f3d..25a930dc1d9 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2343,8 +2343,7 @@ int os::open(const char *path, int oflag, int mode) { // specifically destined for a subprocess should have the // close-on-exec flag set. If we don't set it, then careless 3rd // party native code might fork and exec without closing all - // appropriate file descriptors (e.g. as we do in closeDescriptors in - // UNIXProcess.c), and this in turn might: + // appropriate file descriptors, and this in turn might: // // - cause end-of-file to fail to be detected on some file // descriptors, resulting in mysterious hangs, or diff --git a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp index 154d2887c53..ee76214ddfa 100644 --- a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp +++ b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp @@ -233,10 +233,10 @@ public: mach_msg_type_number_t num_out = TASK_VM_INFO_COUNT; kern_return_t err = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)(&vm_info), &num_out); if (err == KERN_SUCCESS) { - st->print_cr(" vsize: %llu (%llu%s)", vm_info.virtual_size, PROPERFMTARGS(vm_info.virtual_size)); - st->print_cr(" rss: %llu (%llu%s)", vm_info.resident_size, PROPERFMTARGS(vm_info.resident_size)); - st->print_cr(" peak rss: %llu (%llu%s)", vm_info.resident_size_peak, PROPERFMTARGS(vm_info.resident_size_peak)); - st->print_cr(" page size: %d (%ld%s)", vm_info.page_size, PROPERFMTARGS((size_t)vm_info.page_size)); + st->print_cr(" vsize: %llu (" PROPERFMT ")", vm_info.virtual_size, PROPERFMTARGS((size_t)vm_info.virtual_size)); + st->print_cr(" rss: %llu (" PROPERFMT ")", vm_info.resident_size, PROPERFMTARGS((size_t)vm_info.resident_size)); + st->print_cr(" peak rss: %llu (" PROPERFMT ")", vm_info.resident_size_peak, PROPERFMTARGS((size_t)vm_info.resident_size_peak)); + st->print_cr(" page size: %d (" PROPERFMT ")", vm_info.page_size, PROPERFMTARGS((size_t)vm_info.page_size)); } else { st->print_cr("error getting vm_info %d", err); } diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index eb9b4c3f862..b7b88e8e606 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -2251,8 +2251,7 @@ int os::open(const char *path, int oflag, int mode) { // specifically destined for a subprocess should have the // close-on-exec flag set. If we don't set it, then careless 3rd // party native code might fork and exec without closing all - // appropriate file descriptors (e.g. as we do in closeDescriptors in - // UNIXProcess.c), and this in turn might: + // appropriate file descriptors, and this in turn might: // // - cause end-of-file to fail to be detected on some file // descriptors, resulting in mysterious hangs, or diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index b77a1f36954..4d6225cf21e 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4872,9 +4872,8 @@ int os::open(const char *path, int oflag, int mode) { // All file descriptors that are opened in the Java process and not // specifically destined for a subprocess should have the close-on-exec // flag set. If we don't set it, then careless 3rd party native code - // might fork and exec without closing all appropriate file descriptors - // (e.g. as we do in closeDescriptors in UNIXProcess.c), and this in - // turn might: + // might fork and exec without closing all appropriate file descriptors, + // and this in turn might: // // - cause end-of-file to fail to be detected on some file // descriptors, resulting in mysterious hangs, or diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index d417f384a18..1ece8260948 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -713,7 +713,7 @@ bool os::get_host_name(char* buf, size_t buflen) { } #ifndef _LP64 -// Helper, on 32bit, for os::has_allocatable_memory_limit +// Helper, on 32bit, for os::commit_memory_limit static bool is_allocatable(size_t s) { if (s < 2 * G) { return true; @@ -731,31 +731,19 @@ static bool is_allocatable(size_t s) { } #endif // !_LP64 +size_t os::commit_memory_limit() { + // On POSIX systems, the amount of memory that can be commmitted is limited + // by the size of the reservable memory. + size_t reserve_limit = reserve_memory_limit(); -bool os::has_allocatable_memory_limit(size_t* limit) { - struct rlimit rlim; - int getrlimit_res = getrlimit(RLIMIT_AS, &rlim); - // if there was an error when calling getrlimit, assume that there is no limitation - // on virtual memory. - bool result; - if ((getrlimit_res != 0) || (rlim.rlim_cur == RLIM_INFINITY)) { - result = false; - } else { - *limit = (size_t)rlim.rlim_cur; - result = true; - } #ifdef _LP64 - return result; + return reserve_limit; #else - // arbitrary virtual space limit for 32 bit Unices found by testing. If - // getrlimit above returned a limit, bound it with this limit. Otherwise - // directly use it. - const size_t max_virtual_limit = 3800*M; - if (result) { - *limit = MIN2(*limit, max_virtual_limit); - } else { - *limit = max_virtual_limit; - } + // Arbitrary max reserve limit for 32 bit Unices found by testing. + const size_t max_reserve_limit = 3800 * M; + + // Bound the reserve limit with the arbitrary max. + size_t actual_limit = MIN2(reserve_limit, max_reserve_limit); // bound by actually allocatable memory. The algorithm uses two bounds, an // upper and a lower limit. The upper limit is the current highest amount of @@ -769,15 +757,15 @@ bool os::has_allocatable_memory_limit(size_t* limit) { // the minimum amount of memory we care about allocating. const size_t min_allocation_size = M; - size_t upper_limit = *limit; + size_t upper_limit = actual_limit; // first check a few trivial cases if (is_allocatable(upper_limit) || (upper_limit <= min_allocation_size)) { - *limit = upper_limit; + // The actual limit is allocatable, no need to do anything. } else if (!is_allocatable(min_allocation_size)) { // we found that not even min_allocation_size is allocatable. Return it // anyway. There is no point to search for a better value any more. - *limit = min_allocation_size; + actual_limit = min_allocation_size; } else { // perform the binary search. size_t lower_limit = min_allocation_size; @@ -790,12 +778,31 @@ bool os::has_allocatable_memory_limit(size_t* limit) { upper_limit = temp_limit; } } - *limit = lower_limit; + actual_limit = lower_limit; } - return true; + + return actual_limit; #endif } +size_t os::reserve_memory_limit() { + struct rlimit rlim; + int getrlimit_res = getrlimit(RLIMIT_AS, &rlim); + + // If there was an error calling getrlimit, conservatively assume no limit. + if (getrlimit_res != 0) { + return SIZE_MAX; + } + + // If the current limit is not infinity, there is a limit. + if (rlim.rlim_cur != RLIM_INFINITY) { + return (size_t)rlim.rlim_cur; + } + + // No limit + return SIZE_MAX; +} + void* os::get_default_process_handle() { #ifdef __APPLE__ // MacOS X needs to use RTLD_FIRST instead of RTLD_LAZY diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index ac943fd05b4..ffa22bd0365 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -897,13 +897,6 @@ size_t os::rss() { return rss; } -bool os::has_allocatable_memory_limit(size_t* limit) { - MEMORYSTATUSEX ms; - ms.dwLength = sizeof(ms); - GlobalMemoryStatusEx(&ms); - *limit = (size_t)ms.ullAvailVirtual; - return true; -} int os::active_processor_count() { // User has overridden the number of active processors @@ -3303,6 +3296,51 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi return aligned_base; } +size_t os::commit_memory_limit() { + BOOL is_in_job_object = false; + BOOL res = IsProcessInJob(GetCurrentProcess(), nullptr, &is_in_job_object); + if (!res) { + char buf[512]; + size_t buf_len = os::lasterror(buf, sizeof(buf)); + warning("Attempt to determine whether the process is running in a job failed for commit limit: %s", buf_len != 0 ? buf : ""); + + // Conservatively assume no limit when there was an error calling IsProcessInJob. + return SIZE_MAX; + } + + if (!is_in_job_object) { + // Not limited by a Job Object + return SIZE_MAX; + } + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {}; + res = QueryInformationJobObject(nullptr, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli), nullptr); + if (!res) { + char buf[512]; + size_t buf_len = os::lasterror(buf, sizeof(buf)); + warning("Attempt to query job object information failed for commit limit: %s", buf_len != 0 ? buf : ""); + + // Conservatively assume no limit when there was an error calling QueryInformationJobObject. + return SIZE_MAX; + } + + if (jeli.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) { + return jeli.ProcessMemoryLimit; + } + + if (jeli.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) { + return jeli.JobMemoryLimit; + } + + // No limit + return SIZE_MAX; +} + +size_t os::reserve_memory_limit() { + // Virtual address space cannot be limited on Windows. + return SIZE_MAX; +} + char* os::reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool exec) { // exec can be ignored return map_or_reserve_memory_aligned(size, alignment, -1/* file_desc */, mem_tag); diff --git a/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp index b55c1cd27c8..ffa7e1d5d2b 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/vm_version_bsd_aarch64.cpp @@ -67,7 +67,8 @@ void VM_Version::get_os_cpu_info() { // 2) ID_AA64PFR0_EL1 describes AdvSIMD always equals to FP field. // See the Arm ARM, section "ID_AA64PFR0_EL1, AArch64 Processor Feature // Register 0". - _features = CPU_FP | CPU_ASIMD; + set_feature(CPU_FP); + set_feature(CPU_ASIMD); // All Apple-darwin Arm processors have AES, PMULL, SHA1 and SHA2. // See https://github.com/apple-oss-distributions/xnu/blob/main/osfmk/arm/commpage/commpage.c#L412 @@ -75,25 +76,28 @@ void VM_Version::get_os_cpu_info() { // these four CPU features, e.g., "hw.optional.arm.FEAT_AES", but the // corresponding string names are not available before xnu-8019 version. // Hence, assertions are omitted considering backward compatibility. - _features |= CPU_AES | CPU_PMULL | CPU_SHA1 | CPU_SHA2; + set_feature(CPU_AES); + set_feature(CPU_PMULL); + set_feature(CPU_SHA1); + set_feature(CPU_SHA2); if (cpu_has("hw.optional.armv8_crc32")) { - _features |= CPU_CRC32; + set_feature(CPU_CRC32); } if (cpu_has("hw.optional.arm.FEAT_LSE") || cpu_has("hw.optional.armv8_1_atomics")) { - _features |= CPU_LSE; + set_feature(CPU_LSE); } if (cpu_has("hw.optional.arm.FEAT_SHA512") || cpu_has("hw.optional.armv8_2_sha512")) { - _features |= CPU_SHA512; + set_feature(CPU_SHA512); } if (cpu_has("hw.optional.arm.FEAT_SHA3") || cpu_has("hw.optional.armv8_2_sha3")) { - _features |= CPU_SHA3; + set_feature(CPU_SHA3); } if (cpu_has("hw.optional.arm.FEAT_SB")) { - _features |= CPU_SB; + set_feature(CPU_SB); } int cache_line_size; diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index a8edeefc885..1fe06dc640d 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -117,22 +117,22 @@ void VM_Version::get_os_cpu_info() { uint64_t auxv = getauxval(AT_HWCAP); uint64_t auxv2 = getauxval(AT_HWCAP2); - static_assert(CPU_FP == HWCAP_FP, "Flag CPU_FP must follow Linux HWCAP"); - static_assert(CPU_ASIMD == HWCAP_ASIMD, "Flag CPU_ASIMD must follow Linux HWCAP"); - static_assert(CPU_EVTSTRM == HWCAP_EVTSTRM, "Flag CPU_EVTSTRM must follow Linux HWCAP"); - static_assert(CPU_AES == HWCAP_AES, "Flag CPU_AES must follow Linux HWCAP"); - static_assert(CPU_PMULL == HWCAP_PMULL, "Flag CPU_PMULL must follow Linux HWCAP"); - static_assert(CPU_SHA1 == HWCAP_SHA1, "Flag CPU_SHA1 must follow Linux HWCAP"); - static_assert(CPU_SHA2 == HWCAP_SHA2, "Flag CPU_SHA2 must follow Linux HWCAP"); - static_assert(CPU_CRC32 == HWCAP_CRC32, "Flag CPU_CRC32 must follow Linux HWCAP"); - static_assert(CPU_LSE == HWCAP_ATOMICS, "Flag CPU_LSE must follow Linux HWCAP"); - static_assert(CPU_DCPOP == HWCAP_DCPOP, "Flag CPU_DCPOP must follow Linux HWCAP"); - static_assert(CPU_SHA3 == HWCAP_SHA3, "Flag CPU_SHA3 must follow Linux HWCAP"); - static_assert(CPU_SHA512 == HWCAP_SHA512, "Flag CPU_SHA512 must follow Linux HWCAP"); - static_assert(CPU_SVE == HWCAP_SVE, "Flag CPU_SVE must follow Linux HWCAP"); - static_assert(CPU_PACA == HWCAP_PACA, "Flag CPU_PACA must follow Linux HWCAP"); - static_assert(CPU_FPHP == HWCAP_FPHP, "Flag CPU_FPHP must follow Linux HWCAP"); - static_assert(CPU_ASIMDHP == HWCAP_ASIMDHP, "Flag CPU_ASIMDHP must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_FP) == HWCAP_FP, "Flag CPU_FP must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_ASIMD) == HWCAP_ASIMD, "Flag CPU_ASIMD must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_EVTSTRM) == HWCAP_EVTSTRM, "Flag CPU_EVTSTRM must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_AES) == HWCAP_AES, "Flag CPU_AES must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_PMULL) == HWCAP_PMULL, "Flag CPU_PMULL must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_SHA1) == HWCAP_SHA1, "Flag CPU_SHA1 must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_SHA2) == HWCAP_SHA2, "Flag CPU_SHA2 must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_CRC32) == HWCAP_CRC32, "Flag CPU_CRC32 must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_LSE) == HWCAP_ATOMICS, "Flag CPU_LSE must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_DCPOP) == HWCAP_DCPOP, "Flag CPU_DCPOP must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_SHA3) == HWCAP_SHA3, "Flag CPU_SHA3 must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_SHA512) == HWCAP_SHA512, "Flag CPU_SHA512 must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_SVE) == HWCAP_SVE, "Flag CPU_SVE must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_PACA) == HWCAP_PACA, "Flag CPU_PACA must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_FPHP) == HWCAP_FPHP, "Flag CPU_FPHP must follow Linux HWCAP"); + static_assert(BIT_MASK(CPU_ASIMDHP) == HWCAP_ASIMDHP, "Flag CPU_ASIMDHP must follow Linux HWCAP"); _features = auxv & ( HWCAP_FP | HWCAP_ASIMD | @@ -152,8 +152,12 @@ void VM_Version::get_os_cpu_info() { HWCAP_FPHP | HWCAP_ASIMDHP); - if (auxv2 & HWCAP2_SVE2) _features |= CPU_SVE2; - if (auxv2 & HWCAP2_SVEBITPERM) _features |= CPU_SVEBITPERM; + if (auxv2 & HWCAP2_SVE2) { + set_feature(CPU_SVE2); + } + if (auxv2 & HWCAP2_SVEBITPERM) { + set_feature(CPU_SVEBITPERM); + } uint64_t ctr_el0; uint64_t dczid_el0; @@ -187,7 +191,7 @@ void VM_Version::get_os_cpu_info() { _revision = v; } else if (strncmp(buf, "flags", sizeof("flags") - 1) == 0) { if (strstr(p+1, "dcpop")) { - guarantee(_features & CPU_DCPOP, "dcpop availability should be consistent"); + guarantee(supports_feature(CPU_DCPOP), "dcpop availability should be consistent"); } } } diff --git a/src/hotspot/share/adlc/adlc.hpp b/src/hotspot/share/adlc/adlc.hpp index 3e82fe0e210..dd35ebf9e1a 100644 --- a/src/hotspot/share/adlc/adlc.hpp +++ b/src/hotspot/share/adlc/adlc.hpp @@ -29,14 +29,13 @@ // Standard include file for ADLC parser // +#include // standard library constants #include - +#include #include #include #include -#include -#include #include /* Make sure that we have the intptr_t and uintptr_t definitions */ @@ -86,14 +85,8 @@ typedef unsigned __int64 uintptr_t; // ADLC components #include "adlArena.hpp" -#include "opto/adlcVMDeps.hpp" -#include "filebuff.hpp" -#include "dict2.hpp" -#include "forms.hpp" -#include "formsopt.hpp" -#include "formssel.hpp" -#include "archDesc.hpp" #include "adlparse.hpp" +#include "archDesc.hpp" // globally define ArchDesc for convenience. Alternatively every form // could have a backpointer to the AD but it's too complicated to pass diff --git a/src/hotspot/share/adlc/adlparse.hpp b/src/hotspot/share/adlc/adlparse.hpp index e5873ef29c7..bf30527c1f7 100644 --- a/src/hotspot/share/adlc/adlparse.hpp +++ b/src/hotspot/share/adlc/adlparse.hpp @@ -25,6 +25,11 @@ #ifndef SHARE_ADLC_ADLPARSE_HPP #define SHARE_ADLC_ADLPARSE_HPP +#include "filebuff.hpp" +#include "forms.hpp" +#include "formsopt.hpp" +#include "formssel.hpp" + // ADLPARSE.HPP - Definitions for Architecture Description Language Parser // Authors: Chris Vick and Mike Paleczny diff --git a/src/hotspot/share/adlc/forms.hpp b/src/hotspot/share/adlc/forms.hpp index 0b673bf8542..b55cfc00428 100644 --- a/src/hotspot/share/adlc/forms.hpp +++ b/src/hotspot/share/adlc/forms.hpp @@ -25,6 +25,9 @@ #ifndef SHARE_ADLC_FORMS_HPP #define SHARE_ADLC_FORMS_HPP +#include "dict2.hpp" +#include "opto/adlcVMDeps.hpp" + // FORMS.HPP - ADL Parser Generic and Utility Forms Classes #define TRUE 1 diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index 82dea9b0141..d6cabf13d0c 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -166,6 +166,7 @@ bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: #if defined(AMD64) + case vmIntrinsics::_dsinh: case vmIntrinsics::_dtanh: case vmIntrinsics::_dcbrt: #endif diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index 1e4e07360cc..f910ecadc16 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -3296,6 +3296,7 @@ GraphBuilder::GraphBuilder(Compilation* compilation, IRScope* scope) case vmIntrinsics::_dsin : // fall through case vmIntrinsics::_dcos : // fall through case vmIntrinsics::_dtan : // fall through + case vmIntrinsics::_dsinh : // fall through case vmIntrinsics::_dtanh : // fall through case vmIntrinsics::_dcbrt : // fall through case vmIntrinsics::_dlog : // fall through diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index b3ce7242b4f..cbd84b3a11e 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -2865,6 +2865,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::_dsinh: // fall through case vmIntrinsics::_dtanh: // fall through case vmIntrinsics::_dsin : // fall through case vmIntrinsics::_dcos : // fall through diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index b230351c5e1..4a1969788f3 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -362,6 +362,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::dsinh()); FUNCTION_CASE(entry, StubRoutines::dtanh()); FUNCTION_CASE(entry, StubRoutines::dcbrt()); diff --git a/src/hotspot/share/cds/aotArtifactFinder.hpp b/src/hotspot/share/cds/aotArtifactFinder.hpp index 5d293f20af0..405222a8753 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.hpp +++ b/src/hotspot/share/cds/aotArtifactFinder.hpp @@ -63,6 +63,7 @@ class TypeArrayKlass; // be AOT-initialized: // - If we discover at least one instance of class X, then class X is AOT-initialized (** Note1). // - If AOTClassInitializer::can_archive_initialized_mirror(X) is true, then X is AOT-initialized. +// This function checks for the @jdk.internal.vm.annotation.AOTSafeClassInitializer annotation. // - For each AOT-initialized class, we scan all the static fields in its java mirror. This will in // turn discover more Klasses and java heap objects. // - The scanning continues until we reach a steady state. diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index db18a8ee1b5..0c8ea97fba0 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -37,67 +37,6 @@ DEBUG_ONLY(InstanceKlass* _aot_init_class = nullptr;) -// Detector for class names we wish to handle specially. -// It is either an exact string match or a string prefix match. -class AOTClassInitializer::AllowedSpec { - const char* _class_name; - bool _is_prefix; - int _len; -public: - AllowedSpec(const char* class_name, bool is_prefix = false) - : _class_name(class_name), _is_prefix(is_prefix) - { - _len = (class_name == nullptr) ? 0 : (int)strlen(class_name); - } - const char* class_name() { return _class_name; } - - bool matches(Symbol* name, int len) { - assert(_class_name != nullptr, "caller resp."); - if (_is_prefix) { - return len >= _len && name->starts_with(_class_name); - } else { - return len == _len && name->equals(_class_name); - } - } -}; - - -// Tell if ik has a name that matches one of the given specs. -bool AOTClassInitializer::is_allowed(AllowedSpec* specs, InstanceKlass* ik) { - Symbol* name = ik->name(); - int len = name->utf8_length(); - for (AllowedSpec* s = specs; s->class_name() != nullptr; s++) { - if (s->matches(name, len)) { - // If a type is included in the tables inside can_archive_initialized_mirror(), we require that - // - all super classes must be included - // - all super interfaces that have must be included. - // This ensures that in the production run, we don't run the of a supertype but skips - // ik's . - if (ik->java_super() != nullptr) { - DEBUG_ONLY(ResourceMark rm); - assert(AOTClassInitializer::can_archive_initialized_mirror(ik->java_super()), - "super class %s of %s must be aot-initialized", ik->java_super()->external_name(), - ik->external_name()); - } - - Array* interfaces = ik->local_interfaces(); - int len = interfaces->length(); - for (int i = 0; i < len; i++) { - InstanceKlass* intf = interfaces->at(i); - if (intf->class_initializer() != nullptr) { - assert(AOTClassInitializer::can_archive_initialized_mirror(intf), - "super interface %s (which has ) of %s must be aot-initialized", intf->external_name(), - ik->external_name()); - } - } - - return true; - } - } - return false; -} - - bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { assert(!ArchiveBuilder::is_active() || !ArchiveBuilder::current()->is_in_buffer_space(ik), "must be source klass"); if (!CDSConfig::is_initing_classes_at_dump_time()) { @@ -260,74 +199,19 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { // because of invokedynamic. They are few enough for now to be // manually tracked. There may be more in the future. - // IS_PREFIX means that we match all class names that start with a - // prefix. Otherwise, it is an exact match, of just one class name. - const bool IS_PREFIX = true; - { - static AllowedSpec specs[] = { + if (ik == vmClasses::Object_klass()) { // everybody's favorite super - {"java/lang/Object"}, - - {nullptr} - }; - if (is_allowed(specs, ik)) { return true; } } if (CDSConfig::is_dumping_method_handles()) { - // This table was created with the help of CDSHeapVerifier. + // The minimal list of @AOTSafeClassInitializer was created with the help of CDSHeapVerifier. // Also, some $Holder classes are needed. E.g., Invokers. explicitly // initializes Invokers$Holder. Since Invokers. won't be executed // at runtime, we need to make sure Invokers$Holder is also aot-inited. - // - // We hope we can reduce the size of this list over time, and move - // the responsibility for identifying such classes into the JDK - // code itself. See tracking RFE JDK-8342481. - static AllowedSpec indy_specs[] = { - {"java/lang/constant/ConstantDescs"}, - {"java/lang/constant/DynamicConstantDesc"}, - {"java/lang/invoke/BoundMethodHandle"}, - {"java/lang/invoke/BoundMethodHandle$Specializer"}, - {"java/lang/invoke/BoundMethodHandle$Species_", IS_PREFIX}, - {"java/lang/invoke/ClassSpecializer"}, - {"java/lang/invoke/ClassSpecializer$", IS_PREFIX}, - {"java/lang/invoke/DelegatingMethodHandle"}, - {"java/lang/invoke/DelegatingMethodHandle$Holder"}, // UNSAFE.ensureClassInitialized() - {"java/lang/invoke/DirectMethodHandle"}, - {"java/lang/invoke/DirectMethodHandle$Constructor"}, - {"java/lang/invoke/DirectMethodHandle$Holder"}, // UNSAFE.ensureClassInitialized() - {"java/lang/invoke/Invokers"}, - {"java/lang/invoke/Invokers$Holder"}, // UNSAFE.ensureClassInitialized() - {"java/lang/invoke/LambdaForm"}, - {"java/lang/invoke/LambdaForm$Holder"}, // UNSAFE.ensureClassInitialized() - {"java/lang/invoke/LambdaForm$NamedFunction"}, - {"java/lang/invoke/LambdaMetafactory"}, - {"java/lang/invoke/MethodHandle"}, - {"java/lang/invoke/MethodHandles"}, - {"java/lang/invoke/SimpleMethodHandle"}, - {"java/lang/invoke/StringConcatFactory"}, - {"java/lang/invoke/VarHandleGuards"}, - {"java/util/Collections"}, - {"java/util/stream/Collectors"}, - {"jdk/internal/constant/ConstantUtils"}, - {"jdk/internal/constant/PrimitiveClassDescImpl"}, - {"jdk/internal/constant/ReferenceClassDescImpl"}, - - // Can't include this, as it will pull in MethodHandleStatics which has many environment - // dependencies (on system properties, etc). - // MethodHandleStatics is an example of a class that must NOT get the aot-init treatment, - // because of its strong reliance on (a) final fields which are (b) environmentally determined. - //{"java/lang/invoke/InvokerBytecodeGenerator"}, - - {nullptr} - }; - if (is_allowed(indy_specs, ik)) { - return true; - } - - if (ik->name()->starts_with("java/lang/invoke/MethodHandleImpl")) { + if (ik->has_aot_safe_initializer()) { return true; } } @@ -341,17 +225,6 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { return false; } -// TODO: currently we have a hard-coded list. We should turn this into -// an annotation: @jdk.internal.vm.annotation.RuntimeSetupRequired -// See JDK-8342481. -bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) { - return ik == vmClasses::Class_klass() || - ik == vmClasses::internal_Unsafe_klass() || - ik == vmClasses::ConcurrentHashMap_klass() || - ik == vmClasses::MethodHandleImpl_klass() || - ik == vmClasses::Reference_klass(); -} - void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) { assert(ik->has_aot_initialized_mirror(), "sanity"); if (ik->is_runtime_setup_required()) { diff --git a/src/hotspot/share/cds/aotClassInitializer.hpp b/src/hotspot/share/cds/aotClassInitializer.hpp index d2f6d18ef3c..974bbeb903c 100644 --- a/src/hotspot/share/cds/aotClassInitializer.hpp +++ b/src/hotspot/share/cds/aotClassInitializer.hpp @@ -31,15 +31,11 @@ class InstanceKlass; class AOTClassInitializer : AllStatic { - class AllowedSpec; - static bool is_allowed(AllowedSpec* specs, InstanceKlass* ik); - public: // Called by heapShared.cpp to see if src_ik->java_mirror() can be archived in // the initialized state. static bool can_archive_initialized_mirror(InstanceKlass* src_ik); - static bool is_runtime_setup_required(InstanceKlass* ik); static void call_runtime_setup(JavaThread* current, InstanceKlass* ik); // Support for regression testing. Available in debug builds only. diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index b662c5a1b47..4119b9236e1 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -45,8 +45,8 @@ #include "utilities/formatBuffer.hpp" #include "utilities/stringUtils.hpp" -#include #include +#include Array* AOTClassLocationConfig::_dumptime_jar_files = nullptr; AOTClassLocationConfig* AOTClassLocationConfig::_dumptime_instance = nullptr; diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index 460788930a4..89a5e6bc939 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.hpp @@ -28,8 +28,8 @@ #include "memory/allocation.hpp" #include "oops/array.hpp" #include "utilities/exceptions.hpp" -#include "utilities/growableArray.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp index 89d9d5c2476..bab9e263a22 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp @@ -26,8 +26,8 @@ #define SHARE_CDS_AOTCONSTANTPOOLRESOLVER_HPP #include "interpreter/bytecodes.hpp" -#include "memory/allStatic.hpp" #include "memory/allocation.hpp" +#include "memory/allStatic.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/handles.hpp" #include "utilities/exceptions.hpp" diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 86fb5017eb8..0a8b0c4d537 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP #define SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP -#include "memory/allStatic.hpp" #include "memory/allocation.hpp" +#include "memory/allStatic.hpp" #include "runtime/handles.hpp" #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp index 93e84694c35..8fce6e31bb1 100644 --- a/src/hotspot/share/cds/archiveUtils.hpp +++ b/src/hotspot/share/cds/archiveUtils.hpp @@ -30,11 +30,11 @@ #include "logging/log.hpp" #include "memory/metaspace.hpp" #include "memory/virtualspace.hpp" +#include "runtime/nonJavaThread.hpp" +#include "runtime/semaphore.hpp" #include "utilities/bitMap.hpp" #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" -#include "runtime/nonJavaThread.hpp" -#include "runtime/semaphore.hpp" class BootstrapInfo; class ReservedSpace; diff --git a/src/hotspot/share/cds/cdsEnumKlass.cpp b/src/hotspot/share/cds/cdsEnumKlass.cpp index fe55c904312..e46a7eb84f4 100644 --- a/src/hotspot/share/cds/cdsEnumKlass.cpp +++ b/src/hotspot/share/cds/cdsEnumKlass.cpp @@ -25,8 +25,8 @@ #include "cds/archiveHeapLoader.hpp" #include "cds/cdsEnumKlass.hpp" #include "cds/heapShared.hpp" -#include "classfile/vmClasses.hpp" #include "classfile/systemDictionaryShared.hpp" +#include "classfile/vmClasses.hpp" #include "memory/resourceArea.hpp" #include "oops/fieldStreams.inline.hpp" #include "oops/oop.inline.hpp" diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 730902207f0..f4094aec1ac 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -121,6 +121,7 @@ \ product(ccstr, AOTCacheOutput, nullptr, \ "Specifies the file name for writing the AOT cache") \ + constraint(AOTCacheOutputConstraintFunc, AtParse) \ \ product(bool, AOTInvokeDynamicLinking, false, DIAGNOSTIC, \ "AOT-link JVM_CONSTANT_InvokeDynamic entries in cached " \ diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp index fe8b8d917be..319e4f6e95f 100644 --- a/src/hotspot/share/cds/cppVtables.cpp +++ b/src/hotspot/share/cds/cppVtables.cpp @@ -22,8 +22,8 @@ * */ -#include "cds/archiveUtils.hpp" #include "cds/archiveBuilder.hpp" +#include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" #include "cds/cppVtables.hpp" #include "cds/metaspaceShared.hpp" @@ -34,8 +34,8 @@ #include "oops/instanceStackChunkKlass.hpp" #include "oops/methodCounters.hpp" #include "oops/methodData.hpp" -#include "oops/trainingData.hpp" #include "oops/objArrayKlass.hpp" +#include "oops/trainingData.hpp" #include "oops/typeArrayKlass.hpp" #include "runtime/arguments.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp index 2d1f57175a0..90f35c61de1 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp @@ -29,8 +29,8 @@ #include "cds/dumpTimeClassInfo.hpp" #include "cds/cdsConfig.hpp" -#include "classfile/systemDictionaryShared.hpp" #include "classfile/classLoaderData.inline.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "oops/instanceKlass.hpp" #include "oops/klass.inline.hpp" #include "runtime/safepoint.hpp" diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 36afbc65ce3..397f9146afb 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -42,8 +42,8 @@ #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "gc/shared/collectedHeap.hpp" -#include "gc/shared/gcVMOperations.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcVMOperations.hpp" #include "jvm.h" #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 82a4ae7d9ba..a2b9e83ee68 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -55,9 +55,9 @@ #include "memory/universe.hpp" #include "nmt/memTracker.hpp" #include "oops/access.hpp" +#include "oops/compressedKlass.hpp" #include "oops/compressedOops.hpp" #include "oops/compressedOops.inline.hpp" -#include "oops/compressedKlass.hpp" #include "oops/objArrayOop.hpp" #include "oops/oop.inline.hpp" #include "oops/trainingData.hpp" @@ -80,8 +80,8 @@ #include "gc/g1/g1HeapRegion.hpp" #endif -# include -# include +#include +#include #ifndef O_BINARY // if defined (Win32) use binary files. #define O_BINARY 0 // otherwise do nothing. diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 3f624687e39..51572bcace7 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -505,9 +505,6 @@ bool HeapShared::is_archivable_hidden_klass(InstanceKlass* ik) { void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) { ik->set_has_aot_initialized_mirror(); - if (AOTClassInitializer::is_runtime_setup_required(ik)) { - ik->set_is_runtime_setup_required(); - } oop orig_mirror; if (RegeneratedClasses::is_regenerated_object(ik)) { diff --git a/src/hotspot/share/cds/lambdaFormInvokers.inline.hpp b/src/hotspot/share/cds/lambdaFormInvokers.inline.hpp index dddd0e36c47..09034debc13 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.inline.hpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.inline.hpp @@ -26,6 +26,7 @@ #define SHARE_CDS_LAMBDAFORMINVOKERS_INLINE_HPP #include "cds/lambdaFormInvokers.hpp" + #include "classfile/vmSymbols.hpp" inline bool LambdaFormInvokers::may_be_regenerated_class(Symbol* name) { diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 2f817ec3e71..5c97e6cd7fe 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -62,8 +62,8 @@ #include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "gc/shared/gcVMOperations.hpp" -#include "interpreter/bytecodeStream.hpp" #include "interpreter/bytecodes.hpp" +#include "interpreter/bytecodeStream.hpp" #include "jvm_io.h" #include "logging/log.hpp" #include "logging/logMessage.hpp" @@ -245,7 +245,7 @@ static bool shared_base_too_high(char* specified_base, char* aligned_base, size_ static char* compute_shared_base(size_t cds_max) { char* specified_base = (char*)SharedBaseAddress; size_t alignment = MetaspaceShared::core_region_alignment(); - if (UseCompressedClassPointers) { + if (UseCompressedClassPointers && CompressedKlassPointers::needs_class_space()) { alignment = MAX2(alignment, Metaspace::reserve_alignment()); } @@ -1603,8 +1603,7 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File // Set up compressed Klass pointer encoding: the encoding range must // cover both archive and class space. - const address encoding_base = (address)mapped_base_address; - const address klass_range_start = encoding_base + prot_zone_size; + const address klass_range_start = (address)mapped_base_address; const size_t klass_range_size = (address)class_space_rs.end() - klass_range_start; if (INCLUDE_CDS_JAVA_HEAP || UseCompactObjectHeaders) { // The CDS archive may contain narrow Klass IDs that were precomputed at archive generation time: @@ -1615,13 +1614,19 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File // mapping start (including protection zone), shift should be the shift used at archive generation time. CompressedKlassPointers::initialize_for_given_encoding( klass_range_start, klass_range_size, - encoding_base, ArchiveBuilder::precomputed_narrow_klass_shift() // precomputed encoding, see ArchiveBuilder + klass_range_start, ArchiveBuilder::precomputed_narrow_klass_shift() // precomputed encoding, see ArchiveBuilder ); + assert(CompressedKlassPointers::base() == klass_range_start, "must be"); } else { // Let JVM freely choose encoding base and shift CompressedKlassPointers::initialize(klass_range_start, klass_range_size); + assert(CompressedKlassPointers::base() == nullptr || + CompressedKlassPointers::base() == klass_range_start, "must be"); + } + // Establish protection zone, but only if we need one + if (CompressedKlassPointers::base() == klass_range_start) { + CompressedKlassPointers::establish_protection_zone(klass_range_start, prot_zone_size); } - CompressedKlassPointers::establish_protection_zone(encoding_base, prot_zone_size); // map_or_load_heap_region() compares the current narrow oop and klass encodings // with the archived ones, so it must be done after all encodings are determined. diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index cec1ae364d5..9aca20ee29f 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -77,8 +77,8 @@ #include "utilities/bitMap.inline.hpp" #include "utilities/checkedCast.hpp" #include "utilities/copy.hpp" -#include "utilities/formatBuffer.hpp" #include "utilities/exceptions.hpp" +#include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" @@ -941,6 +941,8 @@ public: _jdk_internal_ValueBased, _java_lang_Deprecated, _java_lang_Deprecated_for_removal, + _jdk_internal_vm_annotation_AOTSafeClassInitializer, + _method_AOTRuntimeSetup, _annotation_LIMIT }; const Location _location; @@ -976,6 +978,8 @@ public: void set_stable(bool stable) { set_annotation(_field_Stable); } bool is_stable() const { return has_annotation(_field_Stable); } + + bool has_aot_runtime_setup() const { return has_annotation(_method_AOTRuntimeSetup); } }; // This class also doubles as a holder for metadata cleanup. @@ -1896,6 +1900,16 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, case VM_SYMBOL_ENUM_NAME(java_lang_Deprecated): { return _java_lang_Deprecated; } + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTSafeClassInitializer_signature): { + if (_location != _in_class) break; // only allow for classes + if (!privileged) break; // only allow in privileged code + return _jdk_internal_vm_annotation_AOTSafeClassInitializer; + } + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTRuntimeSetup_signature): { + if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code + return _method_AOTRuntimeSetup; + } default: { break; } @@ -1975,6 +1989,9 @@ void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) { m->set_deprecated_for_removal(); } } + if (has_annotation(_jdk_internal_vm_annotation_AOTSafeClassInitializer)) { + ik->set_has_aot_safe_initializer(); + } } #define MAX_ARGS_SIZE 255 @@ -2661,6 +2678,13 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, if (is_hidden()) { // Mark methods in hidden classes as 'hidden'. m->set_is_hidden(); } + if (parsed_annotations.has_aot_runtime_setup()) { + if (name != vmSymbols::runtimeSetup() || signature != vmSymbols::void_method_signature() || + !access_flags.is_private() || !access_flags.is_static()) { + classfile_parse_error("@AOTRuntimeSetup method must be declared private static void runtimeSetup() for class %s", CHECK_NULL); + } + _has_aot_runtime_setup_method = true; + } // Copy annotations copy_method_annotations(m->constMethod(), @@ -3978,6 +4002,15 @@ void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) { const jint lh = Klass::instance_layout_helper(ik->size_helper(), true); ik->set_layout_helper(lh); } + + // Propagate the AOT runtimeSetup method discovery + if (_has_aot_runtime_setup_method) { + ik->set_is_runtime_setup_required(); + if (log_is_enabled(Info, aot, init)) { + ResourceMark rm; + log_info(aot, init)("Found @AOTRuntimeSetup class %s", ik->external_name()); + } + } } // utility methods for appending an array with check for duplicates @@ -5117,8 +5150,47 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, check_methods_for_intrinsics(ik, methods); // Fill in field values obtained by parse_classfile_attributes - if (_parsed_annotations->has_any_annotations()) { + if (_parsed_annotations->has_any_annotations()) _parsed_annotations->apply_to(ik); + + // AOT-related checks. + // Note we cannot check this in general due to instrumentation or module patching + if (CDSConfig::is_initing_classes_at_dump_time()) { + // Check the aot initialization safe status. + // @AOTSafeClassInitializer is used only to support ahead-of-time initialization of classes + // in the AOT assembly phase. + if (ik->has_aot_safe_initializer()) { + // If a type is included in the tables inside can_archive_initialized_mirror(), we require that + // - all super classes must be included + // - all super interfaces that have must be included. + // This ensures that in the production run, we don't run the of a supertype but skips + // ik's . + if (_super_klass != nullptr) { + guarantee_property(_super_klass->has_aot_safe_initializer(), + "Missing @AOTSafeClassInitializer in superclass %s for class %s", + _super_klass->external_name(), + CHECK); + } + + int len = _local_interfaces->length(); + for (int i = 0; i < len; i++) { + InstanceKlass* intf = _local_interfaces->at(i); + guarantee_property(intf->class_initializer() == nullptr || intf->has_aot_safe_initializer(), + "Missing @AOTSafeClassInitializer in superinterface %s for class %s", + intf->external_name(), + CHECK); + } + + if (log_is_enabled(Info, aot, init)) { + ResourceMark rm; + log_info(aot, init)("Found @AOTSafeClassInitializer class %s", ik->external_name()); + } + } else { + // @AOTRuntimeSetup only meaningful in @AOTClassInitializer + guarantee_property(!ik->is_runtime_setup_required(), + "@AOTRuntimeSetup meaningless in non-@AOTSafeClassInitializer class %s", + CHECK); + } } apply_parsed_class_attributes(ik); @@ -5326,6 +5398,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream, _has_localvariable_table(false), _has_final_method(false), _has_contended_fields(false), + _has_aot_runtime_setup_method(false), _has_finalizer(false), _has_empty_finalizer(false), _max_bootstrap_specifier_index(-1) { diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 707fbf6985f..2f5a1aef39f 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -192,6 +192,7 @@ class ClassFileParser { bool _has_localvariable_table; bool _has_final_method; bool _has_contended_fields; + bool _has_aot_runtime_setup_method; // precomputed flags bool _has_finalizer; diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 359970a3bcd..e5159662708 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -80,8 +80,8 @@ #include "utilities/ostream.hpp" #include "utilities/utf8.hpp" -#include #include +#include // Entry point in java.dll for path canonicalization diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index 4ba1c6c2196..6ef338f0f34 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -35,8 +35,9 @@ class SerializeClosure; class ClassLoaderDataShared : AllStatic { static bool _full_module_graph_loaded; - static void ensure_module_entry_table_exists(oop class_loader); + CDS_JAVA_HEAP_ONLY(static void ensure_module_entry_table_exists(oop class_loader);) public: +#if INCLUDE_CDS_JAVA_HEAP static void ensure_module_entry_tables_exist(); static void allocate_archived_tables(); static void iterate_symbols(MetaspaceClosure* closure); @@ -49,6 +50,7 @@ public: static void restore_java_system_loader_from_archive(ClassLoaderData* loader_data); static ModuleEntry* archived_boot_unnamed_module(); static ModuleEntry* archived_unnamed_module(ClassLoaderData* loader_data); +#endif // INCLUDE_CDS_JAVA_HEAP static bool is_full_module_graph_loaded() { return _full_module_graph_loaded; } }; diff --git a/src/hotspot/share/classfile/defaultMethods.cpp b/src/hotspot/share/classfile/defaultMethods.cpp index f84c3b65f5f..80de93261f4 100644 --- a/src/hotspot/share/classfile/defaultMethods.cpp +++ b/src/hotspot/share/classfile/defaultMethods.cpp @@ -35,13 +35,13 @@ #include "memory/metadataFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/klass.hpp" +#include "oops/method.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/signature.hpp" -#include "oops/instanceKlass.hpp" -#include "oops/klass.hpp" -#include "oops/method.hpp" #include "utilities/accessFlags.hpp" #include "utilities/exceptions.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/classfile/defaultMethods.hpp b/src/hotspot/share/classfile/defaultMethods.hpp index 2176488726d..c369cdc6cfd 100644 --- a/src/hotspot/share/classfile/defaultMethods.hpp +++ b/src/hotspot/share/classfile/defaultMethods.hpp @@ -26,8 +26,8 @@ #define SHARE_CLASSFILE_DEFAULTMETHODS_HPP #include "runtime/handles.hpp" -#include "utilities/growableArray.hpp" #include "utilities/exceptions.hpp" +#include "utilities/growableArray.hpp" class InstanceKlass; class Symbol; diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp index 03afe89f4f8..21f47e3de10 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp @@ -28,8 +28,8 @@ #include "memory/resourceArea.hpp" #include "oops/array.hpp" #include "oops/fieldStreams.inline.hpp" -#include "oops/instanceMirrorKlass.hpp" #include "oops/instanceKlass.inline.hpp" +#include "oops/instanceMirrorKlass.hpp" #include "oops/klass.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 2dcfc43898c..28bebee7d9a 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -55,15 +55,14 @@ #include "oops/fieldStreams.inline.hpp" #include "oops/instanceKlass.inline.hpp" #include "oops/instanceMirrorKlass.hpp" -#include "oops/klass.hpp" #include "oops/klass.inline.hpp" #include "oops/method.inline.hpp" #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" -#include "oops/oopCast.inline.hpp" #include "oops/oop.inline.hpp" -#include "oops/symbol.hpp" +#include "oops/oopCast.inline.hpp" #include "oops/recordComponent.hpp" +#include "oops/symbol.hpp" #include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" @@ -870,6 +869,7 @@ int java_lang_Class::_classRedefinedCount_offset; int java_lang_Class::_reflectionData_offset; int java_lang_Class::_modifiers_offset; int java_lang_Class::_is_primitive_offset; +int java_lang_Class::_raw_access_flags_offset; bool java_lang_Class::_offsets_computed = false; GrowableArray* java_lang_Class::_fixup_mirror_list = nullptr; @@ -1073,6 +1073,10 @@ void java_lang_Class::allocate_mirror(Klass* k, bool is_scratch, Handle protecti // Set the modifiers flag. u2 computed_modifiers = k->compute_modifier_flags(); set_modifiers(mirror(), computed_modifiers); + // Set the raw access_flags, this is used by reflection instead of modifier flags. + // The Java code for array classes gets the access flags from the element type. + assert(!k->is_array_klass() || k->access_flags().as_unsigned_short() == 0, "access flags are not set for arrays"); + set_raw_access_flags(mirror(), k->access_flags().as_unsigned_short()); InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); assert(oop_size(mirror()) == mk->instance_size(k), "should have been set"); @@ -1378,6 +1382,8 @@ oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, Basic assert(static_oop_field_count(java_class) == 0, "should have been zeroed by allocation"); #endif set_modifiers(java_class, JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); + set_raw_access_flags(java_class, JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); + set_is_primitive(java_class); return java_class; } @@ -1519,6 +1525,7 @@ oop java_lang_Class::primitive_mirror(BasicType t) { macro(_reflectionData_offset, k, "reflectionData", java_lang_ref_SoftReference_signature, false); \ macro(_signers_offset, k, "signers", object_array_signature, false); \ macro(_modifiers_offset, k, vmSymbols::modifiers_name(), char_signature, false); \ + macro(_raw_access_flags_offset, k, "classFileAccessFlags", char_signature, false); \ macro(_protection_domain_offset, k, "protectionDomain", java_security_ProtectionDomain_signature, false); \ macro(_is_primitive_offset, k, "primitive", bool_signature, false); @@ -1564,6 +1571,16 @@ void java_lang_Class::set_modifiers(oop the_class_mirror, u2 value) { the_class_mirror->char_field_put(_modifiers_offset, value); } +int java_lang_Class::raw_access_flags(oop the_class_mirror) { + assert(_raw_access_flags_offset != 0, "offsets should have been initialized"); + return the_class_mirror->char_field(_raw_access_flags_offset); +} + +void java_lang_Class::set_raw_access_flags(oop the_class_mirror, u2 value) { + assert(_raw_access_flags_offset != 0, "offsets should have been initialized"); + the_class_mirror->char_field_put(_raw_access_flags_offset, value); +} + // Note: JDK1.1 and before had a privateInfo_offset field which was used for the // platform thread structure, and a eetop offset which was used for thread diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 37ca22e9295..faa325d55dd 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -258,6 +258,7 @@ class java_lang_Class : AllStatic { static int _reflectionData_offset; static int _modifiers_offset; static int _is_primitive_offset; + static int _raw_access_flags_offset; static bool _offsets_computed; @@ -342,6 +343,9 @@ class java_lang_Class : AllStatic { static int modifiers(oop java_class); static void set_modifiers(oop java_class, u2 value); + static int raw_access_flags(oop java_class); + static void set_raw_access_flags(oop java_class, u2 value); + static size_t oop_size(oop java_class); static void set_oop_size(HeapWord* java_class, size_t size); static int static_oop_field_count(oop java_class); diff --git a/src/hotspot/share/classfile/klassFactory.cpp b/src/hotspot/share/classfile/klassFactory.cpp index f2f31de4a37..5dd2cd2385b 100644 --- a/src/hotspot/share/classfile/klassFactory.cpp +++ b/src/hotspot/share/classfile/klassFactory.cpp @@ -27,7 +27,6 @@ #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" #include "classfile/classLoader.hpp" -#include "classfile/classLoaderData.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoadInfo.hpp" #include "classfile/klassFactory.hpp" diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index c7b5a729451..132c1c4ca49 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -31,7 +31,6 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataShared.hpp" #include "classfile/javaAssertions.hpp" -#include "classfile/javaClasses.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/modules.hpp" diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp index 26af9a4936c..9a21578630f 100644 --- a/src/hotspot/share/classfile/packageEntry.cpp +++ b/src/hotspot/share/classfile/packageEntry.cpp @@ -34,8 +34,8 @@ #include "memory/resourceArea.hpp" #include "oops/array.hpp" #include "oops/symbol.hpp" -#include "runtime/java.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/java.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/classfile/packageEntry.hpp b/src/hotspot/share/classfile/packageEntry.hpp index 039c7f21fa6..aa572dc15b2 100644 --- a/src/hotspot/share/classfile/packageEntry.hpp +++ b/src/hotspot/share/classfile/packageEntry.hpp @@ -30,9 +30,9 @@ #include "oops/symbolHandle.hpp" #include "runtime/atomic.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" +#include "utilities/resourceHash.hpp" #if INCLUDE_JFR #include "jfr/support/jfrTraceIdExtension.hpp" #endif diff --git a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp index 6ee421fa023..b2f1ed40106 100644 --- a/src/hotspot/share/classfile/placeholders.cpp +++ b/src/hotspot/share/classfile/placeholders.cpp @@ -25,8 +25,8 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/placeholders.hpp" #include "logging/log.hpp" -#include "logging/logTag.hpp" #include "logging/logStream.hpp" +#include "logging/logTag.hpp" #include "memory/resourceArea.hpp" #include "oops/symbolHandle.hpp" #include "runtime/javaThread.hpp" diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index c3623dc25a1..f482a5bb303 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -60,8 +60,6 @@ #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" -#include "oops/oop.hpp" -#include "oops/oopHandle.hpp" #include "oops/oopHandle.inline.hpp" #include "oops/symbol.hpp" #include "oops/typeArrayKlass.hpp" diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index f863347c152..7dd3a7d1bb2 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -35,8 +35,8 @@ #include "cds/dynamicArchive.hpp" #include "cds/filemap.hpp" #include "cds/heapShared.hpp" -#include "cds/lambdaProxyClassDictionary.hpp" #include "cds/lambdaFormInvokers.inline.hpp" +#include "cds/lambdaProxyClassDictionary.hpp" #include "cds/metaspaceShared.hpp" #include "cds/runTimeClassInfo.hpp" #include "cds/unregisteredClasses.hpp" @@ -45,7 +45,6 @@ #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/dictionary.hpp" -#include "classfile/javaClasses.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 4343bd5f0e8..5f6dd055fd6 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -26,8 +26,8 @@ #define SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP #include "cds/cds_globals.hpp" -#include "cds/filemap.hpp" #include "cds/dumpTimeClassInfo.hpp" +#include "cds/filemap.hpp" #include "cds/runTimeClassInfo.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/packageEntry.hpp" diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 0f1468f0309..4c0a40b837d 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -26,8 +26,8 @@ #include "classfile/classFileStream.hpp" #include "classfile/classLoader.hpp" #include "classfile/javaClasses.hpp" -#include "classfile/stackMapTable.hpp" #include "classfile/stackMapFrame.hpp" +#include "classfile/stackMapTable.hpp" #include "classfile/stackMapTableFormat.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index dd28e1a898c..f9b12df84ca 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -27,12 +27,12 @@ #include "compiler/compilerDirectives.hpp" #include "jvm_constants.h" #include "jvm_io.h" -#ifdef COMPILER2 -#include "opto/c2_globals.hpp" -#endif #include "runtime/vm_version.hpp" #include "utilities/checkedCast.hpp" #include "utilities/tribool.hpp" +#ifdef COMPILER2 +#include "opto/c2_globals.hpp" +#endif // These are flag-matching functions: inline bool match_F_R(u2 flags) { @@ -91,6 +91,7 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dsinh: case vmIntrinsics::_dtanh: case vmIntrinsics::_dcbrt: case vmIntrinsics::_dlog: @@ -144,6 +145,7 @@ bool vmIntrinsics::can_trap(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dsinh: case vmIntrinsics::_dtanh: case vmIntrinsics::_dcbrt: case vmIntrinsics::_dlog: @@ -314,6 +316,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_fmaF: if (!InlineMathNatives || !UseFMA) return true; break; + case vmIntrinsics::_dsinh: case vmIntrinsics::_dtanh: case vmIntrinsics::_dcbrt: if (!InlineMathNatives || !InlineIntrinsics) return true; diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 6699d99e95b..3d110c5706b 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -27,8 +27,8 @@ #include "jfr/support/jfrIntrinsics.hpp" #include "memory/allStatic.hpp" -#include "utilities/globalDefinitions.hpp" #include "utilities/enumIterator.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/vmEnums.hpp" class Method; @@ -135,7 +135,8 @@ 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(tanh_name,"tanh") do_name(cbrt_name,"cbrt") \ + do_name(round_name, "round") do_name(sinh_name,"sinh") do_name(tanh_name,"tanh") \ + do_name(cbrt_name,"cbrt") \ \ do_name(addExact_name,"addExact") \ do_name(decrementExact_name,"decrementExact") \ @@ -161,6 +162,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(_dsinh, java_lang_Math, sinh_name, double_double_signature, F_S) \ do_intrinsic(_dtanh, java_lang_Math, tanh_name, double_double_signature, F_S) \ do_intrinsic(_dcbrt, java_lang_Math, cbrt_name, double_double_signature, F_S) \ do_intrinsic(_dsqrt, java_lang_Math, sqrt_name, double_double_signature, F_S) \ @@ -314,8 +316,6 @@ class methodHandle; do_intrinsic(_Class_cast, java_lang_Class, Class_cast_name, object_object_signature, F_R) \ do_name( Class_cast_name, "cast") \ \ - do_intrinsic(_getClassAccessFlags, reflect_Reflection, getClassAccessFlags_name, class_int_signature, F_SN) \ - do_name( getClassAccessFlags_name, "getClassAccessFlags") \ do_intrinsic(_getLength, java_lang_reflect_Array, getLength_name, object_int_signature, F_SN) \ do_name( getLength_name, "getLength") \ \ diff --git a/src/hotspot/share/classfile/vmSymbols.cpp b/src/hotspot/share/classfile/vmSymbols.cpp index 3dfeebf7ea1..bd32eac4f34 100644 --- a/src/hotspot/share/classfile/vmSymbols.cpp +++ b/src/hotspot/share/classfile/vmSymbols.cpp @@ -29,8 +29,8 @@ #include "compiler/compilerDirectives.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" -#include "memory/oopFactory.hpp" #include "memory/metaspaceClosure.hpp" +#include "memory/oopFactory.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/signature.hpp" diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index c562e3e6ccf..06f27f09c5c 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -29,8 +29,8 @@ #include "jvmci/vmSymbols_jvmci.hpp" #include "memory/iterator.hpp" #include "oops/symbol.hpp" -#include "utilities/macros.hpp" #include "utilities/enumIterator.hpp" +#include "utilities/macros.hpp" class SerializeClosure; @@ -732,8 +732,10 @@ class SerializeClosure; template(java_lang_invoke_DelegatingMethodHandle_Holder, "java/lang/invoke/DelegatingMethodHandle$Holder") \ template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \ template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \ + template(jdk_internal_vm_annotation_AOTSafeClassInitializer_signature, "Ljdk/internal/vm/annotation/AOTSafeClassInitializer;")\ template(java_util_concurrent_ConcurrentHashMap, "java/util/concurrent/ConcurrentHashMap") \ template(java_util_ArrayList, "java/util/ArrayList") \ + template(jdk_internal_vm_annotation_AOTRuntimeSetup_signature, "Ljdk/internal/vm/annotation/AOTRuntimeSetup;") \ template(runtimeSetup, "runtimeSetup") \ template(toFileURL_name, "toFileURL") \ template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 4c080b90acc..df2e9648b84 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -61,8 +61,8 @@ #include "gc/z/zBarrierSetRuntime.hpp" #endif -#include #include +#include const char* aot_code_entry_kind_name[] = { #define DECL_KIND_STRING(kind) XSTR(kind), @@ -799,7 +799,7 @@ bool AOTCodeCache::finish_write() { //------------------Store/Load AOT code ---------------------- -bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name, int entry_offset_count, int* entry_offsets) { +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name) { AOTCodeCache* cache = open_for_dump(); if (cache == nullptr) { return false; @@ -883,18 +883,6 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind return false; } - // Write entries offsets - n = cache->write_bytes(&entry_offset_count, sizeof(int)); - if (n != sizeof(int)) { - return false; - } - for (int i = 0; i < entry_offset_count; i++) { - uint32_t off = (uint32_t)entry_offsets[i]; - n = cache->write_bytes(&off, sizeof(uint32_t)); - if (n != sizeof(uint32_t)) { - return false; - } - } uint entry_size = cache->_write_position - entry_position; AOTCodeEntry* entry = new(cache) AOTCodeEntry(entry_kind, encode_id(entry_kind, id), entry_position, entry_size, name_offset, name_size, @@ -903,13 +891,13 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind return true; } -bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id, int entry_offset_count, int* entry_offsets) { +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id) { assert(AOTCodeEntry::is_blob(entry_kind), "wrong entry kind for blob id %s", StubInfo::name(id)); - return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id), entry_offset_count, entry_offsets); + return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id)); } -CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name, int entry_offset_count, int* entry_offsets) { +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name) { AOTCodeCache* cache = open_for_use(); if (cache == nullptr) { return nullptr; @@ -929,20 +917,20 @@ CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, c return nullptr; } AOTCodeReader reader(cache, entry); - CodeBlob* blob = reader.compile_code_blob(name, entry_offset_count, entry_offsets); + CodeBlob* blob = reader.compile_code_blob(name); log_debug(aot, codecache, stubs)("%sRead blob '%s' (id=%u, kind=%s) from AOT Code Cache", (blob == nullptr? "Failed to " : ""), name, id, aot_code_entry_kind_name[entry_kind]); return blob; } -CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id, int entry_offset_count, int* entry_offsets) { +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id) { assert(AOTCodeEntry::is_blob(entry_kind), "wrong entry kind for blob id %s", StubInfo::name(id)); - return load_code_blob(entry_kind, (uint)id, StubInfo::name(id), entry_offset_count, entry_offsets); + return load_code_blob(entry_kind, (uint)id, StubInfo::name(id)); } -CodeBlob* AOTCodeReader::compile_code_blob(const char* name, int entry_offset_count, int* entry_offsets) { +CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { uint entry_position = _entry->offset(); // Read name @@ -989,21 +977,6 @@ CodeBlob* AOTCodeReader::compile_code_blob(const char* name, int entry_offset_co fix_relocations(code_blob); - // Read entries offsets - offset = read_position(); - int stored_count = *(int*)addr(offset); - assert(stored_count == entry_offset_count, "entry offset count mismatch, count in AOT code cache=%d, expected=%d", stored_count, entry_offset_count); - offset += sizeof(int); - set_read_position(offset); - for (int i = 0; i < stored_count; i++) { - uint32_t off = *(uint32_t*)addr(offset); - offset += sizeof(uint32_t); - const char* entry_name = (_entry->kind() == AOTCodeEntry::Adapter) ? AdapterHandlerEntry::entry_name(i) : ""; - log_trace(aot, codecache, stubs)("Reading adapter '%s:%s' (0x%x) offset: 0x%x from AOT Code Cache", - stored_name, entry_name, _entry->id(), off); - entry_offsets[i] = off; - } - #ifdef ASSERT LogStreamHandle(Trace, aot, codecache, stubs) log; if (log.is_enabled()) { diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 69fd7549b43..778ad34e448 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -332,26 +332,18 @@ public: // save and restore API for non-enumerable code blobs static bool store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, - uint id, const char* name, - int entry_offset_count = 0, - int* entry_offsets = nullptr) NOT_CDS_RETURN_(false); + uint id, const char* name) NOT_CDS_RETURN_(false); static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, - uint id, const char* name, - int entry_offset_count = 0, - int* entry_offsets = nullptr) NOT_CDS_RETURN_(nullptr); + uint id, const char* name) NOT_CDS_RETURN_(nullptr); // save and restore API for enumerable code blobs static bool store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, - BlobId id, - int entry_offset_count = 0, - int* entry_offsets = nullptr) NOT_CDS_RETURN_(false); + BlobId id) NOT_CDS_RETURN_(false); static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, - BlobId id, - int entry_offset_count = 0, - int* entry_offsets = nullptr) NOT_CDS_RETURN_(nullptr); + BlobId id) NOT_CDS_RETURN_(nullptr); static uint store_entries_cnt() { if (is_on_for_dump()) { @@ -414,7 +406,7 @@ private: public: AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry); - CodeBlob* compile_code_blob(const char* name, int entry_offset_count, int* entry_offsets); + CodeBlob* compile_code_blob(const char* name); ImmutableOopMapSet* read_oop_map_set(); diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index cf21f1f89a4..5b87e8c5670 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -389,8 +389,8 @@ void RuntimeBlob::trace_new_stub(RuntimeBlob* stub, const char* name1, const cha //---------------------------------------------------------------------------------------------------- // Implementation of BufferBlob -BufferBlob::BufferBlob(const char* name, CodeBlobKind kind, int size) -: RuntimeBlob(name, kind, size, sizeof(BufferBlob)) +BufferBlob::BufferBlob(const char* name, CodeBlobKind kind, int size, uint16_t header_size) +: RuntimeBlob(name, kind, size, header_size) {} BufferBlob* BufferBlob::create(const char* name, uint buffer_size) { @@ -413,8 +413,8 @@ BufferBlob* BufferBlob::create(const char* name, uint buffer_size) { } -BufferBlob::BufferBlob(const char* name, CodeBlobKind kind, CodeBuffer* cb, int size) - : RuntimeBlob(name, kind, cb, size, sizeof(BufferBlob), CodeOffsets::frame_never_safe, 0, nullptr) +BufferBlob::BufferBlob(const char* name, CodeBlobKind kind, CodeBuffer* cb, int size, uint16_t header_size) + : RuntimeBlob(name, kind, cb, size, header_size, CodeOffsets::frame_never_safe, 0, nullptr) {} // Used by gtest @@ -446,12 +446,20 @@ void BufferBlob::free(BufferBlob *blob) { //---------------------------------------------------------------------------------------------------- // Implementation of AdapterBlob -AdapterBlob::AdapterBlob(int size, CodeBuffer* cb) : - BufferBlob("I2C/C2I adapters", CodeBlobKind::Adapter, cb, size) { +AdapterBlob::AdapterBlob(int size, CodeBuffer* cb, int entry_offset[AdapterBlob::ENTRY_COUNT]) : + BufferBlob("I2C/C2I adapters", CodeBlobKind::Adapter, cb, size, sizeof(AdapterBlob)) { + assert(entry_offset[0] == 0, "sanity check"); + for (int i = 1; i < AdapterBlob::ENTRY_COUNT; i++) { + assert(entry_offset[i] > 0 && entry_offset[i] < cb->insts()->size(), + "invalid entry offset 0x%x", entry_offset[i]); + } + _c2i_offset = entry_offset[1]; + _c2i_unverified_offset = entry_offset[2]; + _c2i_no_clinit_check_offset = entry_offset[3]; CodeCache::commit(this); } -AdapterBlob* AdapterBlob::create(CodeBuffer* cb) { +AdapterBlob* AdapterBlob::create(CodeBuffer* cb, int entry_offset[AdapterBlob::ENTRY_COUNT]) { ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock CodeCache::gc_on_allocation(); @@ -460,7 +468,7 @@ AdapterBlob* AdapterBlob::create(CodeBuffer* cb) { unsigned int size = CodeBlob::allocation_size(cb, sizeof(AdapterBlob)); { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - blob = new (size) AdapterBlob(size, cb); + blob = new (size) AdapterBlob(size, cb, entry_offset); } // Track memory usage statistic after releasing CodeCache_lock MemoryService::track_code_cache_memory_usage(); @@ -468,6 +476,13 @@ AdapterBlob* AdapterBlob::create(CodeBuffer* cb) { return blob; } +void AdapterBlob::get_offsets(int entry_offset[ENTRY_COUNT]) { + entry_offset[0] = 0; + entry_offset[1] = _c2i_offset; + entry_offset[2] = _c2i_unverified_offset; + entry_offset[3] = _c2i_no_clinit_check_offset; +} + //---------------------------------------------------------------------------------------------------- // Implementation of VtableBlob diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 98cc13b3859..407974f0428 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -28,9 +28,9 @@ #include "asm/codeBuffer.hpp" #include "compiler/compilerDefinitions.hpp" #include "compiler/oopMap.hpp" -#include "runtime/javaFrameAnchor.hpp" #include "runtime/frame.hpp" #include "runtime/handles.hpp" +#include "runtime/javaFrameAnchor.hpp" #include "utilities/align.hpp" #include "utilities/macros.hpp" @@ -372,8 +372,8 @@ class BufferBlob: public RuntimeBlob { private: // Creation support - BufferBlob(const char* name, CodeBlobKind kind, int size); - BufferBlob(const char* name, CodeBlobKind kind, CodeBuffer* cb, int size); + BufferBlob(const char* name, CodeBlobKind kind, int size, uint16_t header_size = sizeof(BufferBlob)); + BufferBlob(const char* name, CodeBlobKind kind, CodeBuffer* cb, int size, uint16_t header_size = sizeof(BufferBlob)); void* operator new(size_t s, unsigned size) throw(); @@ -404,12 +404,18 @@ class BufferBlob: public RuntimeBlob { // AdapterBlob: used to hold C2I/I2C adapters class AdapterBlob: public BufferBlob { +public: + static const int ENTRY_COUNT = 4; private: - AdapterBlob(int size, CodeBuffer* cb); - + AdapterBlob(int size, CodeBuffer* cb, int entry_offset[ENTRY_COUNT]); + // _i2c_offset is always 0 so no need to store it + int _c2i_offset; + int _c2i_unverified_offset; + int _c2i_no_clinit_check_offset; public: // Creation - static AdapterBlob* create(CodeBuffer* cb); + static AdapterBlob* create(CodeBuffer* cb, int entry_offset[ENTRY_COUNT]); + void get_offsets(int entry_offset[ENTRY_COUNT]); }; //--------------------------------------------------------------------------------------------------- diff --git a/src/hotspot/share/code/debugInfo.cpp b/src/hotspot/share/code/debugInfo.cpp index 9e895ecf152..b6f58908c2c 100644 --- a/src/hotspot/share/code/debugInfo.cpp +++ b/src/hotspot/share/code/debugInfo.cpp @@ -28,11 +28,11 @@ #include "gc/shared/collectedHeap.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" -#include "runtime/stackValue.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/stackValue.hpp" // Constructors diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index 7f925388eb0..d90695739a1 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -29,16 +29,15 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" #include "code/dependencies.hpp" -#include "compiler/compileLog.hpp" #include "compiler/compileBroker.hpp" +#include "compiler/compileLog.hpp" #include "compiler/compileTask.hpp" #include "memory/resourceArea.hpp" #include "oops/klass.hpp" -#include "oops/oop.inline.hpp" #include "oops/method.inline.hpp" #include "oops/objArrayKlass.hpp" +#include "oops/oop.inline.hpp" #include "runtime/flags/flagSetting.hpp" -#include "runtime/handles.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/jniHandles.inline.hpp" diff --git a/src/hotspot/share/code/dependencyContext.cpp b/src/hotspot/share/code/dependencyContext.cpp index a8ef707978d..6c56ae20ddf 100644 --- a/src/hotspot/share/code/dependencyContext.cpp +++ b/src/hotspot/share/code/dependencyContext.cpp @@ -22,9 +22,9 @@ * */ -#include "code/nmethod.hpp" #include "code/dependencies.hpp" #include "code/dependencyContext.hpp" +#include "code/nmethod.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index c062919f61e..d8cbf6b9e66 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -33,9 +33,9 @@ #include "compiler/compilationLog.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compileLog.hpp" -#include "compiler/compileTask.hpp" #include "compiler/compilerDirectives.hpp" #include "compiler/compilerOracle.hpp" +#include "compiler/compileTask.hpp" #include "compiler/directivesParser.hpp" #include "compiler/disassembler.hpp" #include "compiler/oopMap.inline.hpp" @@ -59,8 +59,8 @@ #include "prims/jvmtiImpl.hpp" #include "prims/jvmtiThreadState.hpp" #include "prims/methodHandles.hpp" -#include "runtime/continuation.hpp" #include "runtime/atomic.hpp" +#include "runtime/continuation.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index c44234fa11c..7f2916ae895 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -249,7 +249,7 @@ HeapWord* G1Allocator::survivor_attempt_allocation(uint node_index, desired_word_size, actual_word_size); if (result == nullptr && !survivor_is_full()) { - MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1FreeList_lock, Mutex::_no_safepoint_check_flag); // Multiple threads may have queued at the FreeList_lock above after checking whether there // actually is still memory available. Redo the check under the lock to avoid unnecessary work; // the memory may have been used up as the threads waited to acquire the lock. @@ -278,7 +278,7 @@ HeapWord* G1Allocator::old_attempt_allocation(size_t min_word_size, desired_word_size, actual_word_size); if (result == nullptr && !old_is_full()) { - MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1FreeList_lock, Mutex::_no_safepoint_check_flag); // Multiple threads may have queued at the FreeList_lock above after checking whether there // actually is still memory available. Redo the check under the lock to avoid unnecessary work; // the memory may have been used up as the threads waited to acquire the lock. diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index cb4baf078ee..e5266a527f0 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -772,7 +772,7 @@ void G1CollectedHeap::prepare_heap_for_full_collection() { // set between the last GC or pause and now. We need to clear the // incremental collection set and then start rebuilding it afresh // after this full GC. - abandon_collection_set(collection_set()); + abandon_collection_set(); _hrm.remove_all_free_regions(); } @@ -848,12 +848,9 @@ void G1CollectedHeap::do_full_collection(bool clear_all_soft_refs, size_t allocation_word_size) { assert_at_safepoint_on_vm_thread(); - const bool do_clear_all_soft_refs = clear_all_soft_refs || - soft_ref_policy()->should_clear_all_soft_refs(); - G1FullGCMark gc_mark; GCTraceTime(Info, gc) tm("Pause Full", nullptr, gc_cause(), true); - G1FullCollector collector(this, do_clear_all_soft_refs, do_maximal_compaction, gc_mark.tracer()); + G1FullCollector collector(this, clear_all_soft_refs, do_maximal_compaction, gc_mark.tracer()); collector.prepare_collection(); collector.collect(); @@ -959,7 +956,7 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { HeapWord* result = satisfy_failed_allocation_helper(word_size, true, /* do_gc */ - false, /* maximum_collection */ + false, /* maximal_compaction */ false /* expect_null_mutator_alloc_region */); if (result != nullptr) { @@ -969,7 +966,7 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { // Attempts to allocate followed by Full GC that will collect all soft references. result = satisfy_failed_allocation_helper(word_size, true, /* do_gc */ - true, /* maximum_collection */ + true, /* maximal_compaction */ true /* expect_null_mutator_alloc_region */); if (result != nullptr) { @@ -979,16 +976,13 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) { // Attempts to allocate, no GC result = satisfy_failed_allocation_helper(word_size, false, /* do_gc */ - false, /* maximum_collection */ + false, /* maximal_compaction */ true /* expect_null_mutator_alloc_region */); if (result != nullptr) { return result; } - assert(!soft_ref_policy()->should_clear_all_soft_refs(), - "Flag should have been handled and cleared prior to this point"); - // What else? We might try synchronous finalization later. If the total // space available is large enough for the allocation, then a more // complete compaction phase than we've tried so far might be @@ -1134,7 +1128,7 @@ public: if (SafepointSynchronize::is_at_safepoint()) { guarantee(Thread::current()->is_VM_thread() || - FreeList_lock->owned_by_self() || OldSets_lock->owned_by_self(), + G1FreeList_lock->owned_by_self() || G1OldSets_lock->owned_by_self(), "master old set MT safety protocol at a safepoint"); } else { guarantee(Heap_lock->owned_by_self(), "master old set MT safety protocol outside a safepoint"); @@ -1157,7 +1151,7 @@ public: if (SafepointSynchronize::is_at_safepoint()) { guarantee(Thread::current()->is_VM_thread() || - OldSets_lock->owned_by_self(), + G1OldSets_lock->owned_by_self(), "master humongous set MT safety protocol at a safepoint"); } else { guarantee(Heap_lock->owned_by_self(), @@ -2386,7 +2380,7 @@ HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size, void G1CollectedHeap::start_concurrent_cycle(bool concurrent_operation_is_full_mark) { assert(!_cm_thread->in_progress(), "Can not start concurrent operation while in progress"); - MutexLocker x(CGC_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1CGC_lock, Mutex::_no_safepoint_check_flag); if (concurrent_operation_is_full_mark) { _cm->post_concurrent_mark_start(); _cm_thread->start_full_mark(); @@ -2394,7 +2388,7 @@ void G1CollectedHeap::start_concurrent_cycle(bool concurrent_operation_is_full_m _cm->post_concurrent_undo_start(); _cm_thread->start_undo_mark(); } - CGC_lock->notify(); + G1CGC_lock->notify(); } bool G1CollectedHeap::is_potential_eager_reclaim_candidate(G1HeapRegion* r) const { @@ -2748,7 +2742,7 @@ void G1CollectedHeap::free_humongous_region(G1HeapRegion* hr, void G1CollectedHeap::remove_from_old_gen_sets(const uint old_regions_removed, const uint humongous_regions_removed) { if (old_regions_removed > 0 || humongous_regions_removed > 0) { - MutexLocker x(OldSets_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1OldSets_lock, Mutex::_no_safepoint_check_flag); _old_set.bulk_remove(old_regions_removed); _humongous_set.bulk_remove(humongous_regions_removed); } @@ -2758,7 +2752,7 @@ void G1CollectedHeap::remove_from_old_gen_sets(const uint old_regions_removed, void G1CollectedHeap::prepend_to_freelist(G1FreeRegionList* list) { assert(list != nullptr, "list can't be null"); if (!list->is_empty()) { - MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1FreeList_lock, Mutex::_no_safepoint_check_flag); _hrm.insert_list_into_free_list(list); } } @@ -2791,24 +2785,22 @@ public: } }; -void G1CollectedHeap::abandon_collection_set(G1CollectionSet* collection_set) { +void G1CollectedHeap::abandon_collection_set() { G1AbandonCollectionSetClosure cl; collection_set_iterate_all(&cl); - collection_set->clear(); - collection_set->stop_incremental_building(); + collection_set()->clear(); + collection_set()->stop_incremental_building(); + + collection_set()->abandon_all_candidates(); + + young_regions_cset_group()->clear(); } bool G1CollectedHeap::is_old_gc_alloc_region(G1HeapRegion* hr) { return _allocator->is_retained_old_region(hr); } -void G1CollectedHeap::set_region_short_lived_locked(G1HeapRegion* hr) { - _eden.add(hr); - _policy->set_region_eden(hr); - young_regions_cset_group()->add(hr); -} - #ifdef ASSERT class NoYoungRegionsClosure: public G1HeapRegionClosure { @@ -2957,9 +2949,13 @@ G1HeapRegion* G1CollectedHeap::new_mutator_alloc_region(size_t word_size, false /* do_expand */, node_index); if (new_alloc_region != nullptr) { - set_region_short_lived_locked(new_alloc_region); - G1HeapRegionPrinter::alloc(new_alloc_region); + new_alloc_region->set_eden(); + _eden.add(new_alloc_region); + _policy->set_region_eden(new_alloc_region); _policy->remset_tracker()->update_at_allocate(new_alloc_region); + // Install the group cardset. + young_regions_cset_group()->add(new_alloc_region); + G1HeapRegionPrinter::alloc(new_alloc_region); return new_alloc_region; } } @@ -2993,7 +2989,7 @@ bool G1CollectedHeap::has_more_regions(G1HeapRegionAttr dest) { } G1HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegionAttr dest, uint node_index) { - assert(FreeList_lock->owned_by_self(), "pre-condition"); + assert(G1FreeList_lock->owned_by_self(), "pre-condition"); if (!has_more_regions(dest)) { return nullptr; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 90e0ea8608a..49bbcf888be 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -804,7 +804,7 @@ public: // Abandon the current collection set without recording policy // statistics or updating free lists. - void abandon_collection_set(G1CollectionSet* collection_set); + void abandon_collection_set(); // The concurrent marker (and the thread it runs in.) G1ConcurrentMark* _cm; @@ -1214,7 +1214,6 @@ public: return named_heap(CollectedHeap::G1); } - void set_region_short_lived_locked(G1HeapRegion* hr); // add appropriate methods for any other surv rate groups G1SurvivorRegions* survivor() { return &_survivor; } diff --git a/src/hotspot/share/gc/g1/g1CommittedRegionMap.cpp b/src/hotspot/share/gc/g1/g1CommittedRegionMap.cpp index d8454cd5cf8..9e5b2fda5c9 100644 --- a/src/hotspot/share/gc/g1/g1CommittedRegionMap.cpp +++ b/src/hotspot/share/gc/g1/g1CommittedRegionMap.cpp @@ -183,7 +183,7 @@ void G1CommittedRegionMap::guarantee_mt_safety_active() const { if (SafepointSynchronize::is_at_safepoint()) { guarantee(Thread::current()->is_VM_thread() || - FreeList_lock->owned_by_self(), + G1FreeList_lock->owned_by_self(), "G1CommittedRegionMap _active-map MT safety protocol at a safepoint"); } else { guarantee(Heap_lock->owned_by_self(), @@ -204,10 +204,10 @@ void G1CommittedRegionMap::guarantee_mt_safety_inactive() const { if (SafepointSynchronize::is_at_safepoint()) { guarantee(Thread::current()->is_VM_thread() || - FreeList_lock->owned_by_self(), + G1FreeList_lock->owned_by_self(), "G1CommittedRegionMap MT safety protocol at a safepoint"); } else { - guarantee(Uncommit_lock->owned_by_self(), + guarantee(G1Uncommit_lock->owned_by_self(), "G1CommittedRegionMap MT safety protocol outside a safepoint"); } } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 9c1282311f7..c5b8c89e2f0 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -164,7 +164,7 @@ G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::ChunkAllocator::allocate_new_ return nullptr; } - MutexLocker x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); if (Atomic::load_acquire(&_buckets[bucket]) == nullptr) { size_t desired_capacity = bucket_size(bucket) * 2; if (!try_expand_to(desired_capacity)) { @@ -293,13 +293,13 @@ void G1CMMarkStack::add_chunk_to_list(TaskQueueEntryChunk* volatile* list, TaskQ } void G1CMMarkStack::add_chunk_to_chunk_list(TaskQueueEntryChunk* elem) { - MutexLocker x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); add_chunk_to_list(&_chunk_list, elem); _chunks_in_chunk_list++; } void G1CMMarkStack::add_chunk_to_free_list(TaskQueueEntryChunk* elem) { - MutexLocker x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag); add_chunk_to_list(&_free_list, elem); } @@ -312,7 +312,7 @@ G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_list(TaskQu } G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_chunk_list() { - MutexLocker x(MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); TaskQueueEntryChunk* result = remove_chunk_from_list(&_chunk_list); if (result != nullptr) { _chunks_in_chunk_list--; @@ -321,7 +321,7 @@ G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_chunk_list( } G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_free_list() { - MutexLocker x(MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1MarkStackFreeList_lock, Mutex::_no_safepoint_check_flag); return remove_chunk_from_list(&_free_list); } @@ -432,9 +432,9 @@ bool G1CMRootMemRegions::contains(const MemRegion mr) const { } void G1CMRootMemRegions::notify_scan_done() { - MutexLocker x(RootRegionScan_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1RootRegionScan_lock, Mutex::_no_safepoint_check_flag); _scan_in_progress = false; - RootRegionScan_lock->notify_all(); + G1RootRegionScan_lock->notify_all(); } void G1CMRootMemRegions::cancel_scan() { @@ -459,7 +459,7 @@ bool G1CMRootMemRegions::wait_until_scan_finished() { } { - MonitorLocker ml(RootRegionScan_lock, Mutex::_no_safepoint_check_flag); + MonitorLocker ml(G1RootRegionScan_lock, Mutex::_no_safepoint_check_flag); while (scan_in_progress()) { ml.wait(); } @@ -517,7 +517,7 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)), _needs_remembered_set_rebuild(false) { - assert(CGC_lock != nullptr, "CGC_lock must be initialized"); + assert(G1CGC_lock != nullptr, "CGC_lock must be initialized"); _mark_bitmap.initialize(g1h->reserved(), bitmap_storage); @@ -1221,21 +1221,25 @@ class G1UpdateRegionLivenessAndSelectForRebuildTask : public WorkerTask { _num_humongous_regions_removed(0), _local_cleanup_list(local_cleanup_list) {} - void reclaim_empty_humongous_region(G1HeapRegion* hr) { + void reclaim_empty_region(G1HeapRegion* hr) { assert(!hr->has_pinned_objects(), "precondition"); + assert(hr->used() > 0, "precondition"); + + _freed_bytes += hr->used(); + hr->set_containing_set(nullptr); + hr->clear_cardtable(); + _cm->clear_statistics(hr); + G1HeapRegionPrinter::mark_reclaim(hr); + } + + void reclaim_empty_humongous_region(G1HeapRegion* hr) { assert(hr->is_starts_humongous(), "precondition"); auto on_humongous_region = [&] (G1HeapRegion* hr) { - assert(hr->used() > 0, "precondition"); - assert(!hr->has_pinned_objects(), "precondition"); assert(hr->is_humongous(), "precondition"); + reclaim_empty_region(hr); _num_humongous_regions_removed++; - _freed_bytes += hr->used(); - hr->set_containing_set(nullptr); - hr->clear_cardtable(); - _g1h->concurrent_mark()->clear_statistics(hr); - G1HeapRegionPrinter::mark_reclaim(hr); _g1h->free_humongous_region(hr, _local_cleanup_list); }; @@ -1243,16 +1247,10 @@ class G1UpdateRegionLivenessAndSelectForRebuildTask : public WorkerTask { } void reclaim_empty_old_region(G1HeapRegion* hr) { - assert(hr->used() > 0, "precondition"); - assert(!hr->has_pinned_objects(), "precondition"); assert(hr->is_old(), "precondition"); + reclaim_empty_region(hr); _num_old_regions_removed++; - _freed_bytes += hr->used(); - hr->set_containing_set(nullptr); - hr->clear_cardtable(); - _g1h->concurrent_mark()->clear_statistics(hr); - G1HeapRegionPrinter::mark_reclaim(hr); _g1h->free_region(hr, _local_cleanup_list); } @@ -1284,7 +1282,8 @@ class G1UpdateRegionLivenessAndSelectForRebuildTask : public WorkerTask { const bool is_live = hr->live_bytes() != 0 || hr->has_pinned_objects(); if (is_live) { - if (tracker->update_old_before_rebuild(hr)) { + const bool selected_for_rebuild = tracker->update_old_before_rebuild(hr); + if (selected_for_rebuild) { _num_selected_for_rebuild++; } _cm->update_top_at_rebuild_start(hr); @@ -2976,7 +2975,6 @@ G1CMTask::G1CMTask(uint worker_id, #define G1PPRL_BYTE_FORMAT " %9zu" #define G1PPRL_BYTE_H_FORMAT " %9s" #define G1PPRL_DOUBLE_FORMAT "%14.1f" -#define G1PPRL_GCEFF_FORMAT " %14s" #define G1PPRL_GCEFF_H_FORMAT " %14s" #define G1PPRL_GID_H_FORMAT " %9s" #define G1PPRL_GID_FORMAT " " UINT32_FORMAT_W(9) @@ -3052,10 +3050,10 @@ bool G1PrintRegionLivenessInfoClosure::do_heap_region(G1HeapRegion* r) { size_t remset_bytes = r->rem_set()->mem_size(); size_t code_roots_bytes = r->rem_set()->code_roots_mem_size(); const char* remset_type = r->rem_set()->get_short_state_str(); - uint cset_groud_gid = 0; + uint cset_group_id = 0; if (r->rem_set()->is_added_to_cset_group()) { - cset_groud_gid = r->rem_set()->cset_group_id(); + cset_group_id = r->rem_set()->cset_group_id(); } _total_used_bytes += used_bytes; @@ -3076,7 +3074,7 @@ bool G1PrintRegionLivenessInfoClosure::do_heap_region(G1HeapRegion* r) { type, p2i(bottom), p2i(end), used_bytes, live_bytes, remset_type, code_roots_bytes, - cset_groud_gid); + cset_group_id); return false; } @@ -3091,7 +3089,7 @@ G1PrintRegionLivenessInfoClosure::~G1PrintRegionLivenessInfoClosure() { // add static memory usages to remembered set sizes _total_remset_bytes += G1HeapRegionRemSet::static_mem_size(); - do_cset_groups(); + log_cset_candidate_groups(); // Print the footer of the output. log_trace(gc, liveness)(G1PPRL_LINE_PREFIX); @@ -3111,10 +3109,33 @@ G1PrintRegionLivenessInfoClosure::~G1PrintRegionLivenessInfoClosure() { bytes_to_mb(_total_code_roots_bytes)); } -void G1PrintRegionLivenessInfoClosure::do_cset_groups() { +void G1PrintRegionLivenessInfoClosure::log_cset_candidate_group_add_total(G1CSetCandidateGroup* group, const char* type) { + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX + G1PPRL_GID_FORMAT + G1PPRL_LEN_FORMAT + G1PPRL_GID_GCEFF_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_BYTE_FORMAT + G1PPRL_TYPE_H_FORMAT, + group->group_id(), + group->length(), + group->gc_efficiency(), + group->liveness(), + group->card_set()->mem_size(), + type); + _total_remset_bytes += group->card_set()->mem_size(); +} + +void G1PrintRegionLivenessInfoClosure::log_cset_candidate_grouplist(G1CSetCandidateGroupList& gl, const char* type) { + for (G1CSetCandidateGroup* group : gl) { + log_cset_candidate_group_add_total(group, type); + } +} + +void G1PrintRegionLivenessInfoClosure::log_cset_candidate_groups() { log_trace(gc, liveness)(G1PPRL_LINE_PREFIX); - log_trace(gc, liveness)(G1PPRL_LINE_PREFIX" Collectionset Candidate Groups"); - log_trace(gc, liveness)(G1PPRL_LINE_PREFIX " Types: Y=Young Regions, M=From Marking Regions, R=Retained Regions"); + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX" Collection Set Candidate Groups"); + log_trace(gc, liveness)(G1PPRL_LINE_PREFIX " Types: Y=Young, M=From Marking Regions, R=Retained Regions"); log_trace(gc, liveness)(G1PPRL_LINE_PREFIX G1PPRL_GID_H_FORMAT G1PPRL_LEN_H_FORMAT @@ -3138,49 +3159,10 @@ void G1PrintRegionLivenessInfoClosure::do_cset_groups() { "(bytes)", ""); G1CollectedHeap* g1h = G1CollectedHeap::heap(); - G1CSetCandidateGroup* young_only_cset_group =g1h->young_regions_cset_group(); - _total_remset_bytes += young_only_cset_group->card_set()->mem_size(); + log_cset_candidate_group_add_total(g1h->young_regions_cset_group(), "Y"); - log_trace(gc, liveness)(G1PPRL_LINE_PREFIX - G1PPRL_GID_FORMAT - G1PPRL_LEN_FORMAT - G1PPRL_GCEFF_FORMAT - G1PPRL_BYTE_FORMAT - G1PPRL_BYTE_FORMAT - G1PPRL_TYPE_H_FORMAT, - young_only_cset_group->group_id(), young_only_cset_group->length(), - "-", - size_t(0), young_only_cset_group->card_set()->mem_size(), - "Y"); - - for (G1CSetCandidateGroup* group : g1h->policy()->candidates()->from_marking_groups()) { - _total_remset_bytes += group->card_set()->mem_size(); - log_trace(gc, liveness)(G1PPRL_LINE_PREFIX - G1PPRL_GID_FORMAT - G1PPRL_LEN_FORMAT - G1PPRL_GID_GCEFF_FORMAT - G1PPRL_BYTE_FORMAT - G1PPRL_BYTE_FORMAT - G1PPRL_TYPE_H_FORMAT, - group->group_id(), group->length(), - group->gc_efficiency(), - group->liveness(), group->card_set()->mem_size(), - "M"); - } - - for (G1CSetCandidateGroup* group : g1h->policy()->candidates()->retained_groups()) { - _total_remset_bytes += group->card_set()->mem_size(); - log_trace(gc, liveness)(G1PPRL_LINE_PREFIX - G1PPRL_GID_FORMAT - G1PPRL_LEN_FORMAT - G1PPRL_GID_GCEFF_FORMAT - G1PPRL_BYTE_FORMAT - G1PPRL_BYTE_FORMAT - G1PPRL_TYPE_H_FORMAT, - group->group_id(), group->length(), - group->gc_efficiency(), - group->liveness(), group->card_set()->mem_size(), - "R"); - } + G1CollectionSetCandidates* candidates = g1h->policy()->candidates(); + log_cset_candidate_grouplist(candidates->from_marking_groups(), "M"); + log_cset_candidate_grouplist(candidates->retained_groups(), "R"); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 3c3416ebcad..4977da4729d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -42,6 +42,8 @@ class ConcurrentGCTimer; class G1CollectedHeap; +class G1CSetCandidateGroup; +class G1CSetCandidateGroupList; class G1ConcurrentMark; class G1ConcurrentMarkThread; class G1CMOopClosure; @@ -974,7 +976,9 @@ class G1PrintRegionLivenessInfoClosure : public G1HeapRegionClosure { return (double) val / (double) M; } - void do_cset_groups(); + void log_cset_candidate_group_add_total(G1CSetCandidateGroup* gr, const char* type); + void log_cset_candidate_grouplist(G1CSetCandidateGroupList& gl, const char* type); + void log_cset_candidate_groups(); public: // The header and footer are printed in the constructor and diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index c05e7cc4be4..629cbae935e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -80,7 +80,7 @@ void G1ConcurrentMarkThread::delay_to_keep_mmu(bool remark) { if (policy->use_adaptive_young_list_length()) { double delay_end_sec = mmu_delay_end(policy, remark); // Wait for timeout or thread termination request. - MonitorLocker ml(CGC_lock, Monitor::_no_safepoint_check_flag); + MonitorLocker ml(G1CGC_lock, Monitor::_no_safepoint_check_flag); while (!_cm->has_aborted() && !should_terminate()) { double sleep_time_sec = (delay_end_sec - os::elapsedTime()); jlong sleep_time_ms = ceil(sleep_time_sec * MILLIUNITS); @@ -143,12 +143,12 @@ void G1ConcurrentMarkThread::stop_service() { _cm->abort_marking_threads(); } - MutexLocker ml(CGC_lock, Mutex::_no_safepoint_check_flag); - CGC_lock->notify_all(); + MutexLocker ml(G1CGC_lock, Mutex::_no_safepoint_check_flag); + G1CGC_lock->notify_all(); } bool G1ConcurrentMarkThread::wait_for_next_cycle() { - MonitorLocker ml(CGC_lock, Mutex::_no_safepoint_check_flag); + MonitorLocker ml(G1CGC_lock, Mutex::_no_safepoint_check_flag); while (!in_progress() && !should_terminate()) { ml.wait(); } diff --git a/src/hotspot/share/gc/g1/g1EdenRegions.hpp b/src/hotspot/share/gc/g1/g1EdenRegions.hpp index a6e005ff2dc..c7ed9008ee7 100644 --- a/src/hotspot/share/gc/g1/g1EdenRegions.hpp +++ b/src/hotspot/share/gc/g1/g1EdenRegions.hpp @@ -42,7 +42,7 @@ public: G1EdenRegions() : _length(0), _used_bytes(0), _regions_on_node() { } uint add(G1HeapRegion* hr) { - assert(!hr->is_eden(), "should not already be set"); + assert(hr->is_eden(), "must be"); _length++; return _regions_on_node.add(hr); } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 4992df8e214..bae8115bb9c 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -246,8 +246,6 @@ void G1FullCollector::complete_collection(size_t allocation_word_size) { _heap->resize_all_tlabs(); - _heap->young_regions_cset_group()->clear(); - _heap->policy()->record_full_collection_end(); _heap->gc_epilogue(true); diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.cpp b/src/hotspot/share/gc/g1/g1FullGCScope.cpp index 5879442b82b..8b92d51a8a3 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.cpp @@ -40,6 +40,7 @@ G1FullGCScope::G1FullGCScope(G1MonitoringSupport* monitoring_support, bool do_maximal_compaction, G1FullGCTracer* tracer) : _rm(), + _should_clear_soft_refs(clear_soft), _do_maximal_compaction(do_maximal_compaction), _g1h(G1CollectedHeap::heap()), _svc_marker(SvcGCMarker::FULL), @@ -47,17 +48,12 @@ G1FullGCScope::G1FullGCScope(G1MonitoringSupport* monitoring_support, _tracer(tracer), _active(), _tracer_mark(&_timer, _tracer), - _soft_refs(clear_soft, _g1h->soft_ref_policy()), _monitoring_scope(monitoring_support), _heap_printer(_g1h), _region_compaction_threshold(do_maximal_compaction ? G1HeapRegion::GrainWords : (1 - MarkSweepDeadRatio / 100.0) * G1HeapRegion::GrainWords) { } -bool G1FullGCScope::should_clear_soft_refs() { - return _soft_refs.should_clear(); -} - STWGCTimer* G1FullGCScope::timer() { return &_timer; } diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.hpp b/src/hotspot/share/gc/g1/g1FullGCScope.hpp index f7362d20d4f..ab5a48f3574 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.hpp @@ -47,6 +47,7 @@ public: // Class used to group scoped objects used in the Full GC together. class G1FullGCScope : public StackObj { ResourceMark _rm; + bool _should_clear_soft_refs; bool _do_maximal_compaction; G1CollectedHeap* _g1h; SvcGCMarker _svc_marker; @@ -54,7 +55,6 @@ class G1FullGCScope : public StackObj { G1FullGCTracer* _tracer; IsSTWGCActiveMark _active; G1FullGCJFRTracerMark _tracer_mark; - ClearedAllSoftRefs _soft_refs; G1FullGCMonitoringScope _monitoring_scope; G1HeapPrinterMark _heap_printer; size_t _region_compaction_threshold; @@ -65,7 +65,7 @@ public: bool do_maximal_compaction, G1FullGCTracer* tracer); - bool should_clear_soft_refs(); + bool should_clear_soft_refs() const { return _should_clear_soft_refs; } bool do_maximal_compaction() { return _do_maximal_compaction; } STWGCTimer* timer(); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index d8c6859c1aa..fe5aa3d2150 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -52,7 +52,7 @@ public: if (SafepointSynchronize::is_at_safepoint()) { guarantee(Thread::current()->is_VM_thread() || - FreeList_lock->owned_by_self(), "master free list MT safety protocol at a safepoint"); + G1FreeList_lock->owned_by_self(), "master free list MT safety protocol at a safepoint"); } else { guarantee(Heap_lock->owned_by_self(), "master free list MT safety protocol outside a safepoint"); } @@ -288,7 +288,7 @@ uint G1HeapRegionManager::uncommit_inactive_regions(uint limit) { uint uncommitted = 0; uint offset = 0; do { - MutexLocker uc(Uncommit_lock, Mutex::_no_safepoint_check_flag); + MutexLocker uc(G1Uncommit_lock, Mutex::_no_safepoint_check_flag); G1HeapRegionRange range = _committed_map.next_inactive_range(offset); // No more regions available for uncommit. Return the number of regions // already uncommitted or 0 if there were no longer any inactive regions. @@ -374,7 +374,7 @@ void G1HeapRegionManager::expand_exact(uint start, uint num_regions, WorkerThrea if (_committed_map.inactive(i)) { // Need to grab the lock since this can be called by a java thread // doing humongous allocations. - MutexLocker uc(Uncommit_lock, Mutex::_no_safepoint_check_flag); + MutexLocker uc(G1Uncommit_lock, Mutex::_no_safepoint_check_flag); // State might change while getting the lock. if (_committed_map.inactive(i)) { reactivate_regions(i, 1); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 49fab954799..3bbc64e0fe7 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -574,7 +574,6 @@ void G1Policy::record_full_collection_start() { // Release the future to-space so that it is available for compaction into. collector_state()->set_in_young_only_phase(false); collector_state()->set_in_full_gc(true); - _collection_set->abandon_all_candidates(); _pending_cards_at_gc_start = 0; } diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 804950f1ef3..3571945d587 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -121,7 +121,6 @@ public: G1OldGenAllocationTracker* old_gen_alloc_tracker() { return &_old_gen_alloc_tracker; } void set_region_eden(G1HeapRegion* hr) { - hr->set_eden(); hr->install_surv_rate_group(_eden_surv_rate_group); } diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 4114f0e7ba5..44b3234d26b 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -986,13 +986,12 @@ class G1MergeHeapRootsTask : public WorkerTask { // Visitor for remembered sets. Several methods of it are called by a region's // card set iterator to drop card set remembered set entries onto the card. - // table. This is in addition to being the HG1eapRegionClosure to iterate over - // all region's remembered sets. + // table. // // We add a small prefetching cache in front of the actual work as dropping // onto the card table is basically random memory access. This improves // performance of this operation significantly. - class G1MergeCardSetClosure : public G1HeapRegionClosure { + class G1MergeCardSetClosure { friend class G1MergeCardSetCache; G1RemSetScanState* _scan_state; @@ -1039,7 +1038,6 @@ class G1MergeHeapRootsTask : public WorkerTask { } public: - G1MergeCardSetClosure(G1RemSetScanState* scan_state) : _scan_state(scan_state), _ct(G1CollectedHeap::heap()->card_table()), @@ -1071,38 +1069,6 @@ class G1MergeHeapRootsTask : public WorkerTask { _scan_state->set_chunk_range_dirty(_region_base_idx + start_card_idx, length); } - // Helper to merge the cards in the card set for the given region onto the card - // table. - // - // Called directly for humongous starts regions because we should not add - // humongous eager reclaim candidates to the "all" list of regions to - // clear the card table by default as we do not know yet whether this region - // will be reclaimed (and reused). - // If the humongous region contains dirty cards, g1 will scan them - // because dumping the remembered set entries onto the card table will add - // the humongous region to the "dirty" region list to scan. Then scanning - // either clears the card during scan (if there is only an initial evacuation - // pass) or the "dirty" list will be merged with the "all" list later otherwise. - // (And there is no problem either way if the region does not contain dirty - // cards). - void merge_card_set_for_region(G1HeapRegion* r) { - assert(r->in_collection_set() || r->is_starts_humongous(), "must be"); - - G1HeapRegionRemSet* rem_set = r->rem_set(); - if (!rem_set->is_empty()) { - rem_set->iterate_for_merge(*this); - } - } - - virtual bool do_heap_region(G1HeapRegion* r) { - assert(r->in_collection_set(), "must be"); - - _scan_state->add_all_dirty_region(r->hrm_index()); - merge_card_set_for_region(r); - - return false; - } - G1MergeCardSetStats stats() { _merge_card_set_cache.flush(); // Compensation for the dummy cards that were initially pushed into the @@ -1189,8 +1155,7 @@ class G1MergeHeapRootsTask : public WorkerTask { "Found a not-small remembered set here. This is inconsistent with previous assumptions."); if (!r->rem_set()->is_empty()) { - _cl.merge_card_set_for_region(r); - + r->rem_set()->iterate_for_merge(_cl); // We should only clear the card based remembered set here as we will not // implicitly rebuild anything else during eager reclaim. Note that at the moment // (and probably never) we do not enter this path if there are other kind of diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 6ddeba3d2e2..6757172b625 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -50,7 +50,9 @@ bool VM_G1CollectFull::skip_operation() const { void VM_G1CollectFull::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); GCCauseSetter x(g1h, _gc_cause); - g1h->do_full_collection(false /* clear_all_soft_refs */, + bool clear_all_soft_refs = _gc_cause == GCCause::_metadata_GC_clear_soft_refs || + _gc_cause == GCCause::_wb_full_gc; + g1h->do_full_collection(clear_all_soft_refs /* clear_all_soft_refs */, false /* do_maximal_compaction */, size_t(0) /* allocation_word_size */); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 3e8f1bbba64..bb567e2af5c 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -765,7 +765,7 @@ class FreeCSetClosure : public G1HeapRegionClosure { assert(retain_region == r->rem_set()->is_tracked(), "When retaining a region, remembered set should be kept."); // Add region to old set, need to hold lock. - MutexLocker x(OldSets_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1OldSets_lock, Mutex::_no_safepoint_check_flag); _g1h->old_set_add(r); } diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 2359ab9e158..c6a9a312e5c 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -310,11 +310,13 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, return result; } - // If certain conditions hold, try allocating from the old gen. - if (!is_tlab && !should_alloc_in_eden(size)) { - result = old_gen()->cas_allocate_noexpand(size); - if (result != nullptr) { - return result; + // Try allocating from the old gen for non-TLAB in certain scenarios. + if (!is_tlab) { + if (!should_alloc_in_eden(size) || _is_heap_almost_full) { + result = old_gen()->cas_allocate_noexpand(size); + if (result != nullptr) { + return result; + } } } } diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index fb57f224652..5d99095c1ed 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -67,7 +67,8 @@ void PSAdaptiveSizePolicy::print_stats(bool is_survivor_overflowing) { mutator_time_percent(), minor_gc_time_estimate() * 1000.0, _gc_distance_seconds_seq.davg(), _gc_distance_seconds_seq.last(), - PROPERFMTARGS(promoted_bytes_estimate()), PROPERFMTARGS(_promoted_bytes.last()), + byte_size_in_proper_unit(promoted_bytes_estimate()), proper_unit_for_byte_size((size_t)promoted_bytes_estimate()), + byte_size_in_proper_unit(_promoted_bytes.last()), proper_unit_for_byte_size((size_t)_promoted_bytes.last()), _promotion_rate_bytes_per_sec.davg()/M, _promotion_rate_bytes_per_sec.last()/M, is_survivor_overflowing ? "true" : "false"); } @@ -258,4 +259,4 @@ void PSAdaptiveSizePolicy::update_averages(bool is_survivor_overflow, double promotion_rate = promoted / (_gc_distance_seconds_seq.last() + _trimmed_minor_gc_time_seconds.last()); _promotion_rate_bytes_per_sec.add(promotion_rate); -} \ No newline at end of file +} diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index d672b2b690c..b549c61e1c0 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -685,6 +685,9 @@ void PSParallelCompact::post_compact() CodeCache::on_gc_marking_cycle_finish(); CodeCache::arm_all_nmethods(); + // Need to clear claim bits for the next full-gc (marking and adjust-pointers). + ClassLoaderDataGraph::clear_claimed_marks(); + for (unsigned int id = old_space_id; id < last_space_id; ++id) { // Clear the marking bitmap, summary data and split info. clear_data_covering_space(SpaceId(id)); @@ -958,18 +961,10 @@ void PSParallelCompact::summary_phase() } } -// This method should contain all heap-specific policy for invoking a full -// collection. invoke_no_policy() will only attempt to compact the heap; it -// will do nothing further. If we need to bail out for policy reasons, scavenge -// before full gc, or any other specialized behavior, it needs to be added here. -// +// This method invokes a full collection. The argument controls whether +// soft-refs should be cleared or not. // Note that this method should only be called from the vm_thread while at a // safepoint. -// -// Note that the all_soft_refs_clear flag in the soft ref policy -// may be true because this method can be called without intervening -// activity. For example when the heap space is tight and full measure -// are being taken to free space. bool PSParallelCompact::invoke(bool clear_all_soft_refs) { assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); assert(Thread::current() == (Thread*)VMThread::vm_thread(), @@ -978,10 +973,6 @@ bool PSParallelCompact::invoke(bool clear_all_soft_refs) { SvcGCMarker sgcm(SvcGCMarker::FULL); IsSTWGCActiveMark mark; - ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); - clear_all_soft_refs = clear_all_soft_refs - || heap->soft_ref_policy()->should_clear_all_soft_refs(); - return PSParallelCompact::invoke_no_policy(clear_all_soft_refs); } @@ -1001,11 +992,6 @@ bool PSParallelCompact::invoke_no_policy(bool clear_all_soft_refs) { PSOldGen* old_gen = heap->old_gen(); PSAdaptiveSizePolicy* size_policy = heap->size_policy(); - // The scope of casr should end after code that can change - // SoftRefPolicy::_should_clear_all_soft_refs. - ClearedAllSoftRefs casr(clear_all_soft_refs, - heap->soft_ref_policy()); - // Make sure data structures are sane, make the heap parsable, and do other // miscellaneous bookkeeping. pre_compact(); @@ -1305,9 +1291,6 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) { ClassLoaderDataGraph::purge(true /* at_safepoint */); DEBUG_ONLY(MetaspaceUtils::verify();) } - - // Need to clear claim bits for the next mark. - ClassLoaderDataGraph::clear_claimed_marks(); } { diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.hpp index 0584fc64d73..a0118717f9d 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.hpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.hpp @@ -761,8 +761,8 @@ private: public: static void fill_dead_objs_in_dense_prefix(uint worker_id, uint num_workers); - static bool invoke(bool maximum_heap_compaction); - static bool invoke_no_policy(bool maximum_heap_compaction); + static bool invoke(bool clear_all_soft_refs); + static bool invoke_no_policy(bool clear_all_soft_refs); template static void adjust_in_space_helper(SpaceId id, volatile uint* claim_counter, Func&& on_stripe); diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index 4f45821a889..a3d45a98354 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -22,6 +22,7 @@ * */ +#include "classfile/classLoaderDataGraph.hpp" #include "gc/serial/cardTableRS.hpp" #include "gc/serial/serialGcRefProcProxyTask.hpp" #include "gc/serial/serialHeap.inline.hpp" @@ -38,8 +39,10 @@ #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcTraceTime.inline.hpp" +#include "gc/shared/oopStorageSet.inline.hpp" #include "gc/shared/referencePolicy.hpp" #include "gc/shared/referenceProcessorPhaseTimes.hpp" +#include "gc/shared/scavengableNMethods.hpp" #include "gc/shared/space.hpp" #include "gc/shared/spaceDecorator.hpp" #include "gc/shared/strongRootsScope.hpp" @@ -605,21 +608,31 @@ bool DefNewGeneration::collect(bool clear_all_soft_refs) { { StrongRootsScope srs(0); - RootScanClosure root_cl{this}; - CLDScanClosure cld_cl{this}; + RootScanClosure oop_closure{this}; + CLDScanClosure cld_closure{this}; - MarkingNMethodClosure code_cl(&root_cl, - NMethodToOopClosure::FixRelocations, - false /* keepalive_nmethods */); + MarkingNMethodClosure nmethod_closure(&oop_closure, + NMethodToOopClosure::FixRelocations, + false /* keepalive_nmethods */); - HeapWord* saved_top_in_old_gen = _old_gen->space()->top(); - heap->process_roots(SerialHeap::SO_ScavengeCodeCache, - &root_cl, - &cld_cl, - &cld_cl, - &code_cl); + // Starting tracing from roots, there are 4 kinds of roots in young-gc. + // + // 1. old-to-young pointers; processing them before relocating other kinds + // of roots. + _old_gen->scan_old_to_young_refs(); - _old_gen->scan_old_to_young_refs(saved_top_in_old_gen); + // 2. CLD; visit all (strong+weak) clds with the same closure, because we + // don't perform class unloading during young-gc. + ClassLoaderDataGraph::cld_do(&cld_closure); + + // 3. Threads stack frames and nmethods. + // Only nmethods that contain pointers into-young need to be processed + // during young-gc, and they are tracked in ScavengableNMethods + Threads::oops_do(&oop_closure, nullptr); + ScavengableNMethods::nmethods_do(&nmethod_closure); + + // 4. VM internal roots. + OopStorageSet::strong_oops_do(&oop_closure); } // "evacuate followers". diff --git a/src/hotspot/share/gc/serial/serialFullGC.cpp b/src/hotspot/share/gc/serial/serialFullGC.cpp index da5b8ba53a0..fc63b81b7ce 100644 --- a/src/hotspot/share/gc/serial/serialFullGC.cpp +++ b/src/hotspot/share/gc/serial/serialFullGC.cpp @@ -49,6 +49,7 @@ #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/modRefBarrierSet.hpp" +#include "gc/shared/oopStorageSet.inline.hpp" #include "gc/shared/preservedMarks.inline.hpp" #include "gc/shared/referencePolicy.hpp" #include "gc/shared/referenceProcessorPhaseTimes.hpp" @@ -66,6 +67,7 @@ #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" #include "runtime/prefetch.inline.hpp" +#include "runtime/threads.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" #include "utilities/events.hpp" @@ -483,13 +485,22 @@ void SerialFullGC::phase1_mark(bool clear_all_softrefs) { { StrongRootsScope srs(0); - CLDClosure* weak_cld_closure = ClassUnloading ? nullptr : &follow_cld_closure; - MarkingNMethodClosure mark_code_closure(&follow_root_closure, !NMethodToOopClosure::FixRelocations, true); - gch->process_roots(SerialHeap::SO_None, - &follow_root_closure, - &follow_cld_closure, - weak_cld_closure, - &mark_code_closure); + MarkingNMethodClosure mark_code_closure(&follow_root_closure, + !NMethodToOopClosure::FixRelocations, + true); + + // Start tracing from roots, there are 3 kinds of roots in full-gc. + // + // 1. CLD. This method internally takes care of whether class loading is + // enabled or not, applying the closure to both strong and weak or only + // strong CLDs. + ClassLoaderDataGraph::always_strong_cld_do(&follow_cld_closure); + + // 2. Threads stack frames and active nmethods in them. + Threads::oops_do(&follow_root_closure, &mark_code_closure); + + // 3. VM internal roots. + OopStorageSet::strong_oops_do(&follow_root_closure); } // Process reference objects found during marking @@ -694,6 +705,16 @@ void SerialFullGC::invoke_at_safepoint(bool clear_all_softrefs) { allocate_stacks(); + // Usually, all class unloading work occurs at the end of phase 1, but Serial + // full-gc accesses dead-objs' klass to find out the start of next live-obj + // during phase 2. This requires klasses of dead-objs to be kept loaded. + // Therefore, we declare ClassUnloadingContext at the same level as + // full-gc phases, and purge dead classes (invoking + // ClassLoaderDataGraph::purge) after all phases of full-gc. + ClassUnloadingContext ctx(1 /* num_nmethod_unlink_workers */, + false /* unregister_nmethods_during_purge */, + false /* lock_nmethod_free_separately */); + phase1_mark(clear_all_softrefs); Compacter compacter{gch}; @@ -717,13 +738,20 @@ void SerialFullGC::invoke_at_safepoint(bool clear_all_softrefs) { ClassLoaderDataGraph::verify_claimed_marks_cleared(ClassLoaderData::_claim_stw_fullgc_adjust); - NMethodToOopClosure code_closure(&adjust_pointer_closure, NMethodToOopClosure::FixRelocations); - gch->process_roots(SerialHeap::SO_AllCodeCache, - &adjust_pointer_closure, - &adjust_cld_closure, - &adjust_cld_closure, - &code_closure); + // Remap strong and weak roots in adjust phase. + // 1. All (strong and weak) CLDs. + ClassLoaderDataGraph::cld_do(&adjust_cld_closure); + // 2. Threads stack frames. No need to visit on-stack nmethods, because all + // nmethods are visited in one go via CodeCache::nmethods_do. + Threads::oops_do(&adjust_pointer_closure, nullptr); + NMethodToOopClosure nmethod_cl(&adjust_pointer_closure, NMethodToOopClosure::FixRelocations); + CodeCache::nmethods_do(&nmethod_cl); + + // 3. VM internal roots + OopStorageSet::strong_oops_do(&adjust_pointer_closure); + + // 4. VM internal weak roots WeakProcessor::oops_do(&adjust_pointer_closure); adjust_marks(); @@ -737,6 +765,13 @@ void SerialFullGC::invoke_at_safepoint(bool clear_all_softrefs) { compacter.phase4_compact(); } + // Delete metaspaces for unloaded class loaders and clean up CLDG. + ClassLoaderDataGraph::purge(true /* at_safepoint */); + DEBUG_ONLY(MetaspaceUtils::verify();) + + // Need to clear claim bits for the next full-gc (specifically phase 1 and 3). + ClassLoaderDataGraph::clear_claimed_marks(); + restore_marks(); deallocate_stacks(); diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index fbbaabf618a..48d6a1b74ea 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -513,38 +513,6 @@ HeapWord* SerialHeap::satisfy_failed_allocation(size_t size, bool is_tlab) { return nullptr; } -void SerialHeap::process_roots(ScanningOption so, - OopClosure* strong_roots, - CLDClosure* strong_cld_closure, - CLDClosure* weak_cld_closure, - NMethodToOopClosure* code_roots) { - // General roots. - assert(code_roots != nullptr, "code root closure should always be set"); - - ClassLoaderDataGraph::roots_cld_do(strong_cld_closure, weak_cld_closure); - - // Only process code roots from thread stacks if we aren't visiting the entire CodeCache anyway - NMethodToOopClosure* roots_from_code_p = (so & SO_AllCodeCache) ? nullptr : code_roots; - - Threads::oops_do(strong_roots, roots_from_code_p); - - OopStorageSet::strong_oops_do(strong_roots); - - if (so & SO_ScavengeCodeCache) { - assert(code_roots != nullptr, "must supply closure for code cache"); - - // We only visit parts of the CodeCache when scavenging. - ScavengableNMethods::nmethods_do(code_roots); - } - if (so & SO_AllCodeCache) { - assert(code_roots != nullptr, "must supply closure for code cache"); - - // CMSCollector uses this to do intermediate-strength collections. - // We scan the entire code cache, since CodeCache::do_unloading is not called. - CodeCache::nmethods_do(code_roots); - } -} - template static void oop_iterate_from(OopClosureType* blk, ContiguousSpace* space, HeapWord** from) { assert(*from != nullptr, "precondition"); @@ -635,9 +603,6 @@ void SerialHeap::do_full_collection(bool clear_all_soft_refs) { gc_prologue(); COMPILER2_OR_JVMCI_PRESENT(DerivedPointerTable::clear()); CodeCache::on_gc_marking_cycle_start(); - ClassUnloadingContext ctx(1 /* num_nmethod_unlink_workers */, - false /* unregister_nmethods_during_purge */, - false /* lock_nmethod_free_separately */); STWGCTimer* gc_timer = SerialFullGC::gc_timer(); gc_timer->register_gc_start(); @@ -662,13 +627,6 @@ void SerialHeap::do_full_collection(bool clear_all_soft_refs) { _old_gen->compute_new_size(); _young_gen->compute_new_size(); - // Delete metaspaces for unloaded class loaders and clean up loader_data graph - ClassLoaderDataGraph::purge(/*at_safepoint*/true); - DEBUG_ONLY(MetaspaceUtils::verify();) - - // Need to clear claim bits for the next mark. - ClassLoaderDataGraph::clear_claimed_marks(); - _old_gen->update_promote_stats(); // Resize the metaspace capacity after full collections diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index d93e895d209..e86eac58515 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -222,23 +222,7 @@ public: // generations in a fully generational heap. CardTableRS* rem_set() { return _rem_set; } - // The ScanningOption determines which of the roots - // the closure is applied to: - // "SO_None" does none; - enum ScanningOption { - SO_None = 0x0, - SO_AllCodeCache = 0x8, - SO_ScavengeCodeCache = 0x10 - }; - public: - // Apply closures on various roots in Young GC or marking/adjust phases of Full GC. - void process_roots(ScanningOption so, - OopClosure* strong_roots, - CLDClosure* strong_cld_closure, - CLDClosure* weak_cld_closure, - NMethodToOopClosure* code_roots); - // Set the saved marks of generations, if that makes sense. // In particular, if any generation might iterate over the oops // in other generations, it should call this method. diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index 589e670a2e4..a28a8c8e1cb 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp @@ -277,8 +277,8 @@ HeapWord* TenuredGeneration::block_start(const void* addr) const { } } -void TenuredGeneration::scan_old_to_young_refs(HeapWord* saved_top_in_old_gen) { - _rs->scan_old_to_young_refs(this, saved_top_in_old_gen); +void TenuredGeneration::scan_old_to_young_refs() { + _rs->scan_old_to_young_refs(this, space()->top()); } TenuredGeneration::TenuredGeneration(ReservedSpace rs, diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.hpp index 1f6871d8ee2..892fc5bb86c 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.hpp @@ -104,7 +104,7 @@ public: HeapWord* block_start(const void* addr) const; - void scan_old_to_young_refs(HeapWord* saved_top_in_old_gen); + void scan_old_to_young_refs(); bool is_in(const void* p) const; diff --git a/src/hotspot/share/gc/shared/gcCause.hpp b/src/hotspot/share/gc/shared/gcCause.hpp index 56070c88143..f775d41340d 100644 --- a/src/hotspot/share/gc/shared/gcCause.hpp +++ b/src/hotspot/share/gc/shared/gcCause.hpp @@ -92,12 +92,6 @@ class GCCause : public AllStatic { cause == GCCause::_dcmd_gc_run); } - inline static bool is_explicit_full_gc(GCCause::Cause cause) { - return (is_user_requested_gc(cause) || - is_serviceability_requested_gc(cause) || - cause == GCCause::_wb_full_gc); - } - inline static bool is_serviceability_requested_gc(GCCause::Cause cause) { return (cause == GCCause::_jvmti_force_gc || cause == GCCause::_heap_inspection || @@ -109,20 +103,6 @@ class GCCause : public AllStatic { cause == _codecache_GC_aggressive); } - // Causes for collection of the tenured generation - inline static bool is_tenured_allocation_failure_gc(GCCause::Cause cause) { - // _allocation_failure is the generic cause a collection which could result - // in the collection of the tenured generation if there is not enough space - // in the tenured generation to support a young GC. - return cause == GCCause::_allocation_failure; - } - - // Causes for collection of the young generation - inline static bool is_allocation_failure_gc(GCCause::Cause cause) { - // _allocation_failure is the generic cause a collection for allocation failure - return cause == GCCause::_allocation_failure; - } - // Return a string describing the GCCause. static const char* to_string(GCCause::Cause cause); }; diff --git a/src/hotspot/share/gc/shared/softRefPolicy.hpp b/src/hotspot/share/gc/shared/softRefPolicy.hpp index b725b843d2d..fe2706288f7 100644 --- a/src/hotspot/share/gc/shared/softRefPolicy.hpp +++ b/src/hotspot/share/gc/shared/softRefPolicy.hpp @@ -58,21 +58,4 @@ class SoftRefPolicy { } }; -class ClearedAllSoftRefs : public StackObj { - bool _clear_all_soft_refs; - SoftRefPolicy* _soft_ref_policy; - public: - ClearedAllSoftRefs(bool clear_all_soft_refs, SoftRefPolicy* soft_ref_policy) : - _clear_all_soft_refs(clear_all_soft_refs), - _soft_ref_policy(soft_ref_policy) {} - - ~ClearedAllSoftRefs() { - if (_clear_all_soft_refs) { - _soft_ref_policy->cleared_all_soft_refs(); - } - } - - bool should_clear() { return _clear_all_soft_refs; } -}; - #endif // SHARE_GC_SHARED_SOFTREFPOLICY_HPP diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 807d4197b12..8210718126b 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2015, 2021, Red Hat, Inc. All rights reserved. - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp index 6ac62e8e9ed..78ae78f4c24 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp @@ -34,6 +34,7 @@ public: enum Type { _alloc_shared, // Allocate common, outside of TLAB _alloc_shared_gc, // Allocate common, outside of GCLAB/PLAB + _alloc_cds, // Allocate for CDS _alloc_tlab, // Allocate TLAB _alloc_gclab, // Allocate GCLAB _alloc_plab, // Allocate PLAB @@ -46,6 +47,8 @@ public: return "Shared"; case _alloc_shared_gc: return "Shared GC"; + case _alloc_cds: + return "CDS"; case _alloc_tlab: return "TLAB"; case _alloc_gclab: @@ -121,6 +124,10 @@ public: return ShenandoahAllocRequest(0, requested_size, _alloc_shared, ShenandoahAffiliation::YOUNG_GENERATION); } + static inline ShenandoahAllocRequest for_cds(size_t requested_size) { + return ShenandoahAllocRequest(0, requested_size, _alloc_cds, ShenandoahAffiliation::YOUNG_GENERATION); + } + inline size_t size() const { return _requested_size; } @@ -163,6 +170,7 @@ public: switch (_alloc_type) { case _alloc_tlab: case _alloc_shared: + case _alloc_cds: return true; case _alloc_gclab: case _alloc_plab: @@ -178,6 +186,7 @@ public: switch (_alloc_type) { case _alloc_tlab: case _alloc_shared: + case _alloc_cds: return false; case _alloc_gclab: case _alloc_plab: @@ -197,6 +206,7 @@ public: return true; case _alloc_shared: case _alloc_shared_gc: + case _alloc_cds: return false; default: ShouldNotReachHere(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index 7c9fa759835..ca9624ec752 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -29,7 +29,10 @@ #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" +#include "oops/oop.inline.hpp" #include "memory/resourceArea.hpp" +#include "runtime/os.hpp" +#include "utilities/vmError.hpp" void print_raw_memory(ShenandoahMessageBuffer &msg, void* loc) { // Be extra safe. Only access data that is guaranteed to be safe: @@ -57,30 +60,42 @@ void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) { ResourceMark rm; stringStream ss; - r->print_on(&ss); - - stringStream mw_ss; - obj->mark().print_on(&mw_ss); + StreamIndentor si(&ss); ShenandoahMarkingContext* const ctx = heap->marking_context(); - Klass* obj_klass = ShenandoahForwarding::klass(obj); - - msg.append(" " PTR_FORMAT " - klass " PTR_FORMAT " %s\n", p2i(obj), p2i(obj_klass), obj_klass->external_name()); - msg.append(" %3s allocated after mark start\n", ctx->allocated_after_mark_start(obj) ? "" : "not"); - msg.append(" %3s after update watermark\n", cast_from_oop(obj) >= r->get_update_watermark() ? "" : "not"); - msg.append(" %3s marked strong\n", ctx->is_marked_strong(obj) ? "" : "not"); - msg.append(" %3s marked weak\n", ctx->is_marked_weak(obj) ? "" : "not"); - msg.append(" %3s in collection set\n", heap->in_collection_set(obj) ? "" : "not"); - if (heap->mode()->is_generational() && !obj->is_forwarded()) { - msg.append(" age: %d\n", obj->age()); + narrowKlass nk = 0; + const Klass* obj_klass = nullptr; + const bool klass_valid = extract_klass_safely(obj, nk, obj_klass); + const char* klass_text = "(invalid)"; + if (klass_valid && os::is_readable_pointer(obj_klass) && Metaspace::contains(obj_klass)) { + klass_text = obj_klass->external_name(); } - msg.append(" mark:%s\n", mw_ss.freeze()); - msg.append(" region: %s", ss.freeze()); - if (obj_klass == vmClasses::Class_klass()) { - msg.append(" mirrored klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::klass_offset()))); - msg.append(" mirrored array klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::array_klass_offset()))); + ss.print_cr(PTR_FORMAT " - nk %u klass " PTR_FORMAT " %s\n", p2i(obj), nk, p2i(obj_klass), klass_text); + { + StreamIndentor si(&ss); + ss.print_cr("%3s allocated after mark start", ctx->allocated_after_mark_start(obj) ? "" : "not"); + ss.print_cr("%3s after update watermark", cast_from_oop(obj) >= r->get_update_watermark() ? "" : "not"); + ss.print_cr("%3s marked strong", ctx->is_marked_strong(obj) ? "" : "not"); + ss.print_cr("%3s marked weak", ctx->is_marked_weak(obj) ? "" : "not"); + ss.print_cr("%3s in collection set", heap->in_collection_set(obj) ? "" : "not"); + if (heap->mode()->is_generational() && !obj->is_forwarded()) { + ss.print_cr("age: %d", obj->age()); + } + ss.print_raw("mark: "); + obj->mark().print_on(&ss); + ss.cr(); + ss.print_raw("region: "); + r->print_on(&ss); + ss.cr(); + if (obj_klass == vmClasses::Class_klass()) { + msg.append(" mirrored klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::klass_offset()))); + msg.append(" mirrored array klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::array_klass_offset()))); + } } + const_address loc = cast_from_oop(obj); + os::print_hex_dump(&ss, loc, loc + 64, 4, true, 32, loc); + msg.append("%s", ss.base()); } void ShenandoahAsserts::print_non_obj(ShenandoahMessageBuffer& msg, void* loc) { @@ -121,6 +136,10 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l ShenandoahHeap* heap = ShenandoahHeap::heap(); ResourceMark rm; + if (!os::is_readable_pointer(obj)) { + level = _safe_unknown; + } + bool loc_in_heap = (loc != nullptr && heap->is_in_reserved(loc)); ShenandoahMessageBuffer msg("%s; %s\n\n", phase, label); @@ -128,7 +147,7 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l msg.append("Referenced from:\n"); if (interior_loc != nullptr) { msg.append(" interior location: " PTR_FORMAT "\n", p2i(interior_loc)); - if (loc_in_heap) { + if (loc_in_heap && os::is_readable_pointer(loc)) { print_obj(msg, loc); } else { print_non_obj(msg, interior_loc); @@ -150,7 +169,7 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj); msg.append("Forwardee:\n"); if (obj != fwd) { - if (level >= _safe_oop_fwd) { + if (level >= _safe_oop_fwd && os::is_readable_pointer(fwd)) { print_obj(msg, fwd); } else { print_obj_safe(msg, fwd); @@ -205,17 +224,10 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* file, line); } - Klass* obj_klass = ShenandoahForwarding::klass(obj); - if (obj_klass == nullptr) { + if (!os::is_readable_pointer(obj)) { print_failure(_safe_unknown, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", - "Object klass pointer should not be null", - file,line); - } - - if (!Metaspace::contains(obj_klass)) { - print_failure(_safe_unknown, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", - "Object klass pointer must go to metaspace", - file,line); + "oop within heap bounds but at unreadable location", + file, line); } if (!heap->is_in(obj)) { @@ -243,9 +255,9 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* file, line); } - if (obj_klass != ShenandoahForwarding::klass(fwd)) { + if (!os::is_readable_pointer(fwd)) { print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", - "Forwardee klass disagrees with object class", + "Forwardee within heap bounds but at unreadable location", file, line); } @@ -271,6 +283,32 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* } } + const Klass* obj_klass = nullptr; + narrowKlass nk = 0; + if (!extract_klass_safely(obj, nk, obj_klass)) { + print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Object klass pointer invalid", + file,line); + } + + if (obj_klass == nullptr) { + print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Object klass pointer should not be null", + file,line); + } + + if (!Metaspace::contains(obj_klass)) { + print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Object klass pointer must go to metaspace", + file,line); + } + + if (!UseCompactObjectHeaders && obj_klass != fwd->klass_or_null()) { + print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Forwardee klass disagrees with object class", + file, line); + } + // Do additional checks for special objects: their fields can hold metadata as well. // We want to check class loading/unloading did not corrupt them. We can only reasonably // trust the forwarded objects, as the from-space object can have the klasses effectively @@ -519,3 +557,34 @@ void ShenandoahAsserts::assert_generations_reconciled(const char* file, int line ShenandoahMessageBuffer msg("Active(%d) & GC(%d) Generations aren't reconciled", agen->type(), ggen->type()); report_vm_error(file, line, msg.buffer()); } + +bool ShenandoahAsserts::extract_klass_safely(oop obj, narrowKlass& nk, const Klass*& k) { + nk = 0; + k = nullptr; + + if (!os::is_readable_pointer(obj)) { + return false; + } + if (UseCompressedClassPointers) { + if (UseCompactObjectHeaders) { // look in forwardee + markWord mark = obj->mark(); + if (mark.is_marked()) { + oop fwd = cast_to_oop(mark.clear_lock_bits().to_pointer()); + if (!os::is_readable_pointer(fwd)) { + return false; + } + mark = fwd->mark(); + } + nk = mark.narrow_klass(); + } else { + nk = obj->narrow_klass(); + } + if (!CompressedKlassPointers::is_valid_narrow_klass_id(nk)) { + return false; + } + k = CompressedKlassPointers::decode_not_null_without_asserts(nk); + } else { + k = obj->klass(); + } + return k != nullptr; +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp index 31a99bf438c..e31ef7c99aa 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. + * Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHASSERTS_HPP #include "memory/iterator.hpp" +#include "oops/compressedKlass.hpp" #include "runtime/mutex.hpp" #include "utilities/formatBuffer.hpp" @@ -77,6 +78,11 @@ public: static void assert_generational(const char* file, int line); static void assert_generations_reconciled(const char* file, int line); + // Given a possibly invalid oop, extract narrowKlass (if UCCP) and Klass* + // from it safely. + // Note: For -UCCP, returned nk is always 0. + static bool extract_klass_safely(oop obj, narrowKlass& nk, const Klass*& k); + #ifdef ASSERT #define shenandoah_assert_in_heap_bounds(interior_loc, obj) \ ShenandoahAsserts::assert_in_heap_bounds(interior_loc, obj, __FILE__, __LINE__) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 795bcc1cf92..6290101bc49 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -146,6 +146,7 @@ void ShenandoahControlThread::run_service() { // If GC was requested, we better dump freeset data for performance debugging heap->free_set()->log_status_under_lock(); + heap->print_before_gc(); switch (mode) { case concurrent_normal: service_concurrent_normal_cycle(cause); @@ -159,6 +160,7 @@ void ShenandoahControlThread::run_service() { default: ShouldNotReachHere(); } + heap->print_after_gc(); // If this was the requested GC cycle, notify waiters about it if (is_gc_requested) { @@ -208,6 +210,12 @@ void ShenandoahControlThread::run_service() { ResourceMark rm; LogStream ls(lt); heap->phase_timings()->print_cycle_on(&ls); +#ifdef NOT_PRODUCT + ShenandoahEvacuationTracker* evac_tracker = heap->evac_tracker(); + ShenandoahCycleStats evac_stats = evac_tracker->flush_cycle_to_global(); + evac_tracker->print_evacuations_on(&ls, &evac_stats.workers, + &evac_stats.mutators); +#endif } } @@ -283,6 +291,7 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau log_info(gc)("Cancelled"); return; } + heap->increment_total_collections(false); ShenandoahGCSession session(cause, heap->global_generation()); @@ -329,6 +338,8 @@ void ShenandoahControlThread::service_stw_full_cycle(GCCause::Cause cause) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahGCSession session(cause, heap->global_generation()); + heap->increment_total_collections(true); + ShenandoahFullGC gc; gc.collect(cause); } @@ -338,6 +349,8 @@ void ShenandoahControlThread::service_stw_degenerated_cycle(GCCause::Cause cause ShenandoahHeap* const heap = ShenandoahHeap::heap(); ShenandoahGCSession session(cause, heap->global_generation()); + heap->increment_total_collections(false); + ShenandoahDegenGC gc(point, heap->global_generation()); gc.collect(cause); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index c941379d576..a99fa3ae62c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -482,7 +482,9 @@ const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) c void ShenandoahDegenGC::upgrade_to_full() { log_info(gc)("Degenerated GC upgrading to Full GC"); - ShenandoahHeap::heap()->shenandoah_policy()->record_degenerated_upgrade_to_full(); + ShenandoahHeap* heap = ShenandoahHeap::heap(); + heap->increment_total_collections(true); + heap->shenandoah_policy()->record_degenerated_upgrade_to_full(); ShenandoahFullGC full_gc; full_gc.op_full(GCCause::_shenandoah_upgrade_to_full_gc); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp index b1d474fa78d..499e1342083 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp @@ -30,10 +30,22 @@ #include "runtime/thread.hpp" #include "runtime/threadSMR.inline.hpp" +ShenandoahEvacuationStats::ShenandoahEvacuations* ShenandoahEvacuationStats::get_category( + ShenandoahAffiliation from, + ShenandoahAffiliation to) { + if (from == YOUNG_GENERATION) { + if (to == YOUNG_GENERATION) { + return &_young; + } + assert(to == OLD_GENERATION, "If not evacuating to young, must be promotion to old"); + return &_promotion; + } + assert(from == OLD_GENERATION, "If not evacuating from young, then must be from old"); + return &_old; +} + ShenandoahEvacuationStats::ShenandoahEvacuationStats() - : _evacuations_completed(0), _bytes_completed(0), - _evacuations_attempted(0), _bytes_attempted(0), - _use_age_table(ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring), + : _use_age_table(ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring), _age_table(nullptr) { if (_use_age_table) { _age_table = new AgeTable(false); @@ -45,14 +57,17 @@ AgeTable* ShenandoahEvacuationStats::age_table() const { return _age_table; } -void ShenandoahEvacuationStats::begin_evacuation(size_t bytes) { - ++_evacuations_attempted; - _bytes_attempted += bytes; +void ShenandoahEvacuationStats::begin_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) { + ShenandoahEvacuations* category = get_category(from, to); + category->_evacuations_attempted++; + category->_bytes_attempted += bytes; + } -void ShenandoahEvacuationStats::end_evacuation(size_t bytes) { - ++_evacuations_completed; - _bytes_completed += bytes; +void ShenandoahEvacuationStats::end_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) { + ShenandoahEvacuations* category = get_category(from, to); + category->_evacuations_completed++; + category->_bytes_completed += bytes; } void ShenandoahEvacuationStats::record_age(size_t bytes, uint age) { @@ -63,34 +78,39 @@ void ShenandoahEvacuationStats::record_age(size_t bytes, uint age) { } void ShenandoahEvacuationStats::accumulate(const ShenandoahEvacuationStats* other) { - _evacuations_completed += other->_evacuations_completed; - _bytes_completed += other->_bytes_completed; - _evacuations_attempted += other->_evacuations_attempted; - _bytes_attempted += other->_bytes_attempted; + _young.accumulate(other->_young); + _old.accumulate(other->_old); + _promotion.accumulate(other->_promotion); + if (_use_age_table) { _age_table->merge(other->age_table()); } } void ShenandoahEvacuationStats::reset() { - _evacuations_completed = _evacuations_attempted = 0; - _bytes_completed = _bytes_attempted = 0; + _young.reset(); + _old.reset(); + _promotion.reset(); + if (_use_age_table) { _age_table->clear(); } } -void ShenandoahEvacuationStats::print_on(outputStream* st) { -#ifndef PRODUCT +void ShenandoahEvacuationStats::ShenandoahEvacuations::print_on(outputStream* st) const { size_t abandoned_size = _bytes_attempted - _bytes_completed; size_t abandoned_count = _evacuations_attempted - _evacuations_completed; - st->print_cr("Evacuated %zu%s across %zu objects, " - "abandoned %zu%s across %zu objects.", - byte_size_in_proper_unit(_bytes_completed), proper_unit_for_byte_size(_bytes_completed), - _evacuations_completed, - byte_size_in_proper_unit(abandoned_size), proper_unit_for_byte_size(abandoned_size), - abandoned_count); -#endif + st->print_cr("Evacuated " PROPERFMT" across %zu objects, " + "abandoned " PROPERFMT " across %zu objects.", + PROPERFMTARGS(_bytes_completed), _evacuations_completed, + PROPERFMTARGS(abandoned_size), abandoned_count); +} + +void ShenandoahEvacuationStats::print_on(outputStream* st) const { + st->print("Young: "); _young.print_on(st); + st->print("Promotion: "); _promotion.print_on(st); + st->print("Old: "); _old.print_on(st); + if (_use_age_table) { _age_table->print_on(st); } @@ -103,10 +123,10 @@ void ShenandoahEvacuationTracker::print_global_on(outputStream* st) { void ShenandoahEvacuationTracker::print_evacuations_on(outputStream* st, ShenandoahEvacuationStats* workers, ShenandoahEvacuationStats* mutators) { - st->print("Workers: "); + st->print_cr("Workers: "); workers->print_on(st); st->cr(); - st->print("Mutators: "); + st->print_cr("Mutators: "); mutators->print_on(st); st->cr(); @@ -160,12 +180,12 @@ ShenandoahCycleStats ShenandoahEvacuationTracker::flush_cycle_to_global() { return {workers, mutators}; } -void ShenandoahEvacuationTracker::begin_evacuation(Thread* thread, size_t bytes) { - ShenandoahThreadLocalData::begin_evacuation(thread, bytes); +void ShenandoahEvacuationTracker::begin_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) { + ShenandoahThreadLocalData::begin_evacuation(thread, bytes, from, to); } -void ShenandoahEvacuationTracker::end_evacuation(Thread* thread, size_t bytes) { - ShenandoahThreadLocalData::end_evacuation(thread, bytes); +void ShenandoahEvacuationTracker::end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) { + ShenandoahThreadLocalData::end_evacuation(thread, bytes, from, to); } void ShenandoahEvacuationTracker::record_age(Thread* thread, size_t bytes, uint age) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp index 7d195656b11..e5d7a7fec94 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp @@ -26,14 +26,45 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHEVACTRACKER_HPP #include "gc/shared/ageTable.hpp" +#include "gc/shenandoah/shenandoahAffiliation.hpp" #include "utilities/ostream.hpp" class ShenandoahEvacuationStats : public CHeapObj { private: - size_t _evacuations_completed; - size_t _bytes_completed; - size_t _evacuations_attempted; - size_t _bytes_attempted; + struct ShenandoahEvacuations { + size_t _evacuations_completed; + size_t _bytes_completed; + size_t _evacuations_attempted; + size_t _bytes_attempted; + ShenandoahEvacuations() + : _evacuations_completed(0) + , _bytes_completed(0) + , _evacuations_attempted(0) + , _bytes_attempted(0) { + } + + void accumulate(const ShenandoahEvacuations& other) { + _evacuations_completed += other._evacuations_completed; + _bytes_completed += other._bytes_completed; + _evacuations_attempted += other._evacuations_attempted; + _bytes_attempted += other._bytes_attempted; + } + + void reset() { + _evacuations_completed = 0; + _bytes_completed = 0; + _evacuations_attempted = 0; + _bytes_attempted = 0; + } + + void print_on(outputStream* st) const; + }; + + ShenandoahEvacuations* get_category(ShenandoahAffiliation from, ShenandoahAffiliation to); + + ShenandoahEvacuations _young; + ShenandoahEvacuations _old; + ShenandoahEvacuations _promotion; bool _use_age_table; AgeTable* _age_table; @@ -43,11 +74,14 @@ private: AgeTable* age_table() const; - void begin_evacuation(size_t bytes); - void end_evacuation(size_t bytes); + // Record that the current thread is attempting to copy this many bytes. + void begin_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to); + + // Record that the current thread has completed copying this many bytes. + void end_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to); void record_age(size_t bytes, uint age); - void print_on(outputStream* st); + void print_on(outputStream* st) const; void accumulate(const ShenandoahEvacuationStats* other); void reset(); }; @@ -66,8 +100,12 @@ private: public: ShenandoahEvacuationTracker() = default; - void begin_evacuation(Thread* thread, size_t bytes); - void end_evacuation(Thread* thread, size_t bytes); + // Record that the given thread has begun to evacuate an object of this size. + void begin_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to); + + // Multiple threads may attempt to evacuate the same object, but only the successful thread will end the evacuation. + // Evacuations that were begun, but not ended are considered 'abandoned'. + void end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to); void record_age(Thread* thread, size_t bytes, uint age); void print_global_on(outputStream* st); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 56a2ff7e01a..293391a86eb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -816,6 +816,7 @@ HeapWord* ShenandoahFreeSet::allocate_single(ShenandoahAllocRequest& req, bool& switch (req.type()) { case ShenandoahAllocRequest::_alloc_tlab: case ShenandoahAllocRequest::_alloc_shared: + case ShenandoahAllocRequest::_alloc_cds: return allocate_for_mutator(req, in_new_region); case ShenandoahAllocRequest::_alloc_gclab: case ShenandoahAllocRequest::_alloc_plab: @@ -1169,8 +1170,8 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah return result; } -HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { - assert(req.is_mutator_alloc(), "All humongous allocations are performed by mutator"); +HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous) { + assert(req.is_mutator_alloc(), "All contiguous allocations are performed by mutator"); shenandoah_assert_heaplocked(); size_t words_size = req.size(); @@ -1244,10 +1245,16 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { assert(i == beg || _heap->get_region(i - 1)->index() + 1 == r->index(), "Should be contiguous"); assert(r->is_empty(), "Should be empty"); - if (i == beg) { - r->make_humongous_start(); + r->set_affiliation(req.affiliation()); + + if (is_humongous) { + if (i == beg) { + r->make_humongous_start(); + } else { + r->make_humongous_cont(); + } } else { - r->make_humongous_cont(); + r->make_regular_allocation(req.affiliation()); } // Trailing region may be non-full, record the remainder there @@ -1257,21 +1264,31 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) { } else { used_words = ShenandoahHeapRegion::region_size_words(); } - - r->set_affiliation(req.affiliation()); r->set_update_watermark(r->bottom()); r->set_top(r->bottom() + used_words); } generation->increase_affiliated_region_count(num); - // retire_range_from_partition() will adjust bounds on Mutator free set if appropriate - _partitions.retire_range_from_partition(ShenandoahFreeSetPartitionId::Mutator, beg, end); - - size_t total_humongous_size = ShenandoahHeapRegion::region_size_bytes() * num; - _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, total_humongous_size); + size_t total_used = 0; + if (is_humongous) { + // Humongous allocation retires all regions at once: no allocation is possible anymore. + _partitions.retire_range_from_partition(ShenandoahFreeSetPartitionId::Mutator, beg, end); + total_used = ShenandoahHeapRegion::region_size_bytes() * num; + } else { + // Non-humongous allocation retires only the regions that cannot be used for allocation anymore. + for (idx_t i = beg; i <= end; i++) { + ShenandoahHeapRegion* r = _heap->get_region(i); + if (r->free() < PLAB::min_size() * HeapWordSize) { + _partitions.retire_from_partition(ShenandoahFreeSetPartitionId::Mutator, i, r->used()); + } + total_used += r->used(); + } + } + _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, total_used); _partitions.assert_bounds(); + req.set_actual_size(words_size); - if (remainder != 0) { + if (remainder != 0 && is_humongous) { req.set_waste(ShenandoahHeapRegion::region_size_words() - remainder); } return _heap->get_region(beg)->bottom(); @@ -2060,7 +2077,10 @@ HeapWord* ShenandoahFreeSet::allocate(ShenandoahAllocRequest& req, bool& in_new_ case ShenandoahAllocRequest::_alloc_shared: case ShenandoahAllocRequest::_alloc_shared_gc: in_new_region = true; - return allocate_contiguous(req); + return allocate_contiguous(req, /* is_humongous = */ true); + case ShenandoahAllocRequest::_alloc_cds: + in_new_region = true; + return allocate_contiguous(req, /* is_humongous = */ false); case ShenandoahAllocRequest::_alloc_plab: case ShenandoahAllocRequest::_alloc_gclab: case ShenandoahAllocRequest::_alloc_tlab: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 55f23480618..8ad7055b3d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -345,7 +345,7 @@ private: // object. No other objects are packed into these regions. // // Precondition: ShenandoahHeapRegion::requires_humongous(req.size()) - HeapWord* allocate_contiguous(ShenandoahAllocRequest& req); + HeapWord* allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous); // Change region r from the Mutator partition to the GC's Collector or OldCollector partition. This requires that the // region is entirely empty. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 54cf8b978df..1cf8b78ef1a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved. - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -240,6 +240,7 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest // Cannot uncommit bitmap slices during concurrent reset ShenandoahNoUncommitMark forbid_region_uncommit(_heap); + _heap->print_before_gc(); switch (gc_mode()) { case concurrent_normal: { service_concurrent_normal_cycle(request); @@ -261,6 +262,7 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest default: ShouldNotReachHere(); } + _heap->print_after_gc(); } // If this cycle completed successfully, notify threads waiting for gc @@ -377,6 +379,8 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She TraceCollectorStats tcs(_heap->monitoring_support()->concurrent_collection_counters()); + _heap->increment_total_collections(false); + switch (original_state) { case ShenandoahOldGeneration::FILLING: { ShenandoahGCSession session(request.cause, old_generation); @@ -526,6 +530,7 @@ void ShenandoahGenerationalControlThread::service_concurrent_cycle(ShenandoahGen assert(!generation->is_old(), "Old GC takes a different control path"); ShenandoahConcurrentGC gc(generation, do_old_gc_bootstrap); + _heap->increment_total_collections(false); if (gc.collect(cause)) { // Cycle is complete _heap->notify_gc_progress(); @@ -593,6 +598,7 @@ bool ShenandoahGenerationalControlThread::check_cancellation_or_degen(Shenandoah } void ShenandoahGenerationalControlThread::service_stw_full_cycle(GCCause::Cause cause) { + _heap->increment_total_collections(true); ShenandoahGCSession session(cause, _heap->global_generation()); maybe_set_aging_cycle(); ShenandoahFullGC gc; @@ -602,6 +608,7 @@ void ShenandoahGenerationalControlThread::service_stw_full_cycle(GCCause::Cause void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const ShenandoahGCRequest& request) { assert(_degen_point != ShenandoahGC::_degenerated_unset, "Degenerated point should be set"); + _heap->increment_total_collections(false); ShenandoahGCSession session(request.cause, request.generation); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index a89fa76ba0f..d05ae713645 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -80,7 +80,6 @@ size_t ShenandoahGenerationalHeap::unsafe_max_tlab_alloc(Thread *thread) const { ShenandoahGenerationalHeap::ShenandoahGenerationalHeap(ShenandoahCollectorPolicy* policy) : ShenandoahHeap(policy), _age_census(nullptr), - _evac_tracker(new ShenandoahEvacuationTracker()), _min_plab_size(calculate_min_plab()), _max_plab_size(calculate_max_plab()), _regulator_thread(nullptr), @@ -100,18 +99,6 @@ void ShenandoahGenerationalHeap::print_init_logger() const { logger.print_all(); } -void ShenandoahGenerationalHeap::print_tracing_info() const { - ShenandoahHeap::print_tracing_info(); - - LogTarget(Info, gc, stats) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - ls.cr(); - ls.cr(); - evac_tracker()->print_global_on(&ls); - } -} - void ShenandoahGenerationalHeap::initialize_heuristics() { // Initialize global generation and heuristics even in generational mode. ShenandoahHeap::initialize_heuristics(); @@ -338,7 +325,7 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, Shena } // Copy the object: - NOT_PRODUCT(evac_tracker()->begin_evacuation(thread, size * HeapWordSize)); + NOT_PRODUCT(evac_tracker()->begin_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen)); Copy::aligned_disjoint_words(cast_from_oop(p), copy, size); oop copy_val = cast_to_oop(copy); @@ -360,7 +347,7 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, Shena ContinuationGCSupport::relativize_stack_chunk(copy_val); // Record that the evacuation succeeded - NOT_PRODUCT(evac_tracker()->end_evacuation(thread, size * HeapWordSize)); + NOT_PRODUCT(evac_tracker()->end_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen)); if (target_gen == OLD_GENERATION) { old_generation()->handle_evacuation(copy, size, from_region->is_young()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 930c8ef7105..f23e49735e9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -36,7 +36,6 @@ class ShenandoahGenerationalControlThread; class ShenandoahAgeCensus; class ShenandoahGenerationalHeap : public ShenandoahHeap { - void print_tracing_info() const override; void stop() override; public: @@ -66,8 +65,6 @@ private: ShenandoahSharedFlag _is_aging_cycle; // Age census used for adapting tenuring threshold ShenandoahAgeCensus* _age_census; - // Used primarily to look for failed evacuation attempts. - ShenandoahEvacuationTracker* _evac_tracker; public: void set_aging_cycle(bool cond) { @@ -83,9 +80,6 @@ public: return _age_census; } - ShenandoahEvacuationTracker* evac_tracker() const { - return _evac_tracker; - } // Ages regions that haven't been used for allocations in the current cycle. // Resets ages for regions that have been used for allocations. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 2dc768363d1..2baf7e25cba 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -567,7 +567,8 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _bitmap_region_special(false), _aux_bitmap_region_special(false), _liveness_cache(nullptr), - _collection_set(nullptr) + _collection_set(nullptr), + _evac_tracker(new ShenandoahEvacuationTracker()) { // Initialize GC mode early, many subsequent initialization procedures depend on it initialize_mode(); @@ -579,20 +580,35 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : #endif void ShenandoahHeap::print_heap_on(outputStream* st) const { - st->print_cr("Shenandoah Heap"); - st->print_cr(" %zu%s max, %zu%s soft max, %zu%s committed, %zu%s used", - byte_size_in_proper_unit(max_capacity()), proper_unit_for_byte_size(max_capacity()), - byte_size_in_proper_unit(soft_max_capacity()), proper_unit_for_byte_size(soft_max_capacity()), - byte_size_in_proper_unit(committed()), proper_unit_for_byte_size(committed()), - byte_size_in_proper_unit(used()), proper_unit_for_byte_size(used())); - st->print_cr(" %zu x %zu %s regions", - num_regions(), - byte_size_in_proper_unit(ShenandoahHeapRegion::region_size_bytes()), - proper_unit_for_byte_size(ShenandoahHeapRegion::region_size_bytes())); + const bool is_generational = mode()->is_generational(); + const char* front_spacing = ""; + if (is_generational) { + st->print_cr("Generational Shenandoah Heap"); + st->print_cr(" Young:"); + st->print_cr(" " PROPERFMT " max, " PROPERFMT " used", PROPERFMTARGS(young_generation()->max_capacity()), PROPERFMTARGS(young_generation()->used())); + st->print_cr(" Old:"); + st->print_cr(" " PROPERFMT " max, " PROPERFMT " used", PROPERFMTARGS(old_generation()->max_capacity()), PROPERFMTARGS(old_generation()->used())); + st->print_cr(" Entire heap:"); + st->print_cr(" " PROPERFMT " soft max, " PROPERFMT " committed", + PROPERFMTARGS(soft_max_capacity()), PROPERFMTARGS(committed())); + front_spacing = " "; + } else { + st->print_cr("Shenandoah Heap"); + st->print_cr(" " PROPERFMT " max, " PROPERFMT " soft max, " PROPERFMT " committed, " PROPERFMT " used", + PROPERFMTARGS(max_capacity()), + PROPERFMTARGS(soft_max_capacity()), + PROPERFMTARGS(committed()), + PROPERFMTARGS(used()) + ); + } + st->print_cr("%s %zu x " PROPERFMT " regions", + front_spacing, + num_regions(), + PROPERFMTARGS(ShenandoahHeapRegion::region_size_bytes())); st->print("Status: "); if (has_forwarded_objects()) st->print("has forwarded objects, "); - if (!mode()->is_generational()) { + if (!is_generational) { if (is_concurrent_mark_in_progress()) st->print("marking,"); } else { if (is_concurrent_old_mark_in_progress()) st->print("old marking, "); @@ -1352,6 +1368,7 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg } // Copy the object: + NOT_PRODUCT(evac_tracker()->begin_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen)); Copy::aligned_disjoint_words(cast_from_oop(p), copy, size); // Try to install the new forwarding pointer. @@ -1361,6 +1378,7 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg // Successfully evacuated. Our copy is now the public one! ContinuationGCSupport::relativize_stack_chunk(copy_val); shenandoah_assert_correct(nullptr, copy_val); + NOT_PRODUCT(evac_tracker()->end_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen)); return copy_val; } else { // Failed to evacuate. We need to deal with the object that is left behind. Since this @@ -1590,6 +1608,13 @@ void ShenandoahHeap::print_tracing_info() const { ResourceMark rm; LogStream ls(lt); +#ifdef NOT_PRODUCT + evac_tracker()->print_global_on(&ls); + + ls.cr(); + ls.cr(); +#endif + phase_timings()->print_global_on(&ls); ls.cr(); @@ -1741,12 +1766,14 @@ void ShenandoahHeap::object_iterate(ObjectClosure* cl) { bool ShenandoahHeap::prepare_aux_bitmap_for_iteration() { assert(SafepointSynchronize::is_at_safepoint(), "safe iteration is only available during safepoints"); - - if (!_aux_bitmap_region_special && !os::commit_memory((char*)_aux_bitmap_region.start(), _aux_bitmap_region.byte_size(), false)) { - log_warning(gc)("Could not commit native memory for auxiliary marking bitmap for heap iteration"); - return false; + if (!_aux_bitmap_region_special) { + bool success = os::commit_memory((char *) _aux_bitmap_region.start(), _aux_bitmap_region.byte_size(), false); + if (!success) { + log_warning(gc)("Auxiliary marking bitmap commit failed: " PTR_FORMAT " (%zu bytes)", + p2i(_aux_bitmap_region.start()), _aux_bitmap_region.byte_size()); + return false; + } } - // Reset bitmap _aux_bit_map.clear(); return true; } @@ -1762,8 +1789,13 @@ void ShenandoahHeap::scan_roots_for_iteration(ShenandoahScanObjectStack* oop_sta } void ShenandoahHeap::reclaim_aux_bitmap_for_iteration() { - if (!_aux_bitmap_region_special && !os::uncommit_memory((char*)_aux_bitmap_region.start(), _aux_bitmap_region.byte_size())) { - log_warning(gc)("Could not uncommit native memory for auxiliary marking bitmap for heap iteration"); + if (!_aux_bitmap_region_special) { + bool success = os::uncommit_memory((char*)_aux_bitmap_region.start(), _aux_bitmap_region.byte_size()); + if (!success) { + log_warning(gc)("Auxiliary marking bitmap uncommit failed: " PTR_FORMAT " (%zu bytes)", + p2i(_aux_bitmap_region.start()), _aux_bitmap_region.byte_size()); + assert(false, "Auxiliary marking bitmap uncommit should always succeed"); + } } } @@ -2555,18 +2587,14 @@ bool ShenandoahHeap::is_bitmap_slice_committed(ShenandoahHeapRegion* r, bool ski return false; } -bool ShenandoahHeap::commit_bitmap_slice(ShenandoahHeapRegion* r) { +void ShenandoahHeap::commit_bitmap_slice(ShenandoahHeapRegion* r) { shenandoah_assert_heaplocked(); - - // Bitmaps in special regions do not need commits - if (_bitmap_region_special) { - return true; - } + assert(!is_bitmap_region_special(), "Not for special memory"); if (is_bitmap_slice_committed(r, true)) { // Some other region from the group is already committed, meaning the bitmap // slice is already committed, we exit right away. - return true; + return; } // Commit the bitmap slice: @@ -2575,39 +2603,34 @@ bool ShenandoahHeap::commit_bitmap_slice(ShenandoahHeapRegion* r) { size_t len = _bitmap_bytes_per_slice; char* start = (char*) _bitmap_region.start() + off; - if (!os::commit_memory(start, len, false)) { - return false; - } + os::commit_memory_or_exit(start, len, false, "Unable to commit bitmap slice"); if (AlwaysPreTouch) { os::pretouch_memory(start, start + len, _pretouch_bitmap_page_size); } - - return true; } -bool ShenandoahHeap::uncommit_bitmap_slice(ShenandoahHeapRegion *r) { +void ShenandoahHeap::uncommit_bitmap_slice(ShenandoahHeapRegion *r) { shenandoah_assert_heaplocked(); - - // Bitmaps in special regions do not need uncommits - if (_bitmap_region_special) { - return true; - } + assert(!is_bitmap_region_special(), "Not for special memory"); if (is_bitmap_slice_committed(r, true)) { // Some other region from the group is still committed, meaning the bitmap // slice should stay committed, exit right away. - return true; + return; } // Uncommit the bitmap slice: size_t slice = r->index() / _bitmap_regions_per_slice; size_t off = _bitmap_bytes_per_slice * slice; size_t len = _bitmap_bytes_per_slice; - if (!os::uncommit_memory((char*)_bitmap_region.start() + off, len)) { - return false; + + char* addr = (char*) _bitmap_region.start() + off; + bool success = os::uncommit_memory(addr, len); + if (!success) { + log_warning(gc)("Bitmap slice uncommit failed: " PTR_FORMAT " (%zu bytes)", p2i(addr), len); + assert(false, "Bitmap slice uncommit should always succeed"); } - return true; } void ShenandoahHeap::forbid_uncommit() { @@ -2750,41 +2773,15 @@ bool ShenandoahHeap::requires_barriers(stackChunkOop obj) const { HeapWord* ShenandoahHeap::allocate_loaded_archive_space(size_t size) { #if INCLUDE_CDS_JAVA_HEAP - // CDS wants a continuous memory range to load a bunch of objects. - // This effectively bypasses normal allocation paths, and requires - // a bit of massaging to unbreak GC invariants. + // CDS wants a raw continuous memory range to load a bunch of objects itself. + // This is an unusual request, since all requested regions should be regular, not humongous. + // + // CDS would guarantee no objects straddle multiple regions, as long as regions are as large + // as MIN_GC_REGION_ALIGNMENT. + guarantee(ShenandoahHeapRegion::region_size_bytes() >= ArchiveHeapWriter::MIN_GC_REGION_ALIGNMENT, "Must be"); - ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared(size); - - // Easy case: a single regular region, no further adjustments needed. - if (!ShenandoahHeapRegion::requires_humongous(size)) { - return allocate_memory(req); - } - - // Hard case: the requested size would cause a humongous allocation. - // We need to make sure it looks like regular allocation to the rest of GC. - - // CDS code would guarantee no objects straddle multiple regions, as long as - // regions are as large as MIN_GC_REGION_ALIGNMENT. It is impractical at this - // point to deal with case when Shenandoah runs with smaller regions. - // TODO: This check can be dropped once MIN_GC_REGION_ALIGNMENT agrees more with Shenandoah. - if (ShenandoahHeapRegion::region_size_bytes() < ArchiveHeapWriter::MIN_GC_REGION_ALIGNMENT) { - return nullptr; - } - - HeapWord* mem = allocate_memory(req); - size_t start_idx = heap_region_index_containing(mem); - size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); - - // Flip humongous -> regular. - { - ShenandoahHeapLocker locker(lock(), false); - for (size_t c = start_idx; c < start_idx + num_regions; c++) { - get_region(c)->make_regular_bypass(); - } - } - - return mem; + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_cds(size); + return allocate_memory(req); #else assert(false, "Archive heap loader should not be available, should not be here"); return nullptr; @@ -2811,17 +2808,25 @@ void ShenandoahHeap::complete_loaded_archive_space(MemRegion archive_space) { "Archive space should be fully used: " PTR_FORMAT " " PTR_FORMAT, p2i(cur), p2i(end)); - // Region bounds are good. - ShenandoahHeapRegion* begin_reg = heap_region_containing(start); - ShenandoahHeapRegion* end_reg = heap_region_containing(end); - assert(begin_reg->is_regular(), "Must be"); - assert(end_reg->is_regular(), "Must be"); - assert(begin_reg->bottom() == start, - "Must agree: archive-space-start: " PTR_FORMAT ", begin-region-bottom: " PTR_FORMAT, - p2i(start), p2i(begin_reg->bottom())); - assert(end_reg->top() == end, - "Must agree: archive-space-end: " PTR_FORMAT ", end-region-top: " PTR_FORMAT, - p2i(end), p2i(end_reg->top())); + // All regions in contiguous space have good state. + size_t begin_reg_idx = heap_region_index_containing(start); + size_t end_reg_idx = heap_region_index_containing(end); + + for (size_t idx = begin_reg_idx; idx <= end_reg_idx; idx++) { + ShenandoahHeapRegion* r = get_region(idx); + assert(r->is_regular(), "Must be regular"); + assert(r->is_young(), "Must be young"); + assert(idx == end_reg_idx || r->top() == r->end(), + "All regions except the last one should be full: " PTR_FORMAT " " PTR_FORMAT, + p2i(r->top()), p2i(r->end())); + assert(idx != begin_reg_idx || r->bottom() == start, + "Archive space start should be at the bottom of first region: " PTR_FORMAT " " PTR_FORMAT, + p2i(r->bottom()), p2i(start)); + assert(idx != end_reg_idx || r->top() == end, + "Archive space end should be at the top of last region: " PTR_FORMAT " " PTR_FORMAT, + p2i(r->top()), p2i(end)); + } + #endif } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index a32334019f1..bec1235e941 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -557,6 +557,10 @@ public: ShenandoahEvacOOMHandler* oom_evac_handler() { return &_oom_evac_handler; } + ShenandoahEvacuationTracker* evac_tracker() const { + return _evac_tracker; + } + void on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation); void on_cycle_end(ShenandoahGeneration* generation); @@ -681,7 +685,7 @@ public: // ---------- CDS archive support - bool can_load_archived_objects() const override { return !ShenandoahCardBarrier; } + bool can_load_archived_objects() const override { return true; } HeapWord* allocate_loaded_archive_space(size_t size) override; void complete_loaded_archive_space(MemRegion archive_space) override; @@ -757,8 +761,9 @@ public: inline bool requires_marking(const void* entry) const; // Support for bitmap uncommits - bool commit_bitmap_slice(ShenandoahHeapRegion *r); - bool uncommit_bitmap_slice(ShenandoahHeapRegion *r); + void commit_bitmap_slice(ShenandoahHeapRegion *r); + void uncommit_bitmap_slice(ShenandoahHeapRegion *r); + bool is_bitmap_region_special() { return _bitmap_region_special; } bool is_bitmap_slice_committed(ShenandoahHeapRegion* r, bool skip_self = false); // During concurrent reset, the control thread will zero out the mark bitmaps for committed regions. @@ -789,6 +794,10 @@ private: oop try_evacuate_object(oop src, Thread* thread, ShenandoahHeapRegion* from_region, ShenandoahAffiliation target_gen); +protected: + // Used primarily to look for failed evacuation attempts. + ShenandoahEvacuationTracker* _evac_tracker; + public: static address in_cset_fast_test_addr(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index 05eb0c299a5..3eb7ccba35a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -142,27 +142,17 @@ void ShenandoahHeapRegion::make_affiliated_maybe() { void ShenandoahHeapRegion::make_regular_bypass() { shenandoah_assert_heaplocked(); - assert (!Universe::is_fully_initialized() || - ShenandoahHeap::heap()->is_full_gc_in_progress() || + assert (ShenandoahHeap::heap()->is_full_gc_in_progress() || ShenandoahHeap::heap()->is_degenerated_gc_in_progress(), - "Only for STW GC or when Universe is initializing (CDS)"); + "Only for STW GC"); reset_age(); - auto cur_state = state(); - switch (cur_state) { + switch (state()) { case _empty_uncommitted: do_commit(); case _empty_committed: case _cset: case _humongous_start: case _humongous_cont: - if (cur_state == _humongous_start || cur_state == _humongous_cont) { - // CDS allocates chunks of the heap to fill with regular objects. The allocator - // will dutifully track any waste in the unused portion of the last region. Once - // CDS has finished initializing the objects, it will convert these regions to - // regular regions. The 'waste' in the last region is no longer wasted at this point, - // so we must stop treating it as such. - decrement_humongous_waste(); - } set_state(_regular); return; case _pinned_cset: @@ -804,25 +794,32 @@ size_t ShenandoahHeapRegion::setup_sizes(size_t max_heap_size) { void ShenandoahHeapRegion::do_commit() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (!heap->is_heap_region_special() && !os::commit_memory((char *) bottom(), RegionSizeBytes, false)) { - report_java_out_of_memory("Unable to commit region"); + if (!heap->is_heap_region_special()) { + os::commit_memory_or_exit((char*) bottom(), RegionSizeBytes, false, "Unable to commit region"); } - if (!heap->commit_bitmap_slice(this)) { - report_java_out_of_memory("Unable to commit bitmaps for region"); + if (!heap->is_bitmap_region_special()) { + heap->commit_bitmap_slice(this); } if (AlwaysPreTouch) { os::pretouch_memory(bottom(), end(), heap->pretouch_heap_page_size()); } + if (ZapUnusedHeapArea) { + SpaceMangler::mangle_region(MemRegion(bottom(), end())); + } heap->increase_committed(ShenandoahHeapRegion::region_size_bytes()); } void ShenandoahHeapRegion::do_uncommit() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (!heap->is_heap_region_special() && !os::uncommit_memory((char *) bottom(), RegionSizeBytes)) { - report_java_out_of_memory("Unable to uncommit region"); + if (!heap->is_heap_region_special()) { + bool success = os::uncommit_memory((char *) bottom(), RegionSizeBytes); + if (!success) { + log_warning(gc)("Region uncommit failed: " PTR_FORMAT " (%zu bytes)", p2i(bottom()), RegionSizeBytes); + assert(false, "Region uncommit should always succeed"); + } } - if (!heap->uncommit_bitmap_slice(this)) { - report_java_out_of_memory("Unable to uncommit bitmaps for region"); + if (!heap->is_bitmap_region_special()) { + heap->uncommit_bitmap_slice(this); } heap->decrease_committed(ShenandoahHeapRegion::region_size_bytes()); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp index 503f6656153..53f86c8cc58 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp @@ -112,6 +112,7 @@ inline void ShenandoahHeapRegion::adjust_alloc_metadata(ShenandoahAllocRequest:: switch (type) { case ShenandoahAllocRequest::_alloc_shared: case ShenandoahAllocRequest::_alloc_shared_gc: + case ShenandoahAllocRequest::_alloc_cds: // Counted implicitly by tlab/gclab allocs break; case ShenandoahAllocRequest::_alloc_tlab: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp index c444a0ba86a..dd500462d0f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp @@ -44,10 +44,7 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() : _plab_promoted(0), _plab_allows_promotion(true), _plab_retries_enabled(true), - _evacuation_stats(nullptr) { - if (ShenandoahHeap::heap()->mode()->is_generational()) { - _evacuation_stats = new ShenandoahEvacuationStats(); - } + _evacuation_stats(new ShenandoahEvacuationStats()) { } ShenandoahThreadLocalData::~ShenandoahThreadLocalData() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index c1cebdf1dde..098e20a72ec 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -30,6 +30,7 @@ #include "gc/shared/gcThreadLocalData.hpp" #include "gc/shared/plab.hpp" #include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "gc/shenandoah/shenandoahAffiliation.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahCodeRoots.hpp" @@ -159,12 +160,12 @@ public: data(thread)->_gclab_size = v; } - static void begin_evacuation(Thread* thread, size_t bytes) { - data(thread)->_evacuation_stats->begin_evacuation(bytes); + static void begin_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) { + data(thread)->_evacuation_stats->begin_evacuation(bytes, from, to); } - static void end_evacuation(Thread* thread, size_t bytes) { - data(thread)->_evacuation_stats->end_evacuation(bytes); + static void end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) { + data(thread)->_evacuation_stats->end_evacuation(bytes, from, to); } static void record_age(Thread* thread, size_t bytes, uint age) { @@ -172,7 +173,6 @@ public: } static ShenandoahEvacuationStats* evacuation_stats(Thread* thread) { - shenandoah_assert_generational(); return data(thread)->_evacuation_stats; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 727b90e82a2..33b8744be3d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved. + * Copyright (c) 2017, 2025, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -149,15 +149,21 @@ private: "oop must be in heap bounds"); check(ShenandoahAsserts::_safe_unknown, obj, is_object_aligned(obj), "oop must be aligned"); + check(ShenandoahAsserts::_safe_unknown, obj, os::is_readable_pointer(obj), + "oop must be accessible"); ShenandoahHeapRegion *obj_reg = _heap->heap_region_containing(obj); - Klass* obj_klass = ShenandoahForwarding::klass(obj); + + narrowKlass nk = 0; + const Klass* obj_klass = nullptr; + const bool klass_valid = ShenandoahAsserts::extract_klass_safely(obj, nk, obj_klass); + + check(ShenandoahAsserts::_safe_unknown, obj, klass_valid, + "Object klass pointer unreadable or invalid"); // Verify that obj is not in dead space: { // Do this before touching obj->size() - check(ShenandoahAsserts::_safe_unknown, obj, obj_klass != nullptr, - "Object klass pointer should not be null"); check(ShenandoahAsserts::_safe_unknown, obj, Metaspace::contains(obj_klass), "Object klass pointer must go to metaspace"); diff --git a/src/hotspot/share/gc/z/zAddressSpaceLimit.cpp b/src/hotspot/share/gc/z/zAddressSpaceLimit.cpp index 9a6fb12779a..862aef89e62 100644 --- a/src/hotspot/share/gc/z/zAddressSpaceLimit.cpp +++ b/src/hotspot/share/gc/z/zAddressSpaceLimit.cpp @@ -30,25 +30,14 @@ #include "utilities/align.hpp" #include "utilities/ostream.hpp" -static size_t address_space_limit() { - size_t limit = 0; - - if (os::has_allocatable_memory_limit(&limit)) { - return limit; - } - - // No limit - return SIZE_MAX; -} - size_t ZAddressSpaceLimit::heap() { // Allow the heap to occupy 50% of the address space - const size_t limit = address_space_limit() / MaxVirtMemFraction; + const size_t limit = os::reserve_memory_limit() / MaxVirtMemFraction; return align_up(limit, ZGranuleSize); } void ZAddressSpaceLimit::print_limits() { - const size_t limit = address_space_limit(); + const size_t limit = os::reserve_memory_limit(); if (limit == SIZE_MAX) { log_info_p(gc, init)("Address Space Size: unlimited"); diff --git a/src/hotspot/share/gc/z/zAllocator.cpp b/src/hotspot/share/gc/z/zAllocator.cpp deleted file mode 100644 index dd1ab3b712a..00000000000 --- a/src/hotspot/share/gc/z/zAllocator.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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 "gc/z/zAllocator.hpp" -#include "gc/z/zObjectAllocator.hpp" -#include "gc/z/zPageAge.inline.hpp" - -ZAllocatorEden* ZAllocator::_eden; -ZAllocatorForRelocation* ZAllocator::_relocation[ZAllocator::_relocation_allocators]; - -ZAllocator::ZAllocator(ZPageAge age) - : _object_allocator(age) {} - -void ZAllocator::retire_pages() { - _object_allocator.retire_pages(); -} - -ZAllocatorEden::ZAllocatorEden() - : ZAllocator(ZPageAge::eden) { - ZAllocator::_eden = this; -} - -size_t ZAllocatorEden::remaining() const { - return _object_allocator.remaining(); -} - -ZPageAge ZAllocatorForRelocation::install() { - for (uint i = 0; i < ZAllocator::_relocation_allocators; ++i) { - if (_relocation[i] == nullptr) { - _relocation[i] = this; - return to_zpageage(i + 1); - } - } - - ShouldNotReachHere(); - return ZPageAge::eden; -} - -ZAllocatorForRelocation::ZAllocatorForRelocation() - : ZAllocator(install()) {} - -zaddress ZAllocatorForRelocation::alloc_object(size_t size) { - return _object_allocator.alloc_object_for_relocation(size); -} - -void ZAllocatorForRelocation::undo_alloc_object(zaddress addr, size_t size) { - _object_allocator.undo_alloc_object_for_relocation(addr, size); -} - -ZPage* ZAllocatorForRelocation::alloc_page_for_relocation(ZPageType type, size_t size, ZAllocationFlags flags) { - return _object_allocator.alloc_page_for_relocation(type, size, flags); -} diff --git a/src/hotspot/share/gc/z/zAllocator.hpp b/src/hotspot/share/gc/z/zAllocator.hpp deleted file mode 100644 index ec4c0ec1c9c..00000000000 --- a/src/hotspot/share/gc/z/zAllocator.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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_GC_Z_ZALLOCATOR_HPP -#define SHARE_GC_Z_ZALLOCATOR_HPP - -#include "gc/z/zAllocationFlags.hpp" -#include "gc/z/zObjectAllocator.hpp" -#include "gc/z/zPageAge.hpp" -#include "gc/z/zPageType.hpp" - -class ZAllocatorEden; -class ZAllocatorForRelocation; -class ZPage; - -class ZAllocator { -public: - static constexpr uint _relocation_allocators = ZPageAgeCount - 1; - -protected: - ZObjectAllocator _object_allocator; - - static ZAllocatorEden* _eden; - static ZAllocatorForRelocation* _relocation[ZAllocator::_relocation_allocators]; - -public: - static ZAllocatorEden* eden(); - static ZAllocatorForRelocation* relocation(ZPageAge page_age); - static ZAllocatorForRelocation* old(); - - ZAllocator(ZPageAge age); - - void retire_pages(); -}; - -class ZAllocatorEden : public ZAllocator { -public: - ZAllocatorEden(); - - // Mutator allocation - zaddress alloc_tlab(size_t size); - zaddress alloc_object(size_t size); - - // Statistics - size_t remaining() const; -}; - -class ZAllocatorForRelocation : public ZAllocator { -private: - ZPageAge install(); - -public: - ZAllocatorForRelocation(); - - // Relocation - zaddress alloc_object(size_t size); - void undo_alloc_object(zaddress addr, size_t size); - - ZPage* alloc_page_for_relocation(ZPageType type, size_t size, ZAllocationFlags flags); -}; - -#endif // SHARE_GC_Z_ZALLOCATOR_HPP diff --git a/src/hotspot/share/gc/z/zAllocator.inline.hpp b/src/hotspot/share/gc/z/zAllocator.inline.hpp deleted file mode 100644 index ec64676dbf0..00000000000 --- a/src/hotspot/share/gc/z/zAllocator.inline.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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_GC_Z_ZALLOCATOR_INLINE_HPP -#define SHARE_GC_Z_ZALLOCATOR_INLINE_HPP - -#include "gc/z/zAllocator.hpp" - -#include "gc/z/zAddress.inline.hpp" -#include "gc/z/zHeap.hpp" -#include "gc/z/zPageAge.inline.hpp" - -inline ZAllocatorEden* ZAllocator::eden() { - return _eden; -} - -inline ZAllocatorForRelocation* ZAllocator::relocation(ZPageAge page_age) { - return _relocation[untype(page_age - 1)]; -} - -inline ZAllocatorForRelocation* ZAllocator::old() { - return relocation(ZPageAge::old); -} - -inline zaddress ZAllocatorEden::alloc_tlab(size_t size) { - guarantee(size <= ZHeap::heap()->max_tlab_size(), "TLAB too large"); - return _object_allocator.alloc_object(size); -} - -inline zaddress ZAllocatorEden::alloc_object(size_t size) { - const zaddress addr = _object_allocator.alloc_object(size); - - if (is_null(addr)) { - ZHeap::heap()->out_of_memory(); - } - - return addr; -} - -#endif // SHARE_GC_Z_ZALLOCATOR_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp index 87cfeb012ec..238b5b06683 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.cpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp @@ -27,7 +27,6 @@ #include "gc/shared/suspendibleThreadSet.hpp" #include "gc/z/zAbort.hpp" #include "gc/z/zAddress.inline.hpp" -#include "gc/z/zAllocator.inline.hpp" #include "gc/z/zCollectedHeap.hpp" #include "gc/z/zContinuation.inline.hpp" #include "gc/z/zDirector.hpp" @@ -136,7 +135,7 @@ bool ZCollectedHeap::requires_barriers(stackChunkOop obj) const { HeapWord* ZCollectedHeap::allocate_new_tlab(size_t min_size, size_t requested_size, size_t* actual_size) { const size_t size_in_bytes = ZUtils::words_to_bytes(align_object_size(requested_size)); - const zaddress addr = ZAllocator::eden()->alloc_tlab(size_in_bytes); + const zaddress addr = ZHeap::heap()->alloc_tlab(size_in_bytes); if (!is_null(addr)) { *actual_size = requested_size; @@ -152,7 +151,7 @@ oop ZCollectedHeap::array_allocate(Klass* klass, size_t size, int length, bool d HeapWord* ZCollectedHeap::mem_allocate(size_t size, bool* gc_overhead_limit_was_exceeded) { const size_t size_in_bytes = ZUtils::words_to_bytes(align_object_size(size)); - return (HeapWord*)ZAllocator::eden()->alloc_object(size_in_bytes); + return (HeapWord*)ZHeap::heap()->alloc_object(size_in_bytes); } MetaWord* ZCollectedHeap::satisfy_failed_metadata_allocation(ClassLoaderData* loader_data, diff --git a/src/hotspot/share/gc/z/zDeferredConstructed.hpp b/src/hotspot/share/gc/z/zDeferredConstructed.hpp new file mode 100644 index 00000000000..16aa4b5e00f --- /dev/null +++ b/src/hotspot/share/gc/z/zDeferredConstructed.hpp @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please 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_GC_Z_ZDEFERREDCONSTRUCTED_HPP +#define SHARE_GC_Z_ZDEFERREDCONSTRUCTED_HPP + +#include "utilities/globalDefinitions.hpp" + +template +class ZDeferredConstructed { + union { + T _t; + }; + + DEBUG_ONLY(bool _initialized;) + + NONCOPYABLE(ZDeferredConstructed); + +public: + ZDeferredConstructed(); + ~ZDeferredConstructed(); + + T* get(); + const T* get() const; + + T& operator*(); + const T& operator*() const; + + T* operator->(); + const T* operator->() const; + + template + void initialize(Ts&&... args); +}; + +#endif // SHARE_GC_Z_ZDEFERREDCONSTRUCTED_HPP diff --git a/src/hotspot/share/gc/z/zDeferredConstructed.inline.hpp b/src/hotspot/share/gc/z/zDeferredConstructed.inline.hpp new file mode 100644 index 00000000000..2b32d8f20bb --- /dev/null +++ b/src/hotspot/share/gc/z/zDeferredConstructed.inline.hpp @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please 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_GC_Z_ZDEFERREDCONSTRUCTED_INLINE_HPP +#define SHARE_GC_Z_ZDEFERREDCONSTRUCTED_INLINE_HPP + +#include "gc/z/zDeferredConstructed.hpp" + +#include +#include + +template +inline ZDeferredConstructed::ZDeferredConstructed() + DEBUG_ONLY(: _initialized(false)) { + // Do not construct value immediately. Value is constructed at a later point + // in time using initialize(). +} + +template +inline ZDeferredConstructed::~ZDeferredConstructed() { + assert(_initialized, "must be initialized before being destructed"); + _t.~T(); +} + +template +inline T* ZDeferredConstructed::get() { + assert(_initialized, "must be initialized before access"); + return &_t; +} + +template +inline const T* ZDeferredConstructed::get() const { + assert(_initialized, "must be initialized before access"); + return &_t; +} + +template +inline T& ZDeferredConstructed::operator*() { + return *get(); +} + +template +inline const T& ZDeferredConstructed::operator*() const { + return *get(); +} + +template +inline T* ZDeferredConstructed::operator->() { + return get(); +} + +template +inline const T* ZDeferredConstructed::operator->() const { + return get(); +} + +template +template +inline void ZDeferredConstructed::initialize(Ts&&... args) { + assert(!_initialized, "Double initialization forbidden"); + DEBUG_ONLY(_initialized = true;) + using NCVP = std::add_pointer_t>; + ::new (const_cast(get())) T(args...); +} + +#endif // SHARE_GC_Z_ZDEFERREDCONSTRUCTED_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index f1256eecd19..e46b1c9e167 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -556,7 +556,7 @@ static double calculate_young_to_old_worker_ratio(const ZDirectorStats& stats) { 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; + return MIN2(old_vs_young_efficiency_ratio, (double)ZOldGCThreads); } static bool rule_major_proactive(const ZDirectorStats& stats) { diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index 29c4d1fcc6a..27697be6837 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -28,7 +28,6 @@ #include "gc/shared/gcVMOperations.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -#include "gc/z/zAllocator.inline.hpp" #include "gc/z/zBarrierSet.hpp" #include "gc/z/zBarrierSetAssembler.hpp" #include "gc/z/zBarrierSetNMethod.hpp" @@ -41,6 +40,7 @@ #include "gc/z/zHeap.inline.hpp" #include "gc/z/zJNICritical.hpp" #include "gc/z/zMark.inline.hpp" +#include "gc/z/zObjectAllocator.hpp" #include "gc/z/zPageAge.inline.hpp" #include "gc/z/zPageAllocator.hpp" #include "gc/z/zRelocationSet.inline.hpp" @@ -704,7 +704,7 @@ uint ZGenerationYoung::compute_tenuring_threshold(ZRelocationSetSelectorStats st uint last_populated_age = 0; size_t last_populated_live = 0; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { const size_t young_live = stats.small(age).live() + stats.medium(age).live() + stats.large(age).live(); if (young_live > 0) { last_populated_age = untype(age); @@ -845,10 +845,7 @@ void ZGenerationYoung::mark_start() { ZHeap::heap()->reset_tlab_used(); // Retire allocating pages - ZAllocator::eden()->retire_pages(); - for (ZPageAge age : ZPageAgeRangeSurvivor) { - ZAllocator::relocation(age)->retire_pages(); - } + ZHeap::heap()->retire_allocating_pages(ZPageAgeRangeYoung); // Reset allocated/reclaimed/used statistics reset_statistics(); @@ -1205,7 +1202,7 @@ void ZGenerationOld::mark_start() { flip_mark_start(); // Retire allocating pages - ZAllocator::old()->retire_pages(); + ZHeap::heap()->retire_allocating_pages(ZPageAgeRangeOld); // Reset allocated/reclaimed/used statistics reset_statistics(); diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 2c1cb5a69cb..e43336c8ea3 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -34,6 +34,7 @@ #include "gc/z/zHeapIterator.hpp" #include "gc/z/zHeuristics.hpp" #include "gc/z/zInitialize.hpp" +#include "gc/z/zObjectAllocator.hpp" #include "gc/z/zPage.inline.hpp" #include "gc/z/zPageTable.inline.hpp" #include "gc/z/zResurrection.hpp" @@ -51,14 +52,15 @@ static const ZStatCounter ZCounterUndoPageAllocation("Memory", "Undo Page Allocation", ZStatUnitOpsPerSecond); static const ZStatCounter ZCounterOutOfMemory("Memory", "Out Of Memory", ZStatUnitOpsPerSecond); +static const ZStatCounter ZCounterUndoObjectAllocationSucceeded("Memory", "Undo Object Allocation Succeeded", ZStatUnitOpsPerSecond); +static const ZStatCounter ZCounterUndoObjectAllocationFailed("Memory", "Undo Object Allocation Failed", ZStatUnitOpsPerSecond); ZHeap* ZHeap::_heap = nullptr; ZHeap::ZHeap() : _page_allocator(MinHeapSize, InitialHeapSize, SoftMaxHeapSize, MaxHeapSize), _page_table(), - _allocator_eden(), - _allocator_relocation(), + _object_allocator(), _serviceability(InitialHeapSize, min_capacity(), max_capacity()), _old(&_page_table, &_page_allocator), _young(&_page_table, _old.forwarding_table(), &_page_allocator), @@ -144,7 +146,7 @@ size_t ZHeap::max_tlab_size() const { } size_t ZHeap::unsafe_max_tlab_alloc() const { - size_t size = _allocator_eden.remaining(); + size_t size = _object_allocator.fast_available(ZPageAge::eden); if (size < MinTLABSize) { // The remaining space in the allocator is not enough to @@ -157,6 +159,7 @@ size_t ZHeap::unsafe_max_tlab_alloc() const { return MIN2(size, max_tlab_size()); } + void ZHeap::reset_tlab_used() { _tlab_usage.reset(); } @@ -216,7 +219,7 @@ void ZHeap::threads_do(ThreadClosure* tc) const { _old.threads_do(tc); } -void ZHeap::out_of_memory() { +void ZHeap::out_of_memory() const { ResourceMark rm; ZStatInc(ZCounterOutOfMemory); @@ -292,6 +295,21 @@ size_t ZHeap::free_empty_pages(ZGenerationId id, const ZArray* pages) { return freed; } +void ZHeap::undo_alloc_object_for_relocation(zaddress addr, size_t size) { + ZPage* const page = this->page(addr); + + if (page->is_large()) { + undo_alloc_page(page); + ZStatInc(ZCounterUndoObjectAllocationSucceeded); + } else { + if (page->undo_alloc_object_atomic(addr, size)) { + ZStatInc(ZCounterUndoObjectAllocationSucceeded); + } else { + ZStatInc(ZCounterUndoObjectAllocationFailed); + } + } +} + void ZHeap::keep_alive(oop obj) { const zaddress addr = to_zaddress(obj); ZBarrier::mark(addr); diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index 49af5d66880..f7d606fc260 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -25,9 +25,9 @@ #define SHARE_GC_Z_ZHEAP_HPP #include "gc/z/zAllocationFlags.hpp" -#include "gc/z/zAllocator.hpp" #include "gc/z/zArray.hpp" #include "gc/z/zGeneration.hpp" +#include "gc/z/zObjectAllocator.hpp" #include "gc/z/zPageAge.hpp" #include "gc/z/zPageAllocator.hpp" #include "gc/z/zPageTable.hpp" @@ -43,22 +43,21 @@ class ZHeap { friend class VMStructs; private: - static ZHeap* _heap; + static ZHeap* _heap; - ZPageAllocator _page_allocator; - ZPageTable _page_table; + ZPageAllocator _page_allocator; + ZPageTable _page_table; - ZAllocatorEden _allocator_eden; - ZAllocatorForRelocation _allocator_relocation[ZAllocator::_relocation_allocators]; + ZObjectAllocator _object_allocator; - ZServiceability _serviceability; + ZServiceability _serviceability; - ZGenerationOld _old; - ZGenerationYoung _young; + ZGenerationOld _old; + ZGenerationYoung _young; - ZTLABUsage _tlab_usage; + ZTLABUsage _tlab_usage; - bool _initialized; + bool _initialized; // Page allocation accounting void account_alloc_page(ZPage* page); @@ -71,7 +70,7 @@ public: bool is_initialized() const; - void out_of_memory(); + void out_of_memory() const; // Heap metrics size_t min_capacity() const; @@ -113,8 +112,14 @@ public: void undo_alloc_page(ZPage* page); void free_page(ZPage* page); size_t free_empty_pages(ZGenerationId id, const ZArray* pages); + void retire_allocating_pages(ZPageAgeRange range); // Object allocation + zaddress alloc_object(size_t size); + zaddress alloc_tlab(size_t size); + zaddress alloc_object_for_relocation(size_t size, ZPageAge age); + void undo_alloc_object_for_relocation(zaddress addr, size_t size); + bool is_alloc_stalling() const; bool is_alloc_stalling_for_old() const; void handle_alloc_stalling_for_young(); diff --git a/src/hotspot/share/gc/z/zHeap.inline.hpp b/src/hotspot/share/gc/z/zHeap.inline.hpp index 26c1b2d1e62..122833cb46e 100644 --- a/src/hotspot/share/gc/z/zHeap.inline.hpp +++ b/src/hotspot/share/gc/z/zHeap.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ #include "gc/z/zForwardingTable.inline.hpp" #include "gc/z/zGenerationId.hpp" #include "gc/z/zMark.inline.hpp" +#include "gc/z/zObjectAllocator.hpp" #include "gc/z/zPage.inline.hpp" #include "gc/z/zPageTable.inline.hpp" #include "gc/z/zRemembered.inline.hpp" @@ -74,6 +75,29 @@ inline bool ZHeap::is_object_strongly_live(zaddress addr) const { return page->is_object_strongly_live(addr); } +inline void ZHeap::retire_allocating_pages(ZPageAgeRange range) { + _object_allocator.retire_pages(range); +} + +inline zaddress ZHeap::alloc_object(size_t size) { + const zaddress addr = _object_allocator.alloc(size); + + if (is_null(addr)) { + out_of_memory(); + } + + return addr; +} + +inline zaddress ZHeap::alloc_tlab(size_t size) { + guarantee(size <= max_tlab_size(), "TLAB too large"); + return _object_allocator.alloc(size); +} + +inline zaddress ZHeap::alloc_object_for_relocation(size_t size, ZPageAge age) { + return _object_allocator.alloc_for_relocation(size, age); +} + inline bool ZHeap::is_alloc_stalling() const { return _page_allocator.is_alloc_stalling(); } diff --git a/src/hotspot/share/gc/z/zObjectAllocator.cpp b/src/hotspot/share/gc/z/zObjectAllocator.cpp index 46baec28094..c6dd0a1a152 100644 --- a/src/hotspot/share/gc/z/zObjectAllocator.cpp +++ b/src/hotspot/share/gc/z/zObjectAllocator.cpp @@ -21,57 +21,50 @@ * questions. */ +#include "gc/z/zDeferredConstructed.inline.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zHeap.inline.hpp" #include "gc/z/zHeuristics.hpp" #include "gc/z/zLock.inline.hpp" #include "gc/z/zObjectAllocator.hpp" #include "gc/z/zPage.inline.hpp" -#include "gc/z/zPageTable.inline.hpp" -#include "gc/z/zStat.hpp" +#include "gc/z/zPageAge.inline.hpp" +#include "gc/z/zPageType.hpp" #include "gc/z/zValue.inline.hpp" -#include "logging/log.hpp" #include "runtime/atomic.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" -static const ZStatCounter ZCounterUndoObjectAllocationSucceeded("Memory", "Undo Object Allocation Succeeded", ZStatUnitOpsPerSecond); -static const ZStatCounter ZCounterUndoObjectAllocationFailed("Memory", "Undo Object Allocation Failed", ZStatUnitOpsPerSecond); - -ZObjectAllocator::ZObjectAllocator(ZPageAge age) +ZObjectAllocator::PerAge::PerAge(ZPageAge age) : _age(age), _use_per_cpu_shared_small_pages(ZHeuristics::use_per_cpu_shared_small_pages()), _shared_small_page(nullptr), _shared_medium_page(nullptr), _medium_page_alloc_lock() {} -ZPage** ZObjectAllocator::shared_small_page_addr() { +ZPage** ZObjectAllocator::PerAge::shared_small_page_addr() { return _use_per_cpu_shared_small_pages ? _shared_small_page.addr() : _shared_small_page.addr(0); } -ZPage* const* ZObjectAllocator::shared_small_page_addr() const { +ZPage* const* ZObjectAllocator::PerAge::shared_small_page_addr() const { return _use_per_cpu_shared_small_pages ? _shared_small_page.addr() : _shared_small_page.addr(0); } -ZPage* ZObjectAllocator::alloc_page(ZPageType type, size_t size, ZAllocationFlags flags) { +ZPage* ZObjectAllocator::PerAge::alloc_page(ZPageType type, size_t size, ZAllocationFlags flags) { return ZHeap::heap()->alloc_page(type, size, flags, _age); } -ZPage* ZObjectAllocator::alloc_page_for_relocation(ZPageType type, size_t size, ZAllocationFlags flags) { - return ZHeap::heap()->alloc_page(type, size, flags, _age); -} - -void ZObjectAllocator::undo_alloc_page(ZPage* page) { +void ZObjectAllocator::PerAge::undo_alloc_page(ZPage* page) { ZHeap::heap()->undo_alloc_page(page); } -zaddress ZObjectAllocator::alloc_object_in_shared_page(ZPage** shared_page, - ZPageType page_type, - size_t page_size, - size_t size, - ZAllocationFlags flags) { +zaddress ZObjectAllocator::PerAge::alloc_object_in_shared_page(ZPage** shared_page, + ZPageType page_type, + size_t page_size, + size_t size, + ZAllocationFlags flags) { zaddress addr = zaddress::null; ZPage* page = Atomic::load_acquire(shared_page); @@ -116,8 +109,8 @@ zaddress ZObjectAllocator::alloc_object_in_shared_page(ZPage** shared_page, return addr; } -zaddress ZObjectAllocator::alloc_object_in_medium_page(size_t size, - ZAllocationFlags flags) { +zaddress ZObjectAllocator::PerAge::alloc_object_in_medium_page(size_t size, + ZAllocationFlags flags) { zaddress addr = zaddress::null; ZPage** shared_medium_page = _shared_medium_page.addr(); ZPage* page = Atomic::load_acquire(shared_medium_page); @@ -165,7 +158,7 @@ zaddress ZObjectAllocator::alloc_object_in_medium_page(size_t size, return addr; } -zaddress ZObjectAllocator::alloc_large_object(size_t size, ZAllocationFlags flags) { +zaddress ZObjectAllocator::PerAge::alloc_large_object(size_t size, ZAllocationFlags flags) { zaddress addr = zaddress::null; // Allocate new large page @@ -179,15 +172,15 @@ zaddress ZObjectAllocator::alloc_large_object(size_t size, ZAllocationFlags flag return addr; } -zaddress ZObjectAllocator::alloc_medium_object(size_t size, ZAllocationFlags flags) { +zaddress ZObjectAllocator::PerAge::alloc_medium_object(size_t size, ZAllocationFlags flags) { return alloc_object_in_medium_page(size, flags); } -zaddress ZObjectAllocator::alloc_small_object(size_t size, ZAllocationFlags flags) { +zaddress ZObjectAllocator::PerAge::alloc_small_object(size_t size, ZAllocationFlags flags) { return alloc_object_in_shared_page(shared_small_page_addr(), ZPageType::small, ZPageSizeSmall, size, flags); } -zaddress ZObjectAllocator::alloc_object(size_t size, ZAllocationFlags flags) { +zaddress ZObjectAllocator::PerAge::alloc_object(size_t size, ZAllocationFlags flags) { if (size <= ZObjectSizeLimitSmall) { // Small return alloc_small_object(size, flags); @@ -200,41 +193,41 @@ zaddress ZObjectAllocator::alloc_object(size_t size, ZAllocationFlags flags) { } } -zaddress ZObjectAllocator::alloc_object(size_t size) { - const ZAllocationFlags flags; - return alloc_object(size, flags); +void ZObjectAllocator::PerAge::retire_pages() { + assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); + + // Reset allocation pages + _shared_medium_page.set(nullptr); + _shared_small_page.set_all(nullptr); } -zaddress ZObjectAllocator::alloc_object_for_relocation(size_t size) { - ZAllocationFlags flags; - flags.set_non_blocking(); +ZObjectAllocator::ZObjectAllocator() + : _allocators() { - return alloc_object(size, flags); -} - -void ZObjectAllocator::undo_alloc_object_for_relocation(zaddress addr, size_t size) { - ZPage* const page = ZHeap::heap()->page(addr); - - if (page->is_large()) { - undo_alloc_page(page); - ZStatInc(ZCounterUndoObjectAllocationSucceeded); - } else { - if (page->undo_alloc_object_atomic(addr, size)) { - ZStatInc(ZCounterUndoObjectAllocationSucceeded); - } else { - ZStatInc(ZCounterUndoObjectAllocationFailed); - } + for (ZPageAge age : ZPageAgeRangeAll) { + _allocators[untype(age)].initialize(age); } } -ZPageAge ZObjectAllocator::age() const { - return _age; +ZObjectAllocator::PerAge* ZObjectAllocator::allocator(ZPageAge age) { + return _allocators[untype(age)].get(); } -size_t ZObjectAllocator::remaining() const { +const ZObjectAllocator::PerAge* ZObjectAllocator::allocator(ZPageAge age) const { + return _allocators[untype(age)].get(); +} + +void ZObjectAllocator::retire_pages(ZPageAgeRange range) { + for (ZPageAge age : range) { + allocator(age)->retire_pages(); + } +} + +size_t ZObjectAllocator::fast_available(ZPageAge age) const { assert(Thread::current()->is_Java_thread(), "Should be a Java thread"); - const ZPage* const page = Atomic::load_acquire(shared_small_page_addr()); + ZPage* const* const shared_addr = allocator(age)->shared_small_page_addr(); + const ZPage* const page = Atomic::load_acquire(shared_addr); if (page != nullptr) { return page->remaining(); } @@ -242,10 +235,17 @@ size_t ZObjectAllocator::remaining() const { return 0; } -void ZObjectAllocator::retire_pages() { - assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); - - // Reset allocation pages - _shared_medium_page.set(nullptr); - _shared_small_page.set_all(nullptr); +zaddress ZObjectAllocator::alloc(size_t size) { + ZAllocationFlags flags; + return allocator(ZPageAge::eden)->alloc_object(size, flags); } + +zaddress ZObjectAllocator::alloc_for_relocation(size_t size, ZPageAge age) { + ZAllocationFlags flags; + + // Object allocation for relocation should not block + flags.set_non_blocking(); + + return allocator(age)->alloc_object(size, flags); +} + diff --git a/src/hotspot/share/gc/z/zObjectAllocator.hpp b/src/hotspot/share/gc/z/zObjectAllocator.hpp index 3a747afecbe..2199f0b68be 100644 --- a/src/hotspot/share/gc/z/zObjectAllocator.hpp +++ b/src/hotspot/share/gc/z/zObjectAllocator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,61 +26,67 @@ #include "gc/z/zAddress.hpp" #include "gc/z/zAllocationFlags.hpp" +#include "gc/z/zDeferredConstructed.hpp" #include "gc/z/zLock.hpp" +#include "gc/z/zPage.hpp" #include "gc/z/zPageAge.hpp" -#include "gc/z/zPageType.hpp" #include "gc/z/zValue.hpp" -class ZPage; -class ZPageTable; - class ZObjectAllocator { private: - ZPageAge _age; - const bool _use_per_cpu_shared_small_pages; - ZPerCPU _shared_small_page; - ZContended _shared_medium_page; - ZLock _medium_page_alloc_lock; + class PerAge { + private: + const ZPageAge _age; + const bool _use_per_cpu_shared_small_pages; + ZPerCPU _shared_small_page; + ZContended _shared_medium_page; + ZLock _medium_page_alloc_lock; - ZPage** shared_small_page_addr(); - ZPage* const* shared_small_page_addr() const; + public: + PerAge(ZPageAge age); - ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags); - void undo_alloc_page(ZPage* page); + ZPage** shared_small_page_addr(); + ZPage* const* shared_small_page_addr() const; - // Allocate an object in a shared page. Allocate and - // atomically install a new page if necessary. - zaddress alloc_object_in_shared_page(ZPage** shared_page, - ZPageType page_type, - size_t page_size, - size_t size, - ZAllocationFlags flags); + ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags); + void undo_alloc_page(ZPage* page); - zaddress alloc_object_in_medium_page(size_t size, - ZAllocationFlags flags); + // Allocate an object in a shared page. Allocate and + // atomically install a new page if necessary. + zaddress alloc_object_in_shared_page(ZPage** shared_page, + ZPageType page_type, + size_t page_size, + size_t size, + ZAllocationFlags flags); - zaddress alloc_large_object(size_t size, ZAllocationFlags flags); - zaddress alloc_medium_object(size_t size, ZAllocationFlags flags); - zaddress alloc_small_object(size_t size, ZAllocationFlags flags); - zaddress alloc_object(size_t size, ZAllocationFlags flags); + zaddress alloc_object_in_medium_page(size_t size, + ZAllocationFlags flags); + + zaddress alloc_large_object(size_t size, ZAllocationFlags flags); + zaddress alloc_medium_object(size_t size, ZAllocationFlags flags); + zaddress alloc_small_object(size_t size, ZAllocationFlags flags); + zaddress alloc_object(size_t size, ZAllocationFlags flags); + + void retire_pages(); + }; + + ZDeferredConstructed _allocators[ZPageAgeCount]; + + PerAge* allocator(ZPageAge age); + const PerAge* allocator(ZPageAge age) const; public: - ZObjectAllocator(ZPageAge age); + ZObjectAllocator(); + + void retire_pages(ZPageAgeRange range); + + size_t fast_available(ZPageAge age) const; // Mutator allocation - zaddress alloc_object(size_t size); + zaddress alloc(size_t size); - // Relocation - zaddress alloc_object_for_relocation(size_t size); - void undo_alloc_object_for_relocation(zaddress addr, size_t size); - - ZPage* alloc_page_for_relocation(ZPageType type, size_t size, ZAllocationFlags flags); - - ZPageAge age() const; - - size_t remaining() const; - - void retire_pages(); + // Mutator relocation + zaddress alloc_for_relocation(size_t size, ZPageAge age); }; #endif // SHARE_GC_Z_ZOBJECTALLOCATOR_HPP diff --git a/src/hotspot/share/gc/z/zPageAge.hpp b/src/hotspot/share/gc/z/zPageAge.hpp index 3209790c008..15757fed8a8 100644 --- a/src/hotspot/share/gc/z/zPageAge.hpp +++ b/src/hotspot/share/gc/z/zPageAge.hpp @@ -49,6 +49,8 @@ enum class ZPageAge : uint8_t { constexpr uint ZPageAgeCount = static_cast(ZPageAge::old) + 1; constexpr ZPageAge ZPageAgeLastPlusOne = static_cast(ZPageAgeCount); +constexpr uint ZNumRelocationAges = ZPageAgeCount - 1; + ENUMERATOR_RANGE(ZPageAge, ZPageAge::eden, ZPageAge::old); @@ -60,5 +62,6 @@ constexpr ZPageAgeRange ZPageAgeRangeYoung = ZPageAgeRange::create(); constexpr ZPageAgeRange ZPageAgeRangeRelocation = ZPageAgeRange::create(); constexpr ZPageAgeRange ZPageAgeRangeOld = ZPageAgeRange::create(); +constexpr ZPageAgeRange ZPageAgeRangeAll = ZPageAgeRange(); #endif // SHARE_GC_Z_ZPAGEAGE_HPP diff --git a/src/hotspot/share/gc/z/zPageAllocator.cpp b/src/hotspot/share/gc/z/zPageAllocator.cpp index 80a53feed71..52d5d775757 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.cpp +++ b/src/hotspot/share/gc/z/zPageAllocator.cpp @@ -547,21 +547,24 @@ public: } void send_event(bool successful) { - EventZPageAllocation event; + if (!EventZPageAllocation::is_enabled()) { + // Event not enabled, exit early + return; + } Ticks end_timestamp = Ticks::now(); const ZPageAllocationStats st = stats(); - event.commit(_start_timestamp, - end_timestamp, - (u8)_type, - size(), - st._total_harvested, - st._total_committed_capacity, - (unsigned)st._num_harvested_vmems, - _is_multi_partition, - successful, - _flags.non_blocking()); + EventZPageAllocation::commit(_start_timestamp, + end_timestamp, + (u8)_type, + size(), + st._total_harvested, + st._total_committed_capacity, + (unsigned)st._num_harvested_vmems, + _is_multi_partition, + successful, + _flags.non_blocking()); } }; diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index b45d8b70e72..95e22cf4c69 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -25,7 +25,6 @@ #include "gc/shared/suspendibleThreadSet.hpp" #include "gc/z/zAbort.inline.hpp" #include "gc/z/zAddress.inline.hpp" -#include "gc/z/zAllocator.inline.hpp" #include "gc/z/zBarrier.inline.hpp" #include "gc/z/zCollectedHeap.hpp" #include "gc/z/zForwarding.inline.hpp" @@ -33,8 +32,9 @@ #include "gc/z/zHeap.inline.hpp" #include "gc/z/zIndexDistributor.inline.hpp" #include "gc/z/zIterator.inline.hpp" +#include "gc/z/zObjectAllocator.hpp" #include "gc/z/zPage.inline.hpp" -#include "gc/z/zPageAge.hpp" +#include "gc/z/zPageAge.inline.hpp" #include "gc/z/zRelocate.hpp" #include "gc/z/zRelocationSet.inline.hpp" #include "gc/z/zRootsIterator.hpp" @@ -325,10 +325,9 @@ static zaddress relocate_object_inner(ZForwarding* forwarding, zaddress from_add // Allocate object const size_t size = ZUtils::object_size(from_addr); + const ZPageAge to_age = forwarding->to_age(); - ZAllocatorForRelocation* allocator = ZAllocator::relocation(forwarding->to_age()); - - const zaddress to_addr = allocator->alloc_object(size); + const zaddress to_addr = ZHeap::heap()->alloc_object_for_relocation(size, to_age); if (is_null(to_addr)) { // Allocation failed @@ -343,7 +342,7 @@ static zaddress relocate_object_inner(ZForwarding* forwarding, zaddress from_add if (to_addr_final != to_addr) { // Already relocated, try undo allocation - allocator->undo_alloc_object(to_addr, size); + ZHeap::heap()->undo_alloc_object_for_relocation(to_addr, size); } return to_addr_final; @@ -385,18 +384,22 @@ zaddress ZRelocate::forward_object(ZForwarding* forwarding, zaddress_unsafe from return to_addr; } -static ZPage* alloc_page(ZAllocatorForRelocation* allocator, ZPageType type, size_t size) { +static ZPage* alloc_page(ZForwarding* forwarding) { if (ZStressRelocateInPlace) { // Simulate failure to allocate a new page. This will // cause the page being relocated to be relocated in-place. return nullptr; } + const ZPageType type = forwarding->type(); + const size_t size = forwarding->size(); + const ZPageAge age = forwarding->to_age(); + ZAllocationFlags flags; flags.set_non_blocking(); flags.set_gc_relocation(); - return allocator->alloc_page_for_relocation(type, size, flags); + return ZHeap::heap()->alloc_page(type, size, flags, age); } static void retire_target_page(ZGeneration* generation, ZPage* page) { @@ -426,8 +429,7 @@ public: _in_place_count(0) {} ZPage* alloc_and_retire_target_page(ZForwarding* forwarding, ZPage* target) { - ZAllocatorForRelocation* const allocator = ZAllocator::relocation(forwarding->to_age()); - ZPage* const page = alloc_page(allocator, forwarding->type(), forwarding->size()); + ZPage* const page = alloc_page(forwarding); if (page == nullptr) { Atomic::inc(&_in_place_count); } @@ -467,7 +469,7 @@ class ZRelocateMediumAllocator { private: ZGeneration* const _generation; ZConditionLock _lock; - ZPage* _shared[ZAllocator::_relocation_allocators]; + ZPage* _shared[ZNumRelocationAges]; bool _in_place; volatile size_t _in_place_count; @@ -480,7 +482,7 @@ public: _in_place_count(0) {} ~ZRelocateMediumAllocator() { - for (uint i = 0; i < ZAllocator::_relocation_allocators; ++i) { + for (uint i = 0; i < ZNumRelocationAges; ++i) { if (_shared[i] != nullptr) { retire_target_page(_generation, _shared[i]); } @@ -509,8 +511,7 @@ public: // a new page. const ZPageAge to_age = forwarding->to_age(); if (shared(to_age) == target) { - ZAllocatorForRelocation* const allocator = ZAllocator::relocation(forwarding->to_age()); - ZPage* const to_page = alloc_page(allocator, forwarding->type(), forwarding->size()); + ZPage* const to_page = alloc_page(forwarding); set_shared(to_age, to_page); if (to_page == nullptr) { Atomic::inc(&_in_place_count); @@ -562,7 +563,7 @@ class ZRelocateWork : public StackObj { private: Allocator* const _allocator; ZForwarding* _forwarding; - ZPage* _target[ZAllocator::_relocation_allocators]; + ZPage* _target[ZNumRelocationAges]; ZGeneration* const _generation; size_t _other_promoted; size_t _other_compacted; @@ -916,7 +917,7 @@ public: _other_compacted(0) {} ~ZRelocateWork() { - for (uint i = 0; i < ZAllocator::_relocation_allocators; ++i) { + for (uint i = 0; i < ZNumRelocationAges; ++i) { _allocator->free_target_page(_target[i]); } // Report statistics on-behalf of non-worker threads diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp index aac4fd51271..1475af5e869 100644 --- a/src/hotspot/share/gc/z/zRelocationSetSelector.cpp +++ b/src/hotspot/share/gc/z/zRelocationSetSelector.cpp @@ -240,7 +240,7 @@ void ZRelocationSetSelector::select() { ZRelocationSetSelectorStats ZRelocationSetSelector::stats() const { ZRelocationSetSelectorStats stats; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { const uint i = untype(age); stats._small[i] = _small.stats(age); stats._medium[i] = _medium.stats(age); diff --git a/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp b/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp index 7740e0764a6..ec8ad67e211 100644 --- a/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp +++ b/src/hotspot/share/gc/z/zRelocationSetSelector.inline.hpp @@ -189,7 +189,7 @@ inline void ZRelocationSetSelector::clear_empty_pages() { inline size_t ZRelocationSetSelector::total() const { size_t sum = 0; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { sum += _small.stats(age).total() + _medium.stats(age).total() + _large.stats(age).total(); } return sum; @@ -197,7 +197,7 @@ inline size_t ZRelocationSetSelector::total() const { inline size_t ZRelocationSetSelector::empty() const { size_t sum = 0; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { sum += _small.stats(age).empty() + _medium.stats(age).empty() + _large.stats(age).empty(); } return sum; @@ -205,7 +205,7 @@ inline size_t ZRelocationSetSelector::empty() const { inline size_t ZRelocationSetSelector::relocate() const { size_t sum = 0; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { sum += _small.stats(age).relocate() + _medium.stats(age).relocate() + _large.stats(age).relocate(); } return sum; diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp index bfde904c1e7..c6f23ab5b67 100644 --- a/src/hotspot/share/gc/z/zStat.cpp +++ b/src/hotspot/share/gc/z/zStat.cpp @@ -1500,7 +1500,7 @@ void ZStatRelocation::print_page_summary() { summary.relocate += stats.relocate(); }; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { account_page_size(small_summary, _selector_stats.small(age)); account_page_size(medium_summary, _selector_stats.medium(age)); account_page_size(large_summary, _selector_stats.large(age)); @@ -1561,7 +1561,7 @@ void ZStatRelocation::print_age_table() { uint oldest_none_empty_age = 0; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { uint i = untype(age); auto summarize_pages = [&](const ZRelocationSetSelectorGroupStats& stats) { live[i] += stats.live(); @@ -1790,7 +1790,7 @@ void ZStatHeap::at_select_relocation_set(const ZRelocationSetSelectorStats& stat ZLocker locker(&_stat_lock); size_t live = 0; - for (ZPageAge age : ZPageAgeRange()) { + for (ZPageAge age : ZPageAgeRangeAll) { live += stats.small(age).live() + stats.medium(age).live() + stats.large(age).live(); } _at_mark_end.live = live; diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index ad39169bca0..0d6ccf3a710 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -137,6 +137,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::_dsinh: return java_lang_math_sinh; case vmIntrinsics::_dtanh: return java_lang_math_tanh; case vmIntrinsics::_dcbrt: return java_lang_math_cbrt; case vmIntrinsics::_dabs: return java_lang_math_abs; @@ -199,6 +200,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_sinh : return vmIntrinsics::_dsinh; case java_lang_math_tanh : return vmIntrinsics::_dtanh; case java_lang_math_cbrt : return vmIntrinsics::_dcbrt; case java_lang_math_abs : return vmIntrinsics::_dabs; @@ -304,6 +306,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_sinh : tty->print("java_lang_math_sinh" ); break; case java_lang_math_tanh : tty->print("java_lang_math_tanh" ); break; case java_lang_math_cbrt : tty->print("java_lang_math_cbrt" ); break; case java_lang_math_abs : tty->print("java_lang_math_abs" ); break; diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp index a3e93aa0a30..6f7523fd00a 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.hpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp @@ -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_sinh, // implementation of java.lang.Math.sinh (x) java_lang_math_tanh, // implementation of java.lang.Math.tanh (x) java_lang_math_cbrt, // implementation of java.lang.Math.cbrt (x) java_lang_math_abs, // implementation of java.lang.Math.abs (x) @@ -152,6 +153,7 @@ class AbstractInterpreter: AllStatic { case vmIntrinsics::_dsin : // fall thru case vmIntrinsics::_dcos : // fall thru case vmIntrinsics::_dtan : // fall thru + case vmIntrinsics::_dsinh : // fall thru case vmIntrinsics::_dtanh : // fall thru case vmIntrinsics::_dcbrt : // fall thru case vmIntrinsics::_dabs : // fall thru diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp index 928d1ac9f9c..9a316f3ba46 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -193,6 +193,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_sinh ) method_entry(java_lang_math_tanh ) method_entry(java_lang_math_cbrt ) method_entry(java_lang_math_abs ) @@ -454,6 +455,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_sinh : // fall thru case Interpreter::java_lang_math_tanh : // fall thru case Interpreter::java_lang_math_cbrt : // fall thru case Interpreter::java_lang_math_abs : // fall thru diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp index 8fa0835216d..148ef2eb572 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp @@ -53,6 +53,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_sinh ); method_entry(java_lang_math_tanh ); method_entry(java_lang_math_cbrt ); method_entry(java_lang_math_abs ); @@ -96,6 +97,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_sinh : // fall thru case Interpreter::java_lang_math_tanh : // fall thru case Interpreter::java_lang_math_cbrt : // fall thru case Interpreter::java_lang_math_abs : // fall thru diff --git a/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.cpp b/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.cpp index 27480999720..b9bedff0f9d 100644 --- a/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.cpp +++ b/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.cpp @@ -132,14 +132,14 @@ InstanceKlass* JfrClassTransformer::create_new_instance_klass(InstanceKlass* ik, } // Redefining / retransforming? -const Klass* JfrClassTransformer::find_existing_klass(const InstanceKlass* ik, JavaThread* thread) { +const InstanceKlass* JfrClassTransformer::find_existing_klass(const InstanceKlass* ik, JavaThread* thread) { assert(ik != nullptr, "invariant"); assert(thread != nullptr, "invariant"); JvmtiThreadState* const state = thread->jvmti_thread_state(); return state != nullptr ? klass_being_redefined(ik, state) : nullptr; } -const Klass* JfrClassTransformer::klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state) { +const InstanceKlass* JfrClassTransformer::klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state) { assert(ik != nullptr, "invariant"); assert(state != nullptr, "invariant"); const GrowableArray* const redef_klasses = state->get_classes_being_redefined(); @@ -149,9 +149,10 @@ const Klass* JfrClassTransformer::klass_being_redefined(const InstanceKlass* ik, for (int i = 0; i < redef_klasses->length(); ++i) { const Klass* const existing_klass = redef_klasses->at(i); assert(existing_klass != nullptr, "invariant"); + assert(existing_klass->is_instance_klass(), "invariant"); if (ik->name() == existing_klass->name() && ik->class_loader_data() == existing_klass->class_loader_data()) { // 'ik' is a scratch klass. Return the klass being redefined. - return existing_klass; + return InstanceKlass::cast(existing_klass); } } return nullptr; diff --git a/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.hpp b/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.hpp index 3a2629bbc97..a05ac6198b8 100644 --- a/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.hpp +++ b/src/hotspot/share/jfr/instrumentation/jfrClassTransformer.hpp @@ -38,10 +38,10 @@ class InstanceKlass; class JfrClassTransformer : AllStatic { private: static InstanceKlass* create_new_instance_klass(InstanceKlass* ik, ClassFileStream* stream, TRAPS); - static const Klass* klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state); + static const InstanceKlass* klass_being_redefined(const InstanceKlass* ik, JvmtiThreadState* state); public: - static const Klass* find_existing_klass(const InstanceKlass* ik, JavaThread* thread); + static const InstanceKlass* find_existing_klass(const InstanceKlass* ik, JavaThread* thread); static InstanceKlass* create_instance_klass(InstanceKlass*& ik, ClassFileStream* stream, bool is_initial_load, JavaThread* thread); static void copy_traceid(const InstanceKlass* ik, const InstanceKlass* new_ik); static void transfer_cached_class_file_data(InstanceKlass* ik, InstanceKlass* new_ik, const ClassFileParser& parser, JavaThread* thread); diff --git a/src/hotspot/share/jfr/jfr.cpp b/src/hotspot/share/jfr/jfr.cpp index f43aab6bdfc..a273df61922 100644 --- a/src/hotspot/share/jfr/jfr.cpp +++ b/src/hotspot/share/jfr/jfr.cpp @@ -36,6 +36,7 @@ #include "jfr/support/jfrResolution.hpp" #include "jfr/support/jfrThreadLocal.hpp" #include "jfr/support/methodtracer/jfrMethodTracer.hpp" +#include "jfr/support/methodtracer/jfrTraceTagging.hpp" #include "oops/instanceKlass.hpp" #include "oops/instanceKlass.inline.hpp" #include "oops/klass.hpp" @@ -88,12 +89,10 @@ void Jfr::on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS) } } -void Jfr::on_klass_redefinition(const InstanceKlass* ik, Thread* thread) { - assert(JfrMethodTracer::in_use(), "invariant"); - JfrMethodTracer::on_klass_redefinition(ik, thread); +void Jfr::on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass) { + JfrTraceTagging::on_klass_redefinition(ik, scratch_klass); } - bool Jfr::is_excluded(Thread* t) { return JfrJavaSupport::is_excluded(t); } @@ -152,9 +151,9 @@ void Jfr::on_resolution(const Method* caller, const Method* target, TRAPS) { } #endif -void Jfr::on_vm_shutdown(bool exception_handler, bool halt) { +void Jfr::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt) { if (!halt && JfrRecorder::is_recording()) { - JfrEmergencyDump::on_vm_shutdown(exception_handler); + JfrEmergencyDump::on_vm_shutdown(emit_old_object_samples, emit_event_shutdown); } } diff --git a/src/hotspot/share/jfr/jfr.hpp b/src/hotspot/share/jfr/jfr.hpp index 471389dfb8b..2e1fc738a61 100644 --- a/src/hotspot/share/jfr/jfr.hpp +++ b/src/hotspot/share/jfr/jfr.hpp @@ -61,7 +61,7 @@ class Jfr : AllStatic { static void include_thread(Thread* thread); static void exclude_thread(Thread* thread); static void on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS); - static void on_klass_redefinition(const InstanceKlass* ik, Thread* thread); + static void on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass); static void on_thread_start(Thread* thread); static void on_thread_exit(Thread* thread); static void on_resolution(const CallInfo& info, TRAPS); @@ -70,7 +70,7 @@ class Jfr : AllStatic { static void on_resolution(const Method* caller, const Method* target, TRAPS); static void on_java_thread_start(JavaThread* starter, JavaThread* startee); static void on_set_current_thread(JavaThread* jt, oop thread); - static void on_vm_shutdown(bool exception_handler = false, bool halt = false); + static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt = false); static void on_vm_error_report(outputStream* st); static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter); static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp deleted file mode 100644 index a4817cbc87d..00000000000 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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 "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" -#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp" -#include "jfr/utilities/jfrTypes.hpp" -#include "runtime/handles.inline.hpp" -#include "runtime/jniHandles.inline.hpp" -#include "runtime/safepoint.hpp" -#include "runtime/semaphore.hpp" -#include "utilities/growableArray.hpp" - -static const int initial_array_size = 30; - -class ThreadGroupExclusiveAccess : public StackObj { - private: - static Semaphore _mutex_semaphore; - public: - ThreadGroupExclusiveAccess() { _mutex_semaphore.wait(); } - ~ThreadGroupExclusiveAccess() { _mutex_semaphore.signal(); } -}; - -Semaphore ThreadGroupExclusiveAccess::_mutex_semaphore(1); -JfrThreadGroup* JfrThreadGroup::_instance = nullptr; - -class JfrThreadGroupPointers : public ResourceObj { - private: - const Handle _thread_group_handle; - jweak _thread_group_weak_ref; - public: - JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref); - Handle thread_group_handle() const; - jweak thread_group_weak_ref() const; - oopDesc* thread_group_oop() const; - jweak transfer_weak_global_handle_ownership(); - void clear_weak_ref(); -}; - -JfrThreadGroupPointers::JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref) : - _thread_group_handle(thread_group_handle), - _thread_group_weak_ref(thread_group_weak_ref) {} - -Handle JfrThreadGroupPointers::thread_group_handle() const { - return _thread_group_handle; -} - -jweak JfrThreadGroupPointers::thread_group_weak_ref() const { - return _thread_group_weak_ref; -} - -oopDesc* JfrThreadGroupPointers::thread_group_oop() const { - assert(_thread_group_weak_ref == nullptr || - JNIHandles::resolve_non_null(_thread_group_weak_ref) == _thread_group_handle(), "invariant"); - return _thread_group_handle(); -} - -jweak JfrThreadGroupPointers::transfer_weak_global_handle_ownership() { - jweak temp = _thread_group_weak_ref; - _thread_group_weak_ref = nullptr; - return temp; -} - -void JfrThreadGroupPointers::clear_weak_ref() { - if (nullptr != _thread_group_weak_ref) { - JNIHandles::destroy_weak_global(_thread_group_weak_ref); - } -} - -class JfrThreadGroupsHelper : public ResourceObj { - private: - static const int invalid_iterator_pos = -1; - GrowableArray* _thread_group_hierarchy; - int _current_iterator_pos; - - int populate_thread_group_hierarchy(const JavaThread* jt, Thread* current); - JfrThreadGroupPointers& at(int index); - - public: - JfrThreadGroupsHelper(const JavaThread* jt, Thread* current); - ~JfrThreadGroupsHelper(); - JfrThreadGroupPointers& next(); - bool is_valid() const; - bool has_next() const; -}; - -JfrThreadGroupsHelper::JfrThreadGroupsHelper(const JavaThread* jt, Thread* current) { - _thread_group_hierarchy = new GrowableArray(10); - _current_iterator_pos = populate_thread_group_hierarchy(jt, current) - 1; -} - -JfrThreadGroupsHelper::~JfrThreadGroupsHelper() { - assert(_current_iterator_pos == invalid_iterator_pos, "invariant"); - for (int i = 0; i < _thread_group_hierarchy->length(); ++i) { - _thread_group_hierarchy->at(i)->clear_weak_ref(); - } -} - -JfrThreadGroupPointers& JfrThreadGroupsHelper::at(int index) { - assert(_thread_group_hierarchy != nullptr, "invariant"); - assert(index > invalid_iterator_pos && index < _thread_group_hierarchy->length(), "invariant"); - return *(_thread_group_hierarchy->at(index)); -} - -bool JfrThreadGroupsHelper::has_next() const { - return _current_iterator_pos > invalid_iterator_pos; -} - -bool JfrThreadGroupsHelper::is_valid() const { - return (_thread_group_hierarchy != nullptr && _thread_group_hierarchy->length() > 0); -} - -JfrThreadGroupPointers& JfrThreadGroupsHelper::next() { - assert(is_valid(), "invariant"); - return at(_current_iterator_pos--); -} - -/* - * If not at a safepoint, we create global weak references for - * all reachable threadgroups for this thread. - * If we are at a safepoint, the caller is the VMThread during - * JFR checkpointing. It can use naked oops, because nothing - * will move before the list of threadgroups is cleared and - * mutator threads restarted. The threadgroup list is cleared - * later by the VMThread as one of the final steps in JFR checkpointing - * (not here). - */ -int JfrThreadGroupsHelper::populate_thread_group_hierarchy(const JavaThread* jt, Thread* current) { - assert(jt != nullptr && jt->is_Java_thread(), "invariant"); - assert(current != nullptr, "invariant"); - assert(_thread_group_hierarchy != nullptr, "invariant"); - - oop thread_oop = jt->threadObj(); - if (thread_oop == nullptr) { - return 0; - } - // immediate thread group - Handle thread_group_handle(current, java_lang_Thread::threadGroup(thread_oop)); - if (thread_group_handle == nullptr) { - return 0; - } - - const bool use_weak_handles = !SafepointSynchronize::is_at_safepoint(); - jweak thread_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(thread_group_handle) : nullptr; - - JfrThreadGroupPointers* thread_group_pointers = new JfrThreadGroupPointers(thread_group_handle, thread_group_weak_ref); - _thread_group_hierarchy->append(thread_group_pointers); - // immediate parent thread group - oop parent_thread_group_obj = java_lang_ThreadGroup::parent(thread_group_handle()); - Handle parent_thread_group_handle(current, parent_thread_group_obj); - - // and check parents parents... - while (parent_thread_group_handle != nullptr) { - const jweak parent_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(parent_thread_group_handle) : nullptr; - thread_group_pointers = new JfrThreadGroupPointers(parent_thread_group_handle, parent_group_weak_ref); - _thread_group_hierarchy->append(thread_group_pointers); - parent_thread_group_obj = java_lang_ThreadGroup::parent(parent_thread_group_handle()); - parent_thread_group_handle = Handle(current, parent_thread_group_obj); - } - return _thread_group_hierarchy->length(); -} - -static traceid next_id() { - static traceid _current_threadgroup_id = 1; // 1 is reserved for thread group "VirtualThreads" - return ++_current_threadgroup_id; -} - -class JfrThreadGroup::JfrThreadGroupEntry : public JfrCHeapObj { - friend class JfrThreadGroup; - private: - traceid _thread_group_id; - traceid _parent_group_id; - char* _thread_group_name; // utf8 format - // If an entry is created during a safepoint, the - // _thread_group_oop contains a direct oop to - // the java.lang.ThreadGroup object. - // If an entry is created on javathread exit time (not at safepoint), - // _thread_group_weak_ref contains a JNI weak global handle - // indirection to the java.lang.ThreadGroup object. - // Note: we cannot use a union here since CHECK_UNHANDLED_OOPS makes oop have - // a ctor which isn't allowed in a union by the SunStudio compiler - oop _thread_group_oop; - jweak _thread_group_weak_ref; - - JfrThreadGroupEntry(const char* tgstr, JfrThreadGroupPointers& ptrs); - ~JfrThreadGroupEntry(); - - traceid thread_group_id() const { return _thread_group_id; } - void set_thread_group_id(traceid tgid) { _thread_group_id = tgid; } - - const char* thread_group_name() const { return _thread_group_name; } - void set_thread_group_name(const char* tgname); - - traceid parent_group_id() const { return _parent_group_id; } - void set_parent_group_id(traceid pgid) { _parent_group_id = pgid; } - - void set_thread_group(JfrThreadGroupPointers& ptrs); - bool is_equal(const JfrThreadGroupPointers& ptrs) const; - oop thread_group() const; -}; - -JfrThreadGroup::JfrThreadGroupEntry::JfrThreadGroupEntry(const char* tgname, JfrThreadGroupPointers& ptrs) : - _thread_group_id(0), - _parent_group_id(0), - _thread_group_name(nullptr), - _thread_group_oop(nullptr), - _thread_group_weak_ref(nullptr) { - set_thread_group_name(tgname); - set_thread_group(ptrs); -} - -JfrThreadGroup::JfrThreadGroupEntry::~JfrThreadGroupEntry() { - if (_thread_group_name != nullptr) { - JfrCHeapObj::free(_thread_group_name, strlen(_thread_group_name) + 1); - } - if (_thread_group_weak_ref != nullptr) { - JNIHandles::destroy_weak_global(_thread_group_weak_ref); - } -} - -void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group_name(const char* tgname) { - assert(_thread_group_name == nullptr, "invariant"); - if (tgname != nullptr) { - size_t len = strlen(tgname); - _thread_group_name = JfrCHeapObj::new_array(len + 1); - strncpy(_thread_group_name, tgname, len + 1); - } -} - -oop JfrThreadGroup::JfrThreadGroupEntry::thread_group() const { - return _thread_group_weak_ref != nullptr ? JNIHandles::resolve(_thread_group_weak_ref) : _thread_group_oop; -} - -void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group(JfrThreadGroupPointers& ptrs) { - _thread_group_weak_ref = ptrs.transfer_weak_global_handle_ownership(); - if (_thread_group_weak_ref == nullptr) { - _thread_group_oop = ptrs.thread_group_oop(); - assert(_thread_group_oop != nullptr, "invariant"); - } else { - _thread_group_oop = nullptr; - } -} - -JfrThreadGroup::JfrThreadGroup() : - _list(new (mtTracing) GrowableArray(initial_array_size, mtTracing)) {} - -JfrThreadGroup::~JfrThreadGroup() { - if (_list != nullptr) { - for (int i = 0; i < _list->length(); i++) { - JfrThreadGroupEntry* e = _list->at(i); - delete e; - } - delete _list; - } -} - -JfrThreadGroup* JfrThreadGroup::instance() { - return _instance; -} - -void JfrThreadGroup::set_instance(JfrThreadGroup* new_instance) { - _instance = new_instance; -} - -traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) { - HandleMark hm(current); - JfrThreadGroupsHelper helper(jt, current); - return helper.is_valid() ? thread_group_id_internal(helper) : 0; -} - -traceid JfrThreadGroup::thread_group_id(JavaThread* const jt) { - return thread_group_id(jt, jt); -} - -traceid JfrThreadGroup::thread_group_id_internal(JfrThreadGroupsHelper& helper) { - ThreadGroupExclusiveAccess lock; - JfrThreadGroup* tg_instance = instance(); - if (tg_instance == nullptr) { - tg_instance = new JfrThreadGroup(); - if (tg_instance == nullptr) { - return 0; - } - set_instance(tg_instance); - } - - JfrThreadGroupEntry* tge = nullptr; - traceid parent_thread_group_id = 0; - while (helper.has_next()) { - JfrThreadGroupPointers& ptrs = helper.next(); - tge = tg_instance->find_entry(ptrs); - if (nullptr == tge) { - tge = tg_instance->new_entry(ptrs); - assert(tge != nullptr, "invariant"); - tge->set_parent_group_id(parent_thread_group_id); - } - parent_thread_group_id = tge->thread_group_id(); - } - // the last entry in the hierarchy is the immediate thread group - return tge->thread_group_id(); -} - -bool JfrThreadGroup::JfrThreadGroupEntry::is_equal(const JfrThreadGroupPointers& ptrs) const { - return ptrs.thread_group_oop() == thread_group(); -} - -JfrThreadGroup::JfrThreadGroupEntry* -JfrThreadGroup::find_entry(const JfrThreadGroupPointers& ptrs) const { - for (int index = 0; index < _list->length(); ++index) { - JfrThreadGroupEntry* curtge = _list->at(index); - if (curtge->is_equal(ptrs)) { - return curtge; - } - } - return (JfrThreadGroupEntry*) nullptr; -} - -// Assumes you already searched for the existence -// of a corresponding entry in find_entry(). -JfrThreadGroup::JfrThreadGroupEntry* -JfrThreadGroup::new_entry(JfrThreadGroupPointers& ptrs) { - JfrThreadGroupEntry* const tge = new JfrThreadGroupEntry(java_lang_ThreadGroup::name(ptrs.thread_group_oop()), ptrs); - add_entry(tge); - return tge; -} - -int JfrThreadGroup::add_entry(JfrThreadGroupEntry* tge) { - assert(tge != nullptr, "attempting to add a null entry!"); - assert(0 == tge->thread_group_id(), "id must be unassigned!"); - tge->set_thread_group_id(next_id()); - return _list->append(tge); -} - -void JfrThreadGroup::write_thread_group_entries(JfrCheckpointWriter& writer) const { - assert(_list != nullptr && !_list->is_empty(), "should not need be here!"); - const int number_of_tg_entries = _list->length(); - writer.write_count(number_of_tg_entries + 1); // + VirtualThread group - writer.write_key(1); // 1 is reserved for VirtualThread group - writer.write(0); // parent - const oop vgroup = java_lang_Thread_Constants::get_VTHREAD_GROUP(); - assert(vgroup != (oop)nullptr, "invariant"); - const char* const vgroup_name = java_lang_ThreadGroup::name(vgroup); - assert(vgroup_name != nullptr, "invariant"); - writer.write(vgroup_name); - for (int index = 0; index < number_of_tg_entries; ++index) { - const JfrThreadGroupEntry* const curtge = _list->at(index); - writer.write_key(curtge->thread_group_id()); - writer.write(curtge->parent_group_id()); - writer.write(curtge->thread_group_name()); - } -} - -void JfrThreadGroup::write_selective_thread_group(JfrCheckpointWriter* writer, traceid thread_group_id) const { - assert(writer != nullptr, "invariant"); - assert(_list != nullptr && !_list->is_empty(), "should not need be here!"); - assert(thread_group_id != 1, "should not need be here!"); - const int number_of_tg_entries = _list->length(); - - // save context - const JfrCheckpointContext ctx = writer->context(); - writer->write_type(TYPE_THREADGROUP); - const jlong count_offset = writer->reserve(sizeof(u4)); // Don't know how many yet - int number_of_entries_written = 0; - for (int index = number_of_tg_entries - 1; index >= 0; --index) { - const JfrThreadGroupEntry* const curtge = _list->at(index); - if (thread_group_id == curtge->thread_group_id()) { - writer->write_key(curtge->thread_group_id()); - writer->write(curtge->parent_group_id()); - writer->write(curtge->thread_group_name()); - ++number_of_entries_written; - thread_group_id = curtge->parent_group_id(); - } - } - if (number_of_entries_written == 0) { - // nothing to write, restore context - writer->set_context(ctx); - return; - } - assert(number_of_entries_written > 0, "invariant"); - writer->write_count(number_of_entries_written, count_offset); -} - -// Write out JfrThreadGroup instance and then delete it -void JfrThreadGroup::serialize(JfrCheckpointWriter& writer) { - ThreadGroupExclusiveAccess lock; - JfrThreadGroup* tg_instance = instance(); - assert(tg_instance != nullptr, "invariant"); - tg_instance->write_thread_group_entries(writer); -} - -// for writing a particular thread group -void JfrThreadGroup::serialize(JfrCheckpointWriter* writer, traceid thread_group_id) { - assert(writer != nullptr, "invariant"); - ThreadGroupExclusiveAccess lock; - JfrThreadGroup* const tg_instance = instance(); - assert(tg_instance != nullptr, "invariant"); - tg_instance->write_selective_thread_group(writer, thread_group_id); -} diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.hpp deleted file mode 100644 index 8226c6ebef2..00000000000 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroup.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUP_HPP -#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUP_HPP - -#include "jfr/utilities/jfrAllocation.hpp" -#include "jfr/utilities/jfrTypes.hpp" -#include "jni.h" - -class JfrCheckpointWriter; -template -class GrowableArray; -class JfrThreadGroupsHelper; -class JfrThreadGroupPointers; - -class JfrThreadGroup : public JfrCHeapObj { - friend class JfrCheckpointThreadClosure; - private: - static JfrThreadGroup* _instance; - class JfrThreadGroupEntry; - GrowableArray* _list; - - JfrThreadGroup(); - JfrThreadGroupEntry* find_entry(const JfrThreadGroupPointers& ptrs) const; - JfrThreadGroupEntry* new_entry(JfrThreadGroupPointers& ptrs); - int add_entry(JfrThreadGroupEntry* const tge); - - void write_thread_group_entries(JfrCheckpointWriter& writer) const; - void write_selective_thread_group(JfrCheckpointWriter* writer, traceid thread_group_id) const; - - static traceid thread_group_id_internal(JfrThreadGroupsHelper& helper); - static JfrThreadGroup* instance(); - static void set_instance(JfrThreadGroup* new_instance); - - public: - ~JfrThreadGroup(); - static void serialize(JfrCheckpointWriter& w); - static void serialize(JfrCheckpointWriter* w, traceid thread_group_id); - static traceid thread_group_id(JavaThread* thread); - static traceid thread_group_id(const JavaThread* thread, Thread* current); -}; - -#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUP_HPP diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroupManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroupManager.cpp new file mode 100644 index 00000000000..55864af4d5c --- /dev/null +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroupManager.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/checkpoint/types/jfrThreadGroupManager.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" +#include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrLinkedList.inline.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/jniHandles.inline.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/semaphore.hpp" +#include "runtime/thread.inline.hpp" +#include "utilities/growableArray.hpp" + +class ThreadGroupExclusiveAccess : public StackObj { + private: + static Semaphore _mutex_semaphore; + public: + ThreadGroupExclusiveAccess() { _mutex_semaphore.wait(); } + ~ThreadGroupExclusiveAccess() { _mutex_semaphore.signal(); } +}; + +Semaphore ThreadGroupExclusiveAccess::_mutex_semaphore(1); + +static traceid next_id() { + static traceid _tgid = 1; // 1 is reserved for thread group "VirtualThreads" + return ++_tgid; +} + +class JfrThreadGroup : public JfrCHeapObj { + template + friend class JfrLinkedList; + private: + mutable const JfrThreadGroup* _next; + const JfrThreadGroup* _parent; + traceid _tgid; + char* _tg_name; // utf8 format + jweak _tg_handle; + mutable u2 _generation; + + public: + JfrThreadGroup(Handle tg, const JfrThreadGroup* parent) : + _next(nullptr), _parent(parent), _tgid(next_id()), _tg_name(nullptr), + _tg_handle(JNIHandles::make_weak_global(tg)), _generation(0) { + const char* name = java_lang_ThreadGroup::name(tg()); + if (name != nullptr) { + const size_t len = strlen(name); + _tg_name = JfrCHeapObj::new_array(len + 1); + strncpy(_tg_name, name, len + 1); + } + } + + ~JfrThreadGroup() { + JNIHandles::destroy_weak_global(_tg_handle); + if (_tg_name != nullptr) { + JfrCHeapObj::free(_tg_name, strlen(_tg_name) + 1); + } + } + + const JfrThreadGroup* next() const { return _next; } + + traceid id() const { return _tgid; } + + const char* name() const { + return _tg_name; + } + + const JfrThreadGroup* parent() const { return _parent; } + + traceid parent_id() const { + return _parent != nullptr ? _parent->id() : 0; + } + + bool is_dead() const { + return JNIHandles::resolve(_tg_handle) == nullptr; + } + + bool operator==(oop tg) const { + assert(tg != nullptr, "invariant"); + return tg == JNIHandles::resolve(_tg_handle); + } + + bool should_write() const { + return !JfrTraceIdEpoch::is_current_epoch_generation(_generation); + } + + void set_written() const { + assert(should_write(), "invariant"); + _generation = JfrTraceIdEpoch::epoch_generation(); + } +}; + +typedef JfrLinkedList JfrThreadGroupList; + +static JfrThreadGroupList* _list = nullptr; + +static JfrThreadGroupList& list() { + assert(_list != nullptr, "invariant"); + return *_list; +} + +bool JfrThreadGroupManager::create() { + assert(_list == nullptr, "invariant"); + _list = new JfrThreadGroupList(); + return _list != nullptr; +} + +void JfrThreadGroupManager::destroy() { + delete _list; + _list = nullptr; +} + +static int populate(GrowableArray* hierarchy, const JavaThread* jt, Thread* current) { + assert(hierarchy != nullptr, "invariant"); + assert(jt != nullptr, "invariant"); + assert(current == Thread::current(), "invariant"); + + oop thread_oop = jt->threadObj(); + if (thread_oop == nullptr) { + return 0; + } + // Immediate thread group. + const Handle tg_handle(current, java_lang_Thread::threadGroup(thread_oop)); + if (tg_handle.is_null()) { + return 0; + } + hierarchy->append(tg_handle); + + // Thread group parent and then its parents... + Handle parent_tg_handle(current, java_lang_ThreadGroup::parent(tg_handle())); + + while (parent_tg_handle != nullptr) { + hierarchy->append(parent_tg_handle); + parent_tg_handle = Handle(current, java_lang_ThreadGroup::parent(parent_tg_handle())); + } + + return hierarchy->length(); +} + +class JfrThreadGroupLookup : public ResourceObj { + static const int invalid_iterator = -1; + private: + GrowableArray* _hierarchy; + mutable int _iterator; + + public: + JfrThreadGroupLookup(const JavaThread* jt, Thread* current) : + _hierarchy(new GrowableArray(16)), + _iterator(populate(_hierarchy, jt, current) - 1) {} + + bool has_next() const { + return _iterator > invalid_iterator; + } + + const Handle& next() const { + assert(has_next(), "invariant"); + return _hierarchy->at(_iterator--); + } +}; + +static const JfrThreadGroup* find_or_add(const Handle& tg_oop, const JfrThreadGroup* parent) { + assert(parent == nullptr || list().in_list(parent), "invariant"); + const JfrThreadGroup* tg = list().head(); + const JfrThreadGroup* result = nullptr; + while (tg != nullptr) { + if (*tg == tg_oop()) { + assert(tg->parent() == parent, "invariant"); + result = tg; + tg = nullptr; + continue; + } + tg = tg->next(); + } + if (result == nullptr) { + result = new JfrThreadGroup(tg_oop, parent); + list().add(result); + } + return result; +} + +static traceid find_tgid(const JfrThreadGroupLookup& lookup) { + const JfrThreadGroup* tg = nullptr; + const JfrThreadGroup* ptg = nullptr; + while (lookup.has_next()) { + tg = find_or_add(lookup.next(), ptg); + ptg = tg; + } + return tg != nullptr ? tg->id() : 0; +} + +static traceid find(const JfrThreadGroupLookup& lookup) { + ThreadGroupExclusiveAccess lock; + return find_tgid(lookup); +} + +traceid JfrThreadGroupManager::thread_group_id(JavaThread* jt) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);) + ResourceMark rm(jt); + HandleMark hm(jt); + const JfrThreadGroupLookup lookup(jt, jt); + return find(lookup); +} + +traceid JfrThreadGroupManager::thread_group_id(const JavaThread* jt, Thread* current) { + assert(jt != nullptr, "invariant"); + assert(current != nullptr, "invariant"); + assert(!current->is_Java_thread() || JavaThread::cast(current)->thread_state() == _thread_in_vm, "invariant"); + ResourceMark rm(current); + HandleMark hm(current); + const JfrThreadGroupLookup lookup(jt, current); + return find(lookup); +} + +static void write_virtual_thread_group(JfrCheckpointWriter& writer) { + writer.write_key(1); // 1 is reserved for VirtualThread group + writer.write(0); // parent + const oop vgroup = java_lang_Thread_Constants::get_VTHREAD_GROUP(); + assert(vgroup != (oop)nullptr, "invariant"); + const char* const vgroup_name = java_lang_ThreadGroup::name(vgroup); + assert(vgroup_name != nullptr, "invariant"); + writer.write(vgroup_name); +} + +static int write_thread_group(JfrCheckpointWriter& writer, const JfrThreadGroup* tg, bool to_blob = false) { + assert(tg != nullptr, "invariant"); + if (tg->should_write() || to_blob) { + writer.write_key(tg->id()); + writer.write(tg->parent_id()); + writer.write(tg->name()); + if (!to_blob) { + tg->set_written(); + } + return 1; + } + return 0; +} + +// For writing all live thread groups while removing and deleting dead thread groups. +void JfrThreadGroupManager::serialize(JfrCheckpointWriter& writer) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(JavaThread::current());) + + const uint64_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet + + // First write the pre-defined ThreadGroup for virtual threads. + write_virtual_thread_group(writer); + int number_of_groups_written = 1; + + const JfrThreadGroup* next = nullptr; + const JfrThreadGroup* prev = nullptr; + + { + ThreadGroupExclusiveAccess lock; + const JfrThreadGroup* tg = list().head(); + while (tg != nullptr) { + next = tg->next(); + if (tg->is_dead()) { + prev = list().excise(prev, tg); + assert(!list().in_list(tg), "invariant"); + delete tg; + tg = next; + continue; + } + number_of_groups_written += write_thread_group(writer, tg); + prev = tg; + tg = next; + } + } + + assert(number_of_groups_written > 0, "invariant"); + writer.write_count(number_of_groups_written, count_offset); +} + +// For writing a specific thread group and its ancestry. +void JfrThreadGroupManager::serialize(JfrCheckpointWriter& writer, traceid tgid, bool to_blob) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(JavaThread::current());) + // save context + const JfrCheckpointContext ctx = writer.context(); + + writer.write_type(TYPE_THREADGROUP); + const uint64_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet + + int number_of_groups_written = 0; + + { + ThreadGroupExclusiveAccess lock; + const JfrThreadGroup* tg = list().head(); + while (tg != nullptr) { + if (tgid == tg->id()) { + while (tg != nullptr) { + number_of_groups_written += write_thread_group(writer, tg, to_blob); + tg = tg->parent(); + } + break; + } + tg = tg->next(); + } + } + + if (number_of_groups_written == 0) { + // nothing to write, restore context + writer.set_context(ctx); + return; + } + + assert(number_of_groups_written > 0, "invariant"); + writer.write_count(number_of_groups_written, count_offset); +} diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/GCTokens.java b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroupManager.hpp similarity index 54% rename from test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/GCTokens.java rename to src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroupManager.hpp index c454127d0e6..22c140c6f8f 100644 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/GCTokens.java +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadGroupManager.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -19,23 +19,30 @@ * Please 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.g1.humongousObjects.objectGraphTest; +#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUPMANAGER_HPP +#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUPMANAGER_HPP -/** - * Contains tokens that could appear in gc log - */ -public final class GCTokens { - // Private c-tor to prevent instantiating - private GCTokens() { - } +#include "jfr/utilities/jfrTypes.hpp" +#include "memory/allStatic.hpp" - public static final String WB_INITIATED_YOUNG_GC = "Young (Normal) (WhiteBox Initiated Young GC)"; - public static final String WB_INITIATED_MIXED_GC = "Young (Mixed) (WhiteBox Initiated Young GC)"; - public static final String WB_INITIATED_CMC = "WhiteBox Initiated Run to Breakpoint"; - public static final String FULL_GC = "Full (System.gc())"; - public static final String FULL_GC_MEMORY_PRESSURE = "WhiteBox Initiated Full GC"; - public static final String CMC = "Concurrent Mark)"; - public static final String YOUNG_GC = "GC pause (young)"; -} +class JfrCheckpointWriter; + +class JfrThreadGroupManager : public AllStatic { + friend class JfrRecorder; + + private: + static bool create(); + static void destroy(); + + public: + static void serialize(JfrCheckpointWriter& w); + static void serialize(JfrCheckpointWriter& w, traceid tgid, bool is_blob); + + static traceid thread_group_id(JavaThread* thread); + static traceid thread_group_id(const JavaThread* thread, Thread* current); +}; + +#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTHREADGROUPMANAGER_HPP diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp index 9179395a451..17d945af65e 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.cpp @@ -32,7 +32,7 @@ #include "gc/shared/gcWhen.hpp" #include "jfr/leakprofiler/leakProfiler.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" -#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp" +#include "jfr/recorder/checkpoint/types/jfrThreadGroupManager.hpp" #include "jfr/recorder/checkpoint/types/jfrThreadState.hpp" #include "jfr/recorder/checkpoint/types/jfrType.hpp" #include "jfr/recorder/jfrRecorder.hpp" @@ -106,7 +106,7 @@ void JfrCheckpointThreadClosure::do_thread(Thread* t) { } else { _writer.write(name); _writer.write(tid); - _writer.write(JfrThreadGroup::thread_group_id(JavaThread::cast(t), _curthread)); + _writer.write(JfrThreadGroupManager::thread_group_id(JavaThread::cast(t), _curthread)); } _writer.write(false); // isVirtual } @@ -115,7 +115,10 @@ void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) { JfrCheckpointThreadClosure tc(writer); JfrJavaThreadIterator javathreads; while (javathreads.has_next()) { - tc.do_thread(javathreads.next()); + JavaThread* const jt = javathreads.next(); + if (jt->jfr_thread_local()->should_write()) { + tc.do_thread(jt); + } } JfrNonJavaThreadIterator nonjavathreads; while (nonjavathreads.has_next()) { @@ -124,7 +127,7 @@ void JfrThreadConstantSet::serialize(JfrCheckpointWriter& writer) { } void JfrThreadGroupConstant::serialize(JfrCheckpointWriter& writer) { - JfrThreadGroup::serialize(writer); + JfrThreadGroupManager::serialize(writer); } static const char* flag_value_origin_to_string(JVMFlagOrigin origin) { @@ -303,11 +306,11 @@ void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) { writer.write(JfrThreadId::jfr_id(_thread, _tid)); // java thread group - VirtualThread threadgroup reserved id 1 const traceid thread_group_id = is_vthread ? 1 : - JfrThreadGroup::thread_group_id(JavaThread::cast(_thread), Thread::current()); + JfrThreadGroupManager::thread_group_id(JavaThread::cast(_thread), Thread::current()); writer.write(thread_group_id); writer.write(is_vthread); // isVirtual - if (!is_vthread) { - JfrThreadGroup::serialize(&writer, thread_group_id); + if (thread_group_id > 1) { + JfrThreadGroupManager::serialize(writer, thread_group_id, _to_blob); } // VirtualThread threadgroup already serialized invariant. } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp index de35ecaf917..9798a6d29c0 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrType.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -109,11 +109,12 @@ class JfrThreadConstant : public JfrSerializer { oop _vthread; const char* _name; int _length; + const bool _to_blob; void write_name(JfrCheckpointWriter& writer); void write_os_name(JfrCheckpointWriter& writer, bool is_vthread); public: - JfrThreadConstant(Thread* t, traceid tid, oop vthread = nullptr) : - _thread(t), _tid(tid), _vthread(vthread), _name(nullptr), _length(-1) {} + JfrThreadConstant(Thread* t, traceid tid, bool to_blob, oop vthread = nullptr) : + _thread(t), _tid(tid), _vthread(vthread), _name(nullptr), _length(-1), _to_blob(to_blob) {} void serialize(JfrCheckpointWriter& writer); }; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp index 493942bade4..58d12f70980 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp @@ -109,7 +109,7 @@ JfrBlobHandle JfrTypeManager::create_thread_blob(JavaThread* jt, traceid tid /* // TYPE_THREAD and count is written unconditionally for blobs, also for vthreads. writer.write_type(TYPE_THREAD); writer.write_count(1); - JfrThreadConstant type_thread(jt, tid, vthread); + JfrThreadConstant type_thread(jt, tid, true, vthread); type_thread.serialize(writer); return writer.move(); } @@ -128,7 +128,7 @@ void JfrTypeManager::write_checkpoint(Thread* t, traceid tid /* 0 */, oop vthrea writer.write_type(TYPE_THREAD); writer.write_count(1); } - JfrThreadConstant type_thread(t, tid, vthread); + JfrThreadConstant type_thread(t, tid, false, vthread); type_thread.serialize(writer); } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index a8248b7714e..69f002138ec 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -533,8 +533,9 @@ static void clear_method_tracer_klasses() { 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 (!used(klass) && klass->is_instance_klass() && InstanceKlass::cast(klass)->is_scratch_class()) { + SET_TRANSIENT(klass); + assert(used(klass), "invariant"); } if (JfrKlassUnloading::on_unload(klass)) { if (JfrTraceId::has_sticky_bit(klass)) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp index 1fb7db1b9ed..657aee9dc53 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp @@ -152,7 +152,7 @@ public: if (!klass->is_instance_klass()) { return false; } - return _current_epoch ? METHOD_USED_THIS_EPOCH(klass) : METHOD_USED_PREVIOUS_EPOCH(klass); + return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass); } }; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp index 378d1af23cc..f10782be0ea 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.hpp @@ -30,6 +30,7 @@ #include "memory/allStatic.hpp" class ClassLoaderData; +class InstanceKlass; class Klass; class Method; class ModuleEntry; @@ -86,7 +87,6 @@ class JfrTraceId : public AllStatic { // through load barrier static traceid load(const Klass* klass); - static traceid load_previous_epoch(const Klass* klass); static traceid load(jclass jc, bool raw = false); static traceid load(const Method* method); static traceid load(const Klass* klass, const Method* method); @@ -146,10 +146,8 @@ class JfrTraceId : public AllStatic { static void set_sticky_bit(const Method* method); static void clear_sticky_bit(const Klass* k); static void clear_sticky_bit(const Method* method); - static bool has_timing_bit(const Klass* k); - static void set_timing_bit(const Klass* k); - static void clear_timing_bit(const Klass* k); - + static bool has_timing_bit(const InstanceKlass* scratch_klass); + static void set_timing_bit(const InstanceKlass* scratch_klass); }; #endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEID_HPP diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp index 2b2c435d986..03647bdeae2 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp @@ -32,6 +32,7 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdMacros.hpp" #include "jfr/support/jfrKlassExtension.hpp" +#include "oops/instanceKlass.hpp" #include "oops/klass.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/mutexLocker.hpp" @@ -81,10 +82,6 @@ inline traceid JfrTraceId::load_leakp_previous_epoch(const Klass* klass, const M return JfrTraceIdLoadBarrier::load_leakp_previous_epoch(klass, method); } -inline traceid JfrTraceId::load_previous_epoch(const Klass* klass) { - return JfrTraceIdLoadBarrier::load_previous_epoch(klass); -} - template inline traceid raw_load(const T* t) { assert(t != nullptr, "invariant"); @@ -198,6 +195,7 @@ inline void JfrTraceId::set_sticky_bit(const Method* method) { assert(method != nullptr, "invariant"); assert_locked_or_safepoint(ClassLoaderDataGraph_lock); assert(!has_sticky_bit(method), "invariant"); + assert(!method->is_old(), "invariant"); SET_METHOD_STICKY_BIT(method); assert(has_sticky_bit(method), "invariant"); } @@ -205,30 +203,22 @@ inline void JfrTraceId::set_sticky_bit(const Method* method) { inline void JfrTraceId::clear_sticky_bit(const Method* method) { assert(method != nullptr, "invarriant"); assert_locked_or_safepoint(ClassLoaderDataGraph_lock); + assert(!method->is_old(), "invariant"); assert(JfrTraceId::has_sticky_bit(method), "invariant"); CLEAR_STICKY_BIT_METHOD(method); assert(!JfrTraceId::has_sticky_bit(method), "invariant"); } -inline bool JfrTraceId::has_timing_bit(const Klass* k) { - assert(k != nullptr, "invariant"); - return HAS_TIMING_BIT(k); +inline bool JfrTraceId::has_timing_bit(const InstanceKlass* scratch_klass) { + assert(scratch_klass != nullptr, "invariant"); + return HAS_TIMING_BIT(scratch_klass); } -inline void JfrTraceId::set_timing_bit(const Klass* k) { - assert(k != nullptr, "invariant"); - assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - assert(!has_timing_bit(k), "invariant"); - SET_TIMING_BIT(k); - assert(has_timing_bit(k), "invariant"); -} - -inline void JfrTraceId::clear_timing_bit(const Klass* k) { - assert(k != nullptr, "invarriant"); - assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - assert(JfrTraceId::has_timing_bit(k), "invariant"); - CLEAR_TIMING_BIT(k); - assert(!JfrTraceId::has_timing_bit(k), "invariant"); +inline void JfrTraceId::set_timing_bit(const InstanceKlass* scratch_klass) { + assert(scratch_klass != nullptr, "invariant"); + assert(!has_timing_bit(scratch_klass), "invariant"); + SET_TIMING_BIT(scratch_klass); + assert(has_timing_bit(scratch_klass), "invariant"); } #endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEID_INLINE_HPP diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp index 120c750b2a8..034b8e653cf 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdBits.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,7 +78,7 @@ inline uint8_t* traceid_meta_byte(const T* ptr) { template <> inline uint8_t* traceid_meta_byte(const Method* ptr) { assert(ptr != nullptr, "invariant"); - return ptr->trace_meta_addr(); + return ptr->trace_flags_meta_addr(); } inline uint8_t traceid_and(uint8_t bits, uint8_t current) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp index ac1c11af2f8..c2b63840107 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp @@ -86,6 +86,27 @@ inline traceid JfrTraceIdLoadBarrier::load(const Klass* klass) { return TRACE_ID(klass); } +inline const Method* latest_version(const Klass* klass, const Method* method) { + assert(klass != nullptr, "invariant"); + assert(method != nullptr, "invariant"); + assert(klass == method->method_holder(), "invariant"); + assert(method->is_old(), "invariant"); + const InstanceKlass* const ik = InstanceKlass::cast(klass); + assert(ik->has_been_redefined(), "invariant"); + const Method* const latest_version = ik->method_with_orig_idnum(method->orig_method_idnum()); + if (latest_version == nullptr) { + assert(AllowRedefinitionToAddDeleteMethods, "invariant"); + // method has been removed. Return old version. + return method; + } + assert(latest_version != nullptr, "invariant"); + assert(latest_version != method, "invariant"); + assert(!latest_version->is_old(), "invariant"); + assert(latest_version->orig_method_idnum() == method->orig_method_idnum(), "invariant"); + assert(latest_version->name() == method->name() && latest_version->signature() == method->signature(), "invariant"); + return latest_version; +} + inline traceid JfrTraceIdLoadBarrier::load(const Method* method) { return load(method->method_holder(), method); } @@ -93,6 +114,9 @@ inline traceid JfrTraceIdLoadBarrier::load(const Method* method) { inline traceid JfrTraceIdLoadBarrier::load(const Klass* klass, const Method* method) { assert(klass != nullptr, "invariant"); assert(method != nullptr, "invariant"); + if (method->is_old()) { + method = latest_version(klass, method); + } if (should_tag(method)) { SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass); SET_METHOD_FLAG_USED_THIS_EPOCH(method); @@ -111,6 +135,9 @@ inline traceid JfrTraceIdLoadBarrier::load_no_enqueue(const Method* method) { inline traceid JfrTraceIdLoadBarrier::load_no_enqueue(const Klass* klass, const Method* method) { assert(klass != nullptr, "invariant"); assert(method != nullptr, "invariant"); + if (method->is_old()) { + method = latest_version(klass, method); + } SET_METHOD_AND_CLASS_USED_THIS_EPOCH(klass); SET_METHOD_FLAG_USED_THIS_EPOCH(method); assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant"); @@ -123,11 +150,12 @@ inline traceid JfrTraceIdLoadBarrier::load(const ClassLoaderData* cld) { if (cld->has_class_mirror_holder()) { return 0; } + const traceid id = set_used_and_get(cld); const Klass* const class_loader_klass = cld->class_loader_klass(); if (class_loader_klass != nullptr) { load(class_loader_klass); } - return set_used_and_get(cld); + return id; } inline traceid JfrTraceIdLoadBarrier::load(const ModuleEntry* module) { @@ -158,6 +186,7 @@ inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass) { inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass, const Method* method) { assert(klass != nullptr, "invariant"); assert(method != nullptr, "invariant"); + assert(!method->is_old(), "invariant"); assert(klass == method->method_holder(), "invariant"); assert(METHOD_AND_CLASS_USED_THIS_EPOCH(klass), "invariant"); if (should_tag(method)) { @@ -175,6 +204,7 @@ inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass, const Metho inline traceid JfrTraceIdLoadBarrier::load_leakp_previous_epoch(const Klass* klass, const Method* method) { assert(klass != nullptr, "invariant"); assert(method != nullptr, "invariant"); + assert(!method->is_old(), "invariant"); assert(klass == method->method_holder(), "invariant"); assert(METHOD_AND_CLASS_USED_PREVIOUS_EPOCH(klass), "invariant"); if (METHOD_FLAG_NOT_USED_PREVIOUS_EPOCH(method)) { diff --git a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp index dd75cb2929f..4ef278ab522 100644 --- a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp +++ b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp @@ -33,6 +33,7 @@ #include "jfr/periodic/sampling/jfrThreadSampler.hpp" #include "jfr/recorder/jfrRecorder.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" +#include "jfr/recorder/checkpoint/types/jfrThreadGroupManager.hpp" #include "jfr/recorder/repository/jfrRepository.hpp" #include "jfr/recorder/service/jfrEventThrottler.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" @@ -311,6 +312,9 @@ bool JfrRecorder::create_components() { if (!create_event_throttler()) { return false; } + if (!create_thread_group_manager()) { + return false; + } return true; } @@ -405,6 +409,10 @@ bool JfrRecorder::create_event_throttler() { return JfrEventThrottler::create(); } +bool JfrRecorder::create_thread_group_manager() { + return JfrThreadGroupManager::create(); +} + void JfrRecorder::destroy_components() { JfrJvmtiAgent::destroy(); if (_post_box != nullptr) { @@ -444,6 +452,7 @@ void JfrRecorder::destroy_components() { _cpu_time_thread_sampling = nullptr; } JfrEventThrottler::destroy(); + JfrThreadGroupManager::destroy(); } bool JfrRecorder::create_recorder_thread() { diff --git a/src/hotspot/share/jfr/recorder/jfrRecorder.hpp b/src/hotspot/share/jfr/recorder/jfrRecorder.hpp index 3099c8ad344..34cc8fda949 100644 --- a/src/hotspot/share/jfr/recorder/jfrRecorder.hpp +++ b/src/hotspot/share/jfr/recorder/jfrRecorder.hpp @@ -53,6 +53,7 @@ class JfrRecorder : public JfrCHeapObj { static bool create_stacktrace_repository(); static bool create_storage(); static bool create_stringpool(); + static bool create_thread_group_manager(); static bool create_thread_sampler(); static bool create_cpu_time_thread_sampling(); static bool create_event_throttler(); diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index b49ce4556c7..da619425393 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp @@ -556,22 +556,22 @@ class JavaThreadInVMAndNative : public StackObj { } }; -static void post_events(bool exception_handler, Thread* thread) { - if (exception_handler) { +static void post_events(bool emit_old_object_samples, bool emit_event_shutdown, Thread* thread) { + if (emit_old_object_samples) { + LeakProfiler::emit_events(max_jlong, false, false); + } + if (emit_event_shutdown) { EventShutdown e; e.set_reason("VM Error"); e.commit(); - } else { - // OOM - LeakProfiler::emit_events(max_jlong, false, false); } EventDumpReason event; - event.set_reason(exception_handler ? "Crash" : "Out of Memory"); + event.set_reason(emit_old_object_samples ? "Out of Memory" : "Crash"); event.set_recordingId(-1); event.commit(); } -void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) { +void JfrEmergencyDump::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown) { if (!guard_reentrancy()) { return; } @@ -584,7 +584,7 @@ void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) { if (!prepare_for_emergency_dump(thread)) { return; } - post_events(exception_handler, thread); + post_events(emit_old_object_samples, emit_event_shutdown, thread); // if JavaThread, transition to _thread_in_native to issue a final flushpoint NoHandleMark nhm; jtivm.transition_to_native(); diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp index 7db4b511746..04c2851a516 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ class JfrEmergencyDump : AllStatic { static const char* chunk_path(const char* repository_path); static void on_vm_error(const char* repository_path); static void on_vm_error_report(outputStream* st, const char* repository_path); - static void on_vm_shutdown(bool exception_handler); + static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown); }; #endif // SHARE_JFR_RECORDER_REPOSITORY_JFREMERGENCYDUMP_HPP diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index 7d1d7ac0a05..f0170bac460 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -645,7 +645,7 @@ static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) { size_t JfrRecorderService::flush() { size_t total_elements = flush_metadata(_chunkwriter); - total_elements = flush_storage(_storage, _chunkwriter); + total_elements += flush_storage(_storage, _chunkwriter); if (_string_pool.is_modified()) { total_elements += flush_stringpool(_string_pool, _chunkwriter); } diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp index 456896fe887..d403a38703e 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp @@ -29,6 +29,7 @@ #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "jfr/support/jfrThreadLocal.hpp" #include "runtime/mutexLocker.hpp" +#include "runtime/safepoint.hpp" /* * There are two separate repository instances. @@ -186,6 +187,7 @@ void JfrStackTraceRepository::record_for_leak_profiler(JavaThread* current_threa } traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) { + assert(!SafepointSynchronize::is_at_safepoint(), "invariant"); MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); assert(stacktrace.number_of_frames() > 0, "invariant"); const size_t index = stacktrace._hash % TABLE_SIZE; diff --git a/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp b/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp index 4eb8662c209..8fc35ca8dc1 100644 --- a/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp +++ b/src/hotspot/share/jfr/support/jfrDeprecationManager.cpp @@ -390,15 +390,16 @@ static inline void write_stacktraces(JfrChunkWriter& cw) { _resolved_list.iterate(scw); } -// First, we consolidate all stack trace blobs into a single TYPE_STACKTRACE checkpoint -// and serialize it to the chunk. Then, all events are serialized, and unique type set blobs -// written into the JfrCheckpoint system to be serialized to the chunk upon return. +// First, all events are serialized, and unique type set blobs are written into the +// JfrCheckpoint system to be serialized to the chunk upon return. +// Then, we consolidate all stack trace blobs into a single TYPE_STACKTRACE checkpoint +// and serialize it directly to the chunk. void JfrDeprecationManager::write_edges(JfrChunkWriter& cw, Thread* thread, bool on_error /* false */) { if (_resolved_list.is_nonempty() && JfrEventSetting::is_enabled(JfrDeprecatedInvocationEvent)) { + write_events(cw, thread, on_error); if (has_stacktrace()) { write_stacktraces(cw); } - write_events(cw, thread, on_error); } } diff --git a/src/hotspot/share/jfr/support/jfrKlassExtension.hpp b/src/hotspot/share/jfr/support/jfrKlassExtension.hpp index 8f096347c77..dc5f7aa7e90 100644 --- a/src/hotspot/share/jfr/support/jfrKlassExtension.hpp +++ b/src/hotspot/share/jfr/support/jfrKlassExtension.hpp @@ -40,8 +40,6 @@ #define EVENT_STICKY_BIT 8192 #define IS_EVENT_KLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS)) != 0) #define IS_EVENT_OR_HOST_KLASS(ptr) (((ptr)->trace_id() & (JDK_JFR_EVENT_KLASS | JDK_JFR_EVENT_SUBKLASS | EVENT_HOST_KLASS)) != 0) -#define KLASS_HAS_STICKY_BIT(ptr) (((ptr)->trace_id() & STICKY_BIT) != 0) -#define ON_KLASS_REDEFINITION(k, t) if (KLASS_HAS_STICKY_BIT(k)) Jfr::on_klass_redefinition(k, t) #define ON_KLASS_CREATION(k, p, t) Jfr::on_klass_creation(k, p, t) #endif // SHARE_JFR_SUPPORT_JFRKLASSEXTENSION_HPP diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index e38e0427a05..291169b9aa7 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -29,6 +29,7 @@ #include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrOopTraceId.inline.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" #include "jfr/recorder/jfrRecorder.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" @@ -75,6 +76,7 @@ JfrThreadLocal::JfrThreadLocal() : _wallclock_time(os::javaTimeNanos()), _non_reentrant_nesting(0), _vthread_epoch(0), + _generation(0), _vthread_excluded(false), _jvm_thread_excluded(false), _enqueued_requests(false), @@ -136,17 +138,33 @@ static void send_java_thread_start_event(JavaThread* jt) { } void JfrThreadLocal::on_start(Thread* t) { - assign_thread_id(t, t->jfr_thread_local()); + JfrThreadLocal* const tl = t->jfr_thread_local(); + assert(tl != nullptr, "invariant"); + assign_thread_id(t, tl); if (JfrRecorder::is_recording()) { - JfrCheckpointManager::write_checkpoint(t); - if (t->is_Java_thread()) { - JavaThread *const jt = JavaThread::cast(t); + if (!t->is_Java_thread()) { + JfrCheckpointManager::write_checkpoint(t); + return; + } + JavaThread* const jt = JavaThread::cast(t); + if (jt->thread_state() == _thread_new) { JfrCPUTimeThreadSampling::on_javathread_create(jt); + } else { + assert(jt->thread_state() == _thread_in_vm, "invariant"); + if (tl->should_write()) { + JfrCheckpointManager::write_checkpoint(t); + } send_java_thread_start_event(jt); + if (tl->has_cached_stack_trace()) { + tl->clear_cached_stack_trace(); + } + return; } } - if (t->jfr_thread_local()->has_cached_stack_trace()) { - t->jfr_thread_local()->clear_cached_stack_trace(); + if (t->is_Java_thread() && JavaThread::cast(t)->thread_state() == _thread_in_vm) { + if (tl->has_cached_stack_trace()) { + tl->clear_cached_stack_trace(); + } } } @@ -229,13 +247,18 @@ void JfrThreadLocal::on_exit(Thread* t) { JfrThreadLocal * const tl = t->jfr_thread_local(); assert(!tl->is_dead(), "invariant"); if (JfrRecorder::is_recording()) { - JfrCheckpointManager::write_checkpoint(t); - } - if (t->is_Java_thread()) { - JavaThread* const jt = JavaThread::cast(t); - send_java_thread_end_event(jt, JfrThreadLocal::jvm_thread_id(jt)); - JfrCPUTimeThreadSampling::on_javathread_terminate(jt); - JfrThreadCPULoadEvent::send_event_for_thread(jt); + if (!t->is_Java_thread()) { + JfrCheckpointManager::write_checkpoint(t); + } else { + JavaThread* const jt = JavaThread::cast(t); + assert(jt->thread_state() == _thread_in_vm, "invariant"); + if (tl->should_write()) { + JfrCheckpointManager::write_checkpoint(t); + } + send_java_thread_end_event(jt, JfrThreadLocal::jvm_thread_id(jt)); + JfrCPUTimeThreadSampling::on_javathread_terminate(jt); + JfrThreadCPULoadEvent::send_event_for_thread(jt); + } } release(tl, Thread::current()); // because it could be that Thread::current() != t } @@ -425,6 +448,15 @@ u2 JfrThreadLocal::vthread_epoch(const JavaThread* jt) { return Atomic::load(&jt->jfr_thread_local()->_vthread_epoch); } +bool JfrThreadLocal::should_write() const { + const u2 current_generation = JfrTraceIdEpoch::epoch_generation(); + if (Atomic::load(&_generation) != current_generation) { + Atomic::store(&_generation, current_generation); + return true; + } + return false; +} + traceid JfrThreadLocal::thread_id(const Thread* t) { assert(t != nullptr, "invariant"); if (is_impersonating(t)) { diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp index 715a2c44f93..8c82dfad8af 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp @@ -75,6 +75,7 @@ class JfrThreadLocal { jlong _wallclock_time; int32_t _non_reentrant_nesting; u2 _vthread_epoch; + mutable u2 _generation; bool _vthread_excluded; bool _jvm_thread_excluded; volatile bool _enqueued_requests; @@ -348,6 +349,9 @@ class JfrThreadLocal { return _sampling_critical_section; } + // Serialization state. + bool should_write() const; + static int32_t make_non_reentrant(Thread* thread); static void make_reentrant(Thread* thread, int32_t previous_nesting); diff --git a/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp b/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp index c73ece42645..bb0e3e082fe 100644 --- a/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp +++ b/src/hotspot/share/jfr/support/jfrTraceIdExtension.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,11 +95,13 @@ class JfrTraceFlag { uint8_t* trace_flags_addr() const { \ return _trace_flags.flags_addr(); \ } \ - uint8_t* trace_meta_addr() const { \ + uint8_t* trace_flags_meta_addr() const { \ return _trace_flags.meta_addr(); \ } \ - void copy_trace_flags(uint16_t rhs_flags) const { \ - _trace_flags.set_flags(_trace_flags.flags() | rhs_flags); \ + void copy_trace_flags(const Method* rhm) const { \ + assert(rhm != nullptr, "invariant"); \ + set_trace_flags(rhm->trace_flags()); \ + assert(trace_flags()==rhm->trace_flags(), ""); \ } #endif // SHARE_JFR_SUPPORT_JFRTRACEIDEXTENSION_HPP diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrInstrumentedClass.hpp b/src/hotspot/share/jfr/support/methodtracer/jfrInstrumentedClass.hpp index eb775581e1f..c3a71ac6933 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrInstrumentedClass.hpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrInstrumentedClass.hpp @@ -36,16 +36,16 @@ class InstanceKlass; class JfrInstrumentedClass { private: traceid _trace_id; - const InstanceKlass* _instance_klass; + const InstanceKlass* _ik; bool _unloaded; public: - JfrInstrumentedClass(traceid trace_id = 0, const InstanceKlass* instance_klass = nullptr, bool unloaded = false) : - _trace_id(trace_id), _instance_klass(instance_klass), _unloaded(unloaded) { + JfrInstrumentedClass(traceid trace_id = 0, const InstanceKlass* ik = nullptr, bool unloaded = false) : + _trace_id(trace_id), _ik(ik), _unloaded(unloaded) { } const InstanceKlass* instance_klass() const { - return _instance_klass; + return _ik; } traceid trace_id() const { diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrMethodProcessor.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrMethodProcessor.cpp index d3226729beb..16573a77016 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrMethodProcessor.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrMethodProcessor.cpp @@ -67,6 +67,8 @@ void JfrMethodProcessor::update_methods(const InstanceKlass* ik) { const uint32_t idx = _methods->at(i).methods_array_index(); Method* const method = ik_methods->at(idx); assert(method != nullptr, "invariant"); + assert(method->name() == _methods->at(i).name(), "invariant"); + assert(method->signature() == _methods->at(i).signature(), "invariant"); _methods->at(i).set_method(method); // This is to keep the method from being unloaded during redefine / retransform. // Equivalent functionality to that provided by the methodHandle. Unfortunately, diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp index 73701445f70..e667324b51a 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.cpp @@ -128,11 +128,11 @@ void JfrMethodTracer::retransform(JNIEnv* env, const JfrFilterClassClosure& clas } } -static void handle_no_bytecode_result(const Klass* klass) { - assert(klass != nullptr, "invariant"); - if (JfrTraceId::has_sticky_bit(klass)) { +static void handle_no_bytecode_result(const InstanceKlass* ik) { + assert(ik != nullptr, "invariant"); + if (JfrTraceId::has_sticky_bit(ik)) { MutexLocker lock(ClassLoaderDataGraph_lock); - JfrTraceTagging::clear_sticky_bit(InstanceKlass::cast(klass)); + JfrTraceTagging::clear_sticky(ik); } } @@ -143,11 +143,11 @@ void JfrMethodTracer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& par ResourceMark rm(THREAD); // 1. Is the ik the initial load, i.e.the first InstanceKlass, or a scratch klass, denoting a redefine / retransform? - const Klass* const existing_klass = JfrClassTransformer::find_existing_klass(ik, THREAD); - const bool is_retransform = existing_klass != nullptr; + const InstanceKlass* const existing_ik = JfrClassTransformer::find_existing_klass(ik, THREAD); + const bool is_retransform = existing_ik != nullptr; // 2. Test the ik and its methods against the currently installed filter object. - JfrMethodProcessor mp(is_retransform ? InstanceKlass::cast(existing_klass) : ik, THREAD); + JfrMethodProcessor mp(is_retransform ? existing_ik : ik, THREAD); if (!mp.has_methods()) { return; } @@ -159,7 +159,7 @@ void JfrMethodTracer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& par // If no bytecode is returned, either an error occurred during transformation, but more // likely the matched instructions were negative, i.e. instructions to remove existing instrumentation // and so Java added no new instrumentation. By not returning a bytecode result, the klass is restored to its original, non-instrumented, version. - handle_no_bytecode_result(is_retransform ? InstanceKlass::cast(existing_klass) : ik); + handle_no_bytecode_result(is_retransform ? existing_ik : ik); return; } // 4. Now create a new InstanceKlass representation from the modified bytecode. @@ -173,13 +173,12 @@ void JfrMethodTracer::on_klass_creation(InstanceKlass*& ik, ClassFileParser& par // Keep the original cached class file data from the existing class. JfrClassTransformer::transfer_cached_class_file_data(ik, new_ik, parser, THREAD); JfrClassTransformer::rewrite_klass_pointer(ik, new_ik, parser, THREAD); // The ik is modified to point to new_ik here. - const InstanceKlass* const existing_ik = InstanceKlass::cast(existing_klass); mp.update_methods(existing_ik); existing_ik->module()->add_read(jdk_jfr_module()); // By setting the sticky bit on the existng klass, we receive a callback into on_klass_redefinition (see below) // when our new methods are installed into the existing klass as part of retransformation / redefinition. // Only when we know our new methods have been installed can we add the klass to the instrumented list (done as part of callback). - JfrTraceTagging::install_sticky_bit_for_retransform_klass(existing_ik, mp.methods(), mp.has_timing()); + JfrTraceTagging::tag_sticky_for_retransform_klass(existing_ik, ik, mp.methods(), mp.has_timing()); return; } // Initial class load. @@ -203,28 +202,22 @@ static inline void log_add(const InstanceKlass* ik) { } } -void JfrMethodTracer::add_timing_entry(const InstanceKlass* ik, traceid klass_id) { - assert(ik != nullptr, "invariant"); +void JfrMethodTracer::add_timing_entry(traceid klass_id) { assert(_timing_entries != nullptr, "invariant"); - if (JfrTraceId::has_timing_bit(ik)) { - JfrTraceId::clear_timing_bit(ik); - _timing_entries->append(klass_id); - } + _timing_entries->append(klass_id); } // At this point we have installed our new retransformed methods into the original klass, which is ik. -// jvmtiRedefineClassses::redefine_single_class() has finished so we are still at a safepoint. -// If the original klass is not already in the list, add it and also dynamically tag all -// artifacts that have the sticky bit set. If the klass has an associated TimedClass, -// also add the klass to the list of _timing_entries for publication. -void JfrMethodTracer::on_klass_redefinition(const InstanceKlass* ik, Thread* thread) { +// jvmtiRedefineClassses::redefine_single_class() is about to finish so we are still at a safepoint. +// If the original klass is not already in the list, add it. If the klass has an associated TimedClass, +// add also the klass_id to the list of _timing_entries for publication. +void JfrMethodTracer::on_klass_redefinition(const InstanceKlass* ik, bool has_timing) { assert(ik != nullptr, "invariant"); assert(!ik->is_scratch_class(), "invarint"); assert(ik->has_been_redefined(), "invariant"); assert(JfrTraceId::has_sticky_bit(ik), "invariant"); assert(in_use(), "invariant"); assert(SafepointSynchronize::is_at_safepoint(), "invariant"); - assert_locked_or_safepoint(ClassLoaderDataGraph_lock); const traceid klass_id = JfrTraceId::load_raw(ik); const JfrInstrumentedClass jic(klass_id, ik, false); @@ -235,8 +228,9 @@ void JfrMethodTracer::on_klass_redefinition(const InstanceKlass* ik, Thread* thr assert(!JfrTraceIdEpoch::has_method_tracer_changed_tag_state(), "invariant"); JfrTraceIdEpoch::set_method_tracer_tag_state(); } - add_timing_entry(ik, klass_id); - JfrTraceTagging::set_dynamic_tag_for_sticky_bit(ik); + if (has_timing) { + add_timing_entry(klass_id); + } log_add(ik); } } @@ -258,8 +252,7 @@ void JfrMethodTracer::add_instrumented_class(InstanceKlass* ik, GrowableArraymodule()->add_read(jdk_jfr_module()); MutexLocker lock(ClassLoaderDataGraph_lock); assert(!in_instrumented_list(ik, instrumented_classes()), "invariant"); - JfrTraceTagging::set_dynamic_tag(ik, methods); - JfrTraceTagging::set_sticky_bit(ik, methods); + JfrTraceTagging::tag_sticky(ik, methods); const JfrInstrumentedClass jik(JfrTraceId::load_raw(ik), ik, false); const int idx = instrumented_classes()->append(jik); if (idx == 0) { diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.hpp b/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.hpp index 1c44a93fcb2..d67de9daa59 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.hpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrMethodTracer.hpp @@ -51,7 +51,7 @@ class JfrMethodTracer: AllStatic { static GrowableArray* _timing_entries; // Guarded by ClassLoaderDataGraph_lock static ModuleEntry* jdk_jfr_module(); - static void add_timing_entry(const InstanceKlass* ik, traceid klass_id); + static void add_timing_entry(traceid klass_id); static void retransform(JNIEnv* env, const JfrFilterClassClosure& classes, TRAPS); static void add_instrumented_class(InstanceKlass* ik, GrowableArray* methods); @@ -61,7 +61,7 @@ class JfrMethodTracer: AllStatic { static void add_to_unloaded_set(const Klass* k); static void trim_instrumented_classes(bool trim); static GrowableArray* instrumented_classes(); - static void on_klass_redefinition(const InstanceKlass* ik, Thread* thread); + static void on_klass_redefinition(const InstanceKlass* ik, bool has_timing); static void on_klass_creation(InstanceKlass*& ik, ClassFileParser& parser, TRAPS); static jlongArray set_filters(JNIEnv* env, jobjectArray classes, diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.cpp index e5018212768..dc70e70360f 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.cpp @@ -25,9 +25,11 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp" #include "jfr/support/methodtracer/jfrInstrumentedClass.hpp" +#include "jfr/support/methodtracer/jfrMethodTracer.hpp" #include "jfr/support/methodtracer/jfrTraceTagging.hpp" #include "oops/instanceKlass.hpp" #include "oops/method.hpp" +#include "runtime/safepoint.hpp" #include "utilities/growableArray.hpp" void JfrTraceTagging::tag_dynamic(const InstanceKlass* ik) { @@ -38,100 +40,39 @@ void JfrTraceTagging::tag_dynamic(const Method* method) { JfrTraceId::load_no_enqueue(method); } -void JfrTraceTagging::tag_dynamic(const InstanceKlass* ik, const GrowableArray* methods) { - assert(ik != nullptr, "invariant"); - assert(methods != nullptr, "invariant"); - - for (int i = 0; i < methods->length(); ++i) { - const Method* const method = methods->at(i).method(); - assert(method != nullptr, "invariant"); - if (!method->is_old()) { - tag_dynamic(method); - continue; - } - // A redefinition / retransformation interleaved. - // Find and tag the latest version of the method. - tag_dynamic(ik->method_with_orig_idnum(method->orig_method_idnum())); - } -} - -void JfrTraceTagging::set_dynamic_tag(const InstanceKlass* ik, const GrowableArray* methods) { - assert(ik != nullptr, "invariant"); - assert(!ik->is_scratch_class(), "invariant"); - - tag_dynamic(ik, methods); - tag_dynamic(ik); -} - -void JfrTraceTagging::set_dynamic_tag_for_sticky_bit(const InstanceKlass* ik) { - assert(ik != nullptr, "invariant"); - assert(!ik->is_scratch_class(), "invariant"); - assert(JfrTraceId::has_sticky_bit(ik), "invariant"); - - const int length = ik->methods()->length(); - for (int i = 0; i < length; ++i) { - const Method* const m = ik->methods()->at(i); - if (JfrTraceId::has_sticky_bit(m)) { - tag_dynamic(m); - } - } - tag_dynamic(ik); -} - void JfrTraceTagging::tag_sticky(const InstanceKlass* ik) { JfrTraceId::set_sticky_bit(ik); } +void JfrTraceTagging::tag_sticky_enqueue(const InstanceKlass* ik) { + tag_sticky(ik); + JfrTraceIdLoadBarrier::enqueue(ik); +} + void JfrTraceTagging::tag_sticky(const Method* method) { JfrTraceId::set_sticky_bit(method); } -void JfrTraceTagging::tag_sticky(const InstanceKlass* ik, const GrowableArray* methods) { - assert(ik != nullptr, "invariant"); +void JfrTraceTagging::tag_sticky(const GrowableArray* methods) { assert(methods != nullptr, "invariant"); - + assert_locked_or_safepoint(ClassLoaderDataGraph_lock); for (int i = 0; i < methods->length(); ++i) { const Method* const method = methods->at(i).method(); assert(method != nullptr, "invariant"); - if (!method->is_old()) { - tag_sticky(method); - continue; - } - // A redefinition / retransformation interleaved. - // Find and tag the latest version of the method. - tag_sticky(ik->method_with_orig_idnum(method->orig_method_idnum())); + tag_sticky(method); } } -void JfrTraceTagging::tag_timing(const InstanceKlass* ik) { - JfrTraceId::set_timing_bit(ik); -} - -void JfrTraceTagging::install_sticky_bit_for_retransform_klass(const InstanceKlass* ik, const GrowableArray* methods, bool timing) { - assert(ik != nullptr, "invariant"); - assert(!ik->is_scratch_class(), "invariant"); - - MutexLocker lock(ClassLoaderDataGraph_lock); - if (JfrTraceId::has_sticky_bit(ik)) { - clear_sticky_bit(ik); - } - tag_sticky(ik, methods); - tag_sticky(ik); - if (timing) { - tag_timing(ik); - } -} - -void JfrTraceTagging::set_sticky_bit(const InstanceKlass* ik, const GrowableArray* methods) { +void JfrTraceTagging::tag_sticky(const InstanceKlass* ik, const GrowableArray* methods) { assert(ik != nullptr, "invariant"); assert(!ik->is_scratch_class(), "invariant"); + assert(methods != nullptr, "invariant"); assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - - tag_sticky(ik, methods); - tag_sticky(ik); + tag_sticky(methods); + tag_sticky_enqueue(ik); } -void JfrTraceTagging::clear_sticky_bit(const InstanceKlass* ik, bool dynamic_tag /* true */) { +void JfrTraceTagging::clear_sticky(const InstanceKlass* ik, bool dynamic_tag /* true */) { assert(ik != nullptr, "invariant"); assert(!ik->is_scratch_class(), "invariant"); assert(JfrTraceId::has_sticky_bit(ik), "invariant"); @@ -153,8 +94,78 @@ void JfrTraceTagging::clear_sticky_bit(const InstanceKlass* ik, bool dynamic_tag tag_dynamic(ik); } JfrTraceId::clear_sticky_bit(ik); - if (JfrTraceId::has_timing_bit(ik)) { - JfrTraceId::clear_timing_bit(ik); - } - assert(!JfrTraceId::has_timing_bit(ik), "invariant"); +} + +void JfrTraceTagging::tag_sticky_for_retransform_klass(const InstanceKlass* existing_klass, const InstanceKlass* scratch_klass, const GrowableArray* methods, bool timing) { + assert(existing_klass != nullptr, "invariant"); + assert(scratch_klass != nullptr, "invariant"); + // The scratch class has not yet received its official status. + // assert(scratch_klass->is_scratch_class(), "invariant"); + if (timing) { + // Can be done outside lock because it is a scratch klass. + // Visibility guaranteed by upcoming safepoint. + JfrTraceId::set_timing_bit(scratch_klass); + } + MutexLocker lock(ClassLoaderDataGraph_lock); + if (JfrTraceId::has_sticky_bit(existing_klass)) { + clear_sticky(existing_klass); + } + tag_sticky(methods); + tag_sticky(existing_klass); +} + +void JfrTraceTagging::on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass) { + assert(ik != nullptr, "invariant"); + assert(ik->has_been_redefined(), "invariant"); + assert(scratch_klass != nullptr, "invariant"); + assert(scratch_klass->is_scratch_class(), "invariant"); + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + + const bool klass_has_sticky_bit = JfrTraceId::has_sticky_bit(ik); + if (klass_has_sticky_bit) { + JfrTraceIdLoadBarrier::enqueue(ik); + } + + const Array* new_methods = ik->methods(); + assert(new_methods != nullptr, "invariant"); + + const int len = new_methods->length(); // Can be shorter, equal to, or longer than old methods length. + + for (int i = 0; i < len; ++i) { + const Method* const nm = new_methods->at(i); + assert(nm != nullptr, "invariant"); + const Method* const om = scratch_klass->method_with_orig_idnum(nm->orig_method_idnum()); + if (om == nullptr) { + assert(AllowRedefinitionToAddDeleteMethods, "invariant"); + // nm is a newly added Method. + continue; + } + assert(nm != om, "invariant"); + assert(om->is_old(), "invariant"); + assert(nm->orig_method_idnum() == om->orig_method_idnum(), "invariant"); + assert(nm->name() == om->name() && nm->signature() == om->signature(), "invariant"); + + if (nm->trace_flags() == om->trace_flags()) { + continue; + } + + const bool is_blessed = IS_METHOD_BLESSED(nm); + + // Copy the old method trace flags under a safepoint. + nm->copy_trace_flags(om); + + assert(nm->trace_flags() == om->trace_flags(), "invariant"); + + if (is_blessed) { + BLESS_METHOD(nm); + assert(IS_METHOD_BLESSED(nm), "invariant"); + } + } + + // A retransformed/redefined klass carrying the sticky bit + // needs additional processing by the JfrMethodTracer subsystem. + if (klass_has_sticky_bit) { + assert(JfrMethodTracer::in_use(), "invariant"); + JfrMethodTracer::on_klass_redefinition(ik, JfrTraceId::has_timing_bit(scratch_klass)); + } } diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.hpp b/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.hpp index d2aa6f8bb04..38ead4d0fed 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.hpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrTraceTagging.hpp @@ -39,18 +39,16 @@ template class GrowableArray; class JfrTraceTagging : AllStatic { private: static void tag_dynamic(const InstanceKlass* ik); - static void tag_dynamic(const InstanceKlass* ik, const GrowableArray* methods); static void tag_dynamic(const Method* method); static void tag_sticky(const InstanceKlass* ik); static void tag_sticky(const Method* method); - static void tag_sticky(const InstanceKlass* ik, const GrowableArray* methods); - static void tag_timing(const InstanceKlass* ik); + static void tag_sticky(const GrowableArray* methods); + static void tag_sticky_enqueue(const InstanceKlass* ik); public: - static void set_dynamic_tag(const InstanceKlass* ik, const GrowableArray* methods); - static void set_dynamic_tag_for_sticky_bit(const InstanceKlass* ik); - static void install_sticky_bit_for_retransform_klass(const InstanceKlass* existing_klass, const GrowableArray* methods, bool timing); - static void set_sticky_bit(const InstanceKlass* ik, const GrowableArray* methods); - static void clear_sticky_bit(const InstanceKlass* ik, bool dynamic_tag = true); + static void clear_sticky(const InstanceKlass* ik, bool dynamic_tag = true); + static void tag_sticky(const InstanceKlass* ik, const GrowableArray* methods); + static void tag_sticky_for_retransform_klass(const InstanceKlass* existing_klass, const InstanceKlass* scratch_klass, const GrowableArray* methods, bool timing); + static void on_klass_redefinition(const InstanceKlass* ik, const InstanceKlass* scratch_klass); }; #endif /* SHARE_JFR_SUPPORT_METHODTRACER_JFRTRACETAGGING_HPP */ diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp index 41531b083fc..f06ee667ff2 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.hpp @@ -124,6 +124,7 @@ class CompilerToVM { static address dsin; static address dcos; static address dtan; + static address dsinh; static address dtanh; static address dcbrt; static address dexp; @@ -131,6 +132,8 @@ class CompilerToVM { static address dlog10; static address dpow; + static address crc_table_addr; + static address symbol_init; static address symbol_clinit; diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index b6d919fdfe9..753d673f54c 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -144,6 +144,7 @@ int CompilerToVM::Data::sizeof_ZStoreBarrierEntry = sizeof(ZStoreBarrierEntry); address CompilerToVM::Data::dsin; address CompilerToVM::Data::dcos; address CompilerToVM::Data::dtan; +address CompilerToVM::Data::dsinh; address CompilerToVM::Data::dtanh; address CompilerToVM::Data::dcbrt; address CompilerToVM::Data::dexp; @@ -151,6 +152,8 @@ address CompilerToVM::Data::dlog; address CompilerToVM::Data::dlog10; address CompilerToVM::Data::dpow; +address CompilerToVM::Data::crc_table_addr; + address CompilerToVM::Data::symbol_init; address CompilerToVM::Data::symbol_clinit; @@ -287,8 +290,10 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { name = nullptr; \ } + SET_TRIGFUNC_OR_NULL(dsinh); SET_TRIGFUNC_OR_NULL(dtanh); SET_TRIGFUNC_OR_NULL(dcbrt); + SET_TRIGFUNC_OR_NULL(crc_table_addr); #undef SET_TRIGFUNC_OR_NULL diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 32ef3eb3e14..737718096c5 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -141,12 +141,14 @@ static_field(CompilerToVM::Data, dsin, address) \ static_field(CompilerToVM::Data, dcos, address) \ static_field(CompilerToVM::Data, dtan, address) \ + static_field(CompilerToVM::Data, dsinh, address) \ static_field(CompilerToVM::Data, dtanh, address) \ static_field(CompilerToVM::Data, dcbrt, address) \ static_field(CompilerToVM::Data, dexp, address) \ static_field(CompilerToVM::Data, dlog, address) \ static_field(CompilerToVM::Data, dlog10, address) \ static_field(CompilerToVM::Data, dpow, address) \ + static_field(CompilerToVM::Data, crc_table_addr, address) \ \ static_field(CompilerToVM::Data, symbol_init, address) \ static_field(CompilerToVM::Data, symbol_clinit, address) \ @@ -417,8 +419,6 @@ static_field(StubRoutines, _dilithiumMontMulByConstant, address) \ static_field(StubRoutines, _dilithiumDecomposePoly, address) \ static_field(StubRoutines, _updateBytesCRC32, address) \ - static_field(StubRoutines, _crc_table_adr, address) \ - static_field(StubRoutines, _crc32c_table_addr, address) \ static_field(StubRoutines, _updateBytesCRC32C, address) \ static_field(StubRoutines, _updateBytesAdler32, address) \ static_field(StubRoutines, _multiplyToLen, address) \ diff --git a/src/hotspot/share/libadt/dict.cpp b/src/hotspot/share/libadt/dict.cpp index e31df748073..31853b9ceb6 100644 --- a/src/hotspot/share/libadt/dict.cpp +++ b/src/hotspot/share/libadt/dict.cpp @@ -29,8 +29,6 @@ // %%%%% includes not needed with AVM framework - Ungar -#include - //------------------------------data----------------------------------------- // String hash tables #define MAXID 20 diff --git a/src/hotspot/share/memory/guardedMemory.hpp b/src/hotspot/share/memory/guardedMemory.hpp index a3bbf48b0cd..2b6d34e8e0a 100644 --- a/src/hotspot/share/memory/guardedMemory.hpp +++ b/src/hotspot/share/memory/guardedMemory.hpp @@ -42,9 +42,10 @@ * |Offset | Content | Description | * |------------------------------------------------------------ * |base_addr | 0xABABABABABABABAB | Head guard | - * |+16 | | User data size | - * |+sizeof(uintptr_t) | | Tag word | - * |+sizeof(uintptr_t) | | Tag word | + * |+GUARD_SIZE | | User data size | + * |+sizeof(size_t) | | Tag word | + * |+sizeof(void*) | | Tag word | + * |+sizeof(void*) | | Padding | * |+sizeof(void*) | 0xF1 ( | User data | * |+user_size | 0xABABABABABABABAB | Tail guard | * ------------------------------------------------------------- @@ -52,6 +53,8 @@ * Where: * - guard padding uses "badResourceValue" (0xAB) * - tag word and tag2 word are general purpose + * - padding is inserted as-needed by the compiler to ensure + * the user data is aligned on a 16-byte boundary * - user data * -- initially padded with "uninitBlockPad" (0xF1), * -- to "freeBlockPad" (0xBA), when freed @@ -132,12 +135,15 @@ protected: /** * Header guard and size + * + * NB: the size and placement of the GuardHeader must be such that the + * user-ptr is maximally aligned i.e. 16-byte alignment for x86 ABI for + * stack alignment and use of vector (xmm) instructions. We use alignas + * to achieve this. */ - class GuardHeader : Guard { + class alignas(16) GuardHeader : Guard { friend class GuardedMemory; protected: - // Take care in modifying fields here, will effect alignment - // e.g. x86 ABI 16 byte stack alignment union { uintptr_t __unused_full_word1; size_t _user_size; diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 7eca79142a6..92b33443457 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -834,14 +834,20 @@ void Metaspace::global_initialize() { } -#endif // _LP64 +#else + // +UseCompressedClassPointers on 32-bit: does not need class space. Klass can live wherever. + if (UseCompressedClassPointers) { + const address start = (address)os::vm_min_address(); // but not in the zero page + const address end = (address)CompressedKlassPointers::max_klass_range_size(); + CompressedKlassPointers::initialize(start, end - start); + } +#endif // __LP64 // Initialize non-class virtual space list, and its chunk manager: MetaspaceContext::initialize_nonclass_space_context(); _tracer = new MetaspaceTracer(); -#ifdef _LP64 if (UseCompressedClassPointers) { // Note: "cds" would be a better fit but keep this for backward compatibility. LogTarget(Info, gc, metaspace) lt; @@ -852,8 +858,6 @@ void Metaspace::global_initialize() { CompressedKlassPointers::print_mode(&ls); } } -#endif - } void Metaspace::post_initialize() { diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index bb59192cf16..d14cc5699fa 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -260,6 +260,16 @@ 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"); } + +#ifndef _LP64 + // On 32-bit, with +UseCompressedClassPointers, the whole address space is the encoding range. We therefore + // don't need a class space. However, as a pragmatic workaround for pesty overflow problems on 32-bit, we leave + // a small area at the end of the address space out of the encoding range. We just assume no Klass will ever live + // there (it won't, for no OS we support on 32-bit has user-addressable memory up there). + assert(!UseCompressedClassPointers || + rs.end() <= (char*)CompressedKlassPointers::max_klass_range_size(), "Weirdly high address"); +#endif // _LP64 + MemTracker::record_virtual_memory_tag(rs, mtMetaspace); assert_is_aligned(rs.base(), chunklevel::MAX_CHUNK_BYTE_SIZE); InternalStats::inc_num_vsnodes_births(); diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index cefe8454fb9..bd47a054bc0 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -909,9 +909,11 @@ jint universe_init() { ClassLoaderData::init_null_class_loader_data(); #if INCLUDE_CDS +#if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_using_full_module_graph()) { ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data(); } +#endif // INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_archive()) { CDSConfig::prepare_for_dumping(); } @@ -950,8 +952,9 @@ ReservedHeapSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { assert(alignment <= Arguments::conservative_max_heap_alignment(), "actual alignment %zu must be within maximum heap alignment %zu", alignment, Arguments::conservative_max_heap_alignment()); + assert(is_aligned(heap_size, alignment), "precondition"); - size_t total_reserved = align_up(heap_size, alignment); + size_t total_reserved = heap_size; assert(!UseCompressedOops || (total_reserved <= (OopEncodingHeapMax - os::vm_page_size())), "heap size is too big for compressed oops"); diff --git a/src/hotspot/share/metaprogramming/primitiveConversions.hpp b/src/hotspot/share/metaprogramming/primitiveConversions.hpp index f43cfde570c..147d690a176 100644 --- a/src/hotspot/share/metaprogramming/primitiveConversions.hpp +++ b/src/hotspot/share/metaprogramming/primitiveConversions.hpp @@ -28,6 +28,7 @@ #include "memory/allStatic.hpp" #include "metaprogramming/enableIf.hpp" #include "utilities/globalDefinitions.hpp" + #include class PrimitiveConversions : public AllStatic { diff --git a/src/hotspot/share/nmt/mallocHeader.inline.hpp b/src/hotspot/share/nmt/mallocHeader.inline.hpp index 34ec891d33f..8b1862332fc 100644 --- a/src/hotspot/share/nmt/mallocHeader.inline.hpp +++ b/src/hotspot/share/nmt/mallocHeader.inline.hpp @@ -104,7 +104,7 @@ inline OutTypeParam MallocHeader::resolve_checked_impl(InTypeParam memblock) { OutTypeParam header_pointer = (OutTypeParam)memblock - 1; if (!header_pointer->check_block_integrity(msg, sizeof(msg), &corruption)) { header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer); - fatal("NMT corruption: Block at " PTR_FORMAT ": %s", p2i(memblock), msg); + fatal("NMT has detected a memory corruption bug. Block at " PTR_FORMAT ": %s", p2i(memblock), msg); } return header_pointer; } diff --git a/src/hotspot/share/nmt/memBaseline.cpp b/src/hotspot/share/nmt/memBaseline.cpp index 8661f91174a..35dff0d8646 100644 --- a/src/hotspot/share/nmt/memBaseline.cpp +++ b/src/hotspot/share/nmt/memBaseline.cpp @@ -215,7 +215,7 @@ bool MemBaseline::aggregate_virtual_memory_allocation_sites() { site = node->data(); } site->reserve_memory(rgn->size()); - site->commit_memory(rgn->committed_size()); + site->commit_memory(VirtualMemoryTracker::Instance::committed_size(rgn)); } _virtual_memory_sites.move(&allocation_sites); diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index c1c847a4f2e..a7c564eff53 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -422,7 +422,7 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* outputStream* out = output(); const char* scale = current_scale(); const NativeCallStack* stack = reserved_rgn->call_stack(); - bool all_committed = reserved_rgn->size() == reserved_rgn->committed_size(); + bool all_committed = reserved_rgn->size() == VirtualMemoryTracker::Instance::committed_size(reserved_rgn); const char* region_type = (all_committed ? "reserved and committed" : "reserved"); out->cr(); print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size()); diff --git a/src/hotspot/share/nmt/memoryFileTracker.cpp b/src/hotspot/share/nmt/memoryFileTracker.cpp index 677cd174650..55f1cb64626 100644 --- a/src/hotspot/share/nmt/memoryFileTracker.cpp +++ b/src/hotspot/share/nmt/memoryFileTracker.cpp @@ -64,12 +64,12 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st stream->print_cr("Memory map of %s", file->_descriptive_name); stream->cr(); - VMATree::TreapNode* prev = nullptr; + const VMATree::TNode* prev = nullptr; #ifdef ASSERT - VMATree::TreapNode* broken_start = nullptr; - VMATree::TreapNode* broken_end = nullptr; + const VMATree::TNode* broken_start = nullptr; + const VMATree::TNode* broken_end = nullptr; #endif - file->_tree.visit_in_order([&](VMATree::TreapNode* current) { + file->_tree.visit_in_order([&](const VMATree::TNode* current) { if (prev == nullptr) { // Must be first node. prev = current; diff --git a/src/hotspot/share/nmt/nmtTreap.hpp b/src/hotspot/share/nmt/nmtTreap.hpp deleted file mode 100644 index 13e759cbd3a..00000000000 --- a/src/hotspot/share/nmt/nmtTreap.hpp +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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_NMT_NMTTREAP_HPP -#define SHARE_NMT_NMTTREAP_HPP - -#include "runtime/os.hpp" -#include "utilities/debug.hpp" -#include "utilities/globalDefinitions.hpp" -#include "utilities/growableArray.hpp" -#include "utilities/macros.hpp" -#include "utilities/powerOfTwo.hpp" -#include - -// A Treap is a self-balanced binary tree where each node is equipped with a -// priority. It adds the invariant that the priority of a parent P is strictly larger -// larger than the priority of its children. When priorities are randomly -// assigned the tree is balanced. -// All operations are defined through merge and split, which are each other's inverse. -// merge(left_treap, right_treap) => treap where left_treap <= right_treap -// split(treap, key) => (left_treap, right_treap) where left_treap <= right_treap -// Recursion is used in these, but the depth of the call stack is the depth of -// the tree which is O(log n) so we are safe from stack overflow. -// TreapNode has LEQ nodes on the left, GT nodes on the right. -// -// COMPARATOR must have a static function `cmp(a,b)` which returns: -// - an int < 0 when a < b -// - an int == 0 when a == b -// - an int > 0 when a > b -// ALLOCATOR must check for oom and exit, as Treap currently does not handle the allocation -// failing. - -template -class Treap { - friend class NMTVMATreeTest; - friend class NMTTreapTest; - friend class VMTWithVMATreeTest; -public: - class TreapNode { - friend Treap; - uint64_t _priority; - const K _key; - V _value; - - TreapNode* _left; - TreapNode* _right; - - public: - TreapNode(const K& k, uint64_t p) : _priority(p), _key(k), _left(nullptr), _right(nullptr) {} - - TreapNode(const K& k, const V& v, uint64_t p) - : _priority(p), - _key(k), - _value(v), - _left(nullptr), - _right(nullptr) { - } - - const K& key() const { return _key; } - V& val() { return _value; } - - TreapNode* left() const { return _left; } - TreapNode* right() const { return _right; } - }; - -private: - ALLOCATOR _allocator; - TreapNode* _root; - - // A random number - static constexpr const uint64_t _initial_seed = 0xC8DD2114AE0543A3; - uint64_t _prng_seed; - int _node_count; - - uint64_t prng_next() { - uint64_t first_half = os::next_random(_prng_seed); - uint64_t second_half = os::next_random(_prng_seed >> 32); - _prng_seed = first_half | (second_half << 32); - return _prng_seed; - } - - struct node_pair { - TreapNode* left; - TreapNode* right; - }; - - enum SplitMode { - LT, // < - LEQ // <= - }; - - // Split tree at head into two trees, SplitMode decides where EQ values go. - // We have SplitMode because it makes remove() trivial to implement. - static node_pair split(TreapNode* head, const K& key, SplitMode mode = LEQ DEBUG_ONLY(COMMA int recur_count = 0)) { - assert(recur_count < 200, "Call-stack depth should never exceed 200"); - - if (head == nullptr) { - return {nullptr, nullptr}; - } - if ((COMPARATOR::cmp(head->_key, key) <= 0 && mode == LEQ) || (COMPARATOR::cmp(head->_key, key) < 0 && mode == LT)) { - node_pair p = split(head->_right, key, mode DEBUG_ONLY(COMMA recur_count + 1)); - head->_right = p.left; - return node_pair{head, p.right}; - } else { - node_pair p = split(head->_left, key, mode DEBUG_ONLY(COMMA recur_count + 1)); - head->_left = p.right; - return node_pair{p.left, head}; - } - } - - // Invariant: left is a treap whose keys are LEQ to the keys in right. - static TreapNode* merge(TreapNode* left, TreapNode* right DEBUG_ONLY(COMMA int recur_count = 0)) { - assert(recur_count < 200, "Call-stack depth should never exceed 200"); - - if (left == nullptr) return right; - if (right == nullptr) return left; - - if (left->_priority > right->_priority) { - // We need - // LEFT - // | - // RIGHT - // for the invariant re: priorities to hold. - left->_right = merge(left->_right, right DEBUG_ONLY(COMMA recur_count + 1)); - return left; - } else { - // We need - // RIGHT - // | - // LEFT - // for the invariant re: priorities to hold. - right->_left = merge(left, right->_left DEBUG_ONLY(COMMA recur_count + 1)); - return right; - } - } - - static TreapNode* find(TreapNode* node, const K& k DEBUG_ONLY(COMMA int recur_count = 0)) { - if (node == nullptr) { - return nullptr; - } - - int key_cmp_k = COMPARATOR::cmp(node->key(), k); - - if (key_cmp_k == 0) { // key EQ k - return node; - } - - if (key_cmp_k < 0) { // key LT k - return find(node->right(), k DEBUG_ONLY(COMMA recur_count + 1)); - } else { // key GT k - return find(node->left(), k DEBUG_ONLY(COMMA recur_count + 1)); - } - } - -#ifdef ASSERT - void verify_self() { - // A balanced binary search tree should have a depth on the order of log(N). - // We take the ceiling of log_2(N + 1) * 3 as our maximum bound. - // For comparison, a RB-tree has a proven max depth of log_2(N + 1) * 2. - const int expected_maximum_depth = ceil(log2i(this->_node_count+1) * 3); - // Find the maximum depth through DFS and ensure that the priority invariant holds. - int maximum_depth_found = 0; - - struct DFS { - int depth; - uint64_t parent_prio; - TreapNode* n; - }; - GrowableArrayCHeap to_visit; - constexpr const uint64_t positive_infinity = 0xFFFFFFFFFFFFFFFF; - - to_visit.push({0, positive_infinity, this->_root}); - while (!to_visit.is_empty()) { - DFS head = to_visit.pop(); - if (head.n == nullptr) continue; - maximum_depth_found = MAX2(maximum_depth_found, head.depth); - - assert(head.parent_prio >= head.n->_priority, "broken priority invariant"); - - to_visit.push({head.depth + 1, head.n->_priority, head.n->left()}); - to_visit.push({head.depth + 1, head.n->_priority, head.n->right()}); - } - assert(maximum_depth_found - expected_maximum_depth <= 3, - "depth unexpectedly large for treap of node count %d, was: %d, expected between %d and %d", - _node_count, maximum_depth_found, expected_maximum_depth - 3, expected_maximum_depth); - - // Visit everything in order, see that the key ordering is monotonically increasing. - TreapNode* last_seen = nullptr; - bool failed = false; - int seen_count = 0; - this->visit_in_order([&](TreapNode* node) { - seen_count++; - if (last_seen == nullptr) { - last_seen = node; - return true; - } - if (COMPARATOR::cmp(last_seen->key(), node->key()) > 0) { - failed = false; - } - last_seen = node; - return true; - }); - assert(seen_count == _node_count, "the number of visited nodes do not match with the number of stored nodes"); - assert(!failed, "keys was not monotonically strongly increasing when visiting in order"); - } -#endif // ASSERT - -public: - NONCOPYABLE(Treap); - - Treap() - : _allocator(), - _root(nullptr), - _prng_seed(_initial_seed), - _node_count(0) { - static_assert(std::is_trivially_destructible::value, "must be"); - } - - ~Treap() { - this->remove_all(); - } - - int size() { - return _node_count; - } - - void upsert(const K& k, const V& v) { - TreapNode* found = find(_root, k); - if (found != nullptr) { - // Already exists, update value. - found->_value = v; - return; - } - _node_count++; - // Doesn't exist, make node - void* node_place = _allocator.allocate(sizeof(TreapNode)); - uint64_t prio = prng_next(); - TreapNode* node = new (node_place) TreapNode(k, v, prio); - - // (LEQ_k, GT_k) - node_pair split_up = split(this->_root, k); - // merge(merge(LEQ_k, EQ_k), GT_k) - this->_root = merge(merge(split_up.left, node), split_up.right); - } - - void remove(const K& k) { - // (LEQ_k, GT_k) - node_pair first_split = split(this->_root, k, LEQ); - // (LT_k, GEQ_k) == (LT_k, EQ_k) since it's from LEQ_k and keys are unique. - node_pair second_split = split(first_split.left, k, LT); - - if (second_split.right != nullptr) { - // The key k existed, we delete it. - _node_count--; - second_split.right->_value.~V(); - _allocator.free(second_split.right); - } - // Merge together everything - _root = merge(second_split.left, first_split.right); - } - - // Delete all nodes. - void remove_all() { - _node_count = 0; - GrowableArrayCHeap to_delete; - to_delete.push(_root); - - while (!to_delete.is_empty()) { - TreapNode* head = to_delete.pop(); - if (head == nullptr) continue; - to_delete.push(head->_left); - to_delete.push(head->_right); - head->_value.~V(); - _allocator.free(head); - } - _root = nullptr; - } - - TreapNode* closest_leq(const K& key) { - TreapNode* candidate = nullptr; - TreapNode* pos = _root; - while (pos != nullptr) { - int cmp_r = COMPARATOR::cmp(pos->key(), key); - if (cmp_r == 0) { // Exact match - candidate = pos; - break; // Can't become better than that. - } - if (cmp_r < 0) { - // Found a match, try to find a better one. - candidate = pos; - pos = pos->_right; - } else if (cmp_r > 0) { - pos = pos->_left; - } - } - return candidate; - } - - struct FindResult { - FindResult(TreapNode* node, bool new_node) : node(node), new_node(new_node) {} - TreapNode* const node; - bool const new_node; - }; - - // Finds the node for the given k in the tree or inserts a new node with the default constructed value. - FindResult find(const K& k) { - if (TreapNode* found = find(_root, k)) { - return FindResult(found, false); - } - _node_count++; - // Doesn't exist, make node - void* node_place = _allocator.allocate(sizeof(TreapNode)); - uint64_t prio = prng_next(); - TreapNode* node = new (node_place) TreapNode(k, prio); - - // (LEQ_k, GT_k) - node_pair split_up = split(this->_root, k); - // merge(merge(LEQ_k, EQ_k), GT_k) - this->_root = merge(merge(split_up.left, node), split_up.right); - return FindResult(node, true); - } - - TreapNode* closest_gt(const K& key) { - TreapNode* candidate = nullptr; - TreapNode* pos = _root; - while (pos != nullptr) { - int cmp_r = COMPARATOR::cmp(pos->key(), key); - if (cmp_r > 0) { - // Found a match, try to find a better one. - candidate = pos; - pos = pos->_left; - } else if (cmp_r <= 0) { - pos = pos->_right; - } - } - return candidate; - } - - struct Range { - TreapNode* start; - TreapNode* end; - Range(TreapNode* start, TreapNode* end) - : start(start), end(end) {} - }; - - // Return the range [start, end) - // where start->key() <= addr < end->key(). - // Failure to find the range leads to start and/or end being null. - Range find_enclosing_range(K addr) { - TreapNode* start = closest_leq(addr); - TreapNode* end = closest_gt(addr); - return Range(start, end); - } - - // Visit all TreapNodes in ascending key order. - template - void visit_in_order(F f) const { - GrowableArrayCHeap to_visit; - TreapNode* head = _root; - while (!to_visit.is_empty() || head != nullptr) { - while (head != nullptr) { - to_visit.push(head); - head = head->left(); - } - head = to_visit.pop(); - if (!f(head)) { - return; - } - head = head->right(); - } - } - - // Visit all TreapNodes in ascending order whose keys are in range [from, to). - template - void visit_range_in_order(const K& from, const K& to, F f) { - assert(COMPARATOR::cmp(from, to) <= 0, "from must be less or equal to to"); - GrowableArrayCHeap to_visit; - TreapNode* head = _root; - while (!to_visit.is_empty() || head != nullptr) { - while (head != nullptr) { - int cmp_from = COMPARATOR::cmp(head->key(), from); - to_visit.push(head); - if (cmp_from >= 0) { - head = head->left(); - } else { - // We've reached a node which is strictly less than from - // We don't need to visit any further to the left. - break; - } - } - head = to_visit.pop(); - const int cmp_from = COMPARATOR::cmp(head->key(), from); - const int cmp_to = COMPARATOR::cmp(head->key(), to); - if (cmp_from >= 0 && cmp_to < 0) { - if (!f(head)) { - return; - } - } - if (cmp_to < 0) { - head = head->right(); - } else { - head = nullptr; - } - } - } -}; - -class TreapCHeapAllocator { -public: - void* allocate(size_t sz) { - void* allocation = os::malloc(sz, mtNMT); - if (allocation == nullptr) { - vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR, "treap failed allocation"); - } - return allocation; - } - - void free(void* ptr) { - os::free(ptr); - } -}; - -template -using TreapCHeap = Treap; - -#endif //SHARE_NMT_NMTTREAP_HPP diff --git a/src/hotspot/share/nmt/regionsTree.cpp b/src/hotspot/share/nmt/regionsTree.cpp index 09686f52067..370c69a2485 100644 --- a/src/hotspot/share/nmt/regionsTree.cpp +++ b/src/hotspot/share/nmt/regionsTree.cpp @@ -48,8 +48,8 @@ void RegionsTree::NodeHelper::print_on(outputStream* st) { } void RegionsTree::print_on(outputStream* st) { - visit_in_order([&](Node* node) { - NodeHelper curr(node); + visit_in_order([&](const Node* node) { + NodeHelper curr(const_cast(node)); curr.print_on(st); return true; }); diff --git a/src/hotspot/share/nmt/regionsTree.hpp b/src/hotspot/share/nmt/regionsTree.hpp index 7fdb4e6fd39..bf2ab711b2d 100644 --- a/src/hotspot/share/nmt/regionsTree.hpp +++ b/src/hotspot/share/nmt/regionsTree.hpp @@ -45,7 +45,7 @@ class RegionsTree : public VMATree { SummaryDiff commit_region(address addr, size_t size, const NativeCallStack& stack); SummaryDiff uncommit_region(address addr, size_t size); - using Node = VMATree::TreapNode; + using Node = VMATree::TNode; class NodeHelper { Node* _node; diff --git a/src/hotspot/share/nmt/regionsTree.inline.hpp b/src/hotspot/share/nmt/regionsTree.inline.hpp index f1b7319f5ca..665f4a93c88 100644 --- a/src/hotspot/share/nmt/regionsTree.inline.hpp +++ b/src/hotspot/share/nmt/regionsTree.inline.hpp @@ -52,8 +52,8 @@ void RegionsTree::visit_reserved_regions(F func) { NodeHelper begin_node, prev; size_t rgn_size = 0; - visit_in_order([&](Node* node) { - NodeHelper curr(node); + visit_in_order([&](const Node* node) { + NodeHelper curr(const_cast(node)); if (prev.is_valid()) { rgn_size += curr.distance_from(prev); } else { diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index 51d8696a9ce..8a97253860c 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -219,20 +219,29 @@ bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) { return true; } -size_t ReservedMemoryRegion::committed_size() const { - size_t committed = 0; +size_t VirtualMemoryTracker::committed_size(const ReservedMemoryRegion* rmr) { size_t result = 0; - VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*this, [&](CommittedMemoryRegion& crgn) { + tree()->visit_committed_regions(*rmr, [&](CommittedMemoryRegion& crgn) { result += crgn.size(); return true; }); return result; } -address ReservedMemoryRegion::thread_stack_uncommitted_bottom() const { - address bottom = base(); - address top = base() + size(); - VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*this, [&](CommittedMemoryRegion& crgn) { +size_t VirtualMemoryTracker::Instance::committed_size(const ReservedMemoryRegion* rmr) { + assert(_tracker != nullptr, "Sanity check"); + return _tracker->committed_size(rmr); +} + +address VirtualMemoryTracker::Instance::thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr) { + assert(_tracker != nullptr, "Sanity check"); + return _tracker->thread_stack_uncommitted_bottom(rmr); +} + +address VirtualMemoryTracker::thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr) { + address bottom = rmr->base(); + address top = rmr->end(); + tree()->visit_committed_regions(*rmr, [&](CommittedMemoryRegion& crgn) { address committed_top = crgn.base() + crgn.size(); if (committed_top < top) { // committed stack guard pages, skip them @@ -291,7 +300,7 @@ public: assert_lock_strong(NmtVirtualMemory_lock); } if (rgn->mem_tag() == mtThreadStack) { - address stack_bottom = rgn->thread_stack_uncommitted_bottom(); + address stack_bottom = VirtualMemoryTracker::Instance::thread_stack_uncommitted_bottom(rgn); address committed_start; size_t committed_size; size_t stack_size = rgn->base() + rgn->size() - stack_bottom; diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index 1c9628cd828..3c6c1efd6a2 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp @@ -330,12 +330,6 @@ class ReservedMemoryRegion : public VirtualMemoryRegion { 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; - - size_t committed_size() const; - - ReservedMemoryRegion& operator= (const ReservedMemoryRegion& other) { set_base(other.base()); set_size(other.size()); @@ -382,6 +376,9 @@ class VirtualMemoryTracker { // Snapshot current thread stacks void snapshot_thread_stacks(); void apply_summary_diff(VMATree::SummaryDiff diff); + size_t committed_size(const ReservedMemoryRegion* rmr); + address thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr); + RegionsTree* tree() { return &_tree; } class Instance : public AllStatic { @@ -404,6 +401,9 @@ class VirtualMemoryTracker { static bool print_containing_region(const void* p, outputStream* st); static void snapshot_thread_stacks(); static void apply_summary_diff(VMATree::SummaryDiff diff); + static size_t committed_size(const ReservedMemoryRegion* rmr); + // uncommitted thread stack bottom, above guard pages if there is any. + static address thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr); static RegionsTree* tree() { return _tracker->tree(); } }; diff --git a/src/hotspot/share/nmt/vmatree.cpp b/src/hotspot/share/nmt/vmatree.cpp index a60c30c812d..4f6f8e12185 100644 --- a/src/hotspot/share/nmt/vmatree.cpp +++ b/src/hotspot/share/nmt/vmatree.cpp @@ -204,7 +204,7 @@ void VMATree::compute_summary_diff(const SingleDiff::delta region_size, // update the region state between n1 and n2. Since n1 and n2 are pointers, any update of them will be visible from tree. // If n1 is noop, it can be removed because its left region (n1->val().in) is already decided and its right state (n1->val().out) is decided here. // The state of right of n2 (n2->val().out) cannot be decided here yet. -void VMATree::update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff) { +void VMATree::update_region(TNode* n1, TNode* n2, const RequestInfo& req, SummaryDiff& diff) { assert(n1 != nullptr,"sanity"); assert(n2 != nullptr,"sanity"); //.........n1......n2...... @@ -261,8 +261,8 @@ VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateTy }; stA.out.set_commit_stack(NativeCallStackStorage::invalid); stB.in.set_commit_stack(NativeCallStackStorage::invalid); - VMATreap::Range rA = _tree.find_enclosing_range(_A); - VMATreap::Range rB = _tree.find_enclosing_range(_B); + VMARBTree::Range rA = _tree.find_enclosing_range(_A); + VMARBTree::Range rB = _tree.find_enclosing_range(_B); // nodes: .....X.......Y...Z......W........U // request: A------------------B @@ -320,24 +320,24 @@ VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateTy // Meaning that whenever any of one item in this sequence is changed, the rest of the consequent items to // be checked/changed. - TreapNode* X = rA.start; - TreapNode* Y = rA.end; - TreapNode* W = rB.start; - TreapNode* U = rB.end; - TreapNode nA{_A, stA, 0}; // the node that represents A - TreapNode nB{_B, stB, 0}; // the node that represents B - TreapNode* A = &nA; - TreapNode* B = &nB; - auto upsert_if= [&](TreapNode* node) { + TNode* X = rA.start; + TNode* Y = rA.end; + TNode* W = rB.start; + TNode* U = rB.end; + TNode nA{_A, stA}; // the node that represents A + TNode nB{_B, stB}; // the node that represents B + TNode* A = &nA; + TNode* B = &nB; + auto upsert_if= [&](TNode* node) { if (!node->val().is_noop()) { _tree.upsert(node->key(), node->val()); } }; // update region between n1 and n2 - auto update = [&](TreapNode* n1, TreapNode* n2) { + auto update = [&](TNode* n1, TNode* n2) { update_region(n1, n2, req, diff); }; - auto remove_if = [&](TreapNode* node) -> bool{ + auto remove_if = [&](TNode* node) -> bool{ if (node->val().is_noop()) { _tree.remove(node->key()); return true; @@ -347,8 +347,8 @@ VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateTy GrowableArrayCHeap to_be_removed; // update regions in range A to B auto update_loop = [&]() { - TreapNode* prev = nullptr; - _tree.visit_range_in_order(_A + 1, _B + 1, [&](TreapNode* curr) { + TNode* prev = nullptr; + _tree.visit_range_in_order(_A + 1, _B + 1, [&](TNode* curr) { if (prev != nullptr) { update_region(prev, curr, req, diff); // during visit, structure of the tree should not be changed @@ -362,7 +362,7 @@ VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateTy }); }; // update region of [A,T) - auto update_A = [&](TreapNode* T) { + auto update_A = [&](TNode* T) { A->val().out = A->val().in; update(A, T); }; @@ -650,7 +650,7 @@ VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateTy #ifdef ASSERT void VMATree::print_on(outputStream* out) { - visit_in_order([&](TreapNode* current) { + visit_in_order([&](const TNode* current) { out->print("%zu (%s) - %s [%d, %d]-> ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()), statetype_to_string(out_state(current).type()), current->val().out.reserved_stack(), current->val().out.committed_stack()); return true; @@ -660,11 +660,11 @@ void VMATree::print_on(outputStream* out) { #endif VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, const MemTag tag) { - auto pos = [](TreapNode* n) { return n->key(); }; + auto pos = [](TNode* n) { return n->key(); }; position from = start; position end = from+size; size_t remsize = size; - VMATreap::Range range(nullptr, nullptr); + VMARBTree::Range range(nullptr, nullptr); // Find the next range to adjust and set range, remsize and from // appropriately. If it returns false, there is no valid next range. diff --git a/src/hotspot/share/nmt/vmatree.hpp b/src/hotspot/share/nmt/vmatree.hpp index 7f52b406309..d2acabdae07 100644 --- a/src/hotspot/share/nmt/vmatree.hpp +++ b/src/hotspot/share/nmt/vmatree.hpp @@ -29,9 +29,9 @@ #include "nmt/memTag.hpp" #include "nmt/memTag.hpp" #include "nmt/nmtNativeCallStackStorage.hpp" -#include "nmt/nmtTreap.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" +#include "utilities/rbTree.inline.hpp" #include // A VMATree stores a sequence of points on the natural number line. @@ -193,17 +193,21 @@ private: }; public: - using VMATreap = TreapCHeap; - using TreapNode = VMATreap::TreapNode; + using VMARBTree = RBTreeCHeap; + using TNode = RBNode; private: - VMATreap _tree; + VMARBTree _tree; - static IntervalState& in_state(TreapNode* node) { + static IntervalState& in_state(TNode* node) { return node->val().in; } - static IntervalState& out_state(TreapNode* node) { + static IntervalState& out_state(TNode* node) { + return node->val().out; + } + + static const IntervalState& out_state(const TNode* node) { return node->val().out; } @@ -281,7 +285,7 @@ public: SIndex get_new_reserve_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const; SIndex get_new_commit_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const; void compute_summary_diff(const SingleDiff::delta region_size, const MemTag t1, const StateType& ex, const RequestInfo& req, const MemTag new_tag, SummaryDiff& diff) const; - void update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff); + void update_region(TNode* n1, TNode* n2, const RequestInfo& req, SummaryDiff& diff); int state_to_index(const StateType st) const { return st == StateType::Released ? 0 : @@ -325,6 +329,6 @@ public: void visit_range_in_order(const position& from, const position& to, F f) { _tree.visit_range_in_order(from, to, f); } - VMATreap& tree() { return _tree; } + VMARBTree& tree() { return _tree; } }; #endif diff --git a/src/hotspot/share/oops/compressedKlass.cpp b/src/hotspot/share/oops/compressedKlass.cpp index a1a634b6aab..25e80b0a975 100644 --- a/src/hotspot/share/oops/compressedKlass.cpp +++ b/src/hotspot/share/oops/compressedKlass.cpp @@ -44,15 +44,19 @@ narrowKlass CompressedKlassPointers::_lowest_valid_narrow_klass_id = (narrowKlas narrowKlass CompressedKlassPointers::_highest_valid_narrow_klass_id = (narrowKlass)-1; size_t CompressedKlassPointers::_protection_zone_size = 0; -#ifdef _LP64 - size_t CompressedKlassPointers::max_klass_range_size() { - // We disallow klass range sizes larger than 4GB even if the encoding - // range would allow for a larger Klass range (e.g. Base=zero, shift=3 -> 32GB). - // That is because many CPU-specific compiler decodings do not want the - // shifted narrow Klass to spill over into the third quadrant of the 64-bit target - // address, e.g. to use a 16-bit move for a simplified base addition. - return MIN2(4 * G, max_encoding_range_size()); +#ifdef _LP64 + const size_t encoding_allows = nth_bit(narrow_klass_pointer_bits() + max_shift()); + constexpr size_t cap = 4 * G; + return MIN2(encoding_allows, cap); +#else + // 32-bit: only 32-bit "narrow" Klass pointers allowed. If we ever support smaller narrow + // Klass pointers here, coding needs to be revised. + // We keep one page safety zone free to guard against size_t overflows on 32-bit. In practice + // this is irrelevant because these upper address space parts are not user-addressable on + // any of our 32-bit platforms. + return align_down(UINT_MAX, os::vm_page_size()); +#endif } void CompressedKlassPointers::pre_initialize() { @@ -60,8 +64,13 @@ void CompressedKlassPointers::pre_initialize() { _narrow_klass_pointer_bits = narrow_klass_pointer_bits_coh; _max_shift = max_shift_coh; } else { +#ifdef _LP64 _narrow_klass_pointer_bits = narrow_klass_pointer_bits_noncoh; _max_shift = max_shift_noncoh; +#else + _narrow_klass_pointer_bits = 32; + _max_shift = 0; +#endif } } @@ -84,6 +93,10 @@ void CompressedKlassPointers::sanity_check_after_initialization() { ASSERT_HERE(_base != (address)-1); ASSERT_HERE(_shift != -1); + // We should need a class space if address space is larger than what narrowKlass can address + const bool should_need_class_space = (BytesPerWord * BitsPerByte) > narrow_klass_pointer_bits(); + ASSERT_HERE(should_need_class_space == needs_class_space()); + const size_t klass_align = klass_alignment_in_bytes(); // must be aligned enough hold 64-bit data @@ -96,7 +109,9 @@ void CompressedKlassPointers::sanity_check_after_initialization() { // Check that Klass range is fully engulfed in the encoding range const address encoding_start = _base; - const address encoding_end = (address)(p2u(_base) + (uintptr_t)nth_bit(narrow_klass_pointer_bits() + _shift)); + const address encoding_end = (address) + LP64_ONLY(p2u(_base) + (uintptr_t)nth_bit(narrow_klass_pointer_bits() + _shift)) + NOT_LP64(max_klass_range_size()); ASSERT_HERE_2(_klass_range_start >= _base && _klass_range_end <= encoding_end, "Resulting encoding range does not fully cover the class range"); @@ -141,7 +156,8 @@ void CompressedKlassPointers::calc_lowest_highest_narrow_klass_id() { address lowest_possible_klass_location = _klass_range_start; // A Klass will never be placed at the Encoding range start, since that would translate to a narrowKlass=0, which - // is disallowed. Note that both Metaspace and CDS prvent allocation at the first address for this reason. + // is disallowed. If the encoding range starts at the klass range start, both Metaspace and CDS establish an + // mprotected zone for this reason (see establish_protection_zone). if (lowest_possible_klass_location == _base) { lowest_possible_klass_location += klass_alignment_in_bytes(); } @@ -238,6 +254,7 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { } else { +#ifdef _LP64 // Traditional (non-compact) header mode const uintptr_t unscaled_max = nth_bit(narrow_klass_pointer_bits()); const uintptr_t zerobased_max = nth_bit(narrow_klass_pointer_bits() + max_shift()); @@ -249,6 +266,7 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { address const end = addr + len; _base = (end <= (address)unscaled_max) ? nullptr : addr; #else + // We try, in order of preference: // -unscaled (base=0 shift=0) // -zero-based (base=0 shift>0) @@ -269,11 +287,19 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { } } #endif // AARCH64 +#else + // 32-bit "compressed class pointer" mode + _base = nullptr; + _shift = 0; + // as our "protection zone", we just assume the lowest protected parts of + // the user address space. + _protection_zone_size = os::vm_min_address(); +#endif // LP64 } calc_lowest_highest_narrow_klass_id(); - // Initialize klass decode mode and check compability with decode instructions + // Initialize JIT-specific decoding settings if (!set_klass_decode_mode()) { // Give fatal error if this is a specified address @@ -287,9 +313,8 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { p2i(_base), _shift); } } -#ifdef ASSERT - sanity_check_after_initialization(); -#endif + + DEBUG_ONLY(sanity_check_after_initialization();) } void CompressedKlassPointers::print_mode(outputStream* st) { @@ -340,4 +365,3 @@ bool CompressedKlassPointers::is_in_protection_zone(address addr) { (addr >= base() && addr < base() + _protection_zone_size) : false; } -#endif // _LP64 diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index 99befc847bc..64b9fcf9c82 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -124,7 +124,7 @@ class CompressedKlassPointers : public AllStatic { static address _base; static int _shift; - // Start and end of the Klass Range. + // Start and end of the Klass Range. Start includes the protection zone if one exists. // Note: guaranteed to be aligned to 1<= _lowest_valid_narrow_klass_id && nk < _highest_valid_narrow_klass_id; +} + inline address CompressedKlassPointers::encoding_range_end() { +#ifdef _LP64 const int max_bits = narrow_klass_pointer_bits() + _shift; return (address)((uintptr_t)_base + nth_bit(max_bits)); +#else + return (address)SIZE_MAX; +#endif } #endif // SHARE_OOPS_COMPRESSEDKLASS_INLINE_HPP diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 0c9d6b0bcdc..9266e8ca091 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -472,7 +472,7 @@ InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& par assert(loader_data != nullptr, "invariant"); InstanceKlass* ik; - const bool use_class_space = parser.klass_needs_narrow_id(); + const bool use_class_space = UseClassMetaspaceForAllClasses || parser.klass_needs_narrow_id(); // Allocation if (parser.is_instance_ref_klass()) { diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 131e8f145f1..3c0dd84b2d2 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -764,6 +764,14 @@ public: bool has_final_method() const { return _misc_flags.has_final_method(); } void set_has_final_method() { _misc_flags.set_has_final_method(true); } + // Indicates presence of @AOTSafeClassInitializer. Also see AOTClassInitializer for more details. + bool has_aot_safe_initializer() const { return _misc_flags.has_aot_safe_initializer(); } + void set_has_aot_safe_initializer() { _misc_flags.set_has_aot_safe_initializer(true); } + + // Indicates @AOTRuntimeSetup private static void runtimeSetup() presence. + bool is_runtime_setup_required() const { return _misc_flags.is_runtime_setup_required(); } + void set_is_runtime_setup_required() { _misc_flags.set_is_runtime_setup_required(true); } + // for adding methods, ConstMethod::UNSET_IDNUM means no more ids available inline u2 next_method_idnum(); void set_initial_method_idnum(u2 value) { _idnum_allocated_count = value; } diff --git a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp index 18a9c76103d..1872c3bc998 100644 --- a/src/hotspot/share/oops/instanceKlassFlags.hpp +++ b/src/hotspot/share/oops/instanceKlassFlags.hpp @@ -54,6 +54,8 @@ class InstanceKlassFlags { flag(has_localvariable_table , 1 << 11) /* has localvariable information */ \ flag(has_miranda_methods , 1 << 12) /* True if this class has miranda methods in it's vtable */ \ flag(has_final_method , 1 << 13) /* True if klass has final method */ \ + flag(has_aot_safe_initializer , 1 << 14) /* has @AOTSafeClassInitializer annotation */ \ + flag(is_runtime_setup_required , 1 << 15) /* has a runtimeSetup method to be called */ \ /* end of list */ #define IK_FLAGS_ENUM_NAME(name, value) _misc_##name = value, diff --git a/src/hotspot/share/oops/instanceMirrorKlass.hpp b/src/hotspot/share/oops/instanceMirrorKlass.hpp index 9783d416a1d..e9928647db9 100644 --- a/src/hotspot/share/oops/instanceMirrorKlass.hpp +++ b/src/hotspot/share/oops/instanceMirrorKlass.hpp @@ -52,6 +52,9 @@ class InstanceMirrorKlass: public InstanceKlass { InstanceMirrorKlass(const ClassFileParser& parser) : InstanceKlass(parser, Kind) {} + template + inline void do_metadata(oop obj, OopClosureType* closure); + public: InstanceMirrorKlass(); diff --git a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp index 867a0580a12..eed87d2644b 100644 --- a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp +++ b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp @@ -46,38 +46,41 @@ void InstanceMirrorKlass::oop_oop_iterate_statics(oop obj, OopClosureType* closu } } +template +void InstanceMirrorKlass::do_metadata(oop obj, OopClosureType* closure) { + Klass* klass = java_lang_Class::as_Klass(obj); + if (klass != nullptr) { + if (klass->class_loader_data() == nullptr) { + // This is a mirror that belongs to a shared class that has not been loaded yet. + assert(klass->is_shared(), "Must be"); + } else if (klass->is_instance_klass() && klass->class_loader_data()->has_class_mirror_holder()) { + // A non-strong hidden class doesn't have its own class loader, + // so when handling the java mirror for the class we need to make sure its class + // loader data is claimed, this is done by calling do_cld explicitly. + // For non-strong hidden classes the call to do_cld is made when the class + // loader itself is handled. + Devirtualizer::do_cld(closure, klass->class_loader_data()); + } else { + Devirtualizer::do_klass(closure, klass); + } + } else { + // Java mirror -> Klass* "nullptr" backlink means either: + // 1. This is a Java mirror for a primitive class. We do not need to follow it, + // these mirrors are always strong roots. + // 2. This is a Java mirror for a newly allocated non-primitive class, and we + // somehow managed to reach the newly allocated Java mirror with not yet + // installed backlink. We cannot do anything here, this case would be handled + // separately by GC, e.g. by keeping the relevant metadata alive during the GC. + // Unfortunately, the existence of corner case (2) prevents us from asserting (1). + } +} + template void InstanceMirrorKlass::oop_oop_iterate(oop obj, OopClosureType* closure) { InstanceKlass::oop_oop_iterate(obj, closure); if (Devirtualizer::do_metadata(closure)) { - Klass* klass = java_lang_Class::as_Klass(obj); - // We'll get null for primitive mirrors. - if (klass != nullptr) { - if (klass->class_loader_data() == nullptr) { - // This is a mirror that belongs to a shared class that has not be loaded yet. - assert(klass->is_shared(), "must be"); - } else if (klass->is_instance_klass() && klass->class_loader_data()->has_class_mirror_holder()) { - // A non-strong hidden class doesn't have its own class loader, - // so when handling the java mirror for the class we need to make sure its class - // loader data is claimed, this is done by calling do_cld explicitly. - // For non-strong hidden classes the call to do_cld is made when the class - // loader itself is handled. - Devirtualizer::do_cld(closure, klass->class_loader_data()); - } else { - Devirtualizer::do_klass(closure, klass); - } - } else { - // We would like to assert here (as below) that if klass has been null, then - // this has been a mirror for a primitive type that we do not need to follow - // as they are always strong roots. - // However, we might get across a klass that just changed during CMS concurrent - // marking if allocation occurred in the old generation. - // This is benign here, as we keep alive all CLDs that were loaded during the - // CMS concurrent phase in the class loading, i.e. they will be iterated over - // and kept alive during remark. - // assert(java_lang_Class::is_primitive(obj), "Sanity check"); - } + do_metadata(obj, closure); } oop_oop_iterate_statics(obj, closure); @@ -121,11 +124,7 @@ void InstanceMirrorKlass::oop_oop_iterate_bounded(oop obj, OopClosureType* closu if (Devirtualizer::do_metadata(closure)) { if (mr.contains(obj)) { - Klass* klass = java_lang_Class::as_Klass(obj); - // We'll get null for primitive mirrors. - if (klass != nullptr) { - Devirtualizer::do_klass(closure, klass); - } + do_metadata(obj, closure); } } diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index 18b8b5885a3..ac382fdba98 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -186,9 +186,6 @@ private: _is_generated_shared_class = 1 << 5, // archived mirror already initialized by AOT-cache assembly: no further need to call _has_aot_initialized_mirror = 1 << 6, - // If this class has been aot-inititalized, do we need to call its runtimeSetup() - // method during the production run? - _is_runtime_setup_required = 1 << 7, }; #endif @@ -380,15 +377,6 @@ protected: NOT_CDS(return false;) } - void set_is_runtime_setup_required() { - assert(has_aot_initialized_mirror(), "sanity"); - CDS_ONLY(_shared_class_flags |= _is_runtime_setup_required;) - } - bool is_runtime_setup_required() const { - CDS_ONLY(return (_shared_class_flags & _is_runtime_setup_required) != 0;) - NOT_CDS(return false;) - } - bool is_shared() const { // shadows MetaspaceObj::is_shared)() CDS_ONLY(return (_shared_class_flags & _is_shared_class) != 0;) NOT_CDS(return false;) diff --git a/src/hotspot/share/oops/klass.inline.hpp b/src/hotspot/share/oops/klass.inline.hpp index b37c5105f64..19d4954ccad 100644 --- a/src/hotspot/share/oops/klass.inline.hpp +++ b/src/hotspot/share/oops/klass.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -182,6 +182,6 @@ inline bool Klass::needs_narrow_id() const { // never instantiated classes out of class space lessens the class space pressure. // For more details, see JDK-8338526. // Note: don't call this function before access flags are initialized. - return !is_abstract() && !is_interface(); + return UseClassMetaspaceForAllClasses || (!is_abstract() && !is_interface()); } #endif // SHARE_OOPS_KLASS_INLINE_HPP diff --git a/src/hotspot/share/oops/objLayout.cpp b/src/hotspot/share/oops/objLayout.cpp index 774fe24d376..b8cd8249da1 100644 --- a/src/hotspot/share/oops/objLayout.cpp +++ b/src/hotspot/share/oops/objLayout.cpp @@ -32,6 +32,7 @@ int ObjLayout::_oop_base_offset_in_bytes = 0; bool ObjLayout::_oop_has_klass_gap = false; void ObjLayout::initialize() { +#ifdef _LP64 assert(_klass_mode == Undefined, "ObjLayout initialized twice"); if (UseCompactObjectHeaders) { _klass_mode = Compact; @@ -46,4 +47,13 @@ void ObjLayout::initialize() { _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(Klass*); _oop_has_klass_gap = false; } +#else + assert(_klass_mode == Undefined, "ObjLayout initialized twice"); + assert(!UseCompactObjectHeaders, "COH unsupported on 32-bit"); + // We support +-UseCompressedClassPointers on 32-bit, but the layout + // is exactly the same as it was with uncompressed klass pointers + _klass_mode = UseCompressedClassPointers ? Compressed : Uncompressed; + _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(Klass*); + _oop_has_klass_gap = false; +#endif } diff --git a/src/hotspot/share/oops/objLayout.inline.hpp b/src/hotspot/share/oops/objLayout.inline.hpp index 677c1a933bd..6aa9e39ce28 100644 --- a/src/hotspot/share/oops/objLayout.inline.hpp +++ b/src/hotspot/share/oops/objLayout.inline.hpp @@ -38,11 +38,7 @@ inline ObjLayout::Mode ObjLayout::klass_mode() { assert(_klass_mode == Uncompressed, "Klass mode does not match flags"); } #endif -#ifdef _LP64 return _klass_mode; -#else - return Uncompressed; -#endif } #endif // SHARE_OOPS_OBJLAYOUT_INLINE_HPP diff --git a/src/hotspot/share/oops/oop.hpp b/src/hotspot/share/oops/oop.hpp index 549b5b0bff8..02f87da2937 100644 --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp @@ -91,6 +91,7 @@ class oopDesc { inline Klass* klass_without_asserts() const; void set_narrow_klass(narrowKlass nk) NOT_CDS_JAVA_HEAP_RETURN; + inline narrowKlass narrow_klass() const; inline void set_klass(Klass* k); static inline void release_set_klass(HeapWord* mem, Klass* k); diff --git a/src/hotspot/share/oops/oop.inline.hpp b/src/hotspot/share/oops/oop.inline.hpp index 683792e5201..4ca1bfce472 100644 --- a/src/hotspot/share/oops/oop.inline.hpp +++ b/src/hotspot/share/oops/oop.inline.hpp @@ -141,6 +141,17 @@ Klass* oopDesc::klass_without_asserts() const { } } +narrowKlass oopDesc::narrow_klass() const { + switch (ObjLayout::klass_mode()) { + case ObjLayout::Compact: + return mark().narrow_klass(); + case ObjLayout::Compressed: + return _metadata._compressed_klass; + default: + ShouldNotReachHere(); + } +} + void oopDesc::set_klass(Klass* k) { assert(Universe::is_bootstrapping() || (k != nullptr && k->is_klass()), "incorrect Klass"); assert(!UseCompactObjectHeaders, "don't set Klass* with compact headers"); diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index e5b1e131505..acc28964627 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -90,6 +90,12 @@ bool C2Compiler::init_c2_runtime() { compiler_stubs_init(true /* in_compiler_thread */); // generate compiler's intrinsics stubs + // If there was an error generating the blob then UseCompiler will + // have been unset and we need to skip the remaining initialization + if (!UseCompiler) { + return false; + } + Compile::pd_compiler2_init(); CompilerThread* thread = CompilerThread::current(); @@ -615,6 +621,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dsinh: case vmIntrinsics::_dtanh: case vmIntrinsics::_dcbrt: case vmIntrinsics::_dabs: @@ -760,7 +767,6 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_isInstance: case vmIntrinsics::_isHidden: case vmIntrinsics::_getSuperclass: - case vmIntrinsics::_getClassAccessFlags: case vmIntrinsics::_floatToRawIntBits: case vmIntrinsics::_floatToIntBits: case vmIntrinsics::_intBitsToFloat: diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 4cab6aabbeb..5a4a6c2667d 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2149,7 +2149,7 @@ void Compile::inline_incrementally_cleanup(PhaseIterGVN& igvn) { } { TracePhase tp(_t_incrInline_igvn); - igvn.reset_from_gvn(initial_gvn()); + igvn.reset(); igvn.optimize(); if (failing()) return; } @@ -2310,8 +2310,7 @@ void Compile::Optimize() { { // Iterative Global Value Numbering, including ideal transforms - // Initialize IterGVN with types and values from parse-time GVN - PhaseIterGVN igvn(initial_gvn()); + PhaseIterGVN igvn; #ifdef ASSERT _modified_nodes = new (comp_arena()) Unique_Node_List(comp_arena()); #endif @@ -2380,7 +2379,7 @@ void Compile::Optimize() { ResourceMark rm; PhaseRenumberLive prl(initial_gvn(), *igvn_worklist()); } - igvn.reset_from_gvn(initial_gvn()); + igvn.reset(); igvn.optimize(); if (failing()) return; } @@ -3620,7 +3619,10 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } else if (t->isa_oopptr()) { new_in2 = ConNode::make(t->make_narrowoop()); } else if (t->isa_klassptr()) { - new_in2 = ConNode::make(t->make_narrowklass()); + ciKlass* klass = t->is_klassptr()->exact_klass(); + if (klass->is_in_encoding_range()) { + new_in2 = ConNode::make(t->make_narrowklass()); + } } } if (new_in2 != nullptr) { @@ -3657,7 +3659,13 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } else if (t->isa_oopptr()) { n->subsume_by(ConNode::make(t->make_narrowoop()), this); } else if (t->isa_klassptr()) { - n->subsume_by(ConNode::make(t->make_narrowklass()), this); + ciKlass* klass = t->is_klassptr()->exact_klass(); + if (klass->is_in_encoding_range()) { + n->subsume_by(ConNode::make(t->make_narrowklass()), this); + } else { + assert(false, "unencodable klass in ConP -> EncodeP"); + C->record_failure("unencodable klass in ConP -> EncodeP"); + } } } if (in1->outcnt() == 0) { diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 818e28a3106..6c5efaafc21 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -245,6 +245,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_dsin: case vmIntrinsics::_dcos: case vmIntrinsics::_dtan: + case vmIntrinsics::_dsinh: case vmIntrinsics::_dtanh: case vmIntrinsics::_dcbrt: case vmIntrinsics::_dabs: @@ -519,8 +520,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_isInstance: case vmIntrinsics::_isHidden: - case vmIntrinsics::_getSuperclass: - case vmIntrinsics::_getClassAccessFlags: return inline_native_Class_query(intrinsic_id()); + case vmIntrinsics::_getSuperclass: return inline_native_Class_query(intrinsic_id()); case vmIntrinsics::_floatToRawIntBits: case vmIntrinsics::_floatToIntBits: @@ -1887,6 +1887,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::_dsinh: + return StubRoutines::dsinh() != nullptr ? + runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dsinh(), "dsinh") : false; case vmIntrinsics::_dtanh: return StubRoutines::dtanh() != nullptr ? runtime_math(OptoRuntime::Math_D_D_Type(), StubRoutines::dtanh(), "dtanh") : false; @@ -4007,10 +4010,6 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { prim_return_value = null(); return_type = TypeInstPtr::MIRROR->cast_to_ptr_type(TypePtr::BotPTR); break; - case vmIntrinsics::_getClassAccessFlags: - prim_return_value = intcon(JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); - return_type = TypeInt::CHAR; - break; default: fatal_unexpected_iid(id); break; @@ -4106,11 +4105,6 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { } break; - case vmIntrinsics::_getClassAccessFlags: - p = basic_plus_adr(kls, in_bytes(Klass::access_flags_offset())); - query_value = make_load(nullptr, p, TypeInt::CHAR, T_CHAR, MemNode::unordered); - break; - default: fatal_unexpected_iid(id); break; diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 1da8f429359..f358729dfb2 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1981,13 +1981,11 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls, "must not happen"); if (tkls->offset() == in_bytes(Klass::access_flags_offset())) { // The field is Klass::_access_flags. Return its (constant) value. - // (Folds up the 2nd indirection in Reflection.getClassAccessFlags(aClassConstant).) assert(Opcode() == Op_LoadUS, "must load an unsigned short from _access_flags"); return TypeInt::make(klass->access_flags()); } if (tkls->offset() == in_bytes(Klass::misc_flags_offset())) { // The field is Klass::_misc_flags. Return its (constant) value. - // (Folds up the 2nd indirection in Reflection.getClassAccessFlags(aClassConstant).) assert(Opcode() == Op_LoadUB, "must load an unsigned byte from _misc_flags"); return TypeInt::make(klass->misc_flags()); } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 541a6ad6ea0..1df2cdb179e 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -807,9 +807,9 @@ PhaseIterGVN::PhaseIterGVN(PhaseIterGVN* igvn) : _delay_transform(igvn->_delay_t } //------------------------------PhaseIterGVN----------------------------------- -// Initialize with previous PhaseGVN info from Parser -PhaseIterGVN::PhaseIterGVN(PhaseGVN* gvn) : _delay_transform(false), - _worklist(*C->igvn_worklist()) +// Initialize from scratch +PhaseIterGVN::PhaseIterGVN() : _delay_transform(false), + _worklist(*C->igvn_worklist()) { _iterGVN = true; uint max; @@ -2558,6 +2558,24 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } } + // Check for redundant conversion patterns: + // ConvD2L->ConvL2D->ConvD2L + // ConvF2I->ConvI2F->ConvF2I + // ConvF2L->ConvL2F->ConvF2L + // ConvI2F->ConvF2I->ConvI2F + // Note: there may be other 3-nodes conversion chains that would require to be added here, but these + // are the only ones that are known to trigger missed optimizations otherwise + if ((n->Opcode() == Op_ConvD2L && use_op == Op_ConvL2D) || + (n->Opcode() == Op_ConvF2I && use_op == Op_ConvI2F) || + (n->Opcode() == Op_ConvF2L && use_op == Op_ConvL2F) || + (n->Opcode() == Op_ConvI2F && use_op == Op_ConvF2I)) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->Opcode() == n->Opcode()) { + worklist.push(u); + } + } + } // If changed AddP inputs: // - check Stores for loop invariant, and // - if the changed input is the offset, check constant-offset AddP users for diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index aeba5e8662d..300c8fc2757 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -459,16 +459,12 @@ protected: public: PhaseIterGVN(PhaseIterGVN* igvn); // Used by CCP constructor - PhaseIterGVN(PhaseGVN* gvn); // Used after Parser + PhaseIterGVN(); - // Reset IGVN from GVN: call deconstructor, and placement new. - // Achieves the same as the following (but without move constructors): - // igvn = PhaseIterGVN(gvn); - void reset_from_gvn(PhaseGVN* gvn) { - if (this != gvn) { - this->~PhaseIterGVN(); - ::new (static_cast(this)) PhaseIterGVN(gvn); - } + // Reset IGVN: call deconstructor, and placement new. + void reset() { + this->~PhaseIterGVN(); + ::new (static_cast(this)) PhaseIterGVN(); } // Reset IGVN with another: call deconstructor, and placement new. diff --git a/src/hotspot/share/opto/printinlining.cpp b/src/hotspot/share/opto/printinlining.cpp index be51c08fcfb..06d14a7f3af 100644 --- a/src/hotspot/share/opto/printinlining.cpp +++ b/src/hotspot/share/opto/printinlining.cpp @@ -46,7 +46,10 @@ void InlinePrinter::print_on(outputStream* tty) const { if (!is_enabled()) { return; } - _root.dump(tty, -1); + // using stringStream prevents interleaving with multiple compile threads + stringStream ss; + _root.dump(&ss, -1); + tty->print_raw(ss.freeze()); } InlinePrinter::IPInlineSite* InlinePrinter::locate(JVMState* state, ciMethod* callee) { @@ -68,21 +71,25 @@ InlinePrinter::IPInlineSite* InlinePrinter::locate(JVMState* state, ciMethod* ca } InlinePrinter::IPInlineSite& InlinePrinter::IPInlineSite::at_bci(int bci, ciMethod* callee) { - auto find_result = _children.find(bci); - IPInlineSite& child = find_result.node->val(); + RBTreeCHeap::Cursor cursor = _children.cursor(bci); - if (find_result.new_node) { - assert(callee != nullptr, "an inline call is missing in the chain up to the root"); - child.set_source(callee, bci); - } else { // We already saw a call at this site before + if (cursor.found()) { // We already saw a call at this site before + IPInlineSite& child = cursor.node()->val(); if (callee != nullptr && callee != child._method) { outputStream* stream = child.add(InliningResult::SUCCESS); stream->print("callee changed to "); CompileTask::print_inline_inner_method_info(stream, callee); } + return child; } - return child; + assert(callee != nullptr, "an inline call is missing in the chain up to the root"); + + RBNode* node = _children.allocate_node(bci); + _children.insert_at_cursor(node, cursor); + node->val().set_source(callee, bci); + + return node->val(); } outputStream* InlinePrinter::IPInlineSite::add(InliningResult result) { diff --git a/src/hotspot/share/opto/printinlining.hpp b/src/hotspot/share/opto/printinlining.hpp index 57f4b51858e..3bf09bc921f 100644 --- a/src/hotspot/share/opto/printinlining.hpp +++ b/src/hotspot/share/opto/printinlining.hpp @@ -26,9 +26,9 @@ #define PRINTINLINING_HPP #include "memory/allocation.hpp" -#include "nmt/nmtTreap.hpp" #include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" +#include "utilities/rbTree.inline.hpp" class JVMState; class ciMethod; @@ -77,7 +77,7 @@ private: ciMethod* _method; int _bci; GrowableArrayCHeap _attempts; - TreapCHeap _children; + RBTreeCHeap _children; public: IPInlineSite(ciMethod* method, int bci) : _method(method), _bci(bci) {} diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index 43461075cff..5f1af09463f 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -2069,9 +2069,15 @@ const Type* ReverseLNode::Value(PhaseGVN* phase) const { return bottom_type(); } -Node* InvolutionNode::Identity(PhaseGVN* phase) { - // Op ( Op x ) => x - if (in(1)->Opcode() == Opcode()) { +Node* ReverseINode::Identity(PhaseGVN* phase) { + if (in(1)->Opcode() == Op_ReverseI) { + return in(1)->in(1); + } + return this; +} + +Node* ReverseLNode::Identity(PhaseGVN* phase) { + if (in(1)->Opcode() == Op_ReverseL) { return in(1)->in(1); } return this; diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index 9a411f3d20e..57a501ecbc3 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -439,18 +439,11 @@ public: virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------InvolutionNode---------------------------------- -// Represents a self-inverse operation, i.e., op(op(x)) = x for any x -class InvolutionNode : public Node { -public: - InvolutionNode(Node* in) : Node(nullptr, in) {} - virtual Node* Identity(PhaseGVN* phase); -}; //------------------------------NegNode---------------------------------------- -class NegNode : public InvolutionNode { +class NegNode : public Node { public: - NegNode(Node* in1) : InvolutionNode(in1) { + NegNode(Node* in1) : Node(nullptr, in1) { init_class_id(Class_Neg); } }; @@ -562,18 +555,16 @@ public: }; -class ReverseBytesNode : public InvolutionNode { +class ReverseBytesNode : public Node { public: - ReverseBytesNode(Node* in) : InvolutionNode(in) {} + ReverseBytesNode(Node* in) : Node(nullptr, in) {} virtual const Type* Value(PhaseGVN* phase) const; }; //-------------------------------ReverseBytesINode-------------------------------- // reverse bytes of an integer class ReverseBytesINode : public ReverseBytesNode { public: - ReverseBytesINode(Node* in) : ReverseBytesNode(in) { - } - + ReverseBytesINode(Node* in) : ReverseBytesNode(in) {} virtual int Opcode() const; const Type* bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } @@ -611,23 +602,25 @@ public: //-------------------------------ReverseINode-------------------------------- // reverse bits of an int -class ReverseINode : public InvolutionNode { +class ReverseINode : public Node { public: - ReverseINode(Node* in) : InvolutionNode(in) {} + ReverseINode(Node* in) : Node(nullptr,in) {} virtual int Opcode() const; const Type* bottom_type() const { return TypeInt::INT; } virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; }; //-------------------------------ReverseLNode-------------------------------- // reverse bits of a long -class ReverseLNode : public InvolutionNode { +class ReverseLNode : public Node { public: - ReverseLNode(Node* in) : InvolutionNode(in) {} + ReverseLNode(Node* in) : Node(nullptr, in) {} virtual int Opcode() const; const Type* bottom_type() const { return TypeLong::LONG; } virtual uint ideal_reg() const { return Op_RegL; } + virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; }; diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 510cb1b51de..2316b0bb49a 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -2597,6 +2597,9 @@ static bool can_subword_truncate(Node* in, const Type* type) { case Op_IsFiniteD: case Op_IsInfiniteF: case Op_IsInfiniteD: + case Op_CmpLTMask: + case Op_RoundF: + case Op_RoundD: case Op_ExtractS: case Op_ExtractC: case Op_ExtractB: diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index 4ffbd2e96b4..cf01b2442e6 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -71,7 +71,7 @@ void PhaseVector::do_cleanup() { } { Compile::TracePhase tp(_t_vector_igvn); - _igvn.reset_from_gvn(C->initial_gvn()); + _igvn.reset(); _igvn.optimize(); if (C->failing()) return; } diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 97c5dbe03ef..10430a09e72 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -686,11 +686,20 @@ bool LibraryCallKit::inline_vector_frombits_coerced() { int opc = bcast_mode == VectorSupport::MODE_BITS_COERCED_LONG_TO_MASK ? Op_VectorLongToMask : Op_Replicate; if (!arch_supports_vector(opc, num_elem, elem_bt, checkFlags, true /*has_scalar_args*/)) { - log_if_needed(" ** not supported: arity=0 op=broadcast vlen=%d etype=%s ismask=%d bcast_mode=%d", - num_elem, type2name(elem_bt), - is_mask ? 1 : 0, - bcast_mode); - return false; // not supported + // If the input long sets or unsets all lanes and Replicate is supported, + // generate a MaskAll or Replicate instead. + + // The "maskAll" API uses the corresponding integer types for floating-point data. + BasicType maskall_bt = elem_bt == T_DOUBLE ? T_LONG : (elem_bt == T_FLOAT ? T_INT: elem_bt); + if (!(opc == Op_VectorLongToMask && + VectorNode::is_maskall_type(bits_type, num_elem) && + arch_supports_vector(Op_Replicate, num_elem, maskall_bt, checkFlags, true /*has_scalar_args*/))) { + log_if_needed(" ** not supported: arity=0 op=broadcast vlen=%d etype=%s ismask=%d bcast_mode=%d", + num_elem, type2name(elem_bt), + is_mask ? 1 : 0, + bcast_mode); + return false; // not supported + } } Node* broadcast = nullptr; @@ -2706,6 +2715,9 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() { index_elem_bt = T_LONG; } + // Check if the platform requires a VectorLoadShuffle node to be generated + bool need_load_shuffle = Matcher::vector_rearrange_requires_load_shuffle(index_elem_bt, num_elem); + bool lowerSelectFromOp = false; if (!arch_supports_vector(Op_SelectFromTwoVector, num_elem, elem_bt, VecMaskNotUsed)) { int cast_vopc = VectorCastNode::opcode(-1, elem_bt, true); @@ -2715,7 +2727,7 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() { !arch_supports_vector(Op_VectorMaskCast, num_elem, elem_bt, VecMaskNotUsed) || !arch_supports_vector(Op_VectorBlend, num_elem, elem_bt, VecMaskUseLoad) || !arch_supports_vector(Op_VectorRearrange, num_elem, elem_bt, VecMaskNotUsed) || - !arch_supports_vector(Op_VectorLoadShuffle, num_elem, index_elem_bt, VecMaskNotUsed) || + (need_load_shuffle && !arch_supports_vector(Op_VectorLoadShuffle, num_elem, index_elem_bt, VecMaskNotUsed)) || !arch_supports_vector(Op_Replicate, num_elem, index_elem_bt, VecMaskNotUsed)) { log_if_needed(" ** not supported: opc=%d vlen=%d etype=%s ismask=useload", Op_SelectFromTwoVector, num_elem, type2name(elem_bt)); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 667e74a4761..c126c91da1b 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -434,6 +434,16 @@ bool VectorNode::implemented(int opc, uint vlen, BasicType bt) { return false; } +bool VectorNode::is_maskall_type(const TypeLong* type, int vlen) { + assert(type != nullptr, "type must not be null"); + if (!type->is_con()) { + return false; + } + long mask = (-1ULL >> (64 - vlen)); + long bit = type->get_con() & mask; + return bit == 0 || bit == mask; +} + bool VectorNode::is_muladds2i(const Node* n) { return n->Opcode() == Op_MulAddS2I; } @@ -1503,6 +1513,45 @@ Node* ReductionNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } +// Convert fromLong to maskAll if the input sets or unsets all lanes. +Node* convertFromLongToMaskAll(PhaseGVN* phase, const TypeLong* bits_type, bool is_mask, const TypeVect* vt) { + uint vlen = vt->length(); + BasicType bt = vt->element_basic_type(); + // The "maskAll" API uses the corresponding integer types for floating-point data. + BasicType maskall_bt = (bt == T_FLOAT) ? T_INT : (bt == T_DOUBLE) ? T_LONG : bt; + + if (VectorNode::is_maskall_type(bits_type, vlen) && + Matcher::match_rule_supported_vector(Op_Replicate, vlen, maskall_bt)) { + Node* con = nullptr; + jlong con_value = bits_type->get_con() == 0L ? 0L : -1L; + if (maskall_bt == T_LONG) { + con = phase->longcon(con_value); + } else { + con = phase->intcon(con_value); + } + Node* res = VectorNode::scalar2vector(con, vlen, maskall_bt, is_mask); + // Convert back to the original floating-point data type. + if (is_floating_point_type(bt)) { + res = new VectorMaskCastNode(phase->transform(res), vt); + } + return res; + } + return nullptr; +} + +Node* VectorLoadMaskNode::Ideal(PhaseGVN* phase, bool can_reshape) { + // VectorLoadMask(VectorLongToMask(-1/0)) => Replicate(-1/0) + if (in(1)->Opcode() == Op_VectorLongToMask) { + const TypeVect* vt = bottom_type()->is_vect(); + Node* res = convertFromLongToMaskAll(phase, in(1)->in(1)->bottom_type()->isa_long(), false, vt); + if (res != nullptr) { + return res; + } + } + + return VectorNode::Ideal(phase, can_reshape); +} + Node* VectorLoadMaskNode::Identity(PhaseGVN* phase) { BasicType out_bt = type()->is_vect()->element_basic_type(); if (!Matcher::has_predicated_vectors() && out_bt == T_BOOLEAN) { @@ -1918,6 +1967,45 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } +Node* VectorMaskCastNode::Identity(PhaseGVN* phase) { + Node* in1 = in(1); + // VectorMaskCast (VectorMaskCast x) => x + if (in1->Opcode() == Op_VectorMaskCast && + vect_type()->eq(in1->in(1)->bottom_type())) { + return in1->in(1); + } + return this; +} + +// This function does the following optimization: +// VectorMaskToLong(MaskAll(l)) => (l & (-1ULL >> (64 - vlen))) +// VectorMaskToLong(VectorStoreMask(Replicate(l))) => (l & (-1ULL >> (64 - vlen))) +// l is -1 or 0. +Node* VectorMaskToLongNode::Ideal_MaskAll(PhaseGVN* phase) { + Node* in1 = in(1); + // VectorMaskToLong follows a VectorStoreMask if predicate is not supported. + if (in1->Opcode() == Op_VectorStoreMask) { + assert(!in1->in(1)->bottom_type()->isa_vectmask(), "sanity"); + in1 = in1->in(1); + } + if (VectorNode::is_all_ones_vector(in1)) { + int vlen = in1->bottom_type()->is_vect()->length(); + return new ConLNode(TypeLong::make(-1ULL >> (64 - vlen))); + } + if (VectorNode::is_all_zeros_vector(in1)) { + return new ConLNode(TypeLong::ZERO); + } + return nullptr; +} + +Node* VectorMaskToLongNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* res = Ideal_MaskAll(phase); + if (res != nullptr) { + return res; + } + return VectorMaskOpNode::Ideal(phase, can_reshape); +} + Node* VectorMaskToLongNode::Identity(PhaseGVN* phase) { if (in(1)->Opcode() == Op_VectorLongToMask) { return in(1)->in(1); @@ -1927,28 +2015,41 @@ Node* VectorMaskToLongNode::Identity(PhaseGVN* phase) { Node* VectorLongToMaskNode::Ideal(PhaseGVN* phase, bool can_reshape) { const TypeVect* dst_type = bottom_type()->is_vect(); + uint vlen = dst_type->length(); + const TypeVectMask* is_mask = dst_type->isa_vectmask(); + if (in(1)->Opcode() == Op_AndL && in(1)->in(1)->Opcode() == Op_VectorMaskToLong && in(1)->in(2)->bottom_type()->isa_long() && in(1)->in(2)->bottom_type()->is_long()->is_con() && - in(1)->in(2)->bottom_type()->is_long()->get_con() == ((1L << dst_type->length()) - 1)) { + in(1)->in(2)->bottom_type()->is_long()->get_con() == ((1L << vlen) - 1)) { // Different src/dst mask length represents a re-interpretation operation, // we can however generate a mask casting operation if length matches. Node* src = in(1)->in(1)->in(1); - if (dst_type->isa_vectmask() == nullptr) { + if (is_mask == nullptr) { if (src->Opcode() != Op_VectorStoreMask) { return nullptr; } src = src->in(1); } const TypeVect* src_type = src->bottom_type()->is_vect(); - if (src_type->length() == dst_type->length() && - ((src_type->isa_vectmask() == nullptr && dst_type->isa_vectmask() == nullptr) || - (src_type->isa_vectmask() && dst_type->isa_vectmask()))) { + if (src_type->length() == vlen && + ((src_type->isa_vectmask() == nullptr && is_mask == nullptr) || + (src_type->isa_vectmask() && is_mask))) { return new VectorMaskCastNode(src, dst_type); } } - return nullptr; + + // VectorLongToMask(-1/0) => MaskAll(-1/0) + const TypeLong* bits_type = in(1)->bottom_type()->isa_long(); + if (bits_type && is_mask) { + Node* res = convertFromLongToMaskAll(phase, bits_type, true, dst_type); + if (res != nullptr) { + return res; + } + } + + return VectorNode::Ideal(phase, can_reshape); } Node* FmaVNode::Ideal(PhaseGVN* phase, bool can_reshape) { diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 3caaf7c59d7..463680d0a52 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -104,6 +104,7 @@ class VectorNode : public TypeNode { static bool implemented(int opc, uint vlen, BasicType bt); static bool is_shift(Node* n); static bool is_vshift_cnt(Node* n); + static bool is_maskall_type(const TypeLong* type, int vlen); static bool is_muladds2i(const Node* n); static bool is_roundopD(Node* n); static bool is_scalar_rotate(Node* n); @@ -1383,6 +1384,8 @@ class VectorMaskToLongNode : public VectorMaskOpNode { VectorMaskToLongNode(Node* mask, const Type* ty): VectorMaskOpNode(mask, ty, Op_VectorMaskToLong) {} virtual int Opcode() const; + Node* Ideal(PhaseGVN* phase, bool can_reshape); + Node* Ideal_MaskAll(PhaseGVN* phase); virtual uint ideal_reg() const { return Op_RegL; } virtual Node* Identity(PhaseGVN* phase); }; @@ -1776,6 +1779,7 @@ class VectorLoadMaskNode : public VectorNode { virtual int Opcode() const; virtual Node* Identity(PhaseGVN* phase); + Node* Ideal(PhaseGVN* phase, bool can_reshape); }; class VectorStoreMaskNode : public VectorNode { @@ -1795,6 +1799,7 @@ class VectorMaskCastNode : public VectorNode { const TypeVect* in_vt = in->bottom_type()->is_vect(); assert(in_vt->length() == vt->length(), "vector length must match"); } + Node* Identity(PhaseGVN* phase); virtual int Opcode() const; }; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 13d89b396fa..005776b00c2 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -1741,19 +1741,6 @@ JVM_ENTRY(jobjectArray, JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofC } JVM_END -JVM_ENTRY(jint, JVM_GetClassAccessFlags(JNIEnv *env, jclass cls)) -{ - oop mirror = JNIHandles::resolve_non_null(cls); - if (java_lang_Class::is_primitive(mirror)) { - // Primitive type - return JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC; - } - - Klass* k = java_lang_Class::as_Klass(mirror); - return k->access_flags().as_class_flags(); -} -JVM_END - JVM_ENTRY(jboolean, JVM_AreNestMates(JNIEnv *env, jclass current, jclass member)) { Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current)); @@ -2893,15 +2880,16 @@ JVM_ENTRY(void, JVM_SleepNanos(JNIEnv* env, jclass threadClass, jlong nanos)) } else { ThreadState old_state = thread->osthread()->get_state(); thread->osthread()->set_state(SLEEPING); - if (!thread->sleep_nanos(nanos)) { // interrupted + if (!thread->sleep_nanos(nanos)) { // interrupted or async exception was installed // An asynchronous exception could have been thrown on // us while we were sleeping. We do not overwrite those. if (!HAS_PENDING_EXCEPTION) { HOTSPOT_THREAD_SLEEP_END(1); - - // TODO-FIXME: THROW_MSG returns which means we will not call set_state() - // to properly restore the thread state. That's likely wrong. - THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted"); + if (!thread->has_async_exception_condition()) { + // TODO-FIXME: THROW_MSG returns which means we will not call set_state() + // to properly restore the thread state. That's likely wrong. + THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted"); + } } } thread->osthread()->set_state(old_state); diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml index 7e76cbabc65..2d1d8c0330d 100644 --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -6127,7 +6127,7 @@ class C2 extends C1 implements I2 { Get Local Variable - Double This function can be used to retrieve the value of a local - variable whose type is long. + variable whose type is double.

The specified thread must be suspended or must be the current thread. diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index cdd32f13646..a82ad2db6b6 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -865,47 +865,6 @@ JvmtiExport::cv_external_thread_to_JavaThread(ThreadsList * t_list, return JVMTI_ERROR_NONE; } -// Convert an oop to a JavaThread found on the specified ThreadsList. -// The ThreadsListHandle in the caller "protects" the returned -// JavaThread *. -// -// On success, *jt_pp is set to the converted JavaThread * and -// JVMTI_ERROR_NONE is returned. On error, returns various -// JVMTI_ERROR_* values. -// -jvmtiError -JvmtiExport::cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop, - JavaThread ** jt_pp) { - assert(t_list != nullptr, "must have a ThreadsList"); - assert(thread_oop != nullptr, "must have an oop"); - assert(jt_pp != nullptr, "must have a return JavaThread pointer"); - - if (!thread_oop->is_a(vmClasses::Thread_klass())) { - // The oop is not a java.lang.Thread. - return JVMTI_ERROR_INVALID_THREAD; - } - // Looks like a java.lang.Thread oop at this point. - - JavaThread * java_thread = java_lang_Thread::thread(thread_oop); - if (java_thread == nullptr) { - // The java.lang.Thread does not contain a JavaThread * so it has - // not yet run or it has died. - return JVMTI_ERROR_THREAD_NOT_ALIVE; - } - // Looks like a live JavaThread at this point. - - if (!t_list->includes(java_thread)) { - // Not on the JavaThreads list so it is not alive. - return JVMTI_ERROR_THREAD_NOT_ALIVE; - } - - // Return a live JavaThread that is "protected" by the - // ThreadsListHandle in the caller. - *jt_pp = java_thread; - - return JVMTI_ERROR_NONE; -} - class JvmtiClassFileLoadHookPoster : public StackObj { private: Symbol* _h_name; diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index e47cd3d6363..4f8c3016908 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -454,8 +454,6 @@ class JvmtiExport : public AllStatic { jthread thread, JavaThread ** jt_pp, oop * thread_oop_p); - static jvmtiError cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop, - JavaThread ** jt_pp); }; // Support class used by JvmtiDynamicCodeEventCollector and others. It diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp index 3066ef74cb8..d5509896064 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp @@ -67,6 +67,9 @@ #include "utilities/checkedCast.hpp" #include "utilities/events.hpp" #include "utilities/macros.hpp" +#if INCLUDE_JFR +#include "jfr/jfr.hpp" +#endif Array* VM_RedefineClasses::_old_methods = nullptr; Array* VM_RedefineClasses::_new_methods = nullptr; @@ -1175,7 +1178,6 @@ jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( } } } - JFR_ONLY(k_new_method->copy_trace_flags(k_old_method->trace_flags());) log_trace(redefine, class, normalize) ("Method matched: new: %s [%d] == old: %s [%d]", k_new_method->name_and_sig_as_C_string(), ni, k_old_method->name_and_sig_as_C_string(), oi); @@ -4406,7 +4408,7 @@ void VM_RedefineClasses::redefine_single_class(Thread* current, jclass the_jclas // keep track of previous versions of this class the_class->add_previous_version(scratch_class, emcp_method_count); - JFR_ONLY(ON_KLASS_REDEFINITION(the_class, current);) + JFR_ONLY(Jfr::on_klass_redefinition(the_class, scratch_class);) _timer_rsc_phase1.stop(); if (log_is_enabled(Info, redefine, class, timer)) { diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index a6300e81468..80dfaf90a28 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -480,7 +480,9 @@ UNSAFE_LEAF (void, Unsafe_WriteBackPostSync0(JNIEnv *env, jobject unsafe)) { ////// Random queries -static jlong find_field_offset(jclass clazz, jstring name, TRAPS) { +// Finds the object field offset of a field with the matching name, or an error code +// Error code -1 is not found, -2 is static field +static jlong find_known_instance_field_offset(jclass clazz, jstring name, TRAPS) { assert(clazz != nullptr, "clazz must not be null"); assert(name != nullptr, "name must not be null"); @@ -489,16 +491,20 @@ static jlong find_field_offset(jclass clazz, jstring name, TRAPS) { InstanceKlass* k = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz))); - jint offset = -1; + jint offset = -1; // Not found for (JavaFieldStream fs(k); !fs.done(); fs.next()) { Symbol *name = fs.name(); if (name->equals(utf_name)) { - offset = fs.offset(); + if (!fs.access_flags().is_static()) { + offset = fs.offset(); + } else { + offset = -2; // A static field + } break; } } if (offset < 0) { - THROW_0(vmSymbols::java_lang_InternalError()); + return offset; // Error code } return field_offset_from_byte_offset(offset); } @@ -527,8 +533,8 @@ UNSAFE_ENTRY(jlong, Unsafe_ObjectFieldOffset0(JNIEnv *env, jobject unsafe, jobje return find_field_offset(field, 0, THREAD); } UNSAFE_END -UNSAFE_ENTRY(jlong, Unsafe_ObjectFieldOffset1(JNIEnv *env, jobject unsafe, jclass c, jstring name)) { - return find_field_offset(c, name, THREAD); +UNSAFE_ENTRY(jlong, Unsafe_KnownObjectFieldOffset0(JNIEnv *env, jobject unsafe, jclass c, jstring name)) { + return find_known_instance_field_offset(c, name, THREAD); } UNSAFE_END UNSAFE_ENTRY(jlong, Unsafe_StaticFieldOffset0(JNIEnv *env, jobject unsafe, jobject field)) { @@ -882,7 +888,7 @@ static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = { {CC "freeMemory0", CC "(" ADR ")V", FN_PTR(Unsafe_FreeMemory0)}, {CC "objectFieldOffset0", CC "(" FLD ")J", FN_PTR(Unsafe_ObjectFieldOffset0)}, - {CC "objectFieldOffset1", CC "(" CLS LANG "String;)J", FN_PTR(Unsafe_ObjectFieldOffset1)}, + {CC "knownObjectFieldOffset0", CC "(" CLS LANG "String;)J", FN_PTR(Unsafe_KnownObjectFieldOffset0)}, {CC "staticFieldOffset0", CC "(" FLD ")J", FN_PTR(Unsafe_StaticFieldOffset0)}, {CC "staticFieldBase0", CC "(" FLD ")" OBJ, FN_PTR(Unsafe_StaticFieldBase0)}, {CC "ensureClassInitialized0", CC "(" CLS ")V", FN_PTR(Unsafe_EnsureClassInitialized0)}, diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 9beb50fe78b..68fab39b23a 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -102,6 +102,7 @@ #include "utilities/macros.hpp" #include "utilities/nativeCallStack.hpp" #include "utilities/ostream.hpp" +#include "utilities/vmError.hpp" #if INCLUDE_G1GC #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" @@ -1501,12 +1502,6 @@ WB_END WB_ENTRY(void, WB_FullGC(JNIEnv* env, jobject o)) Universe::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(true); Universe::heap()->collect(GCCause::_wb_full_gc); -#if INCLUDE_G1GC || INCLUDE_SERIALGC - if (UseG1GC || UseSerialGC) { - // Needs to be cleared explicitly for G1 and Serial GC. - Universe::heap()->soft_ref_policy()->set_should_clear_all_soft_refs(false); - } -#endif // INCLUDE_G1GC || INCLUDE_SERIALGC WB_END WB_ENTRY(void, WB_YoungGC(JNIEnv* env, jobject o)) @@ -2728,6 +2723,14 @@ WB_ENTRY(jlong, WB_Rss(JNIEnv* env, jobject o)) return os::rss(); WB_END +WB_ENTRY(void, WB_ControlledCrash(JNIEnv* env, jobject o, jint how)) +#ifdef ASSERT + VMError::controlled_crash(how); +#else + THROW_MSG(vmSymbols::java_lang_UnsupportedOperationException(), "Only available in debug builds"); +#endif +WB_END + #define CC (char*) static JNINativeMethod methods[] = { @@ -3019,10 +3022,10 @@ static JNINativeMethod methods[] = { {CC"lockAndStuckInSafepoint", CC"()V", (void*)&WB_TakeLockAndHangInSafepoint}, {CC"wordSize", CC"()J", (void*)&WB_WordSize}, {CC"rootChunkWordSize", CC"()J", (void*)&WB_RootChunkWordSize}, - {CC"isStatic", CC"()Z", (void*)&WB_IsStaticallyLinked} + {CC"isStatic", CC"()Z", (void*)&WB_IsStaticallyLinked}, + {CC"controlledCrash",CC"(I)V", (void*)&WB_ControlledCrash}, }; - #undef CC JVM_ENTRY(void, JVM_RegisterWhiteBoxMethods(JNIEnv* env, jclass wbclass)) diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index 5b9d36115f8..97d4f7f228d 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -325,19 +325,6 @@ unsigned int Abstract_VM_Version::jvm_version() { (Abstract_VM_Version::vm_build_number() & 0xFF); } -const char* Abstract_VM_Version::extract_features_string(const char* cpu_info_string, - size_t cpu_info_string_len, - size_t features_offset) { - assert(features_offset <= cpu_info_string_len, ""); - if (features_offset < cpu_info_string_len) { - assert(cpu_info_string[features_offset + 0] == ',', ""); - assert(cpu_info_string[features_offset + 1] == ' ', ""); - return cpu_info_string + features_offset + 2; // skip initial ", " - } else { - return ""; // empty - } -} - bool Abstract_VM_Version::print_matching_lines_from_file(const char* filename, outputStream* st, const char* keywords_to_match[]) { char line[500]; FILE* fp = os::fopen(filename, "r"); diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index 4972f02e3d8..b9c52b27182 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -132,9 +132,6 @@ class Abstract_VM_Version: AllStatic { static const char* features_string() { return _features_string; } static const char* cpu_info_string() { return _cpu_info_string; } - static const char* extract_features_string(const char* cpu_info_string, - size_t cpu_info_string_len, - size_t features_offset); static VirtualizationType get_detected_virtualization() { return _detected_virtualization; diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 8726abb91fe..cc14e5da2c3 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1487,19 +1487,16 @@ jint Arguments::set_ergonomics_flags() { } size_t Arguments::limit_heap_by_allocatable_memory(size_t limit) { - size_t max_allocatable; - size_t result = limit; - if (os::has_allocatable_memory_limit(&max_allocatable)) { - // The AggressiveHeap check is a temporary workaround to avoid calling - // GCarguments::heap_virtual_to_physical_ratio() before a GC has been - // selected. This works because AggressiveHeap implies UseParallelGC - // where we know the ratio will be 1. Once the AggressiveHeap option is - // removed, this can be cleaned up. - size_t heap_virtual_to_physical_ratio = (AggressiveHeap ? 1 : GCConfig::arguments()->heap_virtual_to_physical_ratio()); - size_t fraction = MaxVirtMemFraction * heap_virtual_to_physical_ratio; - result = MIN2(result, max_allocatable / fraction); - } - return result; + // The AggressiveHeap check is a temporary workaround to avoid calling + // GCarguments::heap_virtual_to_physical_ratio() before a GC has been + // selected. This works because AggressiveHeap implies UseParallelGC + // where we know the ratio will be 1. Once the AggressiveHeap option is + // removed, this can be cleaned up. + size_t heap_virtual_to_physical_ratio = (AggressiveHeap ? 1 : GCConfig::arguments()->heap_virtual_to_physical_ratio()); + size_t fraction = MaxVirtMemFraction * heap_virtual_to_physical_ratio; + size_t max_allocatable = os::commit_memory_limit(); + + return MIN2(limit, max_allocatable / fraction); } // Use static initialization to get the default before parsing diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp index 444988efdca..26a46462db4 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp @@ -39,6 +39,14 @@ JVMFlag::Error AOTCacheConstraintFunc(ccstr value, bool verbose) { return JVMFlag::SUCCESS; } +JVMFlag::Error AOTCacheOutputConstraintFunc(ccstr value, bool verbose) { + if (value == nullptr) { + JVMFlag::printError(verbose, "AOTCacheOutput cannot be empty\n"); + return JVMFlag::VIOLATES_CONSTRAINT; + } + return JVMFlag::SUCCESS; +} + JVMFlag::Error AOTConfigurationConstraintFunc(ccstr value, bool verbose) { if (value == nullptr) { JVMFlag::printError(verbose, "AOTConfiguration cannot be empty\n"); diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp index 8425425c768..31512af940d 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp @@ -35,6 +35,7 @@ #define RUNTIME_CONSTRAINTS(f) \ f(ccstr, AOTCacheConstraintFunc) \ + f(ccstr, AOTCacheOutputConstraintFunc) \ f(ccstr, AOTConfigurationConstraintFunc) \ f(ccstr, AOTModeConstraintFunc) \ f(int, ObjectAlignmentInBytesConstraintFunc) \ diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 2da22df4e4e..24dda9ac6d8 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -124,10 +124,6 @@ const size_t minimumSymbolTableSize = 1024; "Use 32-bit object references in 64-bit VM. " \ "lp64_product means flag is always constant in 32 bit VM") \ \ - product(bool, UseCompressedClassPointers, true, \ - "(Deprecated) Use 32-bit class pointers in 64-bit VM. " \ - "lp64_product means flag is always constant in 32 bit VM") \ - \ product(bool, UseCompactObjectHeaders, false, \ "Use compact 64-bit object headers in 64-bit VM") \ \ @@ -146,7 +142,6 @@ const size_t minimumSymbolTableSize = 1024; range, \ constraint) const bool UseCompressedOops = false; -const bool UseCompressedClassPointers = false; const bool UseCompactObjectHeaders = false; const int ObjectAlignmentInBytes = 8; @@ -1398,6 +1393,9 @@ const int ObjectAlignmentInBytes = 8; "Maximum size of Metaspaces (in bytes)") \ constraint(MaxMetaspaceSizeConstraintFunc,AfterErgo) \ \ + product(bool, UseCompressedClassPointers, true, \ + "(Deprecated) Use 32-bit class pointers.") \ + \ product(size_t, CompressedClassSpaceSize, 1*G, \ "Maximum size of class area in Metaspace when compressed " \ "class pointers are used") \ @@ -2000,6 +1998,10 @@ const int ObjectAlignmentInBytes = 8; develop(uint, BinarySearchThreshold, 16, \ "Minimal number of elements in a sorted collection to prefer" \ "binary search over simple linear search." ) \ + \ + product(bool, UseClassMetaspaceForAllClasses, false, DIAGNOSTIC, \ + "Use the class metaspace for all classes including " \ + "abstract and interface classes.") \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index c55803242de..39ae4f18998 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -45,6 +45,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/preserveException.hpp" #include "utilities/systemMemoryBarrier.hpp" +#include "utilities/vmError.hpp" class HandshakeOperation : public CHeapObj { friend class HandshakeState; @@ -201,6 +202,7 @@ static void handle_timeout(HandshakeOperation* op, JavaThread* target) { } if (target != nullptr) { + VMError::set_handshake_timed_out_thread(p2i(target)); if (os::signal_thread(target, SIGILL, "cannot be handshaked")) { // Give target a chance to report the error and terminate the VM. os::naked_sleep(3000); @@ -208,7 +210,11 @@ static void handle_timeout(HandshakeOperation* op, JavaThread* target) { } else { log_error(handshake)("No thread with an unfinished handshake op(" INTPTR_FORMAT ") found.", p2i(op)); } - fatal("Handshake timeout"); + if (target != nullptr) { + fatal("Thread " PTR_FORMAT " has not cleared handshake op %s, and failed to terminate the JVM", p2i(target), op->name()); + } else { + fatal("Handshake timeout"); + } } static void check_handshake_timeout(jlong start_time, HandshakeOperation* op, JavaThread* target = nullptr) { diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index bd96f92c47a..3361ecf1558 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -463,7 +463,10 @@ void before_exit(JavaThread* thread, bool halt) { event.commit(); } - JFR_ONLY(Jfr::on_vm_shutdown(false, halt);) + // 2nd argument (emit_event_shutdown) should be set to false + // because EventShutdown would be emitted at Threads::destroy_vm(). + // (one of the callers of before_exit()) + JFR_ONLY(Jfr::on_vm_shutdown(true, false, halt);) // Stop the WatcherThread. We do this before disenrolling various // PeriodicTasks to reduce the likelihood of races. diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index b84cf6e9011..a0ac7bd4768 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -738,6 +738,8 @@ void JavaThread::run() { assert(JavaThread::current() == this, "sanity check"); assert(!Thread::current()->owns_locks(), "sanity check"); + JFR_ONLY(Jfr::on_thread_start(this);) + DTRACE_THREAD_PROBE(start, this); // This operation might block. We call that after all safepoint checks for a new thread has @@ -1146,7 +1148,6 @@ void JavaThread::install_async_exception(AsyncExceptionHandshakeClosure* aehc) { oop vt_oop = vthread(); if (vt_oop == nullptr || !vt_oop->is_a(vmClasses::BaseVirtualThread_klass())) { // Interrupt thread so it will wake up from a potential wait()/sleep()/park() - java_lang_Thread::set_interrupted(threadObj(), true); this->interrupt(); } } @@ -2098,7 +2099,7 @@ bool JavaThread::sleep(jlong millis) { // java.lang.Thread.sleep support // Returns true if sleep time elapsed as expected, and false -// if the thread was interrupted. +// if the thread was interrupted or async exception was installed. bool JavaThread::sleep_nanos(jlong nanos) { assert(this == Thread::current(), "thread consistency check"); assert(nanos >= 0, "nanos are in range"); @@ -2118,6 +2119,9 @@ bool JavaThread::sleep_nanos(jlong nanos) { jlong nanos_remaining = nanos; for (;;) { + if (has_async_exception_condition()) { + return false; + } // interruption has precedence over timing out if (this->is_interrupted(true)) { return false; diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 5f1c2ee61ae..8cfecd098f6 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -70,13 +70,7 @@ Monitor* ThreadsLockThrottle_lock = nullptr; Monitor* Threads_lock = nullptr; Mutex* NonJavaThreadsList_lock = nullptr; Mutex* NonJavaThreadsListSync_lock = nullptr; -Monitor* CGC_lock = nullptr; Monitor* STS_lock = nullptr; -Monitor* G1OldGCCount_lock = nullptr; -Mutex* G1RareEvent_lock = nullptr; -Mutex* G1DetachedRefinementStats_lock = nullptr; -Mutex* MarkStackFreeList_lock = nullptr; -Mutex* MarkStackChunkList_lock = nullptr; Mutex* MonitoringSupport_lock = nullptr; Monitor* ConcurrentGCBreakpoints_lock = nullptr; Mutex* Compile_lock = nullptr; @@ -103,10 +97,18 @@ Mutex* RawMonitor_lock = nullptr; Mutex* PerfDataMemAlloc_lock = nullptr; Mutex* PerfDataManager_lock = nullptr; -Mutex* FreeList_lock = nullptr; -Mutex* OldSets_lock = nullptr; -Mutex* Uncommit_lock = nullptr; -Monitor* RootRegionScan_lock = nullptr; +#if INCLUDE_G1GC +Monitor* G1CGC_lock = nullptr; +Mutex* G1DetachedRefinementStats_lock = nullptr; +Mutex* G1FreeList_lock = nullptr; +Mutex* G1MarkStackChunkList_lock = nullptr; +Mutex* G1MarkStackFreeList_lock = nullptr; +Monitor* G1OldGCCount_lock = nullptr; +Mutex* G1OldSets_lock = nullptr; +Mutex* G1Uncommit_lock = nullptr; +Monitor* G1RootRegionScan_lock = nullptr; +Mutex* G1RareEvent_lock = nullptr; +#endif Mutex* Management_lock = nullptr; Monitor* MonitorDeflation_lock = nullptr; @@ -209,19 +211,19 @@ void mutex_init() { MUTEX_DEFN(STS_lock , PaddedMonitor, nosafepoint); +#if INCLUDE_G1GC if (UseG1GC) { - MUTEX_DEFN(CGC_lock , PaddedMonitor, nosafepoint); - + MUTEX_DEFN(G1CGC_lock , PaddedMonitor, nosafepoint); MUTEX_DEFN(G1DetachedRefinementStats_lock, PaddedMutex , nosafepoint-2); - - MUTEX_DEFN(FreeList_lock , PaddedMutex , service-1); - MUTEX_DEFN(OldSets_lock , PaddedMutex , nosafepoint); - MUTEX_DEFN(Uncommit_lock , PaddedMutex , service-2); - MUTEX_DEFN(RootRegionScan_lock , PaddedMonitor, nosafepoint-1); - - MUTEX_DEFN(MarkStackFreeList_lock , PaddedMutex , nosafepoint); - MUTEX_DEFN(MarkStackChunkList_lock , PaddedMutex , nosafepoint); + MUTEX_DEFN(G1FreeList_lock , PaddedMutex , service-1); + MUTEX_DEFN(G1MarkStackChunkList_lock , PaddedMutex , nosafepoint); + MUTEX_DEFN(G1MarkStackFreeList_lock , PaddedMutex , nosafepoint); + MUTEX_DEFN(G1OldSets_lock , PaddedMutex , nosafepoint); + MUTEX_DEFN(G1RootRegionScan_lock , PaddedMonitor, nosafepoint-1); + MUTEX_DEFN(G1Uncommit_lock , PaddedMutex , service-2); } +#endif + MUTEX_DEFN(MonitoringSupport_lock , PaddedMutex , service-1); // used for serviceability monitoring support MUTEX_DEFN(StringDedup_lock , PaddedMonitor, nosafepoint); @@ -340,10 +342,12 @@ void mutex_init() { MUTEX_DEFL(VMOperation_lock , PaddedMonitor, Heap_lock, true); MUTEX_DEFL(ClassInitError_lock , PaddedMonitor, Threads_lock); +#if INCLUDE_G1GC if (UseG1GC) { - MUTEX_DEFL(G1OldGCCount_lock , PaddedMonitor, Threads_lock, true); - MUTEX_DEFL(G1RareEvent_lock , PaddedMutex , Threads_lock, true); + MUTEX_DEFL(G1OldGCCount_lock , PaddedMonitor, Threads_lock, true); + MUTEX_DEFL(G1RareEvent_lock , PaddedMutex , Threads_lock, true); } +#endif MUTEX_DEFL(CompileTaskWait_lock , PaddedMonitor, MethodCompileQueue_lock); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 72454ca04ca..f888c789eb7 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -70,14 +70,7 @@ extern Monitor* Threads_lock; // a lock on the Threads table // (also used by Safepoints too to block threads creation/destruction) extern Mutex* NonJavaThreadsList_lock; // a lock on the NonJavaThreads list extern Mutex* NonJavaThreadsListSync_lock; // a lock for NonJavaThreads list synchronization -extern Monitor* CGC_lock; // used for coordination between - // fore- & background GC threads. extern Monitor* STS_lock; // used for joining/leaving SuspendibleThreadSet. -extern Monitor* G1OldGCCount_lock; // in support of "concurrent" full gc -extern Mutex* G1RareEvent_lock; // Synchronizes (rare) parallel GC operations. -extern Mutex* G1DetachedRefinementStats_lock; // Lock protecting detached refinement stats -extern Mutex* MarkStackFreeList_lock; // Protects access to the global mark stack free list. -extern Mutex* MarkStackChunkList_lock; // Protects access to the global mark stack chunk list. extern Mutex* MonitoringSupport_lock; // Protects updates to the serviceability memory pools and allocated memory high water mark. extern Monitor* ConcurrentGCBreakpoints_lock; // Protects concurrent GC breakpoint management extern Mutex* Compile_lock; // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc) @@ -99,15 +92,23 @@ extern Mutex* ExceptionCache_lock; // a lock used to synchronize e extern Mutex* FullGCALot_lock; // a lock to make FullGCALot MT safe #endif // PRODUCT +#if INCLUDE_G1GC +extern Monitor* G1CGC_lock; // used for coordination between fore- & background G1 concurrent GC threads. +extern Mutex* G1DetachedRefinementStats_lock; // Lock protecting detached refinement stats for G1. +extern Mutex* G1FreeList_lock; // protects the G1 free region list during safepoints +extern Mutex* G1MarkStackChunkList_lock; // Protects access to the G1 global mark stack chunk list. +extern Mutex* G1MarkStackFreeList_lock; // Protects access to the G1 global mark stack free list. +extern Monitor* G1OldGCCount_lock; // in support of "concurrent" full gc +extern Mutex* G1OldSets_lock; // protects the G1 old region sets +extern Mutex* G1RareEvent_lock; // Synchronizes (rare) parallel GC operations. +extern Monitor* G1RootRegionScan_lock; // used to notify that the G1 CM threads have finished scanning the root regions +extern Mutex* G1Uncommit_lock; // protects the G1 uncommit list when not at safepoints +#endif + extern Mutex* RawMonitor_lock; extern Mutex* PerfDataMemAlloc_lock; // a lock on the allocator for PerfData memory for performance data extern Mutex* PerfDataManager_lock; // a long on access to PerfDataManager resources -extern Mutex* FreeList_lock; // protects the free region list during safepoints -extern Mutex* OldSets_lock; // protects the old region sets -extern Mutex* Uncommit_lock; // protects the uncommit list when not at safepoints -extern Monitor* RootRegionScan_lock; // used to notify that the CM threads have finished scanning the IM snapshot regions - extern Mutex* Management_lock; // a lock used to serialize JVM management extern Monitor* MonitorDeflation_lock; // a lock used for monitor deflation thread operation extern Monitor* Service_lock; // a lock used for service thread operation diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 6648b08637c..c34bf77e3d6 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -340,7 +340,6 @@ class os: AllStatic { static jlong free_swap_space(); static julong physical_memory(); - static bool has_allocatable_memory_limit(size_t* limit); static bool is_server_class_machine(); static size_t rss(); @@ -449,6 +448,16 @@ class os: AllStatic { // Returns the lowest address the process is allowed to map against. static size_t vm_min_address(); + // Returns an upper limit beyond which reserve_memory() calls are guaranteed + // to fail. It is not guaranteed that reserving less memory than this will + // succeed, however. + static size_t reserve_memory_limit(); + + // Returns an upper limit beyond which commit_memory() calls are guaranteed + // to fail. It is not guaranteed that committing less memory than this will + // succeed, however. + static size_t commit_memory_limit(); + inline static size_t cds_core_region_alignment(); // Reserves virtual memory. diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index ab896290007..6fcf1e6c0f5 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -66,6 +66,7 @@ #include "utilities/events.hpp" #include "utilities/macros.hpp" #include "utilities/systemMemoryBarrier.hpp" +#include "utilities/vmError.hpp" static void post_safepoint_begin_event(EventSafepointBegin& event, uint64_t safepoint_id, @@ -650,6 +651,7 @@ void SafepointSynchronize::print_safepoint_timeout() { // Send the blocking thread a signal to terminate and write an error file. for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur_thread = jtiwh.next(); ) { if (cur_thread->safepoint_state()->is_running()) { + VMError::set_safepoint_timed_out_thread(p2i(cur_thread)); if (!os::signal_thread(cur_thread, SIGILL, "blocking a safepoint")) { break; // Could not send signal. Report fatal error. } diff --git a/src/hotspot/share/runtime/safepointMechanism.cpp b/src/hotspot/share/runtime/safepointMechanism.cpp index d4160161f19..84cc3fc316a 100644 --- a/src/hotspot/share/runtime/safepointMechanism.cpp +++ b/src/hotspot/share/runtime/safepointMechanism.cpp @@ -142,7 +142,6 @@ void SafepointMechanism::process(JavaThread *thread, bool allow_suspend, bool ch do { JavaThreadState state = thread->thread_state(); guarantee(state == _thread_in_vm, "Illegal threadstate encountered: %d", state); - JFR_ONLY(Jfr::check_and_process_sample_request(thread);) if (global_poll()) { // Any load in ::block() must not pass the global poll load. // Otherwise we might load an old safepoint counter (for example). @@ -161,6 +160,7 @@ void SafepointMechanism::process(JavaThread *thread, bool allow_suspend, bool ch need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self(allow_suspend, check_async_exception); } while (need_rechecking); + JFR_ONLY(Jfr::check_and_process_sample_request(thread);) update_poll_values(thread); assert(sp_before == thread->last_Java_sp(), "Anchor has changed"); } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 554b94b56c6..149ebef4294 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -2769,12 +2769,13 @@ AdapterBlob* AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handle ResourceMark rm; const char* name = AdapterHandlerLibrary::name(handler->fingerprint()); const uint32_t id = AdapterHandlerLibrary::id(handler->fingerprint()); - int offsets[AdapterHandlerEntry::ENTRIES_COUNT]; + int offsets[AdapterBlob::ENTRY_COUNT]; AdapterBlob* adapter_blob = nullptr; - CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::Adapter, id, name, AdapterHandlerEntry::ENTRIES_COUNT, offsets); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::Adapter, id, name); if (blob != nullptr) { adapter_blob = blob->as_adapter_blob(); + adapter_blob->get_offsets(offsets); address i2c_entry = adapter_blob->content_begin(); assert(offsets[0] == 0, "sanity check"); handler->set_entry_points(i2c_entry, i2c_entry + offsets[1], i2c_entry + offsets[2], i2c_entry + offsets[3]); @@ -2837,7 +2838,15 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, } #endif - adapter_blob = AdapterBlob::create(&buffer); + int entry_offset[AdapterBlob::ENTRY_COUNT]; + assert(AdapterBlob::ENTRY_COUNT == 4, "sanity"); + address i2c_entry = handler->get_i2c_entry(); + entry_offset[0] = 0; // i2c_entry offset + entry_offset[1] = handler->get_c2i_entry() - i2c_entry; + entry_offset[2] = handler->get_c2i_unverified_entry() - i2c_entry; + entry_offset[3] = handler->get_c2i_no_clinit_check_entry() - i2c_entry; + + adapter_blob = AdapterBlob::create(&buffer, entry_offset); if (adapter_blob == nullptr) { // CodeCache is full, disable compilation // Ought to log this but compile log is only per compile thread @@ -2848,14 +2857,7 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, // try to save generated code const char* name = AdapterHandlerLibrary::name(handler->fingerprint()); const uint32_t id = AdapterHandlerLibrary::id(handler->fingerprint()); - int entry_offset[AdapterHandlerEntry::ENTRIES_COUNT]; - assert(AdapterHandlerEntry::ENTRIES_COUNT == 4, "sanity"); - address i2c_entry = handler->get_i2c_entry(); - entry_offset[0] = 0; // i2c_entry offset - entry_offset[1] = handler->get_c2i_entry() - i2c_entry; - entry_offset[2] = handler->get_c2i_unverified_entry() - i2c_entry; - entry_offset[3] = handler->get_c2i_no_clinit_check_entry() - i2c_entry; - bool success = AOTCodeCache::store_code_blob(*adapter_blob, AOTCodeEntry::Adapter, id, name, AdapterHandlerEntry::ENTRIES_COUNT, entry_offset); + bool success = AOTCodeCache::store_code_blob(*adapter_blob, AOTCodeEntry::Adapter, id, name); assert(success || !AOTCodeCache::is_dumping_adapter(), "caching of adapter must be disabled"); } handler->relocate(adapter_blob->content_begin()); diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index 3574aeaf636..c79caa12a6c 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -631,7 +631,7 @@ do_arch_entry, do_arch_entry_init) \ end_blob(preuniverse) \ -#define STUBGEN_INITIAL_BLOBS_DO(do_blob, end_blob, \ +#define STUBGEN_INITIAL_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ @@ -651,12 +651,9 @@ do_stub(initial, updateBytesCRC32) \ do_entry(initial, updateBytesCRC32, updateBytesCRC32, \ updateBytesCRC32) \ - do_entry(initial, updateBytesCRC32, crc_table_adr, crc_table_addr) \ do_stub(initial, updateBytesCRC32C) \ do_entry(initial, updateBytesCRC32C, updateBytesCRC32C, \ updateBytesCRC32C) \ - do_entry(initial, updateBytesCRC32C, crc32c_table_addr, \ - crc32c_table_addr) \ do_stub(initial, f2hf) \ do_entry(initial, f2hf, f2hf, f2hf_adr) \ do_stub(initial, hf2f) \ @@ -675,6 +672,8 @@ do_entry(initial, dcos, dcos, dcos) \ do_stub(initial, dtan) \ do_entry(initial, dtan, dtan, dtan) \ + do_stub(initial, dsinh) \ + do_entry(initial, dsinh, dsinh, dsinh) \ do_stub(initial, dtanh) \ do_entry(initial, dtanh, dtanh, dtanh) \ do_stub(initial, dcbrt) \ @@ -886,6 +885,9 @@ do_stub(final, verify_oop) \ do_entry(final, verify_oop, verify_oop_subroutine_entry, \ verify_oop_subroutine_entry) \ + do_stub(final, unsafecopy_common) \ + do_entry(final, unsafecopy_common, unsafecopy_common_exit, \ + unsafecopy_common_exit) \ do_stub(final, jbyte_arraycopy) \ do_entry_init(final, jbyte_arraycopy, jbyte_arraycopy, \ jbyte_arraycopy, StubRoutines::jbyte_copy) \ diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 2c50fe50915..86975f7d0a6 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -131,6 +131,35 @@ address UnsafeMemoryAccess::page_error_continue_pc(address pc) { return nullptr; } +// Used to retrieve mark regions that lie within a generated stub so +// they can be saved along with the stub and used to reinit the table +// when the stub is reloaded. + +void UnsafeMemoryAccess::collect_entries(address range_start, address range_end, GrowableArray

& entries) +{ + for (int i = 0; i < _table_length; i++) { + UnsafeMemoryAccess& e = _table[i]; + assert((e._start_pc != nullptr && + e._end_pc != nullptr && + e._error_exit_pc != nullptr), + "search for entries found incomplete table entry"); + if (e._start_pc >= range_start && e._end_pc <= range_end) { + assert(((e._error_exit_pc >= range_start && + e._error_exit_pc <= range_end) || + e._error_exit_pc == _common_exit_stub_pc), + "unexpected error exit pc"); + entries.append(e._start_pc); + entries.append(e._end_pc); + // only return an exit pc when it is within the range of the stub + if (e._error_exit_pc != _common_exit_stub_pc) { + entries.append(e._error_exit_pc); + } else { + // an address outside the stub must be the common exit stub address + entries.append(nullptr); + } + } + } +} static BufferBlob* initialize_stubs(BlobId blob_id, int code_size, int max_aligned_stubs, @@ -144,21 +173,34 @@ static BufferBlob* initialize_stubs(BlobId blob_id, if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("%s\t not generated", buffer_name); - return nullptr; } + return nullptr; } TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime)); // Add extra space for large CodeEntryAlignment int size = code_size + CodeEntryAlignment * max_aligned_stubs; BufferBlob* stubs_code = BufferBlob::create(buffer_name, size); if (stubs_code == nullptr) { + // The compiler blob may be created late by a C2 compiler thread + // rather than during normal initialization by the initial thread. + // In that case we can tolerate an allocation failure because the + // compiler will have been shut down and we have no need of the + // blob. + if (Thread::current()->is_Compiler_thread()) { + assert(blob_id == BlobId::stubgen_compiler_id, "sanity"); + assert(DelayCompilerStubsGeneration, "sanity"); + log_warning(stubs)("%s\t not generated:\t no space left in CodeCache", buffer_name); + return nullptr; + } vm_exit_out_of_memory(code_size, OOM_MALLOC_ERROR, "CodeCache: no room for %s", buffer_name); } CodeBuffer buffer(stubs_code); StubGenerator_generate(&buffer, blob_id); // When new stubs added we need to make sure there is some space left // to catch situation when we should increase size again. - assert(code_size == 0 || buffer.insts_remaining() > 200, "increase %s", assert_msg); + assert(code_size == 0 || buffer.insts_remaining() > 200, + "increase %s, code_size: %d, used: %d, free: %d", + assert_msg, code_size, buffer.total_content_size(), buffer.insts_remaining()); LogTarget(Info, stubs) lt; if (lt.is_enabled()) { @@ -167,6 +209,7 @@ static BufferBlob* initialize_stubs(BlobId blob_id, buffer_name, p2i(stubs_code->content_begin()), p2i(stubs_code->content_end()), buffer.total_content_size(), buffer.insts_remaining()); } + return stubs_code; } diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index b333d6d74b9..97e3e46b870 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -33,6 +33,7 @@ #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubInfo.hpp" #include "runtime/threadWXSetters.inline.hpp" +#include "utilities/growableArray.hpp" #include "utilities/macros.hpp" // StubRoutines provides entry points to assembly routines used by @@ -140,6 +141,10 @@ class UnsafeMemoryAccess : public CHeapObj { static bool contains_pc(address pc); static address page_error_continue_pc(address pc); static void create_table(int max_size); + // Append to entries arrray start, end and exit pcs of all table + // entries that identify a sub-interval of range (range_start, + // range_end). Append nullptr if the exit pc is not in the range. + static void collect_entries(address range_start, address range_end, GrowableArray
& entries); }; class UnsafeMemoryAccessMark : public StackObj { @@ -304,6 +309,12 @@ public: return dest_uninitialized ? _arrayof_oop_disjoint_arraycopy_uninit : _arrayof_oop_disjoint_arraycopy; } + // These methods is implemented in architecture-specific code. + // Any table that is returned must be allocated once-only in + // foreign memory (or C heap) rather generated in the code cache. + static address crc_table_addr(); + static address crc32c_table_addr(); + typedef void (*DataCacheWritebackStub)(void *); static DataCacheWritebackStub DataCacheWriteback_stub() { return CAST_TO_FN_PTR(DataCacheWritebackStub, _data_cache_writeback); } typedef void (*DataCacheWritebackSyncStub)(bool); diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index 631a7ed8d79..c8af260f66e 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -791,10 +791,16 @@ ThreadsListHandle::~ThreadsListHandle() { // associated ThreadsList. This ThreadsListHandle "protects" the // returned JavaThread *. // +// If the jthread resolves to a virtual thread then the JavaThread * +// for its current carrier thread (if any) is returned via *jt_pp. +// It is up to the caller to prevent the virtual thread from changing +// its mounted status, or else account for it when acting on the carrier +// JavaThread. +// // If thread_oop_p is not null, then the caller wants to use the oop -// after this call so the oop is returned. On success, *jt_pp is set +// after this call so the oop is always returned. On success, *jt_pp is set // to the converted JavaThread * and true is returned. On error, -// returns false. +// returns false, and *jt_pp is unchanged. // bool ThreadsListHandle::cv_internal_thread_to_JavaThread(jobject jthread, JavaThread ** jt_pp, @@ -818,10 +824,22 @@ bool ThreadsListHandle::cv_internal_thread_to_JavaThread(jobject jthread, JavaThread *java_thread = java_lang_Thread::thread_acquire(thread_oop); if (java_thread == nullptr) { - // The java.lang.Thread does not contain a JavaThread* so it has not - // run enough to be put on a ThreadsList or it has exited enough to - // make it past ensure_join() where the JavaThread* is cleared. - return false; + if (!java_lang_VirtualThread::is_instance(thread_oop)) { + // The java.lang.Thread does not contain a JavaThread* so it has not + // run enough to be put on a ThreadsList or it has exited enough to + // make it past ensure_join() where the JavaThread* is cleared. + return false; + } else { + // For virtual threads we need to extract the carrier's JavaThread - if any. + oop carrier_thread = java_lang_VirtualThread::carrier_thread(thread_oop); + if (carrier_thread != nullptr) { + java_thread = java_lang_Thread::thread(carrier_thread); + } + if (java_thread == nullptr) { + // Virtual thread was unmounted, or else carrier has now terminated. + return false; + } + } } // Looks like a live JavaThread at this point. diff --git a/src/hotspot/share/runtime/threadSMR.hpp b/src/hotspot/share/runtime/threadSMR.hpp index a379e820587..d2e716eaddf 100644 --- a/src/hotspot/share/runtime/threadSMR.hpp +++ b/src/hotspot/share/runtime/threadSMR.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,8 +45,8 @@ class ThreadsList; // operation. It is no longer necessary to hold the Threads_lock to safely // perform an operation on a target thread. // -// There are several different ways to refer to java.lang.Thread objects -// so we have a few ways to get a protected JavaThread *: +// There are two ways to refer to java.lang.Thread objects so we have two ways +// to get a protected JavaThread*: // // JNI jobject example: // jobject jthread = ...; @@ -69,21 +69,10 @@ class ThreadsList; // } // : // do stuff with 'jt'... // -// JVM/TI oop example (this one should be very rare): -// oop thread_obj = ...; -// : -// JavaThread *jt = nullptr; -// ThreadsListHandle tlh; -// jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &jt); -// if (err != JVMTI_ERROR_NONE) { -// return err; -// } -// : // do stuff with 'jt'... -// // A JavaThread * that is included in the ThreadsList that is held by // a ThreadsListHandle is protected as long as the ThreadsListHandle -// remains in scope. The target JavaThread * may have logically exited, -// but that target JavaThread * will not be deleted until it is no +// remains in scope. The target JavaThread* may have logically exited, +// but that target JavaThread* will not be deleted until it is no // longer protected by a ThreadsListHandle. // // SMR Support for the Threads class. @@ -318,7 +307,7 @@ public: inline Iterator begin(); inline Iterator end(); - bool cv_internal_thread_to_JavaThread(jobject jthread, JavaThread ** jt_pp, oop * thread_oop_p); + bool cv_internal_thread_to_JavaThread(jobject jthread, JavaThread** jt_pp, oop* thread_oop_p); bool includes(JavaThread* p) { return list()->includes(p); diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 7251411a167..1ac44b8a2f5 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -1366,10 +1366,24 @@ void Threads::print_on(outputStream* st, bool print_stacks, char buf[32]; st->print_raw_cr(os::local_time_string(buf, sizeof(buf))); - st->print_cr("Full thread dump %s (%s %s):", + st->print_cr("Full thread dump %s (%s %s)", VM_Version::vm_name(), VM_Version::vm_release(), VM_Version::vm_info_string()); + JDK_Version::current().to_string(buf, sizeof(buf)); + const char* runtime_name = JDK_Version::runtime_name() != nullptr ? + JDK_Version::runtime_name() : ""; + const char* runtime_version = JDK_Version::runtime_version() != nullptr ? + JDK_Version::runtime_version() : ""; + const char* vendor_version = JDK_Version::runtime_vendor_version() != nullptr ? + JDK_Version::runtime_vendor_version() : ""; + const char* jdk_debug_level = VM_Version::printable_jdk_debug_level() != nullptr ? + VM_Version::printable_jdk_debug_level() : ""; + + st->print_cr(" JDK version: %s%s%s (%s) (%sbuild %s)", runtime_name, + (*vendor_version != '\0') ? " " : "", vendor_version, + buf, jdk_debug_level, runtime_version); + st->cr(); #if INCLUDE_SERVICES diff --git a/src/hotspot/share/services/classLoadingService.cpp b/src/hotspot/share/services/classLoadingService.cpp index 2d2e12db741..3f4c1bf9b9f 100644 --- a/src/hotspot/share/services/classLoadingService.cpp +++ b/src/hotspot/share/services/classLoadingService.cpp @@ -22,19 +22,19 @@ * */ +#include "logging/log.hpp" +#include "logging/logConfiguration.hpp" #include "memory/allocation.hpp" #include "memory/resourceArea.hpp" -#include "oops/oop.inline.hpp" #include "oops/instanceKlass.hpp" #include "oops/method.hpp" +#include "oops/oop.inline.hpp" #include "runtime/mutexLocker.hpp" #include "services/classLoadingService.hpp" #include "services/memoryService.hpp" +#include "utilities/defaultStream.hpp" #include "utilities/dtrace.hpp" #include "utilities/macros.hpp" -#include "utilities/defaultStream.hpp" -#include "logging/log.hpp" -#include "logging/logConfiguration.hpp" #ifdef DTRACE_ENABLED diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 8b6fa392c95..5bef650891d 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -22,8 +22,8 @@ * */ -#include "cds/cdsConfig.hpp" #include "cds/cds_globals.hpp" +#include "cds/cdsConfig.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/classLoaderHierarchyDCmd.hpp" #include "classfile/classLoaderStats.hpp" @@ -32,13 +32,13 @@ #include "classfile/vmClasses.hpp" #include "code/codeCache.hpp" #include "compiler/compilationMemoryStatistic.hpp" -#include "compiler/compiler_globals.hpp" #include "compiler/compileBroker.hpp" +#include "compiler/compiler_globals.hpp" #include "compiler/directivesParser.hpp" #include "gc/shared/gcVMOperations.hpp" #include "jvm.h" -#include "memory/metaspaceUtils.hpp" #include "memory/metaspace/metaspaceDCmd.hpp" +#include "memory/metaspaceUtils.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "nmt/memMapPrinter.hpp" @@ -55,8 +55,8 @@ #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.hpp" #include "runtime/os.hpp" -#include "runtime/vmOperations.hpp" #include "runtime/vm_version.hpp" +#include "runtime/vmOperations.hpp" #include "services/diagnosticArgument.hpp" #include "services/diagnosticCommand.hpp" #include "services/diagnosticFramework.hpp" @@ -69,9 +69,10 @@ #include "utilities/macros.hpp" #include "utilities/parseInteger.hpp" #ifdef LINUX -#include "os_posix.hpp" #include "mallocInfoDcmd.hpp" +#include "os_posix.hpp" #include "trimCHeapDCmd.hpp" + #include #endif diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index 18d06f45a6a..001d89a5aef 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -29,6 +29,7 @@ #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" +#include "oops/method.hpp" #include "runtime/arguments.hpp" #include "runtime/os.hpp" #include "runtime/vmThread.hpp" @@ -37,7 +38,6 @@ #include "services/diagnosticFramework.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#include "oops/method.hpp" class HelpDCmd : public DCmdWithParser { protected: diff --git a/src/hotspot/share/services/diagnosticFramework.hpp b/src/hotspot/share/services/diagnosticFramework.hpp index bd45780e1e5..71e7f1370f9 100644 --- a/src/hotspot/share/services/diagnosticFramework.hpp +++ b/src/hotspot/share/services/diagnosticFramework.hpp @@ -31,6 +31,7 @@ #include "runtime/os.hpp" #include "runtime/vmThread.hpp" #include "utilities/ostream.hpp" + #include diff --git a/src/hotspot/share/services/finalizerService.cpp b/src/hotspot/share/services/finalizerService.cpp index 1eb460eb110..969d02ad37d 100644 --- a/src/hotspot/share/services/finalizerService.cpp +++ b/src/hotspot/share/services/finalizerService.cpp @@ -27,8 +27,8 @@ #include "classfile/classLoaderDataGraph.inline.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" -#include "memory/resourceArea.hpp" #include "logging/log.hpp" +#include "memory/resourceArea.hpp" #include "oops/instanceKlass.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/fieldDescriptor.inline.hpp" diff --git a/src/hotspot/share/services/gcNotifier.cpp b/src/hotspot/share/services/gcNotifier.cpp index 11789f7441c..a13836b0ccb 100644 --- a/src/hotspot/share/services/gcNotifier.cpp +++ b/src/hotspot/share/services/gcNotifier.cpp @@ -25,6 +25,9 @@ #include "classfile/javaClasses.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" +#include "memory/oopFactory.hpp" +#include "memory/resourceArea.hpp" +#include "memoryManager.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" @@ -35,9 +38,6 @@ #include "services/gcNotifier.hpp" #include "services/management.hpp" #include "services/memoryService.hpp" -#include "memoryManager.hpp" -#include "memory/oopFactory.hpp" -#include "memory/resourceArea.hpp" GCNotificationRequest *GCNotifier::first_request = nullptr; GCNotificationRequest *GCNotifier::last_request = nullptr; diff --git a/src/hotspot/share/services/gcNotifier.hpp b/src/hotspot/share/services/gcNotifier.hpp index c0d53dea725..f74d81b5b6e 100644 --- a/src/hotspot/share/services/gcNotifier.hpp +++ b/src/hotspot/share/services/gcNotifier.hpp @@ -26,9 +26,9 @@ #define SHARE_SERVICES_GCNOTIFIER_HPP #include "memory/allStatic.hpp" +#include "services/memoryManager.hpp" #include "services/memoryPool.hpp" #include "services/memoryService.hpp" -#include "services/memoryManager.hpp" class GCNotificationRequest : public CHeapObj { friend class GCNotifier; diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index 731a26f7cf1..2e299083ac3 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -53,10 +53,10 @@ #include "runtime/os.hpp" #include "runtime/threads.hpp" #include "runtime/threadSMR.hpp" +#include "runtime/timerTrace.hpp" #include "runtime/vframe.hpp" #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" -#include "runtime/timerTrace.hpp" #include "services/heapDumper.hpp" #include "services/heapDumperCompression.hpp" #include "services/threadService.hpp" diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index 070d4daa3fb..5b0fc96771a 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -57,16 +57,16 @@ #include "services/diagnosticCommand.hpp" #include "services/diagnosticFramework.hpp" #include "services/finalizerService.hpp" -#include "services/writeableFlags.hpp" +#include "services/gcNotifier.hpp" #include "services/heapDumper.hpp" #include "services/lowMemoryDetector.hpp" -#include "services/gcNotifier.hpp" #include "services/management.hpp" #include "services/memoryManager.hpp" #include "services/memoryPool.hpp" #include "services/memoryService.hpp" #include "services/runtimeService.hpp" #include "services/threadService.hpp" +#include "services/writeableFlags.hpp" #include "utilities/debug.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/macros.hpp" diff --git a/src/hotspot/share/services/memoryManager.cpp b/src/hotspot/share/services/memoryManager.cpp index f21550a122f..36ea234c1f3 100644 --- a/src/hotspot/share/services/memoryManager.cpp +++ b/src/hotspot/share/services/memoryManager.cpp @@ -32,12 +32,12 @@ #include "runtime/handles.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/mutexLocker.hpp" +#include "services/gcNotifier.hpp" #include "services/lowMemoryDetector.hpp" #include "services/management.hpp" #include "services/memoryManager.hpp" #include "services/memoryPool.hpp" #include "services/memoryService.hpp" -#include "services/gcNotifier.hpp" #include "utilities/dtrace.hpp" MemoryManager::MemoryManager(const char* name) diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index f30b0c170a6..ad5c19d41c0 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -53,8 +53,8 @@ #include "runtime/threads.hpp" #include "runtime/threadSMR.inline.hpp" #include "runtime/vframe.inline.hpp" -#include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" +#include "runtime/vmThread.hpp" #include "services/threadService.hpp" // TODO: we need to define a naming convention for perf counters @@ -1278,6 +1278,7 @@ public: if (is_virtual) { // mounted vthread, use carrier thread state oop carrier_thread = java_lang_VirtualThread::carrier_thread(_thread_h()); + assert(carrier_thread != nullptr, "should only get here for a mounted vthread"); _thread_status = java_lang_Thread::get_thread_status(carrier_thread); } else { _thread_status = java_lang_Thread::get_thread_status(_thread_h()); @@ -1477,7 +1478,17 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { carrier_thread = Handle(THREAD, java_lang_VirtualThread::carrier_thread(thread_h())); if (carrier_thread != nullptr) { + // Note: The java_thread associated with this carrier_thread may not be + // protected by the ThreadsListHandle above. There could have been an + // unmount and remount after the ThreadsListHandle above was created + // and before the JvmtiVTMSTransitionDisabler was created. However, as + // we have disabled transitions, if we are mounted on it, then it cannot + // terminate and so is safe to handshake with. java_thread = java_lang_Thread::thread(carrier_thread()); + } else { + // We may have previously found a carrier but the virtual thread has unmounted + // after that, so clear that previous reference. + java_thread = nullptr; } } else { java_thread = java_lang_Thread::thread(thread_h()); @@ -1554,4 +1565,3 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { } #endif // INCLUDE_JVMTI - diff --git a/src/hotspot/share/utilities/align.hpp b/src/hotspot/share/utilities/align.hpp index 69dd1a3052b..ef57d6f5edd 100644 --- a/src/hotspot/share/utilities/align.hpp +++ b/src/hotspot/share/utilities/align.hpp @@ -30,6 +30,7 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" + #include // Compute mask to use for aligning to or testing alignment. diff --git a/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp b/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp index f9808ba6c9c..ce4a7298ca5 100644 --- a/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp @@ -28,8 +28,8 @@ // No concurrentHashTableTasks.hpp #include "runtime/atomic.hpp" -#include "utilities/globalDefinitions.hpp" #include "utilities/concurrentHashTable.inline.hpp" +#include "utilities/globalDefinitions.hpp" // This inline file contains BulkDeleteTask and GrowTasks which are both bucket // operations, which they are serialized with each other. diff --git a/src/hotspot/share/utilities/copy.cpp b/src/hotspot/share/utilities/copy.cpp index 8158696532a..4dc1ef7b5bf 100644 --- a/src/hotspot/share/utilities/copy.cpp +++ b/src/hotspot/share/utilities/copy.cpp @@ -22,7 +22,6 @@ * */ -#include "utilities/copy.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" #include "utilities/byteswap.hpp" diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 2502f850b66..2865dbc5991 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -63,8 +63,8 @@ #include "utilities/unsigned5.hpp" #include "utilities/vmError.hpp" -#include #include +#include // These functions needs to be exported on Windows only #define DEBUGEXPORT WINDOWS_ONLY(JNIEXPORT) diff --git a/src/hotspot/share/utilities/elfFile.cpp b/src/hotspot/share/utilities/elfFile.cpp index f2ddbe164f6..e3cbb5ac18e 100644 --- a/src/hotspot/share/utilities/elfFile.cpp +++ b/src/hotspot/share/utilities/elfFile.cpp @@ -36,10 +36,10 @@ #include "utilities/elfSymbolTable.hpp" #include "utilities/ostream.hpp" -#include -#include #include #include +#include +#include const char* ElfFile::USR_LIB_DEBUG_DIRECTORY = "/usr/lib/debug"; diff --git a/src/hotspot/share/utilities/elfFile.hpp b/src/hotspot/share/utilities/elfFile.hpp index 0caafc7645e..979fac5edfc 100644 --- a/src/hotspot/share/utilities/elfFile.hpp +++ b/src/hotspot/share/utilities/elfFile.hpp @@ -67,8 +67,8 @@ typedef Elf32_Sym Elf_Sym; #endif #endif -#include "jvm_md.h" #include "globalDefinitions.hpp" +#include "jvm_md.h" #include "memory/allocation.hpp" #include "utilities/checkedCast.hpp" #include "utilities/decoder.hpp" diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index fd1a2930034..2ee2132ebbd 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -32,13 +32,13 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" -#include "runtime/atomic.hpp" #include "utilities/events.hpp" #include "utilities/exceptions.hpp" #include "utilities/utf8.hpp" diff --git a/src/hotspot/share/utilities/fakeRttiSupport.hpp b/src/hotspot/share/utilities/fakeRttiSupport.hpp index 584da814a24..2208900f7ed 100644 --- a/src/hotspot/share/utilities/fakeRttiSupport.hpp +++ b/src/hotspot/share/utilities/fakeRttiSupport.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_UTILITIES_FAKERTTISUPPORT_HPP #define SHARE_UTILITIES_FAKERTTISUPPORT_HPP -#include "utilities/globalDefinitions.hpp" #include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" // Provides support for checked downcasts in a hierarchy of classes. // The base class provides a member of this type, specialized on that diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index f6d162a81e4..181786cd238 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -25,14 +25,12 @@ #ifndef SHARE_UTILITIES_GLOBALDEFINITIONS_HPP #define SHARE_UTILITIES_GLOBALDEFINITIONS_HPP +#include "classfile_constants.h" #include "utilities/compilerWarnings.hpp" #include "utilities/debug.hpp" #include "utilities/forbiddenFunctions.hpp" #include "utilities/macros.hpp" -// Get constants like JVM_T_CHAR and JVM_SIGNATURE_INT, before pulling in . -#include "classfile_constants.h" - #include COMPILER_HEADER(utilities/globalDefinitions) #include @@ -345,7 +343,7 @@ inline T byte_size_in_proper_unit(T s) { } #define PROPERFMT "%zu%s" -#define PROPERFMTARGS(s) byte_size_in_proper_unit(s), proper_unit_for_byte_size(s) +#define PROPERFMTARGS(s) byte_size_in_proper_unit(s), proper_unit_for_byte_size(s) // Printing a range, with start and bytes given #define RANGEFMT "[" PTR_FORMAT " - " PTR_FORMAT "), (%zu bytes)" @@ -1063,6 +1061,15 @@ const intptr_t OneBit = 1; // only right_most bit set in a word #define nth_bit(n) (((n) >= BitsPerWord) ? 0 : (OneBit << (n))) #define right_n_bits(n) (nth_bit(n) - 1) +// same as nth_bit(n), but allows handing in a type as template parameter. Allows +// us to use nth_bit with 64-bit types on 32-bit platforms +template inline T nth_bit_typed(int n) { + return ((T)1) << n; +} +template inline T right_n_bits_typed(int n) { + return nth_bit_typed(n) - 1; +} + // bit-operations using a mask m inline void set_bits (intptr_t& x, intptr_t m) { x |= m; } inline void clear_bits (intptr_t& x, intptr_t m) { x &= ~m; } diff --git a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp index 94f37e7954e..41b7868f8c7 100644 --- a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp @@ -33,8 +33,13 @@ #include #include +#include +#include +#include #include -#include +#include +#include +#include #include #include #include @@ -48,16 +53,9 @@ #undef malloc extern void *malloc(size_t) asm("vec_malloc"); #endif -#include - -#include +#include #include -#include -#include -#include - -#include -#include +#include #if defined(LINUX) || defined(_ALLBSD_SOURCE) || defined(_AIX) #include diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp index 4b4a0f4d838..536b79165ae 100644 --- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp @@ -31,23 +31,24 @@ // globally used constants & types, class (forward) // declarations and a few frequently used utility functions. -# include -# include -# include -# include -# include -# include // for offsetof -# include -# include // for stream.cpp -# include // for _isnan -# include // for va_list -# include -# include -# include -# include // Need this on windows to get the math constants (e.g., M_PI). #define _USE_MATH_DEFINES + +# include +# include +# include // for _isnan +# include +# include // for stream.cpp +# include # include +# include +# include // for offsetof +# include +# include +# include +# include +# include +# include // Only 64-bit Windows is supported #ifndef _LP64 diff --git a/src/hotspot/share/utilities/intpow.hpp b/src/hotspot/share/utilities/intpow.hpp index 0b441a55c4c..1e767e23ab8 100644 --- a/src/hotspot/share/utilities/intpow.hpp +++ b/src/hotspot/share/utilities/intpow.hpp @@ -26,6 +26,7 @@ #define SHARE_UTILITIES_INTPOW_HPP #include "metaprogramming/enableIf.hpp" + #include #include diff --git a/src/hotspot/share/utilities/json.cpp b/src/hotspot/share/utilities/json.cpp index fa8829491d9..5dba5a32d0b 100644 --- a/src/hotspot/share/utilities/json.cpp +++ b/src/hotspot/share/utilities/json.cpp @@ -33,6 +33,7 @@ #include "utilities/json.hpp" #include "utilities/ostream.hpp" + #include static const char* strchrnul_(const char *s, int c) { diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index f3c469f1486..532e540af9a 100644 --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -1077,11 +1077,11 @@ bufferedStream::~bufferedStream() { #ifndef PRODUCT #if defined(LINUX) || defined(AIX) || defined(_ALLBSD_SOURCE) -#include -#include -#include -#include #include +#include +#include +#include +#include #elif defined(_WINDOWS) #include #endif diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp index 79e95734a53..e971ac4d125 100644 --- a/src/hotspot/share/utilities/ostream.hpp +++ b/src/hotspot/share/utilities/ostream.hpp @@ -165,6 +165,20 @@ class outputStream : public CHeapObjBase { void dec_cr() { dec(); cr(); } void inc_cr() { inc(); cr(); } + + // Append strings returned by gen, separating each with separator. + // Stops when gen returns null. + template + void join(Generator gen, const char* separator) { + bool first = true; + const char* str = gen(); + while (str != nullptr) { + const char* sep = first ? "" : separator; + print("%s%s", sep, str); + first = false; + str = gen(); + } + } }; // standard output @@ -182,7 +196,7 @@ class StreamIndentor { NONCOPYABLE(StreamIndentor); public: - StreamIndentor(outputStream* os, int indentation) : + StreamIndentor(outputStream* os, int indentation = 2) : _stream(os), _indentation(indentation), _old_autoindent(_stream->set_autoindent(true)) { diff --git a/src/hotspot/share/utilities/packedTable.cpp b/src/hotspot/share/utilities/packedTable.cpp index 134cd2cd751..6deb788b313 100644 --- a/src/hotspot/share/utilities/packedTable.cpp +++ b/src/hotspot/share/utilities/packedTable.cpp @@ -21,12 +21,12 @@ * questions. * */ -#include - #include "utilities/align.hpp" #include "utilities/count_leading_zeros.hpp" #include "utilities/packedTable.hpp" +#include + // The thresholds are inclusive, and in practice the limits are rounded // to the nearest power-of-two - 1. // Based on the max_key and max_value we figure out the number of bits required to store diff --git a/src/hotspot/share/utilities/powerOfTwo.hpp b/src/hotspot/share/utilities/powerOfTwo.hpp index b40c3fb7404..8159fb3af2a 100644 --- a/src/hotspot/share/utilities/powerOfTwo.hpp +++ b/src/hotspot/share/utilities/powerOfTwo.hpp @@ -30,6 +30,7 @@ #include "utilities/count_trailing_zeros.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" + #include #include diff --git a/src/hotspot/share/utilities/rbTree.hpp b/src/hotspot/share/utilities/rbTree.hpp index 04c46c85d2c..405fa3e9ae9 100644 --- a/src/hotspot/share/utilities/rbTree.hpp +++ b/src/hotspot/share/utilities/rbTree.hpp @@ -29,6 +29,7 @@ #include "nmt/memTag.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" + #include // An intrusive red-black tree is constructed with two template parameters: @@ -166,6 +167,7 @@ public: template class AbstractRBTree { friend class RBTreeTest; + friend class NMTVMATreeTest; typedef AbstractRBTree TreeType; public: @@ -403,13 +405,21 @@ public: } // Visit all RBNodes in ascending order, calling f on each node. + // If f returns `true` the iteration continues, otherwise it is stopped at the current node. template void visit_in_order(F f) const; + template + void visit_in_order(F f); + // Visit all RBNodes in ascending order whose keys are in range [from, to], calling f on each node. + // If f returns `true` the iteration continues, otherwise it is stopped at the current node. template void visit_range_in_order(const K& from, const K& to, F f) const; + template + void visit_range_in_order(const K &from, const K &to, F f); + // Verifies that the tree is correct and holds rb-properties // If not using a key comparator (when using IntrusiveRBTree for example), // A second `cmp` must exist in COMPARATOR (see top of file). @@ -456,6 +466,12 @@ public: free_node(old_node); } + RBNode* allocate_node(const K& key) { + void* node_place = _allocator.allocate(sizeof(RBNode)); + assert(node_place != nullptr, "rb-tree allocator must exit on failure"); + return new (node_place) RBNode(key); + } + RBNode* allocate_node(const K& key, const V& val) { void* node_place = _allocator.allocate(sizeof(RBNode)); assert(node_place != nullptr, "rb-tree allocator must exit on failure"); diff --git a/src/hotspot/share/utilities/rbTree.inline.hpp b/src/hotspot/share/utilities/rbTree.inline.hpp index c28c2784f6d..365786f7fc1 100644 --- a/src/hotspot/share/utilities/rbTree.inline.hpp +++ b/src/hotspot/share/utilities/rbTree.inline.hpp @@ -25,12 +25,13 @@ #ifndef SHARE_UTILITIES_RBTREE_INLINE_HPP #define SHARE_UTILITIES_RBTREE_INLINE_HPP +#include "utilities/rbTree.hpp" + #include "metaprogramming/enableIf.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" -#include "utilities/rbTree.hpp" inline void IntrusiveRBNode::replace_child(IntrusiveRBNode* old_child, IntrusiveRBNode* new_child) { if (_left == old_child) { @@ -84,7 +85,7 @@ inline IntrusiveRBNode* IntrusiveRBNode::rotate_right() { inline const IntrusiveRBNode* IntrusiveRBNode::prev() const { const IntrusiveRBNode* node = this; - if (_left != nullptr) { // right subtree exists + if (_left != nullptr) { // left subtree exists node = _left; while (node->_right != nullptr) { node = node->_right; @@ -598,7 +599,21 @@ template inline void AbstractRBTree::visit_in_order(F f) const { const NodeType* node = leftmost(); while (node != nullptr) { - f(node); + if (!f(node)) { + return; + } + node = node->next(); + } +} + +template +template +inline void AbstractRBTree::visit_in_order(F f) { + NodeType* node = leftmost(); + while (node != nullptr) { + if (!f(node)) { + return; + } node = node->next(); } } @@ -617,7 +632,30 @@ inline void AbstractRBTree::visit_range_in_order(const const NodeType* end = next(cursor_end).node(); while (start != end) { - f(start); + if (!f(start)) { + return; + } + start = start->next(); + } +} + +template +template +inline void AbstractRBTree::visit_range_in_order(const K& from, const K& to, F f) { + assert_key_leq(from, to); + if (_root == nullptr) { + return; + } + + Cursor cursor_start = cursor(from); + Cursor cursor_end = cursor(to); + NodeType* start = cursor_start.found() ? cursor_start.node() : next(cursor_start).node(); + NodeType* end = next(cursor_end).node(); + + while (start != end) { + if (!f(start)) { + return; + } start = start->next(); } } diff --git a/src/hotspot/share/utilities/singleWriterSynchronizer.cpp b/src/hotspot/share/utilities/singleWriterSynchronizer.cpp index c63cac4faf7..58f0e245780 100644 --- a/src/hotspot/share/utilities/singleWriterSynchronizer.cpp +++ b/src/hotspot/share/utilities/singleWriterSynchronizer.cpp @@ -26,8 +26,8 @@ #include "runtime/orderAccess.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" -#include "utilities/singleWriterSynchronizer.hpp" #include "utilities/macros.hpp" +#include "utilities/singleWriterSynchronizer.hpp" SingleWriterSynchronizer::SingleWriterSynchronizer() : _enter(0), diff --git a/src/hotspot/share/utilities/utf8.cpp b/src/hotspot/share/utilities/utf8.cpp index 698ee557202..4b8b3760065 100644 --- a/src/hotspot/share/utilities/utf8.cpp +++ b/src/hotspot/share/utilities/utf8.cpp @@ -23,11 +23,11 @@ */ #include "memory/allocation.hpp" +#include "runtime/os.hpp" #include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/utf8.hpp" -#include "runtime/os.hpp" // Assume the utf8 string is in legal form and has been // checked in the class file parser/format checker. diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 448df20c44e..bd882a7ef3c 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -57,9 +57,9 @@ #include "runtime/threads.hpp" #include "runtime/threadSMR.hpp" #include "runtime/trimNativeHeap.hpp" +#include "runtime/vm_version.hpp" #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" -#include "runtime/vm_version.hpp" #include "sanitizers/ub.hpp" #include "utilities/debug.hpp" #include "utilities/decoder.hpp" @@ -104,17 +104,20 @@ int VMError::_lineno; size_t VMError::_size; const size_t VMError::_reattempt_required_stack_headroom = 64 * K; const intptr_t VMError::segfault_address = pd_segfault_address; +volatile intptr_t VMError::_handshake_timed_out_thread = p2i(nullptr); +volatile intptr_t VMError::_safepoint_timed_out_thread = p2i(nullptr); // List of environment variables that should be reported in error log file. static const char* env_list[] = { // All platforms "JAVA_HOME", "JAVA_TOOL_OPTIONS", "_JAVA_OPTIONS", "CLASSPATH", - "PATH", "USERNAME", + "JDK_AOT_VM_OPTIONS", + "JAVA_OPTS", "PATH", "USERNAME", "XDG_CACHE_HOME", "XDG_CONFIG_HOME", "FC_LANG", "FONTCONFIG_USE_MMAP", // Env variables that are defined on Linux/BSD - "LD_LIBRARY_PATH", "LD_PRELOAD", "SHELL", "DISPLAY", + "LD_LIBRARY_PATH", "LD_PRELOAD", "SHELL", "DISPLAY", "WAYLAND_DISPLAY", "HOSTTYPE", "OSTYPE", "ARCH", "MACHTYPE", "LANG", "LC_ALL", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "TERM", "TMPDIR", "TZ", @@ -818,7 +821,13 @@ void VMError::report(outputStream* st, bool _verbose) { st->print(" (0x%x)", _id); // signal number st->print(" at pc=" PTR_FORMAT, p2i(_pc)); if (_siginfo != nullptr && os::signal_sent_by_kill(_siginfo)) { - st->print(" (sent by kill)"); + if (_handshake_timed_out_thread == p2i(_thread)) { + st->print(" (sent by handshake timeout handler"); + } else if (_safepoint_timed_out_thread == p2i(_thread)) { + st->print(" (sent by safepoint timeout handler"); + } else { + st->print(" (sent by kill)"); + } } } else { if (should_report_bug(_id)) { @@ -1329,6 +1338,14 @@ void VMError::report(outputStream* st, bool _verbose) { # undef END } +void VMError::set_handshake_timed_out_thread(intptr_t thread_addr) { + _handshake_timed_out_thread = thread_addr; +} + +void VMError::set_safepoint_timed_out_thread(intptr_t thread_addr) { + _safepoint_timed_out_thread = thread_addr; +} + // Report for the vm_info_cmd. This prints out the information above omitting // crash and thread specific information. If output is added above, it should be added // here also, if it is safe to call during a running process. @@ -1381,6 +1398,7 @@ void VMError::print_vm_info(outputStream* st) { CompressedOops::print_mode(st); st->cr(); } +#endif // STEP("printing compressed class ptrs mode") if (UseCompressedClassPointers) { @@ -1389,7 +1407,6 @@ void VMError::print_vm_info(outputStream* st) { CompressedKlassPointers::print_mode(st); st->cr(); } -#endif // Take heap lock over heap, GC and metaspace printing so that information // is consistent. @@ -1856,7 +1873,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt log.set_fd(-1); } - JFR_ONLY(Jfr::on_vm_shutdown(true);) + JFR_ONLY(Jfr::on_vm_shutdown(static_cast(_id) == OOM_JAVA_HEAP_FATAL, true);) if (PrintNMTStatistics) { fdStream fds(fd_out); diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index 0109b9bf0bd..96e37acff57 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -142,6 +142,10 @@ class VMError : public AllStatic { static jlong get_step_start_time(); static void clear_step_start_time(); + // Handshake/safepoint timed out threads + static volatile intptr_t _handshake_timed_out_thread; + static volatile intptr_t _safepoint_timed_out_thread; + WINDOWS_ONLY([[noreturn]] static void raise_fail_fast(const void* exrecord, const void* context);) public: @@ -218,6 +222,9 @@ public: static int prepare_log_file(const char* pattern, const char* default_pattern, bool overwrite_existing, char* buf, size_t buflen); static bool was_assert_poison_crash(const void* sigInfo); + + static void set_handshake_timed_out_thread(intptr_t thread_addr); + static void set_safepoint_timed_out_thread(intptr_t thread_addr); }; class VMErrorCallback { diff --git a/src/hotspot/share/utilities/waitBarrier_generic.cpp b/src/hotspot/share/utilities/waitBarrier_generic.cpp index e09a2d5282b..efaa49b24a9 100644 --- a/src/hotspot/share/utilities/waitBarrier_generic.cpp +++ b/src/hotspot/share/utilities/waitBarrier_generic.cpp @@ -26,8 +26,8 @@ #include "runtime/atomic.hpp" #include "runtime/orderAccess.hpp" #include "runtime/os.hpp" -#include "utilities/waitBarrier_generic.hpp" #include "utilities/spinYield.hpp" +#include "utilities/waitBarrier_generic.hpp" // Implements the striped semaphore wait barrier. // diff --git a/src/hotspot/share/utilities/xmlstream.cpp b/src/hotspot/share/utilities/xmlstream.cpp index 5d57584a8ab..6dacab1dc25 100644 --- a/src/hotspot/share/utilities/xmlstream.cpp +++ b/src/hotspot/share/utilities/xmlstream.cpp @@ -26,8 +26,8 @@ #include "memory/allocation.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" -#include "oops/methodData.hpp" #include "oops/method.inline.hpp" +#include "oops/methodData.hpp" #include "oops/oop.inline.hpp" #include "runtime/deoptimization.hpp" #include "runtime/handles.inline.hpp" diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index a7b856fa449..dfdf515f424 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -47,7 +47,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; import java.lang.reflect.RecordComponent; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -79,6 +78,8 @@ import jdk.internal.reflect.CallerSensitiveAdapter; import jdk.internal.reflect.ConstantPool; import jdk.internal.reflect.Reflection; import jdk.internal.reflect.ReflectionFactory; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -211,6 +212,7 @@ import sun.reflect.annotation.*; * @see java.lang.ClassLoader#defineClass(byte[], int, int) * @since 1.0 */ +@AOTSafeClassInitializer public final class Class implements java.io.Serializable, GenericDeclaration, Type, @@ -226,7 +228,9 @@ public final class Class implements java.io.Serializable, runtimeSetup(); } - // Called from JVM when loading an AOT cache + /// No significant static final fields; [#resetArchivedStates()] handles + /// prevents storing [#reflectionFactory] into AOT image. + @AOTRuntimeSetup private static void runtimeSetup() { registerNatives(); } @@ -236,7 +240,7 @@ public final class Class implements java.io.Serializable, * This constructor is not used and prevents the default constructor being * generated. */ - private Class(ClassLoader loader, Class arrayComponentType, char mods, ProtectionDomain pd, boolean isPrim) { + private Class(ClassLoader loader, Class arrayComponentType, char mods, ProtectionDomain pd, boolean isPrim, char flags) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. // The following assignments are done directly by the VM without calling this constructor. @@ -245,6 +249,7 @@ public final class Class implements java.io.Serializable, modifiers = mods; protectionDomain = pd; primitive = isPrim; + classFileAccessFlags = flags; } /** @@ -1008,6 +1013,7 @@ public final class Class implements java.io.Serializable, private transient Object classData; // Set by VM private transient Object[] signers; // Read by VM, mutable private final transient char modifiers; // Set by the VM + private final transient char classFileAccessFlags; // Set by the VM private final transient boolean primitive; // Set by the VM if the Class is a primitive type. // package-private @@ -1379,13 +1385,13 @@ public final class Class implements java.io.Serializable, // Location.CLASS allows SUPER and AccessFlag.MODULE which // INNER_CLASS forbids. INNER_CLASS allows PRIVATE, PROTECTED, // and STATIC, which are not allowed on Location.CLASS. - // Use getClassAccessFlagsRaw to expose SUPER status. + // Use getClassFileAccessFlags to expose SUPER status. var location = (isMemberClass() || isLocalClass() || isAnonymousClass() || isArray()) ? AccessFlag.Location.INNER_CLASS : AccessFlag.Location.CLASS; return getReflectionFactory().parseAccessFlags((location == AccessFlag.Location.CLASS) ? - getClassAccessFlagsRaw() : getModifiers(), location, this); + getClassFileAccessFlags() : getModifiers(), location, this); } /** @@ -4130,17 +4136,16 @@ public final class Class implements java.io.Serializable, private native int getClassFileVersion0(); - /* - * Return the access flags as they were in the class's bytecode, including - * the original setting of ACC_SUPER. - * - * If the class is an array type then the access flags of the element type is - * returned. If the class is a primitive then ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC. - */ - private int getClassAccessFlagsRaw() { - Class c = isArray() ? elementType() : this; - return c.getClassAccessFlagsRaw0(); - } - - private native int getClassAccessFlagsRaw0(); + /** + * Return the access flags as they were in the class's bytecode, including + * the original setting of ACC_SUPER. + * + * If this {@code Class} object represents a primitive type or + * void, the flags are {@code PUBLIC}, {@code ABSTRACT}, and + * {@code FINAL}. + * If this {@code Class} object represents an array type, return 0. + */ + int getClassFileAccessFlags() { + return classFileAccessFlags; + } } diff --git a/src/java.base/share/classes/java/lang/ClassValue.java b/src/java.base/share/classes/java/lang/ClassValue.java index dfbdf3c5bf8..2a133324fb5 100644 --- a/src/java.base/share/classes/java/lang/ClassValue.java +++ b/src/java.base/share/classes/java/lang/ClassValue.java @@ -476,6 +476,9 @@ public abstract class ClassValue { if (updated != entry) { put(classValue.identity, updated); } + // Add to the cache, to enable the fast path, next time. + checkCacheLoad(); + addToCache(classValue, updated); } return item; } diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 9108ba7d944..ef5d1214b11 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -2672,6 +2672,7 @@ public final class Math { * @return The hyperbolic sine of {@code x}. * @since 1.5 */ + @IntrinsicCandidate public static double sinh(double x) { return StrictMath.sinh(x); } diff --git a/src/java.base/share/classes/java/lang/Object.java b/src/java.base/share/classes/java/lang/Object.java index aa9c8f31237..11dcab1b005 100644 --- a/src/java.base/share/classes/java/lang/Object.java +++ b/src/java.base/share/classes/java/lang/Object.java @@ -25,6 +25,7 @@ package java.lang; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; /** @@ -35,6 +36,7 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; * @see java.lang.Class * @since 1.0 */ +@AOTSafeClassInitializer // for hierarchy checks public class Object { /** diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 722c818017d..08b4072fc35 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -40,6 +41,15 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; import static java.lang.String.UTF16; +/// UTF16 String operations. +/// +/// UTF16 byte arrays have the identical layout as char arrays. They share the +/// same base offset and scale, and for each two-byte unit interpreted as a char, +/// it has the same endianness as a char, which is the platform endianness. +/// This is ensured in the static initializer of StringUTF16. +/// +/// All indices and sizes for byte arrays carrying UTF16 data are in number of +/// chars instead of number of bytes. final class StringUTF16 { // Return a new byte array for a UTF16-coded string for len chars @@ -1312,12 +1322,6 @@ final class StringUTF16 { return StreamSupport.stream(LinesSpliterator.spliterator(value), false); } - private static void putChars(byte[] val, int index, char[] str, int off, int end) { - while (off < end) { - putChar(val, index++, str[off++]); - } - } - public static String newString(byte[] val, int index, int len) { if (len == 0) { return ""; @@ -1486,7 +1490,13 @@ final class StringUTF16 { public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) { checkBoundsBeginEnd(index, index + end - off, val); - putChars(val, index, ca, off, end); + String.checkBoundsBeginEnd(off, end, ca.length); + Unsafe.getUnsafe().copyMemory( + ca, + Unsafe.ARRAY_CHAR_BASE_OFFSET + ((long) off << 1), + val, + Unsafe.ARRAY_BYTE_BASE_OFFSET + ((long) index << 1), + (long) (end - off) << 1); } public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) { @@ -1635,6 +1645,9 @@ final class StringUTF16 { private static final int HI_BYTE_SHIFT; private static final int LO_BYTE_SHIFT; static { + // Assumptions for StringUTF16 operations. Present in `LibraryCallKit::inline_string_char_access` too. + assert Unsafe.ARRAY_CHAR_BASE_OFFSET == Unsafe.ARRAY_BYTE_BASE_OFFSET : "sanity: byte[] and char[] bases agree"; + assert Unsafe.ARRAY_CHAR_INDEX_SCALE == Unsafe.ARRAY_BYTE_INDEX_SCALE * 2 : "sanity: byte[] and char[] scales agree"; if (Unsafe.getUnsafe().isBigEndian()) { HI_BYTE_SHIFT = 8; LO_BYTE_SHIFT = 0; diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 0175558d313..1d62d698896 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2022,6 +2022,9 @@ public final class System { public byte[] getRawExecutableTypeAnnotations(Executable executable) { return Class.getExecutableTypeAnnotationBytes(executable); } + public int getClassFileAccessFlags(Class klass) { + return klass.getClassFileAccessFlags(); + } public > E[] getEnumConstantsShared(Class klass) { return klass.getEnumConstantsShared(); diff --git a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java index ceb8e023072..c976342033f 100644 --- a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java +++ b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import jdk.internal.constant.ClassOrInterfaceDescImpl; import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.MethodTypeDescImpl; import jdk.internal.constant.PrimitiveClassDescImpl; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import java.lang.Enum.EnumDesc; import java.lang.invoke.CallSite; @@ -56,6 +57,7 @@ import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; * * @since 12 */ +@AOTSafeClassInitializer public final class ConstantDescs { // No instances private ConstantDescs() { } diff --git a/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java b/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java index f89e3f361a1..b0dce9a1ffc 100644 --- a/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java +++ b/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,8 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; + import static java.lang.constant.ConstantDescs.CD_Class; import static java.lang.constant.ConstantDescs.CD_VarHandle; import static java.lang.constant.ConstantDescs.DEFAULT_NAME; @@ -56,6 +58,7 @@ import static jdk.internal.constant.ConstantUtils.validateMemberName; * * @since 12 */ +@AOTSafeClassInitializer // for PrimitiveClassDescImpl public abstract non-sealed class DynamicConstantDesc implements ConstantDesc { diff --git a/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java index e1152e99b68..e7cbed61522 100644 --- a/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java @@ -25,6 +25,7 @@ package java.lang.invoke; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import java.util.ArrayList; @@ -44,6 +45,7 @@ import static java.lang.invoke.MethodHandleStatics.uncaughtException; * * All bound arguments are encapsulated in dedicated species. */ +@AOTSafeClassInitializer /*non-public*/ abstract non-sealed class BoundMethodHandle extends MethodHandle { @@ -233,6 +235,7 @@ abstract non-sealed class BoundMethodHandle extends MethodHandle { // concrete BMH classes required to close bootstrap loops // + @AOTSafeClassInitializer private // make it private to force users to access the enclosing class first static final class Species_L extends BoundMethodHandle { @@ -312,6 +315,7 @@ abstract non-sealed class BoundMethodHandle extends MethodHandle { // BMH species meta-data // + @AOTSafeClassInitializer /*non-public*/ static final class SpeciesData extends ClassSpecializer.SpeciesData { @@ -402,6 +406,7 @@ abstract non-sealed class BoundMethodHandle extends MethodHandle { Species_L.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies("L"); } + @AOTSafeClassInitializer /*non-public*/ static final class Specializer extends ClassSpecializer { diff --git a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java index e03504a749b..4b6d74b1b2e 100644 --- a/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java +++ b/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,7 @@ import jdk.internal.constant.ClassOrInterfaceDescImpl; import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.MethodTypeDescImpl; import jdk.internal.loader.BootLoader; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.BytecodeName; import sun.invoke.util.Wrapper; @@ -64,11 +65,15 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; * @param key which identifies individual specializations. * @param species data type. */ +@AOTSafeClassInitializer /*non-public*/ abstract class ClassSpecializer.SpeciesData> { private static final ClassDesc CD_LambdaForm = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm;"); private static final ClassDesc CD_BoundMethodHandle = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/BoundMethodHandle;"); + private static final RuntimeVisibleAnnotationsAttribute AOT_SAFE_ANNOTATION = RuntimeVisibleAnnotationsAttribute.of( + Annotation.of(ConstantUtils.referenceClassDesc(AOTSafeClassInitializer.class)) + ); private static final RuntimeVisibleAnnotationsAttribute STABLE_ANNOTATION = RuntimeVisibleAnnotationsAttribute.of( Annotation.of(ConstantUtils.referenceClassDesc(Stable.class)) ); @@ -233,6 +238,7 @@ abstract class ClassSpecializer.SpeciesDat * it would appear that a shorter species could serve as a supertype of a * longer one which extends it. */ + @AOTSafeClassInitializer abstract class SpeciesData { // Bootstrapping requires circular relations Class -> SpeciesData -> Class // Therefore, we need non-final links in the chain. Use @Stable fields. @@ -469,6 +475,7 @@ abstract class ClassSpecializer.SpeciesDat * Code generation support for instances. * Subclasses can modify the behavior. */ + @AOTSafeClassInitializer class Factory { /** * Constructs a factory. @@ -624,6 +631,7 @@ abstract class ClassSpecializer.SpeciesDat clb.withFlags(ACC_FINAL | ACC_SUPER) .withSuperclass(superClassDesc) .with(SourceFileAttribute.of(classDesc.displayName())) + .with(AOT_SAFE_ANNOTATION) // emit static types and BMH_SPECIES fields .withField(sdFieldName, CD_SPECIES_DATA, new Consumer<>() { 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 0c5a0eab6aa..992c011ae98 100644 --- a/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,9 @@ package java.lang.invoke; import java.util.Arrays; + +import jdk.internal.vm.annotation.AOTSafeClassInitializer; + import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.LambdaForm.Kind.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; @@ -36,6 +39,7 @@ import static java.lang.invoke.MethodHandleStatics.*; * The delegating MH itself can hold extra "intentions" beyond the simple behavior. * @author jrose */ +@AOTSafeClassInitializer /*non-public*/ abstract sealed class DelegatingMethodHandle extends MethodHandle permits MethodHandleImpl.AsVarargsCollector, @@ -193,5 +197,6 @@ abstract sealed class DelegatingMethodHandle extends MethodHandle } /* Placeholder class for DelegatingMethodHandles generated ahead of time */ + @AOTSafeClassInitializer final class Holder {} } 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 ad6387e8c9e..b60304dcd7b 100644 --- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -27,6 +27,7 @@ package java.lang.invoke; import jdk.internal.misc.CDS; import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.ValueConversions; @@ -49,6 +50,7 @@ import static java.lang.invoke.MethodTypeForm.*; * to a class member. * @author jrose */ +@AOTSafeClassInitializer sealed class DirectMethodHandle extends MethodHandle { final MemberName member; final boolean crackable; @@ -466,6 +468,7 @@ sealed class DirectMethodHandle extends MethodHandle { } /** This subclass handles constructor references. */ + @AOTSafeClassInitializer static final class Constructor extends DirectMethodHandle { final MemberName initMethod; final Class instanceClass; @@ -937,5 +940,6 @@ sealed class DirectMethodHandle extends MethodHandle { } /* Placeholder class for DirectMethodHandles generated ahead of time */ + @AOTSafeClassInitializer final class Holder {} } diff --git a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java index 561f9122398..6d9f32575c8 100644 --- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java +++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java @@ -25,9 +25,12 @@ package java.lang.invoke; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import sun.invoke.util.Wrapper; +import java.lang.classfile.Annotation; import java.lang.classfile.ClassFile; +import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; import java.lang.classfile.attribute.SourceFileAttribute; import java.lang.constant.ClassDesc; import java.util.ArrayList; @@ -67,6 +70,7 @@ class GenerateJLIClassesHelper { static final String INVOKERS_HOLDER = "java/lang/invoke/Invokers$Holder"; static final String INVOKERS_HOLDER_CLASS_NAME = INVOKERS_HOLDER.replace('/', '.'); static final String BMH_SPECIES_PREFIX = "java.lang.invoke.BoundMethodHandle$Species_"; + static final Annotation AOT_SAFE_ANNOTATION = Annotation.of(AOTSafeClassInitializer.class.describeConstable().orElseThrow()); static class HolderClassBuilder { @@ -562,6 +566,7 @@ class GenerateJLIClassesHelper { return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> { clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER) .withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC) + .with(RuntimeVisibleAnnotationsAttribute.of(AOT_SAFE_ANNOTATION)) .with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1))); for (int i = 0; i < forms.length; i++) { new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb, false); 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 cbe406c5dc0..478ef2b9068 100644 --- a/src/java.base/share/classes/java/lang/invoke/Invokers.java +++ b/src/java.base/share/classes/java/lang/invoke/Invokers.java @@ -26,6 +26,7 @@ package java.lang.invoke; import jdk.internal.invoke.MhUtil; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.DontInline; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Hidden; @@ -43,6 +44,7 @@ import static java.lang.invoke.LambdaForm.Kind.*; * Construction and caching of often-used invokers. * @author jrose */ +@AOTSafeClassInitializer class Invokers { // exact type (sans leading target MH) for the outgoing call private final MethodType targetType; @@ -696,5 +698,6 @@ class Invokers { } /* Placeholder class for Invokers generated ahead of time */ + @AOTSafeClassInitializer final class Holder {} } 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 c1a07d8d0de..9030a063fad 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -27,6 +27,7 @@ package java.lang.invoke; import java.lang.classfile.TypeKind; import jdk.internal.perf.PerfCounter; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.DontInline; import jdk.internal.vm.annotation.Hidden; import jdk.internal.vm.annotation.Stable; @@ -122,6 +123,7 @@ import static java.lang.invoke.MethodHandleStatics.*; *

* @author John Rose, JSR 292 EG */ +@AOTSafeClassInitializer class LambdaForm { final int arity; final int result; @@ -1032,6 +1034,7 @@ class LambdaForm { return false; } + @AOTSafeClassInitializer static class NamedFunction { final MemberName member; private @Stable MethodHandle resolvedHandle; @@ -1728,6 +1731,7 @@ class LambdaForm { } /* Placeholder class for identity and constant forms generated ahead of time */ + @AOTSafeClassInitializer final class Holder {} // The following hack is necessary in order to suppress TRACE_INTERPRETER diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java index d6099861cee..28d09634042 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ import java.util.Arrays; import java.lang.reflect.Array; import java.util.Objects; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; + /** *

Methods to facilitate the creation of simple "function objects" that * implement one or more interfaces by delegation to a provided {@link MethodHandle}, @@ -247,6 +249,7 @@ import java.util.Objects; * * @since 1.8 */ +@AOTSafeClassInitializer public final class LambdaMetafactory { private LambdaMetafactory() {} 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 d3270600707..99d6dbe19a5 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, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ package java.lang.invoke; import jdk.internal.loader.ClassLoaders; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.DontInline; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -442,6 +443,7 @@ mh.invokeExact(System.out, "Hello, world."); * @author John Rose, JSR 292 EG * @since 1.7 */ +@AOTSafeClassInitializer public abstract sealed class MethodHandle implements Constable permits NativeMethodHandle, DirectMethodHandle, DelegatingMethodHandle, BoundMethodHandle { 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 cf8c9065f2d..cb1bf8294d2 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -33,6 +33,8 @@ import jdk.internal.constant.MethodTypeDescImpl; import jdk.internal.foreign.abi.NativeEntryPoint; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Hidden; import jdk.internal.vm.annotation.Stable; @@ -72,6 +74,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; * Trusted implementation code for MethodHandle. * @author jrose */ +@AOTSafeClassInitializer /*non-public*/ abstract class MethodHandleImpl { @@ -158,6 +161,7 @@ abstract class MethodHandleImpl { return newInternalError("should not reach here (unmatched ArrayAccess: " + a + ")"); } + @AOTSafeClassInitializer static final class ArrayAccessor { /// Support for array element and length access static final int GETTER_INDEX = 0, SETTER_INDEX = 1, LENGTH_INDEX = 2, INDEX_LIMIT = 3; @@ -453,6 +457,7 @@ abstract class MethodHandleImpl { return new AsVarargsCollector(target, arrayType); } + @AOTSafeClassInitializer static final class AsVarargsCollector extends DelegatingMethodHandle { private final MethodHandle target; private final Class arrayType; @@ -675,6 +680,7 @@ abstract class MethodHandleImpl { DONT_INLINE_THRESHOLD); } + @AOTSafeClassInitializer private static final class Makers { /** Constructs reinvoker lambda form which block inlining during JIT-compilation for a particular method handle */ static final Function PRODUCE_BLOCK_INLINING_FORM = new Function() { @@ -710,6 +716,7 @@ abstract class MethodHandleImpl { * Behavior in counting and non-counting states is determined by lambda forms produced by * countingFormProducer & nonCountingFormProducer respectively. */ + @AOTSafeClassInitializer static final class CountingWrapper extends DelegatingMethodHandle { private final MethodHandle target; private int count; @@ -1035,6 +1042,7 @@ abstract class MethodHandleImpl { // Put the whole mess into its own nested class. // That way we can lazily load the code and set up the constants. + @AOTSafeClassInitializer private static class BindCaller { private static final ClassDesc CD_Object_array = ConstantUtils.CD_Object_array; @@ -1143,6 +1151,7 @@ abstract class MethodHandleImpl { return BindCaller.CV_makeInjectedInvoker.get(caller).reflectInvoker(); } + @AOTSafeClassInitializer private static final class InjectedInvokerHolder { private final Class invokerClass; // lazily resolved and cached DMH(s) of invoke_V methods @@ -1285,6 +1294,7 @@ abstract class MethodHandleImpl { } /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */ + @AOTSafeClassInitializer static final class WrappedMember extends DelegatingMethodHandle { private final MethodHandle target; private final MemberName member; @@ -1348,6 +1358,7 @@ abstract class MethodHandleImpl { /** Mark arbitrary method handle as intrinsic. * InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */ + @AOTSafeClassInitializer static final class IntrinsicMethodHandle extends DelegatingMethodHandle { private final MethodHandle target; private final Intrinsic intrinsicName; @@ -1528,7 +1539,7 @@ abstract class MethodHandleImpl { runtimeSetup(); } - // Also called from JVM when loading an AOT cache + @AOTRuntimeSetup private static void runtimeSetup() { SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() { @Override @@ -1778,6 +1789,7 @@ abstract class MethodHandleImpl { return lform.editor().noteLoopLocalTypesForm(BOXED_ARGS, localVarTypes); } + @AOTSafeClassInitializer static class LoopClauses { @Stable final MethodHandle[][] clauses; LoopClauses(MethodHandle[][] clauses) { @@ -2105,6 +2117,7 @@ abstract class MethodHandleImpl { } // use a wrapper because we need this array to be @Stable + @AOTSafeClassInitializer static class CasesHolder { @Stable final MethodHandle[] cases; @@ -2134,6 +2147,7 @@ abstract class MethodHandleImpl { return mh; } + @AOTSafeClassInitializer private static class TableSwitchCacheKey { private static final Map CACHE = new ConcurrentHashMap<>(); 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 b0f7a53ea71..92a37587926 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -32,6 +32,7 @@ import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitiveAdapter; import jdk.internal.reflect.Reflection; import jdk.internal.util.ClassFileDumper; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.ValueConversions; @@ -83,6 +84,7 @@ import static java.lang.invoke.MethodType.methodType; * @author John Rose, JSR 292 EG * @since 1.7 */ +@AOTSafeClassInitializer public final class MethodHandles { private MethodHandles() { } // do not instantiate diff --git a/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java index 70b09589207..95c4f80c20b 100644 --- a/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java @@ -25,17 +25,16 @@ package java.lang.invoke; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; -import static java.lang.invoke.LambdaForm.BasicType.*; -import static java.lang.invoke.MethodHandleStatics.*; - /** * A method handle whose behavior is determined only by its LambdaForm. * Access to SimpleMethodHandle should ensure BoundMethodHandle is initialized * first. * @author jrose */ +@AOTSafeClassInitializer final class SimpleMethodHandle extends BoundMethodHandle { private SimpleMethodHandle(MethodType type, LambdaForm form) { 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 ca296e21a26..1c7995c4ec7 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -35,6 +35,7 @@ import jdk.internal.misc.VM; import jdk.internal.util.ClassFileDumper; import jdk.internal.util.ReferenceKey; import jdk.internal.util.ReferencedKeyMap; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; @@ -116,6 +117,7 @@ import static java.lang.invoke.MethodType.methodType; * * @since 9 */ +@AOTSafeClassInitializer public final class StringConcatFactory { private static final int HIGH_ARITY_THRESHOLD; private static final int CACHE_THRESHOLD; diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java b/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java index 1590053a6d0..49408a22cef 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java @@ -24,10 +24,12 @@ */ package java.lang.invoke; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Hidden; // This class is auto-generated by java.lang.invoke.VarHandles$GuardMethodGenerator. Do not edit. +@AOTSafeClassInitializer final class VarHandleGuards { @ForceInline diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java index d57d2d2d2cd..571587ab42f 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java @@ -746,12 +746,14 @@ final class VarHandles { // public static void main(String[] args) { // System.out.println("package java.lang.invoke;"); // System.out.println(); +// System.out.println("import jdk.internal.vm.annotation.AOTSafeClassInitializer;"); // System.out.println("import jdk.internal.vm.annotation.ForceInline;"); // System.out.println("import jdk.internal.vm.annotation.Hidden;"); // System.out.println(); // System.out.println("// This class is auto-generated by " + // GuardMethodGenerator.class.getName() + // ". Do not edit."); +// System.out.println("@AOTSafeClassInitializer"); // System.out.println("final class VarHandleGuards {"); // // System.out.println(); 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 7d48b394f89..43d9f414b3c 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -25,6 +25,8 @@ package java.lang.ref; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.access.JavaLangRefAccess; @@ -41,7 +43,7 @@ import jdk.internal.access.SharedSecrets; * @since 1.2 * @sealedGraph */ - +@AOTSafeClassInitializer public abstract sealed class Reference<@jdk.internal.RequiresIdentity T> permits PhantomReference, SoftReference, WeakReference, FinalReference { @@ -292,7 +294,7 @@ public abstract sealed class Reference<@jdk.internal.RequiresIdentity T> runtimeSetup(); } - // Also called from JVM when loading an AOT cache + @AOTRuntimeSetup private static void runtimeSetup() { // provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { diff --git a/src/java.base/share/classes/java/net/HttpURLConnection.java b/src/java.base/share/classes/java/net/HttpURLConnection.java index d9e9b738617..c0ae12dfdba 100644 --- a/src/java.base/share/classes/java/net/HttpURLConnection.java +++ b/src/java.base/share/classes/java/net/HttpURLConnection.java @@ -556,6 +556,10 @@ public abstract class HttpURLConnection extends URLConnection { * @return the HTTP response message, or {@code null} */ public String getResponseMessage() throws IOException { + // If the responseMessage is already set then return it + if (responseMessage != null) { + return responseMessage; + } getResponseCode(); return responseMessage; } diff --git a/src/java.base/share/classes/java/net/InMemoryCookieStore.java b/src/java.base/share/classes/java/net/InMemoryCookieStore.java index 0ce3dc5b68c..1c72549e37d 100644 --- a/src/java.base/share/classes/java/net/InMemoryCookieStore.java +++ b/src/java.base/share/classes/java/net/InMemoryCookieStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,6 @@ package java.net; -import java.net.URI; -import java.net.CookieStore; -import java.net.HttpCookie; -import java.net.URISyntaxException; import java.util.List; import java.util.Map; import java.util.ArrayList; @@ -72,6 +68,7 @@ class InMemoryCookieStore implements CookieStore { /** * Add one cookie into cookie store. */ + @Override public void add(URI uri, HttpCookie cookie) { // pre-condition : argument can't be null if (cookie == null) { @@ -109,6 +106,7 @@ class InMemoryCookieStore implements CookieStore { * 3) not expired. * See RFC 2965 sec. 3.3.4 for more detail. */ + @Override public List get(URI uri) { // argument can't be null if (uri == null) { @@ -127,12 +125,13 @@ class InMemoryCookieStore implements CookieStore { lock.unlock(); } - return cookies; + return Collections.unmodifiableList(cookies); } /** * Get all cookies in cookie store, except those have expired */ + @Override public List getCookies() { List rt; @@ -156,6 +155,7 @@ class InMemoryCookieStore implements CookieStore { * Get all URIs, which are associated with at least one cookie * of this cookie store. */ + @Override public List getURIs() { List uris = new ArrayList<>(); @@ -165,7 +165,7 @@ class InMemoryCookieStore implements CookieStore { while (it.hasNext()) { URI uri = it.next(); List cookies = uriIndex.get(uri); - if (cookies == null || cookies.size() == 0) { + if (cookies == null || cookies.isEmpty()) { // no cookies list or an empty list associated with // this uri entry, delete it it.remove(); @@ -176,13 +176,14 @@ class InMemoryCookieStore implements CookieStore { lock.unlock(); } - return uris; + return Collections.unmodifiableList(uris); } /** * Remove a cookie from store */ + @Override public boolean remove(URI uri, HttpCookie ck) { // argument can't be null if (ck == null) { @@ -204,6 +205,7 @@ class InMemoryCookieStore implements CookieStore { /** * Remove all cookies in this cookie store. */ + @Override public boolean removeAll() { lock.lock(); try { diff --git a/src/java.base/share/classes/java/nio/BufferCleaner.java b/src/java.base/share/classes/java/nio/BufferCleaner.java index ddacecbcf63..f8ee76c4689 100644 --- a/src/java.base/share/classes/java/nio/BufferCleaner.java +++ b/src/java.base/share/classes/java/nio/BufferCleaner.java @@ -29,6 +29,7 @@ import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.util.Objects; +import jdk.internal.misc.InnocuousThread; import sun.nio.Cleaner; /** @@ -200,8 +201,9 @@ class BufferCleaner { } } - private static final class CleaningThread extends Thread { - public CleaningThread() {} + + private static final class CleaningRunnable implements Runnable { + public CleaningRunnable() {} @Override public void run() { @@ -234,14 +236,14 @@ class BufferCleaner { private static final CleanerList cleanerList = new CleanerList(); private static final ReferenceQueue queue = new ReferenceQueue(); - private static CleaningThread cleaningThread = null; + private static Thread cleaningThread; private static void startCleaningThreadIfNeeded() { synchronized (cleanerList) { if (cleaningThread != null) { return; } - cleaningThread = new CleaningThread(); + cleaningThread = InnocuousThread.newThread(new CleaningRunnable()); } cleaningThread.setDaemon(true); cleaningThread.start(); diff --git a/src/java.base/share/classes/java/security/AlgorithmParameters.java b/src/java.base/share/classes/java/security/AlgorithmParameters.java index defe25571f7..2a6fe830ba9 100644 --- a/src/java.base/share/classes/java/security/AlgorithmParameters.java +++ b/src/java.base/share/classes/java/security/AlgorithmParameters.java @@ -55,10 +55,11 @@ import java.util.Objects; *
    *
  • {@code AES}
  • *
  • {@code ChaCha20-Poly1305}
  • - *
  • {@code DESede}
  • *
  • {@code DiffieHellman}
  • *
  • {@code DSA}
  • *
  • {@code EC} (secp256r1, secp384r1)
  • + *
  • {@code PBEWithHmacSHA256AndAES_128}
  • + *
  • {@code PBEWithHmacSHA256AndAES_256}
  • *
  • {@code RSASSA-PSS} (MGF1 mask generation function and SHA-256 or SHA-384 * hash algorithms)
  • *
diff --git a/src/java.base/share/classes/java/security/PKCS12Attribute.java b/src/java.base/share/classes/java/security/PKCS12Attribute.java index 114660c06f8..fd844dc7f4d 100644 --- a/src/java.base/share/classes/java/security/PKCS12Attribute.java +++ b/src/java.base/share/classes/java/security/PKCS12Attribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ import java.io.IOException; import java.math.BigInteger; import java.util.Arrays; import java.util.regex.Pattern; + +import jdk.internal.vm.annotation.Stable; import sun.security.util.*; /** @@ -45,7 +47,9 @@ public final class PKCS12Attribute implements KeyStore.Entry.Attribute { private String name; private String value; private final byte[] encoded; - private int hashValue = -1; + + @Stable + private int hashValue; /** * Constructs a PKCS12 attribute from its name and value. @@ -207,7 +211,7 @@ public final class PKCS12Attribute implements KeyStore.Entry.Attribute { @Override public int hashCode() { int h = hashValue; - if (h == -1) { + if (h == 0) { hashValue = h = Arrays.hashCode(encoded); } return h; diff --git a/src/java.base/share/classes/java/security/cert/URICertStoreParameters.java b/src/java.base/share/classes/java/security/cert/URICertStoreParameters.java index b73383cf3d5..0080bb7bc28 100644 --- a/src/java.base/share/classes/java/security/cert/URICertStoreParameters.java +++ b/src/java.base/share/classes/java/security/cert/URICertStoreParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.security.cert; +import jdk.internal.vm.annotation.Stable; + import java.net.URI; /** @@ -57,7 +59,8 @@ public final class URICertStoreParameters implements CertStoreParameters { /* * Hash code for this parameters object. */ - private int myhash = -1; + @Stable + private int myhash; /** * Creates an instance of {@code URICertStoreParameters} with the @@ -105,10 +108,11 @@ public final class URICertStoreParameters implements CertStoreParameters { */ @Override public int hashCode() { - if (myhash == -1) { - myhash = uri.hashCode()*7; + int h = myhash; + if (h == 0) { + myhash = h = uri.hashCode()*7; } - return myhash; + return h; } /** diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index 3aa42ed8601..185d0baad4b 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -114,12 +114,6 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter; * pattern} or using one of the appropriate {@code DecimalFormat} setter methods, * for example, {@link #setMinimumFractionDigits(int)}. These limits have no impact * on parsing behavior. - * @implSpec - * When formatting a {@code Number} other than {@code BigInteger} and - * {@code BigDecimal}, {@code 309} is used as the upper limit for integer digits, - * and {@code 340} as the upper limit for fraction digits. This occurs, even if - * one of the {@code DecimalFormat} getter methods, for example, {@link #getMinimumFractionDigits()} - * returns a numerically greater value. * *

Special Values

*
    @@ -417,6 +411,13 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter; *
  • Exponential patterns may not contain grouping separators. *
* + * @implSpec + * When formatting a {@code Number} other than {@code BigInteger} and + * {@code BigDecimal}, {@code 309} is used as the upper limit for integer digits, + * and {@code 340} as the upper limit for fraction digits. This occurs, even if + * one of the {@code DecimalFormat} getter methods, for example, {@link #getMinimumFractionDigits()} + * returns a numerically greater value. + * * @spec https://www.unicode.org/reports/tr35 * Unicode Locale Data Markup Language (LDML) * @see Java Tutorial diff --git a/src/java.base/share/classes/java/text/DigitList.java b/src/java.base/share/classes/java/text/DigitList.java index d757f03bb84..3eeabb7e77e 100644 --- a/src/java.base/share/classes/java/text/DigitList.java +++ b/src/java.base/share/classes/java/text/DigitList.java @@ -169,13 +169,7 @@ final class DigitList implements Cloneable { if (count == 0) { return 0.0; } - - return Double.parseDouble(getStringBuilder() - .append('.') - .append(digits, 0, count) - .append('E') - .append(decimalAt) - .toString()); + return FloatingDecimal.parseDoubleSignlessDigits(decimalAt, digits, count); } /** @@ -190,17 +184,22 @@ final class DigitList implements Cloneable { return 0; } - // We have to check for this, because this is the one NEGATIVE value + // Parse as unsigned to handle Long.MIN_VALUE, which is the one NEGATIVE value // we represent. If we tried to just pass the digits off to parseLong, // we'd get a parse failure. - if (isLongMIN_VALUE()) { - return Long.MIN_VALUE; + long v = Long.parseUnsignedLong(new String(digits, 0, count)); + if (v < 0) { + if (v == Long.MIN_VALUE) { + return Long.MIN_VALUE; + } + throw new NumberFormatException("Unexpected negative value"); + } + try { + long pow10 = Math.powExact(10L, Math.max(0, decimalAt - count)); + return Math.multiplyExact(v, pow10); + } catch (ArithmeticException e) { + throw new NumberFormatException("Value does not fit into a long"); } - - StringBuilder temp = getStringBuilder(); - temp.append(digits, 0, count); - temp.append("0".repeat(Math.max(0, decimalAt - count))); - return Long.parseLong(temp.toString()); } /** @@ -210,11 +209,7 @@ final class DigitList implements Cloneable { */ public final BigDecimal getBigDecimal() { if (count == 0) { - if (decimalAt == 0) { - return BigDecimal.ZERO; - } else { - return new BigDecimal("0E" + decimalAt); - } + return BigDecimal.valueOf(0, -decimalAt); } if (decimalAt == count) { @@ -726,11 +721,10 @@ final class DigitList implements Cloneable { System.arraycopy(digits, 0, newDigits, 0, digits.length); other.digits = newDigits; - // data and tempBuilder do not need to be copied because they do - // not carry significant information. They will be recreated on demand. - // Setting them to null is needed to avoid sharing across clones. + // Data does not need to be copied because it does + // not carry significant information. It will be recreated on demand. + // Setting it to null is needed to avoid sharing across clones. other.data = null; - other.tempBuilder = null; return other; } catch (CloneNotSupportedException e) { @@ -738,23 +732,7 @@ final class DigitList implements Cloneable { } } - /** - * Returns true if this DigitList represents Long.MIN_VALUE; - * false, otherwise. This is required so that getLong() works. - */ - private boolean isLongMIN_VALUE() { - if (decimalAt != count || count != MAX_COUNT) { - return false; - } - - for (int i = 0; i < count; ++i) { - if (digits[i] != LONG_MIN_REP[i]) return false; - } - - return true; - } - - private static final int parseInt(char[] str, int offset, int strLen) { + private static int parseInt(char[] str, int offset, int strLen) { char c; boolean positive = true; if ((c = str[offset]) == '-') { @@ -787,17 +765,6 @@ final class DigitList implements Cloneable { return "0." + new String(digits, 0, count) + "x10^" + decimalAt; } - private StringBuilder tempBuilder; - - private StringBuilder getStringBuilder() { - if (tempBuilder == null) { - tempBuilder = new StringBuilder(MAX_COUNT); - } else { - tempBuilder.setLength(0); - } - return tempBuilder; - } - private void extendDigits(int len) { if (len > digits.length) { digits = new char[len]; diff --git a/src/java.base/share/classes/java/util/AbstractMap.java b/src/java.base/share/classes/java/util/AbstractMap.java index afc5b339354..f52c569c3a9 100644 --- a/src/java.base/share/classes/java/util/AbstractMap.java +++ b/src/java.base/share/classes/java/util/AbstractMap.java @@ -30,6 +30,8 @@ import java.util.function.Consumer; import java.util.function.IntFunction; import java.util.function.Predicate; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; + /** * This class provides a skeletal implementation of the {@code Map} * interface, to minimize the effort required to implement this interface. @@ -68,7 +70,7 @@ import java.util.function.Predicate; * @see Collection * @since 1.2 */ - +@AOTSafeClassInitializer public abstract class AbstractMap implements Map { /** * Sole constructor. (For invocation by subclass constructors, typically diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index 7d3f4dd8573..f18e17890b9 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -42,6 +42,7 @@ import java.util.stream.IntStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; import jdk.internal.access.SharedSecrets; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; /** * This class consists exclusively of static methods that operate on or return @@ -82,6 +83,7 @@ import jdk.internal.access.SharedSecrets; * @since 1.2 */ +@AOTSafeClassInitializer public final class Collections { // Suppresses default constructor, ensuring non-instantiability. private Collections() { 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 b9df942fbfd..4cb7048a798 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -70,6 +70,8 @@ import java.util.function.ToLongFunction; import java.util.stream.Stream; import jdk.internal.misc.Unsafe; import jdk.internal.util.ArraysSupport; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; /** @@ -263,6 +265,7 @@ import jdk.internal.vm.annotation.Stable; * @param the type of keys maintained by this map * @param the type of mapped values */ +@AOTSafeClassInitializer public class ConcurrentHashMap extends AbstractMap implements ConcurrentMap, Serializable { private static final long serialVersionUID = 7249069246763182397L; @@ -602,7 +605,7 @@ public class ConcurrentHashMap extends AbstractMap runtimeSetup(); } - // Called from JVM when loading an AOT cache. + @AOTRuntimeSetup private static void runtimeSetup() { NCPU = Runtime.getRuntime().availableProcessors(); } 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 f793d2bcabd..9ddf6c74ddf 100644 --- a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java +++ b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java @@ -292,9 +292,7 @@ public class SubmissionPublisher implements Publisher, /** * Creates a new SubmissionPublisher using the {@link - * ForkJoinPool#commonPool()} for async delivery to subscribers - * (unless it does not support a parallelism level of at least two, - * in which case, a new Thread is created to run each task), with + * ForkJoinPool#commonPool()} for async delivery to subscribers, with * maximum buffer capacity of {@link Flow#defaultBufferSize}, and no * handler for Subscriber exceptions in method {@link * Flow.Subscriber#onNext(Object) onNext}. diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index f947eb4f7db..2250009e8f5 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -403,6 +403,9 @@ public abstract class AtomicIntegerFieldUpdater { if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); + if (Modifier.isStatic(modifiers)) + throw new IllegalArgumentException("Must not be a static field"); + // Access to protected field members is restricted to receivers only // of the accessing class, or one of its subclasses, and the // accessing class must in turn be a subclass (or package sibling) diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index b31a8edf53a..5f0a666cb04 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -398,6 +398,9 @@ public abstract class AtomicLongFieldUpdater { if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); + if (Modifier.isStatic(modifiers)) + throw new IllegalArgumentException("Must not be a static field"); + // Access to protected field members is restricted to receivers only // of the accessing class, or one of its subclasses, and the // accessing class must in turn be a subclass (or package sibling) diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index e3ca4830d5a..4a758f77a47 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -363,6 +363,9 @@ public abstract class AtomicReferenceFieldUpdater { if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); + if (Modifier.isStatic(modifiers)) + throw new IllegalArgumentException("Must not be a static field"); + // Access to protected field members is restricted to receivers only // of the accessing class, or one of its subclasses, and the // accessing class must in turn be a subclass (or package sibling) diff --git a/src/java.base/share/classes/java/util/stream/Collectors.java b/src/java.base/share/classes/java/util/stream/Collectors.java index 6099ba6fd78..87b2e66a8bb 100644 --- a/src/java.base/share/classes/java/util/stream/Collectors.java +++ b/src/java.base/share/classes/java/util/stream/Collectors.java @@ -57,6 +57,7 @@ import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; import jdk.internal.access.SharedSecrets; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; /** * Implementations of {@link Collector} that implement various useful reduction @@ -103,6 +104,7 @@ import jdk.internal.access.SharedSecrets; * * @since 1.8 */ +@AOTSafeClassInitializer public final class Collectors { static final Set CH_CONCURRENT_ID diff --git a/src/java.base/share/classes/javax/crypto/Cipher.java b/src/java.base/share/classes/javax/crypto/Cipher.java index 74971182039..82a607a5553 100644 --- a/src/java.base/share/classes/javax/crypto/Cipher.java +++ b/src/java.base/share/classes/javax/crypto/Cipher.java @@ -127,11 +127,8 @@ import sun.security.util.KnownOIDs; *
  • {@code AES/ECB/PKCS5Padding} (128)
  • *
  • {@code AES/GCM/NoPadding} (128, 256)
  • *
  • {@code ChaCha20-Poly1305}
  • - *
  • {@code DESede/CBC/NoPadding} (168)
  • - *
  • {@code DESede/CBC/PKCS5Padding} (168)
  • - *
  • {@code DESede/ECB/NoPadding} (168)
  • - *
  • {@code DESede/ECB/PKCS5Padding} (168)
  • - *
  • {@code RSA/ECB/PKCS1Padding} (1024, 2048)
  • + *
  • {@code PBEWithHmacSHA256AndAES_128}
  • + *
  • {@code PBEWithHmacSHA256AndAES_256}
  • *
  • {@code RSA/ECB/OAEPWithSHA-1AndMGF1Padding} (1024, 2048)
  • *
  • {@code RSA/ECB/OAEPWithSHA-256AndMGF1Padding} (1024, 2048)
  • * diff --git a/src/java.base/share/classes/javax/crypto/KeyGenerator.java b/src/java.base/share/classes/javax/crypto/KeyGenerator.java index 02d0bd75753..7bbfc0a5e08 100644 --- a/src/java.base/share/classes/javax/crypto/KeyGenerator.java +++ b/src/java.base/share/classes/javax/crypto/KeyGenerator.java @@ -98,7 +98,6 @@ import sun.security.util.Debug; *
      *
    • {@code AES} (128, 256)
    • *
    • {@code ChaCha20}
    • - *
    • {@code DESede} (168)
    • *
    • {@code HmacSHA1}
    • *
    • {@code HmacSHA256}
    • *
    diff --git a/src/java.base/share/classes/javax/crypto/Mac.java b/src/java.base/share/classes/javax/crypto/Mac.java index fb1eb2c310a..82874693cf2 100644 --- a/src/java.base/share/classes/javax/crypto/Mac.java +++ b/src/java.base/share/classes/javax/crypto/Mac.java @@ -58,6 +58,7 @@ import sun.security.jca.GetInstance.Instance; *
      *
    • {@code HmacSHA1}
    • *
    • {@code HmacSHA256}
    • + *
    • {@code PBEWithHmacSHA256}
    • *
    * These algorithms are described in the * diff --git a/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java b/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java index d7163e4d240..7ad90eaa858 100644 --- a/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java +++ b/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java @@ -59,7 +59,9 @@ import sun.security.jca.GetInstance.Instance; *

    Every implementation of the Java platform is required to support the * following standard {@code SecretKeyFactory} algorithms: *

      - *
    • {@code DESede}
    • + *
    • {@code PBEWithHmacSHA256AndAES_128}
    • + *
    • {@code PBEWithHmacSHA256AndAES_256}
    • + *
    • {@code PBKDF2WithHmacSHA256}
    • *
    * These algorithms are described in the
    diff --git a/src/java.base/share/classes/javax/net/ssl/SSLEngine.java b/src/java.base/share/classes/javax/net/ssl/SSLEngine.java index 228750e080c..9376b7d1876 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLEngine.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLEngine.java @@ -1088,7 +1088,17 @@ public abstract class SSLEngine { /** - * Initiates handshaking (initial or renegotiation) on this SSLEngine. + * Begins handshaking on this {@code SSLEngine}. + *

    + * Common reasons include a need to initiate a new protected session, + * create new encryption keys, or to change cipher suites. To force + * complete reauthentication, the current session should be invalidated + * before starting this handshake. + *

    + * The behavior of this method is protocol (and possibly implementation) + * dependent. For example, in TLSv1.3 calling this method after the + * connection has been established will force a key update. For prior TLS + * versions it will force a renegotiation (re-handshake). *

    * This method is not needed for the initial handshake, as the * {@code wrap()} and {@code unwrap()} methods will @@ -1102,9 +1112,6 @@ public abstract class SSLEngine { * SSLSocket#startHandshake()} method, this method does not block * until handshaking is completed. *

    - * To force a complete SSL/TLS/DTLS session renegotiation, the current - * session should be invalidated prior to calling this method. - *

    * Some protocols may not support multiple handshakes on an existing * engine and may throw an {@code SSLException}. * diff --git a/src/java.base/share/classes/javax/net/ssl/SSLSocket.java b/src/java.base/share/classes/javax/net/ssl/SSLSocket.java index a18c203320d..117f2de9bbc 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLSocket.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLSocket.java @@ -502,19 +502,26 @@ public abstract class SSLSocket extends Socket /** - * Starts an SSL handshake on this connection. Common reasons include - * a need to use new encryption keys, to change cipher suites, or to - * initiate a new session. To force complete reauthentication, the - * current session could be invalidated before starting this handshake. - * - *

    If data has already been sent on the connection, it continues + * Starts handshaking on this {@code SSLSocket}. + *

    + * Common reasons include a need to initiate a new protected session, + * create new encryption keys, or to change cipher suites. To force + * complete reauthentication, the current session should be invalidated + * before starting this handshake. + *

    + * The behavior of this method is protocol (and possibly implementation) + * dependent. For example, in TLSv1.3 calling this method after the + * connection has been established will force a key update. For prior TLS + * versions it will force a renegotiation (re-handshake). + *

    + * If data has already been sent on the connection, it continues * to flow during this handshake. When the handshake completes, this * will be signaled with an event. - * + *

    * This method is synchronous for the initial handshake on a connection * and returns when the negotiated handshake is complete. Some * protocols may not support multiple handshakes on an existing socket - * and may throw an IOException. + * and may throw an {@code IOException}. * * @throws IOException on a network level error * @see #addHandshakeCompletedListener(HandshakeCompletedListener) diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index e8343274cac..efa36b5b2d8 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -112,6 +112,11 @@ public interface JavaLangAccess { */ byte[] getRawExecutableTypeAnnotations(Executable executable); + /** + * Get the int value of the Class's class-file access flags. + */ + int getClassFileAccessFlags(Class klass); + /** * Returns the elements of an enum class or null if the * Class object does not represent an enum type; diff --git a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java index 5c6212d0bf6..e20a1e77423 100644 --- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java +++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java @@ -25,6 +25,8 @@ package jdk.internal.access; +import jdk.internal.vm.annotation.Stable; + import javax.crypto.SealedObject; import javax.crypto.spec.SecretKeySpec; import java.io.ObjectInputFilter; @@ -59,37 +61,38 @@ import javax.security.auth.x500.X500Principal; */ public class SharedSecrets { + // This field is not necessarily stable private static JavaAWTFontAccess javaAWTFontAccess; - private static JavaBeansAccess javaBeansAccess; - private static JavaLangAccess javaLangAccess; - private static JavaLangInvokeAccess javaLangInvokeAccess; - private static JavaLangModuleAccess javaLangModuleAccess; - private static JavaLangRefAccess javaLangRefAccess; - private static JavaLangReflectAccess javaLangReflectAccess; - private static JavaIOAccess javaIOAccess; - private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; - private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess; - private static JavaObjectInputStreamReadString javaObjectInputStreamReadString; - private static JavaObjectInputStreamAccess javaObjectInputStreamAccess; - private static JavaObjectInputFilterAccess javaObjectInputFilterAccess; - private static JavaObjectStreamReflectionAccess javaObjectStreamReflectionAccess; - private static JavaNetInetAddressAccess javaNetInetAddressAccess; - private static JavaNetHttpCookieAccess javaNetHttpCookieAccess; - private static JavaNetUriAccess javaNetUriAccess; - private static JavaNetURLAccess javaNetURLAccess; - private static JavaNioAccess javaNioAccess; - private static JavaUtilCollectionAccess javaUtilCollectionAccess; - private static JavaUtilConcurrentTLRAccess javaUtilConcurrentTLRAccess; - private static JavaUtilConcurrentFJPAccess javaUtilConcurrentFJPAccess; - private static JavaUtilJarAccess javaUtilJarAccess; - private static JavaUtilZipFileAccess javaUtilZipFileAccess; - private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess; - private static JavaSecurityPropertiesAccess javaSecurityPropertiesAccess; - private static JavaSecuritySignatureAccess javaSecuritySignatureAccess; - private static JavaSecuritySpecAccess javaSecuritySpecAccess; - private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; - private static JavaxCryptoSpecAccess javaxCryptoSpecAccess; - private static JavaxSecurityAccess javaxSecurityAccess; + @Stable private static JavaBeansAccess javaBeansAccess; + @Stable private static JavaLangAccess javaLangAccess; + @Stable private static JavaLangInvokeAccess javaLangInvokeAccess; + @Stable private static JavaLangModuleAccess javaLangModuleAccess; + @Stable private static JavaLangRefAccess javaLangRefAccess; + @Stable private static JavaLangReflectAccess javaLangReflectAccess; + @Stable private static JavaIOAccess javaIOAccess; + @Stable private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; + @Stable private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess; + @Stable private static JavaObjectInputStreamReadString javaObjectInputStreamReadString; + @Stable private static JavaObjectInputStreamAccess javaObjectInputStreamAccess; + @Stable private static JavaObjectInputFilterAccess javaObjectInputFilterAccess; + @Stable private static JavaObjectStreamReflectionAccess javaObjectStreamReflectionAccess; + @Stable private static JavaNetInetAddressAccess javaNetInetAddressAccess; + @Stable private static JavaNetHttpCookieAccess javaNetHttpCookieAccess; + @Stable private static JavaNetUriAccess javaNetUriAccess; + @Stable private static JavaNetURLAccess javaNetURLAccess; + @Stable private static JavaNioAccess javaNioAccess; + @Stable private static JavaUtilCollectionAccess javaUtilCollectionAccess; + @Stable private static JavaUtilConcurrentTLRAccess javaUtilConcurrentTLRAccess; + @Stable private static JavaUtilConcurrentFJPAccess javaUtilConcurrentFJPAccess; + @Stable private static JavaUtilJarAccess javaUtilJarAccess; + @Stable private static JavaUtilZipFileAccess javaUtilZipFileAccess; + @Stable private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess; + @Stable private static JavaSecurityPropertiesAccess javaSecurityPropertiesAccess; + @Stable private static JavaSecuritySignatureAccess javaSecuritySignatureAccess; + @Stable private static JavaSecuritySpecAccess javaSecuritySpecAccess; + @Stable private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; + @Stable private static JavaxCryptoSpecAccess javaxCryptoSpecAccess; + @Stable private static JavaxSecurityAccess javaxSecurityAccess; public static void setJavaUtilCollectionAccess(JavaUtilCollectionAccess juca) { javaUtilCollectionAccess = juca; diff --git a/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java b/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java index 875c75296ee..15a178d7b29 100644 --- a/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java +++ b/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ */ package jdk.internal.constant; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; @@ -41,6 +42,7 @@ import static jdk.internal.constant.PrimitiveClassDescImpl.*; /** * Helper methods for the implementation of {@code java.lang.constant}. */ +@AOTSafeClassInitializer // initialization dependency of PrimitiveClassDescImpl public final class ConstantUtils { private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); diff --git a/src/java.base/share/classes/jdk/internal/constant/PrimitiveClassDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/PrimitiveClassDescImpl.java index 31ad92a2736..8d5ebb0a080 100644 --- a/src/java.base/share/classes/jdk/internal/constant/PrimitiveClassDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/PrimitiveClassDescImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.lang.constant.ConstantDescs; import java.lang.constant.DynamicConstantDesc; import java.lang.invoke.MethodHandles; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; @@ -38,6 +39,7 @@ import static java.util.Objects.requireNonNull; * A nominal descriptor for the class * constant corresponding to a primitive type (e.g., {@code int.class}). */ +@AOTSafeClassInitializer // identity-sensitive static final fields public final class PrimitiveClassDescImpl extends DynamicConstantDesc> implements ClassDesc { diff --git a/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java b/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java index 81887144ed1..cc68ce1282b 100644 --- a/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java +++ b/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java @@ -122,6 +122,19 @@ public class FloatingDecimal{ return readJavaFormatString(s, BINARY_32_IX).floatValue(); } + /** + * Converts a sequence of digits ('0'-'9') as well as an exponent to a positive + * double value + * + * @param decExp The decimal exponent of the value to generate + * @param digits The digits of the significand. + * @param length Number of digits to use + * @return The double-precision value of the conversion + */ + public static double parseDoubleSignlessDigits(int decExp, char[] digits, int length) { + return readDoubleSignlessDigits(decExp, digits, length).doubleValue(); + } + /** * A converter which can process single or double precision floating point * values into an ASCII String representation. @@ -1824,6 +1837,20 @@ public class FloatingDecimal{ return buf; } + static ASCIIToBinaryConverter readDoubleSignlessDigits(int decExp, char[] digits, int length) { + + // Prevent an extreme negative exponent from causing overflow issues in doubleValue(). + // Large positive values are handled within doubleValue(); + if (decExp < MIN_DECIMAL_EXPONENT) { + return A2BC_POSITIVE_ZERO; + } + byte[] buf = new byte[length]; + for (int i = 0; i < length; i++) { + buf[i] = (byte) digits[i]; + } + return new ASCIIToBinaryBuffer(false, decExp, buf, length); + } + /** * The input must match the {@link Double#valueOf(String) rules described here}, * about leading and trailing whitespaces, and the grammar. diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java index 1aff92eb36d..016566ae659 100644 --- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java @@ -25,6 +25,8 @@ package jdk.internal.misc; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import sun.nio.Cleaner; @@ -52,7 +54,7 @@ import static jdk.internal.misc.UnsafeConstants.*; * @author John R. Rose * @see #getUnsafe */ - +@AOTSafeClassInitializer public final class Unsafe { private static native void registerNatives(); @@ -60,7 +62,9 @@ public final class Unsafe { runtimeSetup(); } - // Called from JVM when loading an AOT cache + /// BASE_OFFSET, INDEX_SCALE, and ADDRESS_SIZE fields are equivalent if the + /// AOT initialized heap is reused, so just register natives + @AOTRuntimeSetup private static void runtimeSetup() { registerNatives(); } @@ -1065,6 +1069,9 @@ public final class Unsafe { * the field locations in a form usable by {@link #getInt(Object,long)}. * Therefore, code which will be ported to such JVMs on 64-bit platforms * must preserve all bits of static field offsets. + * + * @throws NullPointerException if the field is {@code null} + * @throws IllegalArgumentException if the field is static * @see #getInt(Object, long) */ public long objectFieldOffset(Field f) { @@ -1076,13 +1083,17 @@ public final class Unsafe { } /** - * Reports the location of the field with a given name in the storage - * allocation of its class. + * (For compile-time known instance fields in JDK code only) Reports the + * location of the field with a given name in the storage allocation of its + * class. + *

    + * This API is used to avoid creating reflective Objects in Java code at + * startup. This should not be used to find fields in non-trusted code. + * Use the {@link #objectFieldOffset(Field) Field}-accepting version for + * arbitrary fields instead. * * @throws NullPointerException if any parameter is {@code null}. - * @throws InternalError if there is no field named {@code name} declared - * in class {@code c}, i.e., if {@code c.getDeclaredField(name)} - * would throw {@code java.lang.NoSuchFieldException}. + * @throws InternalError if the presumably known field couldn't be found * * @see #objectFieldOffset(Field) */ @@ -1091,7 +1102,16 @@ public final class Unsafe { throw new NullPointerException(); } - return objectFieldOffset1(c, name); + long result = knownObjectFieldOffset0(c, name); + if (result < 0) { + String type = switch ((int) result) { + case -2 -> "a static field"; + case -1 -> "not found"; + default -> "unknown"; + }; + throw new InternalError("Field %s.%s %s".formatted(c.getTypeName(), name, type)); + } + return result; } /** @@ -1109,6 +1129,9 @@ public final class Unsafe { * a few bits to encode an offset within a non-array object, * However, for consistency with other methods in this class, * this method reports its result as a long value. + * + * @throws NullPointerException if the field is {@code null} + * @throws IllegalArgumentException if the field is not static * @see #getInt(Object, long) */ public long staticFieldOffset(Field f) { @@ -1128,6 +1151,9 @@ public final class Unsafe { * which is a "cookie", not guaranteed to be a real Object, and it should * not be used in any way except as argument to the get and put routines in * this class. + * + * @throws NullPointerException if the field is {@code null} + * @throws IllegalArgumentException if the field is not static */ public Object staticFieldBase(Field f) { if (f == null) { @@ -3844,10 +3870,10 @@ public final class Unsafe { @IntrinsicCandidate private native void copyMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); private native void copySwapMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes, long elemSize); - private native long objectFieldOffset0(Field f); - private native long objectFieldOffset1(Class c, String name); - private native long staticFieldOffset0(Field f); - private native Object staticFieldBase0(Field f); + private native long objectFieldOffset0(Field f); // throws IAE + private native long knownObjectFieldOffset0(Class c, String name); // error code: -1 not found, -2 static + private native long staticFieldOffset0(Field f); // throws IAE + private native Object staticFieldBase0(Field f); // throws IAE private native boolean shouldBeInitialized0(Class c); private native void ensureClassInitialized0(Class c); private native int arrayBaseOffset0(Class arrayClass); // public version returns long to promote correct arithmetic diff --git a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java index 5025b81dd10..d39fd9231da 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/Reflection.java +++ b/src/java.base/share/classes/jdk/internal/reflect/Reflection.java @@ -81,8 +81,9 @@ public class Reflection { to compatibility reasons; see 4471811. Only the values of the low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be valid. */ - @IntrinsicCandidate - public static native int getClassAccessFlags(Class c); + public static int getClassAccessFlags(Class c) { + return SharedSecrets.getJavaLangAccess().getClassFileAccessFlags(c); + } /** diff --git a/src/java.base/share/classes/jdk/internal/util/Exceptions.java b/src/java.base/share/classes/jdk/internal/util/Exceptions.java index eb4286cd1af..70ee7026a7b 100644 --- a/src/java.base/share/classes/jdk/internal/util/Exceptions.java +++ b/src/java.base/share/classes/jdk/internal/util/Exceptions.java @@ -274,12 +274,9 @@ public final class Exceptions { */ public static IOException ioException(IOException e, SocketAddress addr) { setup(); - if (addr == null) { + if (!enhancedSocketExceptionText || addr == null) { return e; } - if (!enhancedSocketExceptionText) { - return create(e, e.getMessage()); - } if (addr instanceof UnixDomainSocketAddress) { return ofUnixDomain(e, (UnixDomainSocketAddress)addr); } else if (addr instanceof InetSocketAddress) { diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java new file mode 100644 index 00000000000..c3a0c283dc3 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTRuntimeSetup.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.vm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/// Indicates that if the enclosing class or interface is present in the AOT +/// cache in the AOT-initialized state, the annotated method must be executed +/// before bootstrap phase 3 (that is, before [System#initPhase3]). +/// +/// The annotated method must be declared `private` and `static`, must be named +/// `runtimeSetup`, and must have no arguments or return value. The enclosing +/// class must be annotated with [AOTSafeClassInitializer], meaning that it is +/// allowed to be stored in the AOT-initialized state. +/// +/// The annotated method will be executed if and only if the class was loaded +/// in the AOT-initialized state from the AOT cache. +/// +/// The author of the class is responsible for deciding whether some or all of +/// a class's initialization state should be re-initialized in any way. In all +/// cases, the static initializer (`` method) of any given class or +/// interface is run at most once, either in the assembly phase (only for an +/// AOT-initialized class) or in the production run. +/// +/// After a `static` `final` field is assigned a value in an AOT-initialized +/// class, its value may never be changed, as such values are always immutable +/// runtime constants. (...Barring `System.out` and its two siblings.) +/// Rarely, a `static` field may require differing values in the assembly phase +/// for an AOT cache, and for the production run. Such variables must be +/// marked non-`final`, and should be adjusted by the `runtimeSetup` method. +/// Full constant folding (as if with a `final` field) may usually be recovered +/// by also marking the field as [Stable]. That annotation instructs the JIT +/// to perform constant folding, and _only_ during the production run, after +/// `runtimeSetup` has had a chance to give the field its "finally final" +/// value. +/// +/// A related method is `resetArchivedStates`, which allows special handling of +/// an AOT-initialized class, at the end of the assembly phase run which builds +/// an AOT cache. The `resetArchivedStates` may "tear down" state that should +/// not be stored in the AOT cache, which the `runtimeSetup` method may then +/// "build up again" as the production run begins. This additional method is +/// currently only used by [Class] to reset a cache field, but it may be +/// expanded to other classes and interfaces later on, using more +/// annotation-driven logic. +/// +/// The logic in `classFileParser.cpp` performs checks on the annotated method: If the +/// annotated method's signature differs from that described above, or if (during the +/// assembly phase) the class is not marked to have an AOT-safe initializer, a +/// [ClassFormatError] will be thrown. +/// +/// This annotation is only recognized on privileged code and is ignored elsewhere. +/// +/// @since 26 +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AOTRuntimeSetup { +} diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/AOTSafeClassInitializer.java b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTSafeClassInitializer.java new file mode 100644 index 00000000000..3c2dab3209a --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/AOTSafeClassInitializer.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.vm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/// Indicates that the static initializer of this class or interface +/// (its `` method) is allowed to be _AOT-initialized_, +/// because its author considers it safe to execute during the AOT +/// assembly phase. +/// +/// This annotation directs the VM to expect that normal execution of Java code +/// during the assembly phase could trigger initialization of this class, +/// and if that happens, to store the resulting static field values in the +/// AOT cache. (These fields happen to be allocated in the `Class` mirror.) +/// +/// During the production run, the static initializer (``) of +/// this class or interface will not be executed, if it was already +/// executed during the assembling of the AOT being used to start the +/// production run. In that case the resulting static field states +/// (within the `Class` mirror) were already stored in the AOT cache. +/// +/// Currently, this annotation is used mainly for supporting AOT +/// linking of APIs, including bootstrap methods, in the +/// `java.lang.invoke` package. +/// +/// In more detail, the AOT assembly phase performs the following: +/// +/// 1. It loads and links (but does not initialize) the classes that were loaded +/// during the application's training run. +/// 2. During linking of these classes, it resolves their constant pool +/// entries, when it is safe and beneficial to do so. +/// 3. As part of those resolutions, bootstrap methods may be called and may +/// create graphs of Java objects to support linkage states. +/// 4. Every object within those graphs must have its class AOT-initialized, +/// along with every relevant superclass and implemented interface, along +/// with classes for every object created during the course of static +/// initialization (running `` for each such class or interface). +/// +/// Thus, in order to determine that a class or interface _X_ is safe to +/// AOT-initialize requires evaluating every other class or interface _Y_ that +/// the `` of _X_ will initialize (during AOT cache assembly), and +/// ensuring that each such _Y_ is (recursively) safe to AOT-initialize. +/// +/// For example, an AOT-resolved constant pool entry for an invokedynamic or +/// invokehandle bytecode can have direct or indirect references to Java objects. +/// To ensure the correctness of the AOT-resolved constant pool entrties, the VM +/// must AOT-initialize the classes of such Java objects. +/// +/// In addition, such Java objects may have references to static fields whose +/// object identity is important. For example, `PrimitiveClassDescImpl::CD_void`. +/// To ensure correctness, we must also store classes like `PrimitiveClassDescImpl` +/// in the initialized state. The VM requires implementor to manually annotate +/// such classes with `@AOTSafeClassInitializer`. +/// +/// There is one more requirement for a class to be safe for +/// AOT initialization, and that is compatibility with all eventual production +/// runs. The state of an AOT-initialized class _X_ must not contain any data +/// (anything reachable from _X_) that is incompatible with the eventual +/// production run. +/// +/// In general, if some sort of computed datum, environmental setting, or +/// variable behavior may differ between the AOT assembly phase and the +/// production run, it may not be immutably bound to _X_, if _X_ is to be +/// marked AOT-initialized. Here are specific examples: +/// +/// - The value of an environment string (if it may differ in the production run). +/// +/// - A transient configuration parameter specific to this VM run, such as +/// wall clock time, process ID, host name, temporary directory names, etc. +/// +/// - A random seed or key that may need to be re-sampled at production +/// startup. +/// +/// What is more, if the initialization of _X_ computes with some value _V_ +/// obtained from some other class _Y_, _Y_ should also be safe for AOT +/// initialization, if there is any way for _X_ to detect a mismatch between +/// the version of _V_ produced at AOT time, and the version of _V_ produced in +/// the production run. Specifically, if _V_ has an object identity, _X_ +/// should not test that identity (compare it against another or get its +/// hashcode) unless _Y_ is also marked for AOT initialization. +/// +/// Thus, to support AOT-time linkage, a class _X_ should be marked for (possible) +/// AOT initialization whenever objects it creates (such as `MethodHandle`s) +/// may be required to execute a `java.lang.invoke` API request, or (more +/// remotely) if the execution of such an API touches _X_ for initialization, +/// or even if such an API request is in any way sensitive to values stored in +/// the fields of _X_, even if the sensitivity is a simple reference identity +/// test. As noted above, all supertypes of _X_ must also have the +/// `@AOTSafeClassInitializer` annotation, and must also be safe for AOT +/// initialization. +/// +/// The author of an AOT-initialized class may elect to patch some states at +/// production startup, using an [AOTRuntimeSetup] method, as long as the +/// pre-patched field values (present during AOT assembly) are determined to be +/// compatible with the post-patched values that apply to the production run. +/// +/// In the assembly phase, `classFileParser.cpp` performs checks on the annotated +/// classes, to ensure all supertypes of this class that must be initialized when +/// this class is initialized have the `@AOTSafeClassInitializer` annotation. +/// Otherwise, a [ClassFormatError] will be thrown. (This assembly phase restriction +/// allows module patching and instrumentation to work on annotated classes when +/// AOT is not enabled) +/// +/// This annotation is only recognized on privileged code and is ignored elsewhere. +/// +/// @since 26 +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface AOTSafeClassInitializer { +} diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 857c2f6ad6d..aee9670ce26 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -1080,6 +1080,11 @@ public class HttpURLConnection extends java.net.HttpURLConnection { if (logger.isLoggable(PlatformLogger.Level.FINE)) { logger.fine("response code received " + responseCode); } + if (sa.length > 2) + responseMessage = String.join(" ", Arrays.copyOfRange(sa, 2, sa.length)); + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine("response message received " + responseMessage); + } } catch (NumberFormatException numberFormatException) { } } diff --git a/src/java.base/share/classes/sun/nio/cs/HKSCS.java b/src/java.base/share/classes/sun/nio/cs/HKSCS.java index 04bbd386a31..cfe9f879c04 100644 --- a/src/java.base/share/classes/sun/nio/cs/HKSCS.java +++ b/src/java.base/share/classes/sun/nio/cs/HKSCS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,12 +28,9 @@ package sun.nio.cs; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.util.Arrays; -import sun.nio.cs.DoubleByte; -import sun.nio.cs.Surrogate; + import static sun.nio.cs.CharsetMapping.*; public class HKSCS { @@ -355,12 +352,6 @@ public class HKSCS { return encodeBufferLoop(src, dst); } - @SuppressWarnings("this-escape") - private byte[] repl = replacement(); - protected void implReplaceWith(byte[] newReplacement) { - repl = newReplacement; - } - public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; int sl = sp + len; diff --git a/src/java.base/share/classes/sun/security/ec/ECDSASignature.java b/src/java.base/share/classes/sun/security/ec/ECDSASignature.java index c6c49bd6c21..029affbac3d 100644 --- a/src/java.base/share/classes/sun/security/ec/ECDSASignature.java +++ b/src/java.base/share/classes/sun/security/ec/ECDSASignature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.nio.ByteBuffer; import java.security.*; import java.security.interfaces.*; import java.security.spec.*; +import java.util.Arrays; import java.util.Optional; import sun.security.jca.JCAUtil; @@ -436,7 +437,14 @@ abstract class ECDSASignature extends SignatureSpi { random.nextBytes(seedBytes); ECDSAOperations.Seed seed = new ECDSAOperations.Seed(seedBytes); try { - return ops.signDigest(s, digest, seed); + // a new allocation for this value, rather than just + // returning it, is a trade-off to zero-out the local + // value for "s" when necessary + byte[] retValue = ops.signDigest(s, digest, seed); + if (!(priv instanceof ECPrivateKeyImpl)) { + Arrays.fill(s, (byte)0x00); + } + return retValue; } catch (IntermediateValueException ex) { // try again in the next iteration } diff --git a/src/java.base/share/classes/sun/security/provider/certpath/Builder.java b/src/java.base/share/classes/sun/security/provider/certpath/Builder.java index 3331e98412e..3e6c2b1fbb7 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/Builder.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/Builder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -139,7 +139,7 @@ abstract class Builder { * cert's certificate policies extension in order for a cert to be selected. */ Set getMatchingPolicies() { - if (matchingPolicies != null) { + if (matchingPolicies == null) { Set initialPolicies = buildParams.initialPolicies(); if ((!initialPolicies.isEmpty()) && (!initialPolicies.contains(PolicyChecker.ANY_POLICY)) && diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index 3ac6c37d80e..a2c82ee70c4 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -154,7 +154,7 @@ public final class SSLLogger { } public static void finest(String msg, Object... params) { - SSLLogger.log(Level.ALL, msg, params); + SSLLogger.log(Level.TRACE, msg, params); } private static void log(Level level, String msg, Object... params) { diff --git a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java index 63be91e1967..b9faaf62b5f 100644 --- a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.security.ssl; import java.net.Socket; +import java.security.AlgorithmConstraints; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; @@ -39,85 +40,58 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; +import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLEngine; -import javax.net.ssl.X509ExtendedKeyManager; import javax.security.auth.x500.X500Principal; /** * An implementation of X509KeyManager backed by a KeyStore. - * + *

    * The backing KeyStore is inspected when this object is constructed. * All key entries containing a PrivateKey and a non-empty chain of * X509Certificate are then copied into an internal store. This means * that subsequent modifications of the KeyStore have no effect on the * X509KeyManagerImpl object. - * + *

    * Note that this class assumes that all keys are protected by the same * password. - * - * The JSSE handshake code currently calls into this class via - * chooseClientAlias() and chooseServerAlias() to find the certificates to - * use. As implemented here, both always return the first alias returned by - * getClientAliases() and getServerAliases(). In turn, these methods are - * implemented by calling getAliases(), which performs the actual lookup. - * - * Note that this class currently implements no checking of the local - * certificates. In particular, it is *not* guaranteed that: - * . the certificates are within their validity period and not revoked - * . the signatures verify - * . they form a PKIX compliant chain. - * . the certificate extensions allow the certificate to be used for - * the desired purpose. - * - * Chains that fail any of these criteria will probably be rejected by - * the remote peer. + *

    + * Algorithm constraints and certificate checks can be disabled by setting + * "jdk.tls.SunX509KeyManager.certChecking" system property to "false" + * before calling a class constructor. * */ -final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { - private static final String[] STRING0 = new String[0]; +final class SunX509KeyManagerImpl extends X509KeyManagerCertChecking { /* * The credentials from the KeyStore as * Map: String(alias) -> X509Credentials(credentials) */ - private final Map credentialsMap; + private final Map credentialsMap; - /* - * Cached server aliases for the case issuers == null. - * (in the current JSSE implementation, issuers are always null for - * server certs). See chooseServerAlias() for details. - * - * Map: String(keyType) -> String[](alias) - */ - private final Map serverAliasCache; + @Override + protected boolean isCheckingDisabled() { + return "false".equalsIgnoreCase(System.getProperty( + "jdk.tls.SunX509KeyManager.certChecking", "true")); + } /* * Basic container for credentials implemented as an inner class. */ private static class X509Credentials { + final PrivateKey privateKey; final X509Certificate[] certificates; - private final Set issuerX500Principals; X509Credentials(PrivateKey privateKey, X509Certificate[] certificates) { // assert privateKey and certificates != null this.privateKey = privateKey; this.certificates = certificates; - this.issuerX500Principals = HashSet.newHashSet(certificates.length); - for (X509Certificate certificate : certificates) { - issuerX500Principals.add(certificate.getIssuerX500Principal()); - } - } - - Set getIssuerX500Principals() { - return issuerX500Principals; } } @@ -126,14 +100,13 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { NoSuchAlgorithmException, UnrecoverableKeyException { credentialsMap = new HashMap<>(); - serverAliasCache = Collections.synchronizedMap( - new HashMap<>()); + if (ks == null) { return; } for (Enumeration aliases = ks.aliases(); - aliases.hasMoreElements(); ) { + aliases.hasMoreElements(); ) { String alias = aliases.nextElement(); if (!ks.isKeyEntry(alias)) { continue; @@ -153,8 +126,8 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { certs = tmp; } - X509Credentials cred = new X509Credentials((PrivateKey)key, - (X509Certificate[])certs); + X509Credentials cred = new X509Credentials((PrivateKey) key, + (X509Certificate[]) certs); credentialsMap.put(alias, cred); if (SSLLogger.logging && SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { SSLLogger.fine("found key for : " + alias, (Object[])certs); @@ -205,24 +178,8 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { @Override public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { - /* - * We currently don't do anything with socket, but - * someday we might. It might be a useful hint for - * selecting one of the aliases we get back from - * getClientAliases(). - */ - - if (keyTypes == null) { - return null; - } - - for (int i = 0; i < keyTypes.length; i++) { - String[] aliases = getClientAliases(keyTypes[i], issuers); - if ((aliases != null) && (aliases.length > 0)) { - return aliases[0]; - } - } - return null; + return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, + getAlgorithmConstraints(socket), null, null); } /* @@ -230,17 +187,12 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { * SSLEngine connection given the public key type * and the list of certificate issuer authorities recognized by * the peer (if any). - * - * @since 1.5 */ @Override - public String chooseEngineClientAlias(String[] keyType, + public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) { - /* - * If we ever start using socket as a selection criteria, - * we'll need to adjust this. - */ - return chooseClientAlias(keyType, issuers, null); + return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, + getAlgorithmConstraints(engine), null, null); } /* @@ -251,35 +203,9 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { - /* - * We currently don't do anything with socket, but - * someday we might. It might be a useful hint for - * selecting one of the aliases we get back from - * getServerAliases(). - */ - if (keyType == null) { - return null; - } - - String[] aliases; - - if (issuers == null || issuers.length == 0) { - aliases = serverAliasCache.get(keyType); - if (aliases == null) { - aliases = getServerAliases(keyType, issuers); - // Cache the result (positive and negative lookups) - if (aliases == null) { - aliases = STRING0; - } - serverAliasCache.put(keyType, aliases); - } - } else { - aliases = getServerAliases(keyType, issuers); - } - if ((aliases != null) && (aliases.length > 0)) { - return aliases[0]; - } - return null; + return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, + getAlgorithmConstraints(socket), + X509TrustManagerImpl.getRequestedServerNames(socket), "HTTPS"); } /* @@ -287,17 +213,13 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { * SSLEngine connection given the public key type * and the list of certificate issuer authorities recognized by * the peer (if any). - * - * @since 1.5 */ @Override public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { - /* - * If we ever start using socket as a selection criteria, - * we'll need to adjust this. - */ - return chooseServerAlias(keyType, issuers, null); + return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, + getAlgorithmConstraints(engine), + X509TrustManagerImpl.getRequestedServerNames(engine), "HTTPS"); } /* @@ -307,7 +229,8 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { */ @Override public String[] getClientAliases(String keyType, Principal[] issuers) { - return getAliases(keyType, issuers); + return getAliases(getKeyTypes(keyType), issuers, CheckType.CLIENT, + null, null, null); } /* @@ -317,7 +240,23 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { */ @Override public String[] getServerAliases(String keyType, Principal[] issuers) { - return getAliases(keyType, issuers); + return getAliases(getKeyTypes(keyType), issuers, CheckType.SERVER, + null, null, null); + } + + private String chooseAlias(List keyTypes, Principal[] issuers, + CheckType checkType, AlgorithmConstraints constraints, + List requestedServerNames, String idAlgorithm) { + + String[] aliases = getAliases( + keyTypes, issuers, checkType, + constraints, requestedServerNames, idAlgorithm); + + if (aliases != null && aliases.length > 0) { + return aliases[0]; + } + + return null; } /* @@ -327,105 +266,47 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { * * Issuers come to us in the form of X500Principal[]. */ - private String[] getAliases(String keyType, Principal[] issuers) { - if (keyType == null) { + private String[] getAliases(List keyTypes, Principal[] issuers, + CheckType checkType, AlgorithmConstraints constraints, + List requestedServerNames, + String idAlgorithm) { + + if (keyTypes == null || keyTypes.isEmpty()) { return null; } - if (issuers == null) { - issuers = new X500Principal[0]; - } - if (!(issuers instanceof X500Principal[])) { - // normally, this will never happen but try to recover if it does - issuers = convertPrincipals(issuers); - } - String sigType; - if (keyType.contains("_")) { - int k = keyType.indexOf('_'); - sigType = keyType.substring(k + 1); - keyType = keyType.substring(0, k); - } else { - sigType = null; - } - X500Principal[] x500Issuers = (X500Principal[])issuers; - // the algorithm below does not produce duplicates, so avoid Set - List aliases = new ArrayList<>(); + Set issuerSet = getIssuerSet(issuers); + List results = null; - for (Map.Entry entry : - credentialsMap.entrySet()) { + for (Map.Entry entry : + credentialsMap.entrySet()) { - String alias = entry.getKey(); - X509Credentials credentials = entry.getValue(); - X509Certificate[] certs = credentials.certificates; + EntryStatus status = checkAlias(0, entry.getKey(), + entry.getValue().certificates, + null, keyTypes, issuerSet, checkType, + constraints, requestedServerNames, idAlgorithm); - if (!keyType.equals(certs[0].getPublicKey().getAlgorithm())) { + if (status == null) { continue; } - if (sigType != null) { - if (certs.length > 1) { - // if possible, check the public key in the issuer cert - if (!sigType.equals( - certs[1].getPublicKey().getAlgorithm())) { - continue; - } - } else { - // Check the signature algorithm of the certificate itself. - // Look for the "withRSA" in "SHA1withRSA", etc. - String sigAlgName = - certs[0].getSigAlgName().toUpperCase(Locale.ENGLISH); - String pattern = "WITH" + - sigType.toUpperCase(Locale.ENGLISH); - if (!sigAlgName.contains(pattern)) { - continue; - } - } + + if (results == null) { + results = new ArrayList<>(); } - if (issuers.length == 0) { - // no issuer specified, match all - aliases.add(alias); - if (SSLLogger.logging && - SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine("matching alias: " + alias); - } - } else { - Set certIssuers = - credentials.getIssuerX500Principals(); - for (int i = 0; i < x500Issuers.length; i++) { - if (certIssuers.contains(issuers[i])) { - aliases.add(alias); - if (SSLLogger.logging && + results.add(status); + } + + if (results == null) { + if (SSLLogger.logging && SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine("matching alias: " + alias); - } - break; - } - } + SSLLogger.fine("KeyMgr: no matching key found"); } + return null; } - String[] aliasStrings = aliases.toArray(STRING0); - return ((aliasStrings.length == 0) ? null : aliasStrings); - } - - /* - * Convert an array of Principals to an array of X500Principals, if - * possible. Principals that cannot be converted are ignored. - */ - private static X500Principal[] convertPrincipals(Principal[] principals) { - List list = new ArrayList<>(principals.length); - for (int i = 0; i < principals.length; i++) { - Principal p = principals[i]; - if (p instanceof X500Principal) { - list.add((X500Principal)p); - } else { - try { - list.add(new X500Principal(p.getName())); - } catch (IllegalArgumentException e) { - // ignore - } - } - } - return list.toArray(new X500Principal[0]); + // Sort results in order of alias preference. + Collections.sort(results); + return results.stream().map(r -> r.alias).toArray(String[]::new); } } diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java new file mode 100644 index 00000000000..00a7ae84352 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.net.Socket; +import java.security.AlgorithmConstraints; +import java.security.Principal; +import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import javax.net.ssl.ExtendedSSLSession; +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.StandardConstants; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.security.auth.x500.X500Principal; +import sun.security.provider.certpath.AlgorithmChecker; +import sun.security.util.KnownOIDs; +import sun.security.validator.Validator; + +/* + * Layer that adds algorithm constraints and certificate checking functionality + * to a key manager: + * 1) Check against peer supported certificate signature algorithms (sent with + * "signature_algorithms_cert" TLS extension). + * 2) Check against local TLS algorithm constraints ("java.security" config + * file). + * 3) Mark alias results based on validity period and certificate extensions, + * so results can be sorted to find the best match. See "CheckResult" and + * "EntryStatus" for details. + */ + +abstract class X509KeyManagerCertChecking extends X509ExtendedKeyManager { + + // Indicates whether we should skip the certificate checks. + private final boolean checksDisabled; + + protected X509KeyManagerCertChecking() { + checksDisabled = isCheckingDisabled(); + } + + abstract boolean isCheckingDisabled(); + + // Entry point to do all certificate checks. + protected EntryStatus checkAlias(int keyStoreIndex, String alias, + Certificate[] chain, Date verificationDate, List keyTypes, + Set issuerSet, CheckType checkType, + AlgorithmConstraints constraints, + List requestedServerNames, String idAlgorithm) { + + // --- Mandatory checks --- + + if ((chain == null) || (chain.length == 0)) { + return null; + } + + for (Certificate cert : chain) { + if (!(cert instanceof X509Certificate)) { + // Not an X509Certificate, ignore this alias + return null; + } + } + + // Check key type, get key type index. + int keyIndex = -1; + int j = 0; + + for (KeyType keyType : keyTypes) { + if (keyType.matches(chain)) { + keyIndex = j; + break; + } + j++; + } + + if (keyIndex == -1) { + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("Ignore alias " + alias + + ": key algorithm does not match"); + } + return null; + } + + // Check issuers + if (issuerSet != null && !issuerSet.isEmpty()) { + boolean found = false; + for (Certificate cert : chain) { + X509Certificate xcert = (X509Certificate) cert; + if (issuerSet.contains(xcert.getIssuerX500Principal())) { + found = true; + break; + } + } + if (!found) { + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine( + "Ignore alias " + alias + + ": issuers do not match"); + } + return null; + } + } + + // --- Optional checks, depending on "checksDisabled" toggle --- + + // Check the algorithm constraints + if (constraints != null && + !conformsToAlgorithmConstraints(constraints, chain, + checkType.getValidator())) { + + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("Ignore alias " + alias + + ": certificate chain does not conform to " + + "algorithm constraints"); + } + return null; + } + + // Endpoint certificate check + CheckResult checkResult = certificateCheck(checkType, + (X509Certificate) chain[0], + verificationDate == null ? new Date() : verificationDate, + requestedServerNames, idAlgorithm); + + return new EntryStatus( + keyStoreIndex, keyIndex, alias, chain, checkResult); + } + + // Gets algorithm constraints of the socket. + protected AlgorithmConstraints getAlgorithmConstraints(Socket socket) { + + if (checksDisabled) { + return null; + } + + if (socket != null && socket.isConnected() && + socket instanceof SSLSocket sslSocket) { + + SSLSession session = sslSocket.getHandshakeSession(); + + if (session != null) { + if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { + String[] peerSupportedSignAlgs = null; + + if (session instanceof ExtendedSSLSession extSession) { + // Peer supported certificate signature algorithms + // sent with "signature_algorithms_cert" TLS extension. + peerSupportedSignAlgs = + extSession.getPeerSupportedSignatureAlgorithms(); + } + + return SSLAlgorithmConstraints.forSocket( + sslSocket, peerSupportedSignAlgs, true); + } + } + + return SSLAlgorithmConstraints.forSocket(sslSocket, true); + } + + return SSLAlgorithmConstraints.DEFAULT; + } + + // Gets algorithm constraints of the engine. + protected AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) { + + if (checksDisabled) { + return null; + } + + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + if (session != null) { + if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { + String[] peerSupportedSignAlgs = null; + + if (session instanceof ExtendedSSLSession extSession) { + // Peer supported certificate signature algorithms + // sent with "signature_algorithms_cert" TLS extension. + peerSupportedSignAlgs = + extSession.getPeerSupportedSignatureAlgorithms(); + } + + return SSLAlgorithmConstraints.forEngine( + engine, peerSupportedSignAlgs, true); + } + } + } + + return SSLAlgorithmConstraints.forEngine(engine, true); + } + + // Algorithm constraints check. + private boolean conformsToAlgorithmConstraints( + AlgorithmConstraints constraints, Certificate[] chain, + String variant) { + + if (checksDisabled) { + return true; + } + + AlgorithmChecker checker = new AlgorithmChecker(constraints, variant); + try { + checker.init(false); + } catch (CertPathValidatorException cpve) { + // unlikely to happen + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine( + "Cannot initialize algorithm constraints checker", + cpve); + } + + return false; + } + + // It is a forward checker, so we need to check from trust to target. + for (int i = chain.length - 1; i >= 0; i--) { + Certificate cert = chain[i]; + try { + // We don't care about the unresolved critical extensions. + checker.check(cert, Collections.emptySet()); + } catch (CertPathValidatorException cpve) { + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("Certificate does not conform to " + + "algorithm constraints", cert, cpve); + } + + return false; + } + } + + return true; + } + + // Certificate check. + private CheckResult certificateCheck( + CheckType checkType, X509Certificate cert, Date date, + List serverNames, String idAlgorithm) { + return checksDisabled ? CheckResult.OK + : checkType.check(cert, date, serverNames, idAlgorithm); + } + + // enum for the result of the extension check + // NOTE: the order of the constants is important as they are used + // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH + enum CheckResult { + OK, // ok or not checked + INSENSITIVE, // server name indication insensitive + EXPIRED, // extensions valid but cert expired + EXTENSION_MISMATCH, // extensions invalid (expiration not checked) + } + + // enum for the type of certificate check we want to perform + // (client or server) + // also includes the check code itself + enum CheckType { + + // enum constant for "no check" (currently not used) + NONE(Collections.emptySet()), + + // enum constant for "tls client" check + // valid EKU for TLS client: any, tls_client + CLIENT(new HashSet<>(List.of( + KnownOIDs.anyExtendedKeyUsage.value(), + KnownOIDs.clientAuth.value() + ))), + + // enum constant for "tls server" check + // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc + SERVER(new HashSet<>(List.of( + KnownOIDs.anyExtendedKeyUsage.value(), + KnownOIDs.serverAuth.value(), + KnownOIDs.NETSCAPE_ExportApproved.value(), + KnownOIDs.MICROSOFT_ExportApproved.value() + ))); + + // set of valid EKU values for this type + final Set validEku; + + CheckType(Set validEku) { + this.validEku = validEku; + } + + private static boolean getBit(boolean[] keyUsage, int bit) { + return (bit < keyUsage.length) && keyUsage[bit]; + } + + // Check if this certificate is appropriate for this type of use. + // First check extensions, if they match then check expiration. + // NOTE: `conformsToAlgorithmConstraints` call above also does some + // basic keyUsage checks. + CheckResult check(X509Certificate cert, Date date, + List serverNames, String idAlgorithm) { + + if (this == NONE) { + return CheckResult.OK; + } + + // check extensions + try { + // check extended key usage + List certEku = cert.getExtendedKeyUsage(); + if ((certEku != null) && + Collections.disjoint(validEku, certEku)) { + // if extension is present and does not contain any of + // the valid EKU OIDs, return extension_mismatch + return CheckResult.EXTENSION_MISMATCH; + } + + // check key usage + boolean[] ku = cert.getKeyUsage(); + if (ku != null) { + String algorithm = cert.getPublicKey().getAlgorithm(); + boolean supportsDigitalSignature = getBit(ku, 0); + switch (algorithm) { + case "RSA": + // require either signature bit + // or if server also allow key encipherment bit + if (!supportsDigitalSignature) { + if (this == CLIENT || !getBit(ku, 2)) { + return CheckResult.EXTENSION_MISMATCH; + } + } + break; + case "RSASSA-PSS": + if (!supportsDigitalSignature && (this == SERVER)) { + return CheckResult.EXTENSION_MISMATCH; + } + break; + case "DSA": + // require signature bit + if (!supportsDigitalSignature) { + return CheckResult.EXTENSION_MISMATCH; + } + break; + case "DH": + // require key agreement bit + if (!getBit(ku, 4)) { + return CheckResult.EXTENSION_MISMATCH; + } + break; + case "EC": + // require signature bit + if (!supportsDigitalSignature) { + return CheckResult.EXTENSION_MISMATCH; + } + // For servers, also require key agreement. + // This is not totally accurate as the keyAgreement + // bit is only necessary for static ECDH key + // exchange and not ephemeral ECDH. We leave it in + // for now until there are signs that this check + // causes problems for real world EC certificates. + if (this == SERVER && !getBit(ku, 4)) { + return CheckResult.EXTENSION_MISMATCH; + } + break; + } + } + } catch (CertificateException e) { + // extensions unparseable, return failure + return CheckResult.EXTENSION_MISMATCH; + } + + try { + cert.checkValidity(date); + } catch (CertificateException e) { + return CheckResult.EXPIRED; + } + + if (serverNames != null && !serverNames.isEmpty()) { + for (SNIServerName serverName : serverNames) { + if (serverName.getType() == + StandardConstants.SNI_HOST_NAME) { + if (!(serverName instanceof SNIHostName)) { + try { + serverName = new SNIHostName( + serverName.getEncoded()); + } catch (IllegalArgumentException iae) { + // unlikely to happen, just in case ... + if (SSLLogger.isOn && + SSLLogger.isOn("keymanager")) { + SSLLogger.fine("Illegal server name: " + + serverName); + } + + return CheckResult.INSENSITIVE; + } + } + String hostname = + ((SNIHostName) serverName).getAsciiName(); + + try { + X509TrustManagerImpl.checkIdentity(hostname, + cert, idAlgorithm); + } catch (CertificateException e) { + if (SSLLogger.isOn && + SSLLogger.isOn("keymanager")) { + SSLLogger.fine( + "Certificate identity does not match " + + "Server Name Indication (SNI): " + + hostname); + } + return CheckResult.INSENSITIVE; + } + + break; + } + } + } + + return CheckResult.OK; + } + + String getValidator() { + if (this == CLIENT) { + return Validator.VAR_TLS_CLIENT; + } else if (this == SERVER) { + return Validator.VAR_TLS_SERVER; + } + return Validator.VAR_GENERIC; + } + } + + // A candidate match. + // Identifies the entry by key store index and alias + // and includes the result of the certificate check. + protected static class EntryStatus implements Comparable { + + final int keyStoreIndex; + final int keyIndex; + final String alias; + final CheckResult checkResult; + + EntryStatus(int keyStoreIndex, int keyIndex, String alias, + Certificate[] chain, CheckResult checkResult) { + this.keyStoreIndex = keyStoreIndex; + this.keyIndex = keyIndex; + this.alias = alias; + this.checkResult = checkResult; + } + + @Override + public int compareTo(EntryStatus other) { + int result = this.checkResult.compareTo(other.checkResult); + return (result == 0) ? (this.keyIndex - other.keyIndex) : result; + } + + @Override + public String toString() { + String s = alias + " (verified: " + checkResult + ")"; + if (keyStoreIndex == 0) { + return s; + } else { + return "KeyStore #" + keyStoreIndex + ", alias: " + s; + } + } + } + + // Class to help verify that the public key algorithm (and optionally + // the signature algorithm) of a certificate matches what we need. + protected static class KeyType { + + final String keyAlgorithm; + + // In TLS 1.2, the signature algorithm has been obsoleted by the + // supported_signature_algorithms, and the certificate type no longer + // restricts the algorithm used to sign the certificate. + // + // However, because we don't support certificate type checking other + // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the + // protocol version here. + final String sigKeyAlgorithm; + + KeyType(String algorithm) { + int k = algorithm.indexOf('_'); + if (k == -1) { + keyAlgorithm = algorithm; + sigKeyAlgorithm = null; + } else { + keyAlgorithm = algorithm.substring(0, k); + sigKeyAlgorithm = algorithm.substring(k + 1); + } + } + + boolean matches(Certificate[] chain) { + if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) { + return false; + } + if (sigKeyAlgorithm == null) { + return true; + } + if (chain.length > 1) { + // if possible, check the public key in the issuer cert + return sigKeyAlgorithm.equals( + chain[1].getPublicKey().getAlgorithm()); + } else { + // Check the signature algorithm of the certificate itself. + // Look for the "withRSA" in "SHA1withRSA", etc. + X509Certificate issuer = (X509Certificate) chain[0]; + String sigAlgName = + issuer.getSigAlgName().toUpperCase(Locale.ENGLISH); + String pattern = + "WITH" + sigKeyAlgorithm.toUpperCase(Locale.ENGLISH); + return sigAlgName.endsWith(pattern); + } + } + } + + // Make a list of key types. + protected static List getKeyTypes(String... keyTypes) { + if ((keyTypes == null) || + (keyTypes.length == 0) || (keyTypes[0] == null)) { + return null; + } + List list = new ArrayList<>(keyTypes.length); + for (String keyType : keyTypes) { + list.add(new KeyType(keyType)); + } + return list; + } + + // Make a set out of the array. + protected static Set getIssuerSet(Principal[] issuers) { + + if (issuers != null && issuers.length != 0) { + Set ret = new HashSet<>(issuers.length); + + for (Principal p : issuers) { + if (p instanceof X500Principal) { + ret.add((X500Principal) p); + } else { + // Normally, this will never happen but try to recover if + // it does. + try { + ret.add(new X500Principal(p.getName())); + } catch (Exception e) { + // ignore + } + } + } + return ret.isEmpty() ? null : ret; + } else { + return null; + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java index db8d993aaf6..55e3208c06f 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,18 +32,14 @@ import java.security.KeyStore; import java.security.KeyStore.Builder; import java.security.KeyStore.Entry; import java.security.KeyStore.PrivateKeyEntry; +import java.security.KeyStoreException; import java.security.Principal; import java.security.PrivateKey; -import java.security.cert.CertPathValidatorException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.*; import java.util.concurrent.atomic.AtomicLong; import javax.net.ssl.*; -import sun.security.provider.certpath.AlgorithmChecker; -import sun.security.validator.Validator; -import sun.security.util.KnownOIDs; +import javax.security.auth.x500.X500Principal; /** * The new X509 key manager implementation. The main differences to the @@ -62,8 +58,8 @@ import sun.security.util.KnownOIDs; * * @author Andreas Sterbenz */ -final class X509KeyManagerImpl extends X509ExtendedKeyManager - implements X509KeyManager { + +final class X509KeyManagerImpl extends X509KeyManagerCertChecking { // for unit testing only, set via privileged reflection private static Date verificationDate; @@ -84,12 +80,15 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager X509KeyManagerImpl(List builders) { this.builders = builders; uidCounter = new AtomicLong(); - entryCacheMap = Collections.synchronizedMap - (new SizedMap<>()); + entryCacheMap = Collections.synchronizedMap(new SizedMap<>()); } - // LinkedHashMap with a max size of 10 - // see LinkedHashMap JavaDocs + @Override + protected boolean isCheckingDisabled() { + return false; + } + + // LinkedHashMap with a max size of 10, see LinkedHashMap JavaDocs private static class SizedMap extends LinkedHashMap { @java.io.Serial private static final long serialVersionUID = -8211222668790986062L; @@ -120,14 +119,14 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, - getAlgorithmConstraints(socket)); + getAlgorithmConstraints(socket), null, null); } @Override public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) { return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, - getAlgorithmConstraints(engine)); + getAlgorithmConstraints(engine), null, null); } @Override @@ -168,73 +167,24 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager @Override public String[] getClientAliases(String keyType, Principal[] issuers) { - return getAliases(keyType, issuers, CheckType.CLIENT, null); + return getAliases(keyType, issuers, CheckType.CLIENT); } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { - return getAliases(keyType, issuers, CheckType.SERVER, null); + return getAliases(keyType, issuers, CheckType.SERVER); } // // implementation private methods // - // Gets algorithm constraints of the socket. - private AlgorithmConstraints getAlgorithmConstraints(Socket socket) { - if (socket != null && socket.isConnected() && - socket instanceof SSLSocket sslSocket) { - - SSLSession session = sslSocket.getHandshakeSession(); - - if (session != null) { - if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { - String[] peerSupportedSignAlgs = null; - - if (session instanceof ExtendedSSLSession extSession) { - peerSupportedSignAlgs = - extSession.getPeerSupportedSignatureAlgorithms(); - } - - return SSLAlgorithmConstraints.forSocket( - sslSocket, peerSupportedSignAlgs, true); - } - } - - return SSLAlgorithmConstraints.forSocket(sslSocket, true); - } - - return SSLAlgorithmConstraints.DEFAULT; - } - - // Gets algorithm constraints of the engine. - private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) { - if (engine != null) { - SSLSession session = engine.getHandshakeSession(); - if (session != null) { - if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { - String[] peerSupportedSignAlgs = null; - - if (session instanceof ExtendedSSLSession extSession) { - peerSupportedSignAlgs = - extSession.getPeerSupportedSignatureAlgorithms(); - } - - return SSLAlgorithmConstraints.forEngine( - engine, peerSupportedSignAlgs, true); - } - } - } - - return SSLAlgorithmConstraints.forEngine(engine, true); - } - // we construct the alias we return to JSSE as seen in the code below // a unique id is included to allow us to reliably cache entries // between the calls to getCertificateChain() and getPrivateKey() // even if tokens are inserted or removed private String makeAlias(EntryStatus entry) { - return uidCounter.incrementAndGet() + "." + entry.builderIndex + "." + return uidCounter.incrementAndGet() + "." + entry.keyStoreIndex + "." + entry.alias; } @@ -279,68 +229,6 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager } } - // Class to help verify that the public key algorithm (and optionally - // the signature algorithm) of a certificate matches what we need. - private static class KeyType { - - final String keyAlgorithm; - - // In TLS 1.2, the signature algorithm has been obsoleted by the - // supported_signature_algorithms, and the certificate type no longer - // restricts the algorithm used to sign the certificate. - // - // However, because we don't support certificate type checking other - // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the - // protocol version here. - final String sigKeyAlgorithm; - - KeyType(String algorithm) { - int k = algorithm.indexOf('_'); - if (k == -1) { - keyAlgorithm = algorithm; - sigKeyAlgorithm = null; - } else { - keyAlgorithm = algorithm.substring(0, k); - sigKeyAlgorithm = algorithm.substring(k + 1); - } - } - - boolean matches(Certificate[] chain) { - if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) { - return false; - } - if (sigKeyAlgorithm == null) { - return true; - } - if (chain.length > 1) { - // if possible, check the public key in the issuer cert - return sigKeyAlgorithm.equals( - chain[1].getPublicKey().getAlgorithm()); - } else { - // Check the signature algorithm of the certificate itself. - // Look for the "withRSA" in "SHA1withRSA", etc. - X509Certificate issuer = (X509Certificate)chain[0]; - String sigAlgName = - issuer.getSigAlgName().toUpperCase(Locale.ENGLISH); - String pattern = - "WITH" + sigKeyAlgorithm.toUpperCase(Locale.ENGLISH); - return sigAlgName.contains(pattern); - } - } - } - - private static List getKeyTypes(String ... keyTypes) { - if ((keyTypes == null) || - (keyTypes.length == 0) || (keyTypes[0] == null)) { - return null; - } - List list = new ArrayList<>(keyTypes.length); - for (String keyType : keyTypes) { - list.add(new KeyType(keyType)); - } - return list; - } - /* * Return the best alias that fits the given parameters. * The algorithm we use is: @@ -354,13 +242,6 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager * with appropriate key usage to certs with the wrong key usage. * return the first one of them. */ - private String chooseAlias(List keyTypeList, Principal[] issuers, - CheckType checkType, AlgorithmConstraints constraints) { - - return chooseAlias(keyTypeList, issuers, - checkType, constraints, null, null); - } - private String chooseAlias(List keyTypeList, Principal[] issuers, CheckType checkType, AlgorithmConstraints constraints, List requestedServerNames, String idAlgorithm) { @@ -369,8 +250,9 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager return null; } - Set issuerSet = getIssuerSet(issuers); + Set issuerSet = getIssuerSet(issuers); List allResults = null; + for (int i = 0, n = builders.size(); i < n; i++) { try { List results = getAliases(i, keyTypeList, @@ -391,7 +273,7 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager } allResults.addAll(results); } - } catch (Exception e) { + } catch (KeyStoreException e) { // ignore } } @@ -416,27 +298,28 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager * and certificates with the wrong extensions). * The perfect matches will be first in the array. */ - public String[] getAliases(String keyType, Principal[] issuers, - CheckType checkType, AlgorithmConstraints constraints) { + private String[] getAliases( + String keyType, Principal[] issuers, CheckType checkType) { + if (keyType == null) { return null; } - Set issuerSet = getIssuerSet(issuers); + Set issuerSet = getIssuerSet(issuers); List keyTypeList = getKeyTypes(keyType); List allResults = null; + for (int i = 0, n = builders.size(); i < n; i++) { try { List results = getAliases(i, keyTypeList, - issuerSet, true, checkType, constraints, - null, null); + issuerSet, true, checkType, null, null, null); if (results != null) { if (allResults == null) { allResults = new ArrayList<>(); } allResults.addAll(results); } - } catch (Exception e) { + } catch (KeyStoreException e) { // ignore } } @@ -463,232 +346,6 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager return s; } - // make a Set out of the array - private Set getIssuerSet(Principal[] issuers) { - if ((issuers != null) && (issuers.length != 0)) { - return new HashSet<>(Arrays.asList(issuers)); - } else { - return null; - } - } - - // a candidate match - // identifies the entry by builder and alias - // and includes the result of the certificate check - private static class EntryStatus implements Comparable { - - final int builderIndex; - final int keyIndex; - final String alias; - final CheckResult checkResult; - - EntryStatus(int builderIndex, int keyIndex, String alias, - Certificate[] chain, CheckResult checkResult) { - this.builderIndex = builderIndex; - this.keyIndex = keyIndex; - this.alias = alias; - this.checkResult = checkResult; - } - - @Override - public int compareTo(EntryStatus other) { - int result = this.checkResult.compareTo(other.checkResult); - return (result == 0) ? (this.keyIndex - other.keyIndex) : result; - } - - @Override - public String toString() { - String s = alias + " (verified: " + checkResult + ")"; - if (builderIndex == 0) { - return s; - } else { - return "Builder #" + builderIndex + ", alias: " + s; - } - } - } - - // enum for the type of certificate check we want to perform - // (client or server) - // also includes the check code itself - private enum CheckType { - - // enum constant for "no check" (currently not used) - NONE(Collections.emptySet()), - - // enum constant for "tls client" check - // valid EKU for TLS client: any, tls_client - CLIENT(new HashSet<>(List.of( - KnownOIDs.anyExtendedKeyUsage.value(), - KnownOIDs.clientAuth.value() - ))), - - // enum constant for "tls server" check - // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc - SERVER(new HashSet<>(List.of( - KnownOIDs.anyExtendedKeyUsage.value(), - KnownOIDs.serverAuth.value(), - KnownOIDs.NETSCAPE_ExportApproved.value(), - KnownOIDs.MICROSOFT_ExportApproved.value() - ))); - - // set of valid EKU values for this type - final Set validEku; - - CheckType(Set validEku) { - this.validEku = validEku; - } - - private static boolean getBit(boolean[] keyUsage, int bit) { - return (bit < keyUsage.length) && keyUsage[bit]; - } - - // Check if this certificate is appropriate for this type of use - // first check extensions, if they match, check expiration. - // - // Note: we may want to move this code into the sun.security.validator - // package - CheckResult check(X509Certificate cert, Date date, - List serverNames, String idAlgorithm) { - - if (this == NONE) { - return CheckResult.OK; - } - - // check extensions - try { - // check extended key usage - List certEku = cert.getExtendedKeyUsage(); - if ((certEku != null) && - Collections.disjoint(validEku, certEku)) { - // if extension is present and does not contain any of - // the valid EKU OIDs, return extension_mismatch - return CheckResult.EXTENSION_MISMATCH; - } - - // check key usage - boolean[] ku = cert.getKeyUsage(); - if (ku != null) { - String algorithm = cert.getPublicKey().getAlgorithm(); - boolean supportsDigitalSignature = getBit(ku, 0); - switch (algorithm) { - case "RSA": - // require either signature bit - // or if server also allow key encipherment bit - if (!supportsDigitalSignature) { - if (this == CLIENT || !getBit(ku, 2)) { - return CheckResult.EXTENSION_MISMATCH; - } - } - break; - case "RSASSA-PSS": - if (!supportsDigitalSignature && (this == SERVER)) { - return CheckResult.EXTENSION_MISMATCH; - } - break; - case "DSA": - // require signature bit - if (!supportsDigitalSignature) { - return CheckResult.EXTENSION_MISMATCH; - } - break; - case "DH": - // require keyagreement bit - if (!getBit(ku, 4)) { - return CheckResult.EXTENSION_MISMATCH; - } - break; - case "EC": - // require signature bit - if (!supportsDigitalSignature) { - return CheckResult.EXTENSION_MISMATCH; - } - // For servers, also require key agreement. - // This is not totally accurate as the keyAgreement - // bit is only necessary for static ECDH key - // exchange and not ephemeral ECDH. We leave it in - // for now until there are signs that this check - // causes problems for real world EC certificates. - if (this == SERVER && !getBit(ku, 4)) { - return CheckResult.EXTENSION_MISMATCH; - } - break; - } - } - } catch (CertificateException e) { - // extensions unparseable, return failure - return CheckResult.EXTENSION_MISMATCH; - } - - try { - cert.checkValidity(date); - } catch (CertificateException e) { - return CheckResult.EXPIRED; - } - - if (serverNames != null && !serverNames.isEmpty()) { - for (SNIServerName serverName : serverNames) { - if (serverName.getType() == - StandardConstants.SNI_HOST_NAME) { - if (!(serverName instanceof SNIHostName)) { - try { - serverName = - new SNIHostName(serverName.getEncoded()); - } catch (IllegalArgumentException iae) { - // unlikely to happen, just in case ... - if (SSLLogger.logging && - SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine( - "Illegal server name: " + serverName); - } - - return CheckResult.INSENSITIVE; - } - } - String hostname = - ((SNIHostName)serverName).getAsciiName(); - - try { - X509TrustManagerImpl.checkIdentity(hostname, - cert, idAlgorithm); - } catch (CertificateException e) { - if (SSLLogger.logging && - SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine( - "Certificate identity does not match " + - "Server Name Indication (SNI): " + - hostname); - } - return CheckResult.INSENSITIVE; - } - - break; - } - } - } - - return CheckResult.OK; - } - - public String getValidator() { - if (this == CLIENT) { - return Validator.VAR_TLS_CLIENT; - } else if (this == SERVER) { - return Validator.VAR_TLS_SERVER; - } - return Validator.VAR_GENERIC; - } - } - - // enum for the result of the extension check - // NOTE: the order of the constants is important as they are used - // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH - private enum CheckResult { - OK, // ok or not checked - INSENSITIVE, // server name indication insensitive - EXPIRED, // extensions valid but cert expired - EXTENSION_MISMATCH, // extensions invalid (expiration not checked) - } - /* * Return a List of all candidate matches in the specified builder * that fit the parameters. @@ -716,109 +373,42 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager * matches */ private List getAliases(int builderIndex, - List keyTypes, Set issuerSet, + List keyTypes, Set issuerSet, boolean findAll, CheckType checkType, AlgorithmConstraints constraints, List requestedServerNames, - String idAlgorithm) throws Exception { + String idAlgorithm) throws KeyStoreException { Builder builder = builders.get(builderIndex); KeyStore ks = builder.getKeyStore(); List results = null; - Date date = verificationDate; boolean preferred = false; + for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) { + String alias = e.nextElement(); + // check if it is a key entry (private key or secret key) if (!ks.isKeyEntry(alias)) { continue; } - Certificate[] chain = ks.getCertificateChain(alias); - if ((chain == null) || (chain.length == 0)) { - // must be secret key entry, ignore + EntryStatus status = checkAlias(builderIndex, alias, + ks.getCertificateChain(alias), + verificationDate, keyTypes, issuerSet, checkType, + constraints, requestedServerNames, idAlgorithm); + + if (status == null) { continue; } - boolean incompatible = false; - for (Certificate cert : chain) { - if (!(cert instanceof X509Certificate)) { - // not an X509Certificate, ignore this alias - incompatible = true; - break; - } - } - if (incompatible) { - continue; - } - - // check keytype - int keyIndex = -1; - int j = 0; - for (KeyType keyType : keyTypes) { - if (keyType.matches(chain)) { - keyIndex = j; - break; - } - j++; - } - if (keyIndex == -1) { - if (SSLLogger.logging && - SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine("Ignore alias " + alias - + ": key algorithm does not match"); - } - continue; - } - // check issuers - if (issuerSet != null) { - boolean found = false; - for (Certificate cert : chain) { - X509Certificate xcert = (X509Certificate)cert; - if (issuerSet.contains(xcert.getIssuerX500Principal())) { - found = true; - break; - } - } - if (!found) { - if (SSLLogger.logging && - SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine( - "Ignore alias " + alias - + ": issuers do not match"); - } - continue; - } - } - - // check the algorithm constraints - if (constraints != null && - !conformsToAlgorithmConstraints(constraints, chain, - checkType.getValidator())) { - - if (SSLLogger.logging && - SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine("Ignore alias " + alias + - ": certificate list does not conform to " + - "algorithm constraints"); - } - continue; - } - - if (date == null) { - date = new Date(); - } - CheckResult checkResult = - checkType.check((X509Certificate)chain[0], date, - requestedServerNames, idAlgorithm); - EntryStatus status = - new EntryStatus(builderIndex, keyIndex, - alias, chain, checkResult); - if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) { + if (!preferred && status.checkResult == CheckResult.OK + && status.keyIndex == 0) { preferred = true; } + if (preferred && !findAll) { - // if we have a good match and do not need all matches, + // If we have a good match and do not need all matches, // return immediately return Collections.singletonList(status); } else { @@ -828,43 +418,7 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager results.add(status); } } + return results; } - - private static boolean conformsToAlgorithmConstraints( - AlgorithmConstraints constraints, Certificate[] chain, - String variant) { - - AlgorithmChecker checker = new AlgorithmChecker(constraints, variant); - try { - checker.init(false); - } catch (CertPathValidatorException cpve) { - // unlikely to happen - if (SSLLogger.logging && SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine( - "Cannot initialize algorithm constraints checker", cpve); - } - - return false; - } - - // It is a forward checker, so we need to check from trust to target. - for (int i = chain.length - 1; i >= 0; i--) { - Certificate cert = chain[i]; - try { - // We don't care about the unresolved critical extensions. - checker.check(cert, Collections.emptySet()); - } catch (CertPathValidatorException cpve) { - if (SSLLogger.logging && - SSLLogger.isOn(SSLLogger.Opt.KEYMANAGER)) { - SSLLogger.fine("Certificate does not conform to " + - "algorithm constraints", cert, cpve); - } - - return false; - } - } - - return true; - } } diff --git a/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java index 4c4906d8eb3..f93e534f46a 100644 --- a/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java +++ b/src/java.base/share/classes/sun/security/validator/EntrustTLSPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,19 +71,7 @@ final class EntrustTLSPolicy { // OU=(c) 1999 Entrust.net Limited, // OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), // O=Entrust.net - "6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177", - // cacerts alias: affirmtrustcommercialca - // DN: CN=AffirmTrust Commercial, O=AffirmTrust, C=US - "0376AB1D54C5F9803CE4B2E201A0EE7EEF7B57B636E8A93C9B8D4860C96F5FA7", - // cacerts alias: affirmtrustnetworkingca - // DN: CN=AffirmTrust Networking, O=AffirmTrust, C=US - "0A81EC5A929777F145904AF38D5D509F66B5E2C58FCDB531058B0E17F3F0B41B", - // cacerts alias: affirmtrustpremiumca - // DN: CN=AffirmTrust Premium, O=AffirmTrust, C=US - "70A73F7F376B60074248904534B11482D5BF0E698ECC498DF52577EBF2E93B9A", - // cacerts alias: affirmtrustpremiumeccca - // DN: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US - "BD71FDF6DA97E4CF62D1647ADD2581B07D79ADF8397EB4ECBA9C5E8488821423" + "6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177" ); // Any TLS Server certificate that is anchored by one of the Entrust diff --git a/src/java.base/share/data/cacerts/affirmtrustcommercialca b/src/java.base/share/data/cacerts/affirmtrustcommercialca deleted file mode 100644 index 5caddfd3a0a..00000000000 --- a/src/java.base/share/data/cacerts/affirmtrustcommercialca +++ /dev/null @@ -1,27 +0,0 @@ -Owner: CN=AffirmTrust Commercial, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Commercial, O=AffirmTrust, C=US -Serial number: 7777062726a9b17c -Valid from: Fri Jan 29 14:06:06 GMT 2010 until: Tue Dec 31 14:06:06 GMT 2030 -Signature algorithm name: SHA256withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP -Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr -ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL -MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 -yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr -VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ -nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG -XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj -vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt -Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g -N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC -nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- diff --git a/src/java.base/share/data/cacerts/affirmtrustnetworkingca b/src/java.base/share/data/cacerts/affirmtrustnetworkingca deleted file mode 100644 index c773326d4b9..00000000000 --- a/src/java.base/share/data/cacerts/affirmtrustnetworkingca +++ /dev/null @@ -1,27 +0,0 @@ -Owner: CN=AffirmTrust Networking, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Networking, O=AffirmTrust, C=US -Serial number: 7c4f04391cd4992d -Valid from: Fri Jan 29 14:08:24 GMT 2010 until: Tue Dec 31 14:08:24 GMT 2030 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y -YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua -kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL -QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp -6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG -yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i -QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO -tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu -QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ -Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u -olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 -x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- diff --git a/src/java.base/share/data/cacerts/affirmtrustpremiumca b/src/java.base/share/data/cacerts/affirmtrustpremiumca deleted file mode 100644 index 275b495a25e..00000000000 --- a/src/java.base/share/data/cacerts/affirmtrustpremiumca +++ /dev/null @@ -1,38 +0,0 @@ -Owner: CN=AffirmTrust Premium, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Premium, O=AffirmTrust, C=US -Serial number: 6d8c1446b1a60aee -Valid from: Fri Jan 29 14:10:36 GMT 2010 until: Mon Dec 31 14:10:36 GMT 2040 -Signature algorithm name: SHA384withRSA -Subject Public Key Algorithm: 4096-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz -dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG -A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U -cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf -qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ -JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ -+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS -s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 -HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 -70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG -V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S -qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S -5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia -C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX -OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE -FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 -KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B -8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ -MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc -0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ -u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF -u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH -YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 -GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO -RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e -KeC2uAloGRwYQw== ------END CERTIFICATE----- diff --git a/src/java.base/share/data/cacerts/affirmtrustpremiumeccca b/src/java.base/share/data/cacerts/affirmtrustpremiumeccca deleted file mode 100644 index d0fcc1e7793..00000000000 --- a/src/java.base/share/data/cacerts/affirmtrustpremiumeccca +++ /dev/null @@ -1,20 +0,0 @@ -Owner: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US -Issuer: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US -Serial number: 7497258ac73f7a54 -Valid from: Fri Jan 29 14:20:24 GMT 2010 until: Mon Dec 31 14:20:24 GMT 2040 -Signature algorithm name: SHA384withECDSA -Subject Public Key Algorithm: 384-bit EC (secp384r1) key -Version: 3 ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC -VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ -cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ -BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt -VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D -0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 -ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G -A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs -aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I -flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== ------END CERTIFICATE----- diff --git a/src/java.base/share/native/libjava/Class.c b/src/java.base/share/native/libjava/Class.c index 3ab3e764bff..9fb348d9217 100644 --- a/src/java.base/share/native/libjava/Class.c +++ b/src/java.base/share/native/libjava/Class.c @@ -75,7 +75,6 @@ static JNINativeMethod methods[] = { {"isRecord0", "()Z", (void *)&JVM_IsRecord}, {"getPermittedSubclasses0", "()[" CLS, (void *)&JVM_GetPermittedSubclasses}, {"getClassFileVersion0", "()I", (void *)&JVM_GetClassFileVersion}, - {"getClassAccessFlagsRaw0", "()I", (void *)&JVM_GetClassAccessFlags}, }; #undef OBJ diff --git a/src/java.base/share/native/libjava/Reflection.c b/src/java.base/share/native/libjava/Reflection.c index d6722feb7d1..d556ee92310 100644 --- a/src/java.base/share/native/libjava/Reflection.c +++ b/src/java.base/share/native/libjava/Reflection.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,12 +33,6 @@ Java_jdk_internal_reflect_Reflection_getCallerClass(JNIEnv *env, jclass unused) return JVM_GetCallerClass(env); } -JNIEXPORT jint JNICALL -Java_jdk_internal_reflect_Reflection_getClassAccessFlags(JNIEnv *env, jclass unused, jclass cls) -{ - return JVM_GetClassAccessFlags(env, cls); -} - JNIEXPORT jboolean JNICALL Java_jdk_internal_reflect_Reflection_areNestMates(JNIEnv *env, jclass unused, jclass current, jclass member) { diff --git a/src/java.base/unix/native/libjava/childproc.c b/src/java.base/unix/native/libjava/childproc.c index 0dc0788879e..93d2200a465 100644 --- a/src/java.base/unix/native/libjava/childproc.c +++ b/src/java.base/unix/native/libjava/childproc.c @@ -372,7 +372,7 @@ childProcess(void *arg) jtregSimulateCrash(0, 6); #endif /* Close the parent sides of the pipes. - Closing pipe fds here is redundant, since closeDescriptors() + Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() would do it anyways, but a little paranoia is a good thing. */ if ((closeSafely(p->in[1]) == -1) || (closeSafely(p->out[0]) == -1) || diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java index 46315533515..b1de66ac4f2 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,8 +72,9 @@ class WindowsConstants { public static final int BACKUP_SPARSE_BLOCK = 0x00000009; // reparse point/symbolic link related constants - public static final int IO_REPARSE_TAG_SYMLINK = 0xA000000C; public static final int IO_REPARSE_TAG_AF_UNIX = 0x80000023; + public static final int IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003; + public static final int IO_REPARSE_TAG_SYMLINK = 0xA000000C; public static final int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; public static final int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1; public static final int SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2; diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java index 6e544b1c926..3c94e8bc4a2 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -412,6 +412,10 @@ class WindowsFileAttributes return isSymbolicLink() && ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0); } + boolean isDirectoryJunction() { + return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + } + @Override public boolean isSymbolicLink() { return reparseTag == IO_REPARSE_TAG_SYMLINK; @@ -423,10 +427,8 @@ class WindowsFileAttributes @Override public boolean isDirectory() { - // ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link - if (isSymbolicLink()) - return false; - return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0); + return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0 && + (fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0); } @Override diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java index 7c280d87f62..3a1bb416fe7 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java @@ -243,7 +243,8 @@ class WindowsFileSystemProvider try { // need to know if file is a directory or junction attrs = WindowsFileAttributes.get(file, false); - if (attrs.isDirectory() || attrs.isDirectoryLink()) { + if (attrs.isDirectory() || attrs.isDirectoryLink() || + attrs.isDirectoryJunction()) { RemoveDirectory(file.getPathForWin32Calls()); } else { DeleteFile(file.getPathForWin32Calls()); diff --git a/src/java.desktop/macosx/classes/apple/laf/JRSUIControl.java b/src/java.desktop/macosx/classes/apple/laf/JRSUIControl.java index f1f5ebe5ff1..f28e05d1877 100644 --- a/src/java.desktop/macosx/classes/apple/laf/JRSUIControl.java +++ b/src/java.desktop/macosx/classes/apple/laf/JRSUIControl.java @@ -27,6 +27,8 @@ package apple.laf; import java.nio.*; import java.util.*; +import sun.java2d.Disposer; +import sun.java2d.DisposerRecord; import apple.laf.JRSUIConstants.*; @@ -91,7 +93,8 @@ public final class JRSUIControl { private final HashMap nativeMap; private final HashMap changes; - private long cfDictionaryPtr; + private final long cfDictionaryPtr; + private final Object disposerReferent = new Object(); private long priorEncodedProperties; private long currentEncodedProperties; @@ -101,6 +104,7 @@ public final class JRSUIControl { this.flipped = flipped; cfDictionaryPtr = getCFDictionary(flipped); if (cfDictionaryPtr == 0) throw new RuntimeException("Unable to create native representation"); + Disposer.addRecord(disposerReferent, new JRSUIControlDisposerRecord(cfDictionaryPtr)); nativeMap = new HashMap(); changes = new HashMap(); } @@ -109,17 +113,25 @@ public final class JRSUIControl { flipped = other.flipped; cfDictionaryPtr = getCFDictionary(flipped); if (cfDictionaryPtr == 0) throw new RuntimeException("Unable to create native representation"); + Disposer.addRecord(disposerReferent, new JRSUIControlDisposerRecord(cfDictionaryPtr)); nativeMap = new HashMap(); changes = new HashMap(other.nativeMap); changes.putAll(other.changes); } - @Override - @SuppressWarnings("removal") - protected synchronized void finalize() throws Throwable { - if (cfDictionaryPtr == 0) return; - disposeCFDictionary(cfDictionaryPtr); - cfDictionaryPtr = 0; + private static class JRSUIControlDisposerRecord implements DisposerRecord { + + private final long cfDictionaryPtr; + JRSUIControlDisposerRecord(long ptr) { + cfDictionaryPtr = ptr; + } + + public void dispose() { + try { + disposeCFDictionary(cfDictionaryPtr); + } catch (Throwable t) { + } + } } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java index c6224c63543..8d160230844 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java @@ -157,6 +157,9 @@ public final class AquaKeyBindings { "shift alt KP_LEFT", null, "shift alt RIGHT", null, "shift alt KP_RIGHT", null, + "alt BACK_SPACE", null, + "ctrl W", null, + "alt DELETE", null, })); } diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index 69b53433c4f..4b0ac9fd8d3 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -39,7 +39,7 @@ public final class CStrike extends PhysicalStrike { int fmHint); // Disposes the native strike - private static native void disposeNativeStrikePtr(long nativeStrikePtr); + static native void disposeNativeStrikePtr(long nativeStrikePtr); // Creates a StrikeMetrics from the underlying native system fonts private static native StrikeMetrics getFontMetrics(long nativeStrikePtr); @@ -70,14 +70,11 @@ public final class CStrike extends PhysicalStrike { private AffineTransform invDevTx; private final GlyphInfoCache glyphInfoCache; private final GlyphAdvanceCache glyphAdvanceCache; - private long nativeStrikePtr; + private final long nativeStrikePtr; CStrike(final CFont font, final FontStrikeDesc inDesc) { nativeFont = font; desc = inDesc; - glyphInfoCache = new GlyphInfoCache(font, desc); - glyphAdvanceCache = new GlyphAdvanceCache(); - disposer = glyphInfoCache; // Normally the device transform should be the identity transform // for screen operations. The device transform only becomes @@ -92,12 +89,18 @@ public final class CStrike extends PhysicalStrike { // so we won't worry about it. } } + nativeStrikePtr = initNativeStrikePtr(); // after setting up invDevTx + glyphInfoCache = new GlyphInfoCache(font, desc, nativeStrikePtr); + glyphAdvanceCache = new GlyphAdvanceCache(); + disposer = glyphInfoCache; } public long getNativeStrikePtr() { - if (nativeStrikePtr != 0) { - return nativeStrikePtr; - } + return nativeStrikePtr; + } + + public long initNativeStrikePtr() { + long nativeStrikePtr = 0L; final double[] glyphTx = new double[6]; desc.glyphTx.getMatrix(glyphTx); @@ -136,16 +139,6 @@ public final class CStrike extends PhysicalStrike { return nativeStrikePtr; } - @Override - @SuppressWarnings("removal") - protected synchronized void finalize() throws Throwable { - if (nativeStrikePtr != 0) { - disposeNativeStrikePtr(nativeStrikePtr); - } - nativeStrikePtr = 0; - } - - @Override public int getNumGlyphs() { return nativeFont.getNumGlyphs(); @@ -379,8 +372,8 @@ public final class CStrike extends PhysicalStrike { private SparseBitShiftingTwoLayerArray secondLayerCache; private HashMap generalCache; - GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) { - super(nativeFont, desc); + GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc, long pScalerContext) { + super(nativeFont, desc, pScalerContext); firstLayerCache = new long[FIRST_LAYER_SIZE]; } diff --git a/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java b/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java index f032d716e90..c37209c8396 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java @@ -48,7 +48,7 @@ package sun.font; */ class CStrikeDisposer extends FontStrikeDisposer { - long pNativeScalerContext; + private final long pNativeScalerContext; public CStrikeDisposer(Font2D font2D, FontStrikeDesc desc, long pContext, int[] images) @@ -73,19 +73,18 @@ class CStrikeDisposer extends FontStrikeDisposer { public CStrikeDisposer(Font2D font2D, FontStrikeDesc desc) { super(font2D, desc); + pNativeScalerContext = 0L; } @Override public synchronized void dispose() { if (!disposed) { if (pNativeScalerContext != 0L) { - freeNativeScalerContext(pNativeScalerContext); + CStrike.disposeNativeStrikePtr(pNativeScalerContext); } super.dispose(); } } - private native void freeNativeScalerContext(long pContext); - protected static native void removeGlyphInfoFromCache(long glyphInfo); } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java index dba8d878169..d5adb4e8d35 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java @@ -55,6 +55,8 @@ import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.MemoryCacheImageOutputStream; import org.w3c.dom.Node; +import sun.java2d.Disposer; +import sun.java2d.DisposerRecord; /** * Base class for all possible forms of JPEG compression in TIFF. @@ -219,12 +221,14 @@ public abstract class TIFFBaseJPEGCompressor extends TIFFCompressor { if(supportsStreamMetadata) { String smName = spi.getNativeStreamMetadataFormatName(); if(smName == null || !smName.equals(STREAM_METADATA_NAME)) { + this.JPEGWriter.dispose(); this.JPEGWriter = null; } } if(this.JPEGWriter != null && supportsImageMetadata) { String imName = spi.getNativeImageMetadataFormatName(); if(imName == null || !imName.equals(IMAGE_METADATA_NAME)) { + this.JPEGWriter.dispose(); this.JPEGWriter = null; } } @@ -263,6 +267,12 @@ public abstract class TIFFBaseJPEGCompressor extends TIFFCompressor { // Set the writer. this.JPEGWriter = writer; + // The JDK built-in JPEGImageWriter will self-dispose. + // So a Disposer is only needed here if it is an unknown reader. + // This is not common, so likely this will rarely be needed. + if (!(this.JPEGWriter instanceof com.sun.imageio.plugins.jpeg.JPEGImageWriter)) { + Disposer.addRecord(this, new ImageWriterDisposerRecord(this.JPEGWriter)); + } break; } @@ -435,11 +445,16 @@ public abstract class TIFFBaseJPEGCompressor extends TIFFCompressor { return compDataLength; } - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - super.finalize(); - if(JPEGWriter != null) { - JPEGWriter.dispose(); + private static class ImageWriterDisposerRecord implements DisposerRecord { + private final ImageWriter writer; + + public ImageWriterDisposerRecord(ImageWriter writer) { + this.writer = writer; + } + + @Override + public void dispose() { + writer.dispose(); } } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFJPEGDecompressor.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFJPEGDecompressor.java index 0b21835901b..3c2ce905958 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFJPEGDecompressor.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFJPEGDecompressor.java @@ -34,6 +34,8 @@ import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.stream.ImageInputStream; import javax.imageio.plugins.tiff.BaselineTIFFTagSet; import javax.imageio.plugins.tiff.TIFFField; +import sun.java2d.Disposer; +import sun.java2d.DisposerRecord; public class TIFFJPEGDecompressor extends TIFFDecompressor { // Start of Image @@ -65,6 +67,13 @@ public class TIFFJPEGDecompressor extends TIFFDecompressor { // Initialize reader to the first one. this.JPEGReader = iter.next(); + // The JDK built-in ImageReader will self-dispose. + // So a Disposer is only needed here if it is an unknown reader. + // This is not common, so likely this will rarely be needed. + if (!(this.JPEGReader instanceof com.sun.imageio.plugins.jpeg.JPEGImageReader)) { + Disposer.addRecord(this, new ImageReaderDisposerRecord(this.JPEGReader)); + } + this.JPEGParam = JPEGReader.getDefaultReadParam(); } @@ -139,9 +148,16 @@ public class TIFFJPEGDecompressor extends TIFFDecompressor { JPEGReader.read(0, JPEGParam); } - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - super.finalize(); - JPEGReader.dispose(); + private static class ImageReaderDisposerRecord implements DisposerRecord { + private final ImageReader reader; + + public ImageReaderDisposerRecord(ImageReader reader) { + this.reader = reader; + } + + @Override + public void dispose() { + reader.dispose(); + } } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFOldJPEGDecompressor.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFOldJPEGDecompressor.java index bc787f9127c..2935ca5caf8 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFOldJPEGDecompressor.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFOldJPEGDecompressor.java @@ -610,8 +610,4 @@ public class TIFFOldJPEGDecompressor extends TIFFJPEGDecompressor { JPEGReader.read(0, JPEGParam); } - protected void finalize() throws Throwable { - super.finalize(); - JPEGReader.dispose(); - } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java index 277b00ba4cf..a17f1f480ed 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,13 @@ package com.sun.java.swing; +import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; import java.awt.Stroke; import java.awt.Window; import java.awt.geom.AffineTransform; @@ -36,11 +39,16 @@ import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; +import javax.swing.ButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; +import javax.swing.JMenu; import javax.swing.RepaintManager; import sun.awt.AppContext; import sun.awt.SunToolkit; +import sun.swing.MenuItemLayoutHelper; +import sun.swing.SwingUtilities2; import static sun.java2d.pipe.Region.clipRound; @@ -61,6 +69,10 @@ public class SwingUtilities3 { private static final Object DELEGATE_REPAINT_MANAGER_KEY = new StringBuilder("DelegateRepaintManagerKey"); + private static Color disabledForeground; + private static Color acceleratorSelectionForeground; + private static Color acceleratorForeground; + /** * Registers delegate RepaintManager for {@code JComponent}. */ @@ -137,6 +149,128 @@ public class SwingUtilities3 { return delegate; } + public static void applyInsets(Rectangle rect, Insets insets) { + if (insets != null) { + rect.x += insets.left; + rect.y += insets.top; + rect.width -= (insets.right + rect.x); + rect.height -= (insets.bottom + rect.y); + } + } + + public static void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color holdc, Color foreground) { + if (lh.getCheckIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } else { + g.setColor(holdc); + } + if (lh.useCheckAndArrow()) { + lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, + lr.getCheckRect().x, lr.getCheckRect().y); + } + g.setColor(holdc); + } + } + + public static void paintIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc) { + if (lh.getIcon() != null) { + Icon icon; + ButtonModel model = lh.getMenuItem().getModel(); + if (!model.isEnabled()) { + icon = lh.getMenuItem().getDisabledIcon(); + } else if (model.isPressed() && model.isArmed()) { + icon = lh.getMenuItem().getPressedIcon(); + if (icon == null) { + // Use default icon + icon = lh.getMenuItem().getIcon(); + } + } else { + icon = lh.getMenuItem().getIcon(); + } + + if (icon != null) { + icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, + lr.getIconRect().y); + g.setColor(holdc); + } + } + } + + + public static void paintAccText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (!lh.getAccText().isEmpty()) { + ButtonModel model = lh.getMenuItem().getModel(); + g.setFont(lh.getAccFontMetrics().getFont()); + if (!model.isEnabled()) { + + // paint the accText disabled + if (disabledForeground != null) { + g.setColor(disabledForeground); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + } else { + g.setColor(lh.getMenuItem().getBackground().brighter()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + g.setColor(lh.getMenuItem().getBackground().darker()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x - 1, + lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); + } + } else { + + // paint the accText normally + if (model.isArmed() + || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(acceleratorSelectionForeground); + } else { + g.setColor(acceleratorForeground); + } + SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), + lr.getAccRect().x, lr.getAccRect().y + + lh.getAccFontMetrics().getAscent()); + } + } + } + + public static void setDisabledForeground(Color disabledFg) { + disabledForeground = disabledFg; + } + + public static void setAcceleratorSelectionForeground(Color acceleratorSelectionFg) { + acceleratorForeground = acceleratorSelectionFg; + } + + public static void setAcceleratorForeground(Color acceleratorFg) { + acceleratorForeground = acceleratorFg; + } + + public static void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color foreground) { + if (lh.getArrowIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { + g.setColor(foreground); + } + if (lh.useCheckAndArrow()) { + lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, + lr.getArrowRect().x, lr.getArrowRect().y); + } + } + } + /** * A task which paints an unscaled border after {@code Graphics} * transforms are removed. It's used with the diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index 2749d68a2cc..38bde1a97fa 100644 --- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java +++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,6 +127,8 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { transient Frame frame; private int desiredLocationX,desiredLocationY; + private PropertyChangeListener propListener = new AncestorListener(); + private String label = null; private boolean paintBorder = true; private Insets margin = null; @@ -929,6 +931,17 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { return this.invoker; } + private final class AncestorListener implements PropertyChangeListener, Serializable { + @Override + public void propertyChange(PropertyChangeEvent e) { + if (e.getOldValue() != null + && e.getNewValue() == null + && isVisible()) { + setVisible(false); + } + } + } + /** * Sets the invoker of this popup menu -- the component in which * the popup menu is to be displayed. @@ -941,8 +954,13 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { public void setInvoker(Component invoker) { Component oldInvoker = this.invoker; this.invoker = invoker; + if ((oldInvoker != this.invoker) && (ui != null)) { ui.uninstallUI(this); + if (oldInvoker != null) { + oldInvoker.removePropertyChangeListener("ancestor", propListener); + } + invoker.addPropertyChangeListener("ancestor", propListener); ui.installUI(this); } invalidate(); @@ -1357,6 +1375,10 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { values.addElement("popup"); values.addElement(popup); } + if (propListener instanceof Serializable) { + values.addElement("propListener"); + values.addElement(propListener); + } s.writeObject(values); if (getUIClassID().equals(uiClassID)) { @@ -1401,6 +1423,11 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { popup = (Popup)values.elementAt(++indexCounter); indexCounter++; } + if(indexCounter < maxCounter && values.elementAt(indexCounter). + equals("propListener")) { + propListener = (PropertyChangeListener) values.elementAt(++indexCounter); + indexCounter++; + } } diff --git a/src/java.desktop/share/classes/javax/swing/SpringLayout.java b/src/java.desktop/share/classes/javax/swing/SpringLayout.java index 76050d31ce3..925ef138bc2 100644 --- a/src/java.desktop/share/classes/javax/swing/SpringLayout.java +++ b/src/java.desktop/share/classes/javax/swing/SpringLayout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,15 @@ package javax.swing; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; -import java.awt.FontMetrics; import java.awt.Insets; import java.awt.LayoutManager2; -import java.awt.Rectangle; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * A SpringLayout lays out the children of its associated container @@ -403,8 +407,7 @@ public class SpringLayout implements LayoutManager2 { boolean valid = true; List history = horizontal ? horizontalHistory : verticalHistory; - if (history.contains(name)) { - history.remove(name); + if (history.remove(name)) { valid = false; } else if (history.size() == 2 && value != null) { history.remove(0); diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java index 8d55a12c98f..e9d95a3f970 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicDesktopPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ import javax.swing.plaf.*; import java.beans.*; -import java.awt.event.*; +import java.awt.event.ActionEvent; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; @@ -374,7 +374,7 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { private static String PREVIOUS_FRAME = "selectPreviousFrame"; private static String NAVIGATE_NEXT = "navigateNext"; private static String NAVIGATE_PREVIOUS = "navigatePrevious"; - private final int MOVE_RESIZE_INCREMENT = 10; + private static final int MOVE_RESIZE_INCREMENT = 10; private static boolean moving = false; private static boolean resizing = false; private static JInternalFrame sourceFrame = null; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java index dcd569910e9..524f0337a8f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,52 @@ package javax.swing.plaf.basic; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.InputMap; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.MenuDragMouseEvent; +import javax.swing.event.MenuDragMouseListener; +import javax.swing.event.MenuKeyListener; + +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ComponentInputMapUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.MenuItemUI; +import javax.swing.plaf.UIResource; import javax.swing.text.View; -import sun.swing.*; +import com.sun.java.swing.SwingUtilities3; +import sun.swing.MenuItemCheckIconFactory; +import sun.swing.MenuItemLayoutHelper; +import sun.swing.SwingUtilities2; +import sun.swing.UIAction; + /** * BasicMenuItem implementation @@ -670,84 +705,22 @@ public class BasicMenuItemUI extends MenuItemUI private void paintIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc) { - if (lh.getIcon() != null) { - Icon icon; - ButtonModel model = lh.getMenuItem().getModel(); - if (!model.isEnabled()) { - icon = lh.getMenuItem().getDisabledIcon(); - } else if (model.isPressed() && model.isArmed()) { - icon = lh.getMenuItem().getPressedIcon(); - if (icon == null) { - // Use default icon - icon = lh.getMenuItem().getIcon(); - } - } else { - icon = lh.getMenuItem().getIcon(); - } - - if (icon != null) { - icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, - lr.getIconRect().y); - g.setColor(holdc); - } - } + SwingUtilities3.paintIcon(g, lh, lr, holdc); } private void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color holdc, Color foreground) { - if (lh.getCheckIcon() != null) { - ButtonModel model = lh.getMenuItem().getModel(); - if (model.isArmed() || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(foreground); - } else { - g.setColor(holdc); - } - if (lh.useCheckAndArrow()) { - lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, - lr.getCheckRect().x, lr.getCheckRect().y); - } - g.setColor(holdc); - } + SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); } private void paintAccText(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr) { - if (!lh.getAccText().isEmpty()) { - ButtonModel model = lh.getMenuItem().getModel(); - g.setFont(lh.getAccFontMetrics().getFont()); - if (!model.isEnabled()) { - // *** paint the accText disabled - if (disabledForeground != null) { - g.setColor(disabledForeground); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x, - lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); - } else { - g.setColor(lh.getMenuItem().getBackground().brighter()); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x, - lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); - g.setColor(lh.getMenuItem().getBackground().darker()); - SwingUtilities2.drawString(lh.getMenuItem(), g, - lh.getAccText(), lr.getAccRect().x - 1, - lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); - } - } else { - // *** paint the accText normally - if (model.isArmed() - || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(acceleratorSelectionForeground); - } else { - g.setColor(acceleratorForeground); - } - SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), - lr.getAccRect().x, lr.getAccRect().y + - lh.getAccFontMetrics().getAscent()); - } - } + SwingUtilities3.setDisabledForeground(disabledForeground); + SwingUtilities3.setAcceleratorSelectionForeground( + acceleratorSelectionForeground); + SwingUtilities3.setAcceleratorForeground(acceleratorForeground); + SwingUtilities3.paintAccText(g, lh, lr); } private void paintText(Graphics g, MenuItemLayoutHelper lh, @@ -766,26 +739,11 @@ public class BasicMenuItemUI extends MenuItemUI private void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr, Color foreground) { - if (lh.getArrowIcon() != null) { - ButtonModel model = lh.getMenuItem().getModel(); - if (model.isArmed() || (lh.getMenuItem() instanceof JMenu - && model.isSelected())) { - g.setColor(foreground); - } - if (lh.useCheckAndArrow()) { - lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, - lr.getArrowRect().x, lr.getArrowRect().y); - } - } + SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); } private void applyInsets(Rectangle rect, Insets insets) { - if(insets != null) { - rect.x += insets.left; - rect.y += insets.top; - rect.width -= (insets.right + rect.x); - rect.height -= (insets.bottom + rect.y); - } + SwingUtilities3.applyInsets(rect, insets); } /** diff --git a/src/java.desktop/share/classes/javax/swing/text/StringContent.java b/src/java.desktop/share/classes/javax/swing/text/StringContent.java index 562b336c770..1ce375fa3ab 100644 --- a/src/java.desktop/share/classes/javax/swing/text/StringContent.java +++ b/src/java.desktop/share/classes/javax/swing/text/StringContent.java @@ -27,6 +27,7 @@ package javax.swing.text; import java.util.Vector; import java.io.Serializable; import javax.swing.undo.*; +import java.lang.ref.WeakReference; /** * An implementation of the AbstractDocument.Content interface that is @@ -227,7 +228,7 @@ public final class StringContent implements AbstractDocument.Content, Serializab int n = marks.size(); for (int i = 0; i < n; i++) { PosRec mark = marks.elementAt(i); - if (mark.unused) { + if (mark.get() == null) { // this record is no longer used, get rid of it marks.removeElementAt(i); i -= 1; @@ -242,7 +243,7 @@ public final class StringContent implements AbstractDocument.Content, Serializab int n = marks.size(); for (int i = 0; i < n; i++) { PosRec mark = marks.elementAt(i); - if (mark.unused) { + if (mark.get() == null) { // this record is no longer used, get rid of it marks.removeElementAt(i); i -= 1; @@ -278,7 +279,7 @@ public final class StringContent implements AbstractDocument.Content, Serializab Vector placeIn = (v == null) ? new Vector() : v; for (int i = 0; i < n; i++) { PosRec mark = marks.elementAt(i); - if (mark.unused) { + if (mark.get() == null) { // this record is no longer used, get rid of it marks.removeElementAt(i); i -= 1; @@ -303,7 +304,7 @@ public final class StringContent implements AbstractDocument.Content, Serializab for(int counter = positions.size() - 1; counter >= 0; counter--) { UndoPosRef ref = (UndoPosRef) positions.elementAt(counter); // Check if the Position is still valid. - if(ref.rec.unused) { + if(ref.rec.get() == null) { positions.removeElementAt(counter); } else @@ -323,26 +324,20 @@ public final class StringContent implements AbstractDocument.Content, Serializab * it.... the update table holds only a reference * to this grungy thing. */ - static final class PosRec { + static final class PosRec extends WeakReference { - PosRec(int offset) { + PosRec(int offset, StickyPosition position) { + super(position); this.offset = offset; } int offset; - boolean unused; } - /** - * This really wants to be a weak reference but - * in 1.1 we don't have a 100% pure solution for - * this... so this class tries to hack a solution - * to causing the marks to be collected. - */ final class StickyPosition implements Position { StickyPosition(int offset) { - rec = new PosRec(offset); + rec = new PosRec(offset, this); marks.addElement(rec); } @@ -350,13 +345,6 @@ public final class StringContent implements AbstractDocument.Content, Serializab return rec.offset; } - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // schedule the record to be removed later - // on another thread. - rec.unused = true; - } - public String toString() { return Integer.toString(getOffset()); } diff --git a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java index 03b36b3b819..cac9f8baab2 100644 --- a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,13 +23,22 @@ * questions. */ -/*- +/* * Reads xbitmap format images into a DIBitmap structure. */ package sun.awt.image; -import java.io.*; -import java.awt.image.*; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.lang.Math.multiplyExact; /** * Parse files of the form: @@ -50,6 +59,8 @@ public class XbmImageDecoder extends ImageDecoder { ImageConsumer.COMPLETESCANLINES | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME); + private static final int MAX_XBM_SIZE = 16384; + private static final int HEADER_SCAN_LIMIT = 100; public XbmImageDecoder(InputStreamImageSource src, InputStream is) { super(src, is); @@ -72,107 +83,125 @@ public class XbmImageDecoder extends ImageDecoder { * produce an image from the stream. */ public void produceImage() throws IOException, ImageFormatException { - char[] nm = new char[80]; - int c; - int i = 0; - int state = 0; int H = 0; int W = 0; int x = 0; int y = 0; - boolean start = true; + int n = 0; + int state = 0; byte[] raster = null; IndexColorModel model = null; - while (!aborted && (c = input.read()) != -1) { - if ('a' <= c && c <= 'z' || - 'A' <= c && c <= 'Z' || - '0' <= c && c <= '9' || c == '#' || c == '_') { - if (i < 78) - nm[i++] = (char) c; - } else if (i > 0) { - int nc = i; - i = 0; - if (start) { - if (nc != 7 || - nm[0] != '#' || - nm[1] != 'd' || - nm[2] != 'e' || - nm[3] != 'f' || - nm[4] != 'i' || - nm[5] != 'n' || - nm[6] != 'e') - { - error("Not an XBM file"); + + String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]"; + String replaceRegex = "(0[xX])|,|[\\s+]|[};]"; + + String line; + int lineNum = 0; + + try (BufferedReader br = new BufferedReader(new InputStreamReader(input))) { + // loop to process XBM header - width, height and create raster + while (!aborted && (line = br.readLine()) != null + && lineNum <= HEADER_SCAN_LIMIT) { + lineNum++; + // process #define stmts + if (line.trim().startsWith("#define")) { + String[] token = line.split("\\s+"); + if (token.length != 3) { + error("Error while parsing define statement"); + } + try { + if (!token[2].isBlank() && state == 0) { + W = Integer.parseInt(token[2]); + state = 1; // after width is set + } else if (!token[2].isBlank() && state == 1) { + H = Integer.parseInt(token[2]); + state = 2; // after height is set + } + } catch (NumberFormatException nfe) { + // parseInt() can throw NFE + error("Error while parsing width or height."); } - start = false; } - if (nm[nc - 1] == 'h') - state = 1; /* expecting width */ - else if (nm[nc - 1] == 't' && nc > 1 && nm[nc - 2] == 'h') - state = 2; /* expecting height */ - else if (nc > 2 && state < 0 && nm[0] == '0' && nm[1] == 'x') { - int n = 0; - for (int p = 2; p < nc; p++) { - c = nm[p]; - if ('0' <= c && c <= '9') - c = c - '0'; - else if ('A' <= c && c <= 'Z') - c = c - 'A' + 10; - else if ('a' <= c && c <= 'z') - c = c - 'a' + 10; - else - c = 0; - n = n * 16 + c; + + if (state == 2) { + if (W <= 0 || H <= 0) { + error("Invalid values for width or height."); } - for (int mask = 1; mask <= 0x80; mask <<= 1) { - if (x < W) { - if ((n & mask) != 0) - raster[x] = 1; - else - raster[x] = 0; - } - x++; + if (multiplyExact(W, H) > MAX_XBM_SIZE) { + error("Large XBM file size." + + " Maximum allowed size: " + MAX_XBM_SIZE); } - if (x >= W) { - if (setPixels(0, y, W, 1, model, raster, 0, W) <= 0) { - return; + model = new IndexColorModel(8, 2, XbmColormap, + 0, false, 0); + setDimensions(W, H); + setColorModel(model); + setHints(XbmHints); + headerComplete(); + raster = new byte[W]; + state = 3; + break; + } + } + + if (state != 3) { + error("Width or Height of XBM file not defined"); + } + + // loop to process image data + while (!aborted && (line = br.readLine()) != null) { + lineNum++; + + if (line.contains("[]")) { + Matcher matcher = Pattern.compile(matchRegex).matcher(line); + while (matcher.find()) { + if (y >= H) { + error("Scan size of XBM file exceeds" + + " the defined width x height"); } - x = 0; - if (y++ >= H) { - break; + + int startIndex = matcher.start(); + int endIndex = matcher.end(); + String hexByte = line.substring(startIndex, endIndex); + + if (!(hexByte.startsWith("0x") + || hexByte.startsWith("0X"))) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); } - } - } else { - int n = 0; - for (int p = 0; p < nc; p++) - if ('0' <= (c = nm[p]) && c <= '9') - n = n * 10 + c - '0'; - else { - n = -1; - break; + hexByte = hexByte.replaceAll(replaceRegex, ""); + if (hexByte.length() != 2) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); } - if (n > 0 && state > 0) { - if (state == 1) - W = n; - else - H = n; - if (W == 0 || H == 0) - state = 0; - else { - model = new IndexColorModel(8, 2, XbmColormap, - 0, false, 0); - setDimensions(W, H); - setColorModel(model); - setHints(XbmHints); - headerComplete(); - raster = new byte[W]; - state = -1; + + try { + n = Integer.parseInt(hexByte, 16); + } catch (NumberFormatException nfe) { + error("Error parsing hexadecimal at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + for (int mask = 1; mask <= 0x80; mask <<= 1) { + if (x < W) { + if ((n & mask) != 0) + raster[x] = 1; + else + raster[x] = 0; + } + x++; + } + + if (x >= W) { + int result = setPixels(0, y, W, 1, model, raster, 0, W); + if (result <= 0) { + error("Unexpected error occurred during setPixel()"); + } + x = 0; + y++; } } } } + imageComplete(ImageConsumer.STATICIMAGEDONE, true); } - input.close(); - imageComplete(ImageConsumer.STATICIMAGEDONE, true); } } diff --git a/src/java.desktop/share/classes/sun/print/PrintJob2D.java b/src/java.desktop/share/classes/sun/print/PrintJob2D.java index b3811e36367..8128dae1057 100644 --- a/src/java.desktop/share/classes/sun/print/PrintJob2D.java +++ b/src/java.desktop/share/classes/sun/print/PrintJob2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,47 +28,12 @@ package sun.print; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.PrintJob; import java.awt.JobAttributes; -import java.awt.JobAttributes.*; import java.awt.PageAttributes; -import java.awt.PageAttributes.*; - -import java.awt.print.PageFormat; -import java.awt.print.Paper; -import java.awt.print.Printable; -import java.awt.print.PrinterException; -import java.awt.print.PrinterJob; - -import java.io.File; -import java.io.IOException; - -import java.net.URI; -import java.net.URISyntaxException; - -import java.util.ArrayList; import java.util.Properties; - -import javax.print.PrintService; -import javax.print.attribute.HashPrintRequestAttributeSet; -import javax.print.attribute.PrintRequestAttributeSet; -import javax.print.attribute.Size2DSyntax; -import javax.print.attribute.standard.Chromaticity; -import javax.print.attribute.standard.Copies; -import javax.print.attribute.standard.Destination; -import javax.print.attribute.standard.DialogTypeSelection; -import javax.print.attribute.standard.DialogOwner; -import javax.print.attribute.standard.JobName; -import javax.print.attribute.standard.MediaSize; -import javax.print.attribute.standard.PrintQuality; -import javax.print.attribute.standard.SheetCollate; -import javax.print.attribute.standard.Sides; -import javax.print.attribute.standard.Media; -import javax.print.attribute.standard.OrientationRequested; -import javax.print.attribute.standard.MediaSizeName; -import javax.print.attribute.standard.PageRanges; - +import sun.java2d.Disposer; +import sun.java2d.DisposerRecord; /** * A class which initiates and executes a print job using @@ -77,667 +42,27 @@ import javax.print.attribute.standard.PageRanges; * @see java.awt.Toolkit#getPrintJob * */ -public class PrintJob2D extends PrintJob implements Printable, Runnable { +public class PrintJob2D extends PrintJob { - private static final MediaType[] SIZES = { - MediaType.ISO_4A0, MediaType.ISO_2A0, MediaType.ISO_A0, - MediaType.ISO_A1, MediaType.ISO_A2, MediaType.ISO_A3, - MediaType.ISO_A4, MediaType.ISO_A5, MediaType.ISO_A6, - MediaType.ISO_A7, MediaType.ISO_A8, MediaType.ISO_A9, - MediaType.ISO_A10, MediaType.ISO_B0, MediaType.ISO_B1, - MediaType.ISO_B2, MediaType.ISO_B3, MediaType.ISO_B4, - MediaType.ISO_B5, MediaType.ISO_B6, MediaType.ISO_B7, - MediaType.ISO_B8, MediaType.ISO_B9, MediaType.ISO_B10, - MediaType.JIS_B0, MediaType.JIS_B1, MediaType.JIS_B2, - MediaType.JIS_B3, MediaType.JIS_B4, MediaType.JIS_B5, - MediaType.JIS_B6, MediaType.JIS_B7, MediaType.JIS_B8, - MediaType.JIS_B9, MediaType.JIS_B10, MediaType.ISO_C0, - MediaType.ISO_C1, MediaType.ISO_C2, MediaType.ISO_C3, - MediaType.ISO_C4, MediaType.ISO_C5, MediaType.ISO_C6, - MediaType.ISO_C7, MediaType.ISO_C8, MediaType.ISO_C9, - MediaType.ISO_C10, MediaType.ISO_DESIGNATED_LONG, - MediaType.EXECUTIVE, MediaType.FOLIO, MediaType.INVOICE, - MediaType.LEDGER, MediaType.NA_LETTER, MediaType.NA_LEGAL, - MediaType.QUARTO, MediaType.A, MediaType.B, - MediaType.C, MediaType.D, MediaType.E, - MediaType.NA_10X15_ENVELOPE, MediaType.NA_10X14_ENVELOPE, - MediaType.NA_10X13_ENVELOPE, MediaType.NA_9X12_ENVELOPE, - MediaType.NA_9X11_ENVELOPE, MediaType.NA_7X9_ENVELOPE, - MediaType.NA_6X9_ENVELOPE, MediaType.NA_NUMBER_9_ENVELOPE, - MediaType.NA_NUMBER_10_ENVELOPE, MediaType.NA_NUMBER_11_ENVELOPE, - MediaType.NA_NUMBER_12_ENVELOPE, MediaType.NA_NUMBER_14_ENVELOPE, - MediaType.INVITE_ENVELOPE, MediaType.ITALY_ENVELOPE, - MediaType.MONARCH_ENVELOPE, MediaType.PERSONAL_ENVELOPE - }; - - /* This array maps the above array to the objects used by the - * javax.print APIs - */ - private static final MediaSizeName[] JAVAXSIZES = { - null, null, MediaSizeName.ISO_A0, - MediaSizeName.ISO_A1, MediaSizeName.ISO_A2, MediaSizeName.ISO_A3, - MediaSizeName.ISO_A4, MediaSizeName.ISO_A5, MediaSizeName.ISO_A6, - MediaSizeName.ISO_A7, MediaSizeName.ISO_A8, MediaSizeName.ISO_A9, - MediaSizeName.ISO_A10, MediaSizeName.ISO_B0, MediaSizeName.ISO_B1, - MediaSizeName.ISO_B2, MediaSizeName.ISO_B3, MediaSizeName.ISO_B4, - MediaSizeName.ISO_B5, MediaSizeName.ISO_B6, MediaSizeName.ISO_B7, - MediaSizeName.ISO_B8, MediaSizeName.ISO_B9, MediaSizeName.ISO_B10, - MediaSizeName.JIS_B0, MediaSizeName.JIS_B1, MediaSizeName.JIS_B2, - MediaSizeName.JIS_B3, MediaSizeName.JIS_B4, MediaSizeName.JIS_B5, - MediaSizeName.JIS_B6, MediaSizeName.JIS_B7, MediaSizeName.JIS_B8, - MediaSizeName.JIS_B9, MediaSizeName.JIS_B10, MediaSizeName.ISO_C0, - MediaSizeName.ISO_C1, MediaSizeName.ISO_C2, MediaSizeName.ISO_C3, - MediaSizeName.ISO_C4, MediaSizeName.ISO_C5, MediaSizeName.ISO_C6, - null, null, null, null, - MediaSizeName.ISO_DESIGNATED_LONG, MediaSizeName.EXECUTIVE, - MediaSizeName.FOLIO, MediaSizeName.INVOICE, MediaSizeName.LEDGER, - MediaSizeName.NA_LETTER, MediaSizeName.NA_LEGAL, - MediaSizeName.QUARTO, MediaSizeName.A, MediaSizeName.B, - MediaSizeName.C, MediaSizeName.D, MediaSizeName.E, - MediaSizeName.NA_10X15_ENVELOPE, MediaSizeName.NA_10X14_ENVELOPE, - MediaSizeName.NA_10X13_ENVELOPE, MediaSizeName.NA_9X12_ENVELOPE, - MediaSizeName.NA_9X11_ENVELOPE, MediaSizeName.NA_7X9_ENVELOPE, - MediaSizeName.NA_6X9_ENVELOPE, - MediaSizeName.NA_NUMBER_9_ENVELOPE, - MediaSizeName.NA_NUMBER_10_ENVELOPE, - MediaSizeName.NA_NUMBER_11_ENVELOPE, - MediaSizeName.NA_NUMBER_12_ENVELOPE, - MediaSizeName.NA_NUMBER_14_ENVELOPE, - null, MediaSizeName.ITALY_ENVELOPE, - MediaSizeName.MONARCH_ENVELOPE, MediaSizeName.PERSONAL_ENVELOPE, - }; - - - // widths and lengths in PostScript points (1/72 in.) - private static final int[] WIDTHS = { - /*iso-4a0*/ 4768, /*iso-2a0*/ 3370, /*iso-a0*/ 2384, /*iso-a1*/ 1684, - /*iso-a2*/ 1191, /*iso-a3*/ 842, /*iso-a4*/ 595, /*iso-a5*/ 420, - /*iso-a6*/ 298, /*iso-a7*/ 210, /*iso-a8*/ 147, /*iso-a9*/ 105, - /*iso-a10*/ 74, /*iso-b0*/ 2835, /*iso-b1*/ 2004, /*iso-b2*/ 1417, - /*iso-b3*/ 1001, /*iso-b4*/ 709, /*iso-b5*/ 499, /*iso-b6*/ 354, - /*iso-b7*/ 249, /*iso-b8*/ 176, /*iso-b9*/ 125, /*iso-b10*/ 88, - /*jis-b0*/ 2920, /*jis-b1*/ 2064, /*jis-b2*/ 1460, /*jis-b3*/ 1032, - /*jis-b4*/ 729, /*jis-b5*/ 516, /*jis-b6*/ 363, /*jis-b7*/ 258, - /*jis-b8*/ 181, /*jis-b9*/ 128, /*jis-b10*/ 91, /*iso-c0*/ 2599, - /*iso-c1*/ 1837, /*iso-c2*/ 1298, /*iso-c3*/ 918, /*iso-c4*/ 649, - /*iso-c5*/ 459, /*iso-c6*/ 323, /*iso-c7*/ 230, /*iso-c8*/ 162, - /*iso-c9*/ 113, /*iso-c10*/ 79, /*iso-designated-long*/ 312, - /*executive*/ 522, /*folio*/ 612, /*invoice*/ 396, /*ledger*/ 792, - /*na-letter*/ 612, /*na-legal*/ 612, /*quarto*/ 609, /*a*/ 612, - /*b*/ 792, /*c*/ 1224, /*d*/ 1584, /*e*/ 2448, - /*na-10x15-envelope*/ 720, /*na-10x14-envelope*/ 720, - /*na-10x13-envelope*/ 720, /*na-9x12-envelope*/ 648, - /*na-9x11-envelope*/ 648, /*na-7x9-envelope*/ 504, - /*na-6x9-envelope*/ 432, /*na-number-9-envelope*/ 279, - /*na-number-10-envelope*/ 297, /*na-number-11-envelope*/ 324, - /*na-number-12-envelope*/ 342, /*na-number-14-envelope*/ 360, - /*invite-envelope*/ 624, /*italy-envelope*/ 312, - /*monarch-envelope*/ 279, /*personal-envelope*/ 261 - }; - private static final int[] LENGTHS = { - /*iso-4a0*/ 6741, /*iso-2a0*/ 4768, /*iso-a0*/ 3370, /*iso-a1*/ 2384, - /*iso-a2*/ 1684, /*iso-a3*/ 1191, /*iso-a4*/ 842, /*iso-a5*/ 595, - /*iso-a6*/ 420, /*iso-a7*/ 298, /*iso-a8*/ 210, /*iso-a9*/ 147, - /*iso-a10*/ 105, /*iso-b0*/ 4008, /*iso-b1*/ 2835, /*iso-b2*/ 2004, - /*iso-b3*/ 1417, /*iso-b4*/ 1001, /*iso-b5*/ 729, /*iso-b6*/ 499, - /*iso-b7*/ 354, /*iso-b8*/ 249, /*iso-b9*/ 176, /*iso-b10*/ 125, - /*jis-b0*/ 4127, /*jis-b1*/ 2920, /*jis-b2*/ 2064, /*jis-b3*/ 1460, - /*jis-b4*/ 1032, /*jis-b5*/ 729, /*jis-b6*/ 516, /*jis-b7*/ 363, - /*jis-b8*/ 258, /*jis-b9*/ 181, /*jis-b10*/ 128, /*iso-c0*/ 3677, - /*iso-c1*/ 2599, /*iso-c2*/ 1837, /*iso-c3*/ 1298, /*iso-c4*/ 918, - /*iso-c5*/ 649, /*iso-c6*/ 459, /*iso-c7*/ 323, /*iso-c8*/ 230, - /*iso-c9*/ 162, /*iso-c10*/ 113, /*iso-designated-long*/ 624, - /*executive*/ 756, /*folio*/ 936, /*invoice*/ 612, /*ledger*/ 1224, - /*na-letter*/ 792, /*na-legal*/ 1008, /*quarto*/ 780, /*a*/ 792, - /*b*/ 1224, /*c*/ 1584, /*d*/ 2448, /*e*/ 3168, - /*na-10x15-envelope*/ 1080, /*na-10x14-envelope*/ 1008, - /*na-10x13-envelope*/ 936, /*na-9x12-envelope*/ 864, - /*na-9x11-envelope*/ 792, /*na-7x9-envelope*/ 648, - /*na-6x9-envelope*/ 648, /*na-number-9-envelope*/ 639, - /*na-number-10-envelope*/ 684, /*na-number-11-envelope*/ 747, - /*na-number-12-envelope*/ 792, /*na-number-14-envelope*/ 828, - /*invite-envelope*/ 624, /*italy-envelope*/ 652, - /*monarch-envelope*/ 540, /*personal-envelope*/ 468 - }; - - - private Frame frame; - private String docTitle = ""; - private JobAttributes jobAttributes; - private PageAttributes pageAttributes; - private PrintRequestAttributeSet attributes; - - /* - * Displays the native or cross-platform dialog and allows the - * user to update job & page attributes - */ - - /** - * The PrinterJob being uses to implement the PrintJob. - */ - private PrinterJob printerJob; - - /** - * The size of the page being used for the PrintJob. - */ - private PageFormat pageFormat; - - /** - * The PrinterJob and the application run on different - * threads and communicate through a pair of message - * queues. This queue is the list of Graphics that - * the PrinterJob has requested rendering for, but - * for which the application has not yet called getGraphics(). - * In practice the length of this message queue is always - * 0 or 1. - */ - private MessageQ graphicsToBeDrawn = new MessageQ("tobedrawn"); - - /** - * Used to communicate between the application's thread - * and the PrinterJob's thread this message queue holds - * the list of Graphics into which the application has - * finished drawing, but that have not yet been returned - * to the PrinterJob thread. Again, in practice, the - * length of this message queue is always 0 or 1. - */ - private MessageQ graphicsDrawn = new MessageQ("drawn"); - - /** - * The last Graphics returned to the application via - * getGraphics. This is the Graphics into which the - * application is currently drawing. - */ - private Graphics2D currentGraphics; - - /** - * The zero based index of the page currently being rendered - * by the application. - */ - private int pageIndex = -1; - - // The following Strings are maintained for backward-compatibility with - // Properties based print control. - private static final String DEST_PROP = "awt.print.destination"; - private static final String PRINTER = "printer"; - private static final String FILE = "file"; - - private static final String PRINTER_PROP = "awt.print.printer"; - - private static final String FILENAME_PROP = "awt.print.fileName"; - - private static final String NUMCOPIES_PROP = "awt.print.numCopies"; - - private static final String OPTIONS_PROP = "awt.print.options"; - - private static final String ORIENT_PROP = "awt.print.orientation"; - private static final String PORTRAIT = "portrait"; - private static final String LANDSCAPE = "landscape"; - - private static final String PAPERSIZE_PROP = "awt.print.paperSize"; - private static final String LETTER = "letter"; - private static final String LEGAL = "legal"; - private static final String EXECUTIVE = "executive"; - private static final String A4 = "a4"; - - private Properties props; - - private String options = ""; // REMIND: needs implementation - - /** - * The thread on which PrinterJob is running. - * This is different than the applications thread. - */ - private Thread printerJobThread; + private final PrintJobDelegate printJobDelegate; public PrintJob2D(Frame frame, String doctitle, final Properties props) { - this.props = props; - this.jobAttributes = new JobAttributes(); - this.pageAttributes = new PageAttributes(); - translateInputProps(); - initPrintJob2D(frame, doctitle, - this.jobAttributes, this.pageAttributes); + printJobDelegate = new PrintJobDelegate(frame, doctitle, props); + Disposer.addRecord(this, new PrintJobDisposerRecord(printJobDelegate)); } public PrintJob2D(Frame frame, String doctitle, JobAttributes jobAttributes, PageAttributes pageAttributes) { - initPrintJob2D(frame, doctitle, jobAttributes, pageAttributes); + printJobDelegate = new PrintJobDelegate(frame, doctitle, jobAttributes, pageAttributes); + Disposer.addRecord(this, new PrintJobDisposerRecord(printJobDelegate)); } - private void initPrintJob2D(Frame frame, String doctitle, - JobAttributes jobAttributes, - PageAttributes pageAttributes) { - - if (frame == null && - (jobAttributes == null || - jobAttributes.getDialog() == DialogType.NATIVE)) { - throw new NullPointerException("Frame must not be null"); - } - this.frame = frame; - - this.docTitle = (doctitle == null) ? "" : doctitle; - this.jobAttributes = (jobAttributes != null) - ? jobAttributes : new JobAttributes(); - this.pageAttributes = (pageAttributes != null) - ? pageAttributes : new PageAttributes(); - - // Currently, we always reduce page ranges to xxx or xxx-xxx - int[][] pageRanges = this.jobAttributes.getPageRanges(); - int first = pageRanges[0][0]; - int last = pageRanges[pageRanges.length - 1][1]; - this.jobAttributes.setPageRanges(new int[][] { - new int[] { first, last } - }); - this.jobAttributes.setToPage(last); - this.jobAttributes.setFromPage(first); - - - // Verify that the cross feed and feed resolutions are the same - int[] res = this.pageAttributes.getPrinterResolution(); - if (res[0] != res[1]) { - throw new IllegalArgumentException("Differing cross feed and feed"+ - " resolutions not supported."); - } - - // Verify that the app has access to the file system - DestinationType dest= this.jobAttributes.getDestination(); - if (dest == DestinationType.FILE) { - - // check if given filename is valid - String destStr = jobAttributes.getFileName(); - if ((destStr != null) && - (jobAttributes.getDialog() == JobAttributes.DialogType.NONE)) { - - File f = new File(destStr); - try { - // check if this is a new file and if filename chars are valid - // createNewFile returns false if file exists - if (f.createNewFile()) { - f.delete(); - } - } catch (IOException ioe) { - throw new IllegalArgumentException("Cannot write to file:"+ - destStr); - } - - File pFile = f.getParentFile(); - if ((f.exists() && - (!f.isFile() || !f.canWrite())) || - ((pFile != null) && - (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) { - throw new IllegalArgumentException("Cannot write to file:"+ - destStr); - } - } - } - } + // PrintJob2D API, not PrintJob public boolean printDialog() { - - boolean proceedWithPrint = false; - - printerJob = PrinterJob.getPrinterJob(); - if (printerJob == null) { - return false; - } - DialogType d = this.jobAttributes.getDialog(); - PrintService pServ = printerJob.getPrintService(); - if ((pServ == null) && (d == DialogType.NONE)){ - return false; - } - copyAttributes(pServ); - - DefaultSelectionType select = - this.jobAttributes.getDefaultSelection(); - if (select == DefaultSelectionType.RANGE) { - attributes.add(SunPageSelection.RANGE); - } else if (select == DefaultSelectionType.SELECTION) { - attributes.add(SunPageSelection.SELECTION); - } else { - attributes.add(SunPageSelection.ALL); - } - - if (frame != null) { - attributes.add(new DialogOwner(frame)); - } - - if ( d == DialogType.NONE) { - proceedWithPrint = true; - } else { - if (d == DialogType.NATIVE) { - attributes.add(DialogTypeSelection.NATIVE); - } else { // (d == DialogType.COMMON) - attributes.add(DialogTypeSelection.COMMON); - } - if (proceedWithPrint = printerJob.printDialog(attributes)) { - if (pServ == null) { - // Windows gives an option to install a service - // when it detects there are no printers so - // we make sure we get the updated print service. - pServ = printerJob.getPrintService(); - if (pServ == null) { - return false; - } - } - updateAttributes(); - translateOutputProps(); - } - } - - if (proceedWithPrint) { - - JobName jname = (JobName)attributes.get(JobName.class); - if (jname != null) { - printerJob.setJobName(jname.toString()); - } - - pageFormat = new PageFormat(); - - Media media = (Media)attributes.get(Media.class); - MediaSize mediaSize = null; - if (media instanceof MediaSizeName msn) { - mediaSize = MediaSize.getMediaSizeForName(msn); - } - - Paper p = pageFormat.getPaper(); - if (mediaSize != null) { - p.setSize(mediaSize.getX(MediaSize.INCH)*72.0, - mediaSize.getY(MediaSize.INCH)*72.0); - } - - if (pageAttributes.getOrigin()==OriginType.PRINTABLE) { - // AWT uses 1/4" borders by default - p.setImageableArea(18.0, 18.0, - p.getWidth()-36.0, - p.getHeight()-36.0); - } else { - p.setImageableArea(0.0,0.0,p.getWidth(),p.getHeight()); - } - - pageFormat.setPaper(p); - - OrientationRequested orient = - (OrientationRequested)attributes.get(OrientationRequested.class); - if (orient!= null && - orient == OrientationRequested.REVERSE_LANDSCAPE) { - pageFormat.setOrientation(PageFormat.REVERSE_LANDSCAPE); - } else if (orient == OrientationRequested.LANDSCAPE) { - pageFormat.setOrientation(PageFormat.LANDSCAPE); - } else { - pageFormat.setOrientation(PageFormat.PORTRAIT); - } - - PageRanges pageRangesAttr - = (PageRanges) attributes.get(PageRanges.class); - if (pageRangesAttr != null) { - // Get the PageRanges from print dialog. - int[][] range = pageRangesAttr.getMembers(); - - int prevFromPage = this.jobAttributes.getFromPage(); - int prevToPage = this.jobAttributes.getToPage(); - - int currFromPage = range[0][0]; - int currToPage = range[range.length - 1][1]; - - // if from < to update fromPage first followed by toPage - // else update toPage first followed by fromPage - if (currFromPage < prevToPage) { - this.jobAttributes.setFromPage(currFromPage); - this.jobAttributes.setToPage(currToPage); - } else { - this.jobAttributes.setToPage(currToPage); - this.jobAttributes.setFromPage(currFromPage); - } - } - printerJob.setPrintable(this, pageFormat); - - } - - return proceedWithPrint; - } - - private void updateAttributes() { - Copies c = (Copies)attributes.get(Copies.class); - jobAttributes.setCopies(c.getValue()); - - SunPageSelection sel = - (SunPageSelection)attributes.get(SunPageSelection.class); - if (sel == SunPageSelection.RANGE) { - jobAttributes.setDefaultSelection(DefaultSelectionType.RANGE); - } else if (sel == SunPageSelection.SELECTION) { - jobAttributes.setDefaultSelection(DefaultSelectionType.SELECTION); - } else { - jobAttributes.setDefaultSelection(DefaultSelectionType.ALL); - } - - Destination dest = (Destination)attributes.get(Destination.class); - if (dest != null) { - jobAttributes.setDestination(DestinationType.FILE); - jobAttributes.setFileName(dest.getURI().getPath()); - } else { - jobAttributes.setDestination(DestinationType.PRINTER); - } - - PrintService serv = printerJob.getPrintService(); - if (serv != null) { - jobAttributes.setPrinter(serv.getName()); - } - - PageRanges range = (PageRanges)attributes.get(PageRanges.class); - int[][] members = range.getMembers(); - jobAttributes.setPageRanges(members); - - SheetCollate collation = - (SheetCollate)attributes.get(SheetCollate.class); - if (collation == SheetCollate.COLLATED) { - jobAttributes.setMultipleDocumentHandling( - MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES); - } else { - jobAttributes.setMultipleDocumentHandling( - MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES); - } - - Sides sides = (Sides)attributes.get(Sides.class); - if (sides == Sides.TWO_SIDED_LONG_EDGE) { - jobAttributes.setSides(SidesType.TWO_SIDED_LONG_EDGE); - } else if (sides == Sides.TWO_SIDED_SHORT_EDGE) { - jobAttributes.setSides(SidesType.TWO_SIDED_SHORT_EDGE); - } else { - jobAttributes.setSides(SidesType.ONE_SIDED); - } - - // PageAttributes - - Chromaticity color = - (Chromaticity)attributes.get(Chromaticity.class); - if (color == Chromaticity.COLOR) { - pageAttributes.setColor(ColorType.COLOR); - } else { - pageAttributes.setColor(ColorType.MONOCHROME); - } - - OrientationRequested orient = - (OrientationRequested)attributes.get(OrientationRequested.class); - if (orient == OrientationRequested.LANDSCAPE) { - pageAttributes.setOrientationRequested( - OrientationRequestedType.LANDSCAPE); - } else { - pageAttributes.setOrientationRequested( - OrientationRequestedType.PORTRAIT); - } - - PrintQuality qual = (PrintQuality)attributes.get(PrintQuality.class); - if (qual == PrintQuality.DRAFT) { - pageAttributes.setPrintQuality(PrintQualityType.DRAFT); - } else if (qual == PrintQuality.HIGH) { - pageAttributes.setPrintQuality(PrintQualityType.HIGH); - } else { // NORMAL - pageAttributes.setPrintQuality(PrintQualityType.NORMAL); - } - - Media media = (Media)attributes.get(Media.class); - if (media instanceof MediaSizeName msn) { - MediaType mType = unMapMedia(msn); - - if (mType != null) { - pageAttributes.setMedia(mType); - } - } - debugPrintAttributes(false, false); - } - - private void debugPrintAttributes(boolean ja, boolean pa ) { - if (ja) { - System.out.println("new Attributes\ncopies = "+ - jobAttributes.getCopies()+ - "\nselection = "+ - jobAttributes.getDefaultSelection()+ - "\ndest "+jobAttributes.getDestination()+ - "\nfile "+jobAttributes.getFileName()+ - "\nfromPage "+jobAttributes.getFromPage()+ - "\ntoPage "+jobAttributes.getToPage()+ - "\ncollation "+ - jobAttributes.getMultipleDocumentHandling()+ - "\nPrinter "+jobAttributes.getPrinter()+ - "\nSides2 "+jobAttributes.getSides() - ); - } - - if (pa) { - System.out.println("new Attributes\ncolor = "+ - pageAttributes.getColor()+ - "\norientation = "+ - pageAttributes.getOrientationRequested()+ - "\nquality "+pageAttributes.getPrintQuality()+ - "\nMedia2 "+pageAttributes.getMedia() - ); - } - } - - - /* From JobAttributes we will copy job name and duplex printing - * and destination. - * The majority of the rest of the attributes are reflected - * attributes. - * - * From PageAttributes we copy color, media size, orientation, - * origin type, resolution and print quality. - * We use the media, orientation in creating the page format, and - * the origin type to set its imageable area. - * - * REMIND: Interpretation of resolution, additional media sizes. - */ - private void copyAttributes(PrintService printServ) { - - attributes = new HashPrintRequestAttributeSet(); - attributes.add(new JobName(docTitle, null)); - PrintService pServ = printServ; - - String printerName = jobAttributes.getPrinter(); - if (printerName != null && printerName != "" - && pServ != null && !printerName.equals(pServ.getName())) { - - // Search for the given printerName in the list of PrintServices - PrintService []services = PrinterJob.lookupPrintServices(); - try { - for (int i=0; i queue = new ArrayList<>(); - - MessageQ(String id) { - qid = id; - } - - synchronized void closeWhenEmpty() { - - while (queue != null && queue.size() > 0) { - try { - wait(1000); - } catch (InterruptedException e) { - // do nothing. - } - } - - queue = null; - notifyAll(); - } - - synchronized void close() { - queue = null; - notifyAll(); - } - - synchronized boolean append(Graphics2D g) { - - boolean queued = false; - - if (queue != null) { - queue.add(g); - queued = true; - notify(); - } - - return queued; - } - - synchronized Graphics2D pop() { - Graphics2D g = null; - - while (g == null && queue != null) { - - if (queue.size() > 0) { - g = queue.remove(0); - notify(); - - } else { - try { - wait(2000); - } catch (InterruptedException e) { - // do nothing. - } - } - } - - return g; - } - - synchronized boolean isClosed() { - return queue == null; - } - - } - - - private static int[] getSize(MediaType mType) { - int []dim = new int[2]; - dim[0] = 612; - dim[1] = 792; - - for (int i=0; i < SIZES.length; i++) { - if (SIZES[i] == mType) { - dim[0] = WIDTHS[i]; - dim[1] = LENGTHS[i]; - break; - } - } - return dim; - } - - public static MediaSizeName mapMedia(MediaType mType) { - MediaSizeName media = null; - - // JAVAXSIZES.length and SIZES.length must be equal! - // Attempt to recover by getting the smaller size. - int length = Math.min(SIZES.length, JAVAXSIZES.length); - - for (int i=0; i < length; i++) { - if (SIZES[i] == mType) { - if ((JAVAXSIZES[i] != null) && - MediaSize.getMediaSizeForName(JAVAXSIZES[i]) != null) { - media = JAVAXSIZES[i]; - break; - } else { - /* create Custom Media */ - media = new CustomMediaSizeName(SIZES[i].toString()); - - float w = (float)Math.rint(WIDTHS[i] / 72.0); - float h = (float)Math.rint(LENGTHS[i] / 72.0); - if (w > 0.0 && h > 0.0) { - // add new created MediaSize to our static map - // so it will be found when we call findMedia - new MediaSize(w, h, Size2DSyntax.INCH, media); - } - - break; - } - } - } - return media; - } - - - public static MediaType unMapMedia(MediaSizeName mSize) { - MediaType media = null; - - // JAVAXSIZES.length and SIZES.length must be equal! - // Attempt to recover by getting the smaller size. - int length = Math.min(SIZES.length, JAVAXSIZES.length); - - for (int i=0; i < length; i++) { - if (JAVAXSIZES[i] == mSize) { - if (SIZES[i] != null) { - media = SIZES[i]; - break; - } - } - } - return media; - } - - private void translateInputProps() { - if (props == null) { - return; - } - - String str; - - str = props.getProperty(DEST_PROP); - if (str != null) { - if (str.equals(PRINTER)) { - jobAttributes.setDestination(DestinationType.PRINTER); - } else if (str.equals(FILE)) { - jobAttributes.setDestination(DestinationType.FILE); - } - } - str = props.getProperty(PRINTER_PROP); - if (str != null) { - jobAttributes.setPrinter(str); - } - str = props.getProperty(FILENAME_PROP); - if (str != null) { - jobAttributes.setFileName(str); - } - str = props.getProperty(NUMCOPIES_PROP); - if (str != null) { - jobAttributes.setCopies(Integer.parseInt(str)); - } - - this.options = props.getProperty(OPTIONS_PROP, ""); - - str = props.getProperty(ORIENT_PROP); - if (str != null) { - if (str.equals(PORTRAIT)) { - pageAttributes.setOrientationRequested( - OrientationRequestedType.PORTRAIT); - } else if (str.equals(LANDSCAPE)) { - pageAttributes.setOrientationRequested( - OrientationRequestedType.LANDSCAPE); - } - } - str = props.getProperty(PAPERSIZE_PROP); - if (str != null) { - if (str.equals(LETTER)) { - pageAttributes.setMedia(SIZES[MediaType.LETTER.hashCode()]); - } else if (str.equals(LEGAL)) { - pageAttributes.setMedia(SIZES[MediaType.LEGAL.hashCode()]); - } else if (str.equals(EXECUTIVE)) { - pageAttributes.setMedia(SIZES[MediaType.EXECUTIVE.hashCode()]); - } else if (str.equals(A4)) { - pageAttributes.setMedia(SIZES[MediaType.A4.hashCode()]); - } + public void dispose() { + printJobDelegate.end(); } } - - private void translateOutputProps() { - if (props == null) { - return; - } - - String str; - - props.setProperty(DEST_PROP, - (jobAttributes.getDestination() == DestinationType.PRINTER) ? - PRINTER : FILE); - str = jobAttributes.getPrinter(); - if (str != null && !str.isEmpty()) { - props.setProperty(PRINTER_PROP, str); - } - str = jobAttributes.getFileName(); - if (str != null && !str.isEmpty()) { - props.setProperty(FILENAME_PROP, str); - } - int copies = jobAttributes.getCopies(); - if (copies > 0) { - props.setProperty(NUMCOPIES_PROP, "" + copies); - } - str = this.options; - if (str != null && !str.isEmpty()) { - props.setProperty(OPTIONS_PROP, str); - } - props.setProperty(ORIENT_PROP, - (pageAttributes.getOrientationRequested() == - OrientationRequestedType.PORTRAIT) - ? PORTRAIT : LANDSCAPE); - MediaType media = SIZES[pageAttributes.getMedia().hashCode()]; - if (media == MediaType.LETTER) { - str = LETTER; - } else if (media == MediaType.LEGAL) { - str = LEGAL; - } else if (media == MediaType.EXECUTIVE) { - str = EXECUTIVE; - } else if (media == MediaType.A4) { - str = A4; - } else { - str = media.toString(); - } - props.setProperty(PAPERSIZE_PROP, str); - } - } diff --git a/src/java.desktop/share/classes/sun/print/PrintJobDelegate.java b/src/java.desktop/share/classes/sun/print/PrintJobDelegate.java new file mode 100644 index 00000000000..5a88d4b9d45 --- /dev/null +++ b/src/java.desktop/share/classes/sun/print/PrintJobDelegate.java @@ -0,0 +1,1227 @@ +/* + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.print; + +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.PrintJob; +import java.awt.JobAttributes; +import java.awt.JobAttributes.*; +import java.awt.PageAttributes; +import java.awt.PageAttributes.*; + +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; + +import java.io.File; +import java.io.IOException; + +import java.net.URI; +import java.net.URISyntaxException; + +import java.util.ArrayList; +import java.util.Properties; + +import javax.print.PrintService; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.print.attribute.Size2DSyntax; +import javax.print.attribute.standard.Chromaticity; +import javax.print.attribute.standard.Copies; +import javax.print.attribute.standard.Destination; +import javax.print.attribute.standard.DialogTypeSelection; +import javax.print.attribute.standard.DialogOwner; +import javax.print.attribute.standard.JobName; +import javax.print.attribute.standard.MediaSize; +import javax.print.attribute.standard.PrintQuality; +import javax.print.attribute.standard.SheetCollate; +import javax.print.attribute.standard.Sides; +import javax.print.attribute.standard.Media; +import javax.print.attribute.standard.OrientationRequested; +import javax.print.attribute.standard.MediaSizeName; +import javax.print.attribute.standard.PageRanges; + + +/** + * A class which initiates and executes a print job using + * the underlying PrinterJob graphics conversions. + * + * @see java.awt.Toolkit#getPrintJob + * + */ +public class PrintJobDelegate implements Printable, Runnable { + + private static final MediaType[] SIZES = { + MediaType.ISO_4A0, MediaType.ISO_2A0, MediaType.ISO_A0, + MediaType.ISO_A1, MediaType.ISO_A2, MediaType.ISO_A3, + MediaType.ISO_A4, MediaType.ISO_A5, MediaType.ISO_A6, + MediaType.ISO_A7, MediaType.ISO_A8, MediaType.ISO_A9, + MediaType.ISO_A10, MediaType.ISO_B0, MediaType.ISO_B1, + MediaType.ISO_B2, MediaType.ISO_B3, MediaType.ISO_B4, + MediaType.ISO_B5, MediaType.ISO_B6, MediaType.ISO_B7, + MediaType.ISO_B8, MediaType.ISO_B9, MediaType.ISO_B10, + MediaType.JIS_B0, MediaType.JIS_B1, MediaType.JIS_B2, + MediaType.JIS_B3, MediaType.JIS_B4, MediaType.JIS_B5, + MediaType.JIS_B6, MediaType.JIS_B7, MediaType.JIS_B8, + MediaType.JIS_B9, MediaType.JIS_B10, MediaType.ISO_C0, + MediaType.ISO_C1, MediaType.ISO_C2, MediaType.ISO_C3, + MediaType.ISO_C4, MediaType.ISO_C5, MediaType.ISO_C6, + MediaType.ISO_C7, MediaType.ISO_C8, MediaType.ISO_C9, + MediaType.ISO_C10, MediaType.ISO_DESIGNATED_LONG, + MediaType.EXECUTIVE, MediaType.FOLIO, MediaType.INVOICE, + MediaType.LEDGER, MediaType.NA_LETTER, MediaType.NA_LEGAL, + MediaType.QUARTO, MediaType.A, MediaType.B, + MediaType.C, MediaType.D, MediaType.E, + MediaType.NA_10X15_ENVELOPE, MediaType.NA_10X14_ENVELOPE, + MediaType.NA_10X13_ENVELOPE, MediaType.NA_9X12_ENVELOPE, + MediaType.NA_9X11_ENVELOPE, MediaType.NA_7X9_ENVELOPE, + MediaType.NA_6X9_ENVELOPE, MediaType.NA_NUMBER_9_ENVELOPE, + MediaType.NA_NUMBER_10_ENVELOPE, MediaType.NA_NUMBER_11_ENVELOPE, + MediaType.NA_NUMBER_12_ENVELOPE, MediaType.NA_NUMBER_14_ENVELOPE, + MediaType.INVITE_ENVELOPE, MediaType.ITALY_ENVELOPE, + MediaType.MONARCH_ENVELOPE, MediaType.PERSONAL_ENVELOPE + }; + + /* This array maps the above array to the objects used by the + * javax.print APIs + */ + private static final MediaSizeName[] JAVAXSIZES = { + null, null, MediaSizeName.ISO_A0, + MediaSizeName.ISO_A1, MediaSizeName.ISO_A2, MediaSizeName.ISO_A3, + MediaSizeName.ISO_A4, MediaSizeName.ISO_A5, MediaSizeName.ISO_A6, + MediaSizeName.ISO_A7, MediaSizeName.ISO_A8, MediaSizeName.ISO_A9, + MediaSizeName.ISO_A10, MediaSizeName.ISO_B0, MediaSizeName.ISO_B1, + MediaSizeName.ISO_B2, MediaSizeName.ISO_B3, MediaSizeName.ISO_B4, + MediaSizeName.ISO_B5, MediaSizeName.ISO_B6, MediaSizeName.ISO_B7, + MediaSizeName.ISO_B8, MediaSizeName.ISO_B9, MediaSizeName.ISO_B10, + MediaSizeName.JIS_B0, MediaSizeName.JIS_B1, MediaSizeName.JIS_B2, + MediaSizeName.JIS_B3, MediaSizeName.JIS_B4, MediaSizeName.JIS_B5, + MediaSizeName.JIS_B6, MediaSizeName.JIS_B7, MediaSizeName.JIS_B8, + MediaSizeName.JIS_B9, MediaSizeName.JIS_B10, MediaSizeName.ISO_C0, + MediaSizeName.ISO_C1, MediaSizeName.ISO_C2, MediaSizeName.ISO_C3, + MediaSizeName.ISO_C4, MediaSizeName.ISO_C5, MediaSizeName.ISO_C6, + null, null, null, null, + MediaSizeName.ISO_DESIGNATED_LONG, MediaSizeName.EXECUTIVE, + MediaSizeName.FOLIO, MediaSizeName.INVOICE, MediaSizeName.LEDGER, + MediaSizeName.NA_LETTER, MediaSizeName.NA_LEGAL, + MediaSizeName.QUARTO, MediaSizeName.A, MediaSizeName.B, + MediaSizeName.C, MediaSizeName.D, MediaSizeName.E, + MediaSizeName.NA_10X15_ENVELOPE, MediaSizeName.NA_10X14_ENVELOPE, + MediaSizeName.NA_10X13_ENVELOPE, MediaSizeName.NA_9X12_ENVELOPE, + MediaSizeName.NA_9X11_ENVELOPE, MediaSizeName.NA_7X9_ENVELOPE, + MediaSizeName.NA_6X9_ENVELOPE, + MediaSizeName.NA_NUMBER_9_ENVELOPE, + MediaSizeName.NA_NUMBER_10_ENVELOPE, + MediaSizeName.NA_NUMBER_11_ENVELOPE, + MediaSizeName.NA_NUMBER_12_ENVELOPE, + MediaSizeName.NA_NUMBER_14_ENVELOPE, + null, MediaSizeName.ITALY_ENVELOPE, + MediaSizeName.MONARCH_ENVELOPE, MediaSizeName.PERSONAL_ENVELOPE, + }; + + + // widths and lengths in PostScript points (1/72 in.) + private static final int[] WIDTHS = { + /*iso-4a0*/ 4768, /*iso-2a0*/ 3370, /*iso-a0*/ 2384, /*iso-a1*/ 1684, + /*iso-a2*/ 1191, /*iso-a3*/ 842, /*iso-a4*/ 595, /*iso-a5*/ 420, + /*iso-a6*/ 298, /*iso-a7*/ 210, /*iso-a8*/ 147, /*iso-a9*/ 105, + /*iso-a10*/ 74, /*iso-b0*/ 2835, /*iso-b1*/ 2004, /*iso-b2*/ 1417, + /*iso-b3*/ 1001, /*iso-b4*/ 709, /*iso-b5*/ 499, /*iso-b6*/ 354, + /*iso-b7*/ 249, /*iso-b8*/ 176, /*iso-b9*/ 125, /*iso-b10*/ 88, + /*jis-b0*/ 2920, /*jis-b1*/ 2064, /*jis-b2*/ 1460, /*jis-b3*/ 1032, + /*jis-b4*/ 729, /*jis-b5*/ 516, /*jis-b6*/ 363, /*jis-b7*/ 258, + /*jis-b8*/ 181, /*jis-b9*/ 128, /*jis-b10*/ 91, /*iso-c0*/ 2599, + /*iso-c1*/ 1837, /*iso-c2*/ 1298, /*iso-c3*/ 918, /*iso-c4*/ 649, + /*iso-c5*/ 459, /*iso-c6*/ 323, /*iso-c7*/ 230, /*iso-c8*/ 162, + /*iso-c9*/ 113, /*iso-c10*/ 79, /*iso-designated-long*/ 312, + /*executive*/ 522, /*folio*/ 612, /*invoice*/ 396, /*ledger*/ 792, + /*na-letter*/ 612, /*na-legal*/ 612, /*quarto*/ 609, /*a*/ 612, + /*b*/ 792, /*c*/ 1224, /*d*/ 1584, /*e*/ 2448, + /*na-10x15-envelope*/ 720, /*na-10x14-envelope*/ 720, + /*na-10x13-envelope*/ 720, /*na-9x12-envelope*/ 648, + /*na-9x11-envelope*/ 648, /*na-7x9-envelope*/ 504, + /*na-6x9-envelope*/ 432, /*na-number-9-envelope*/ 279, + /*na-number-10-envelope*/ 297, /*na-number-11-envelope*/ 324, + /*na-number-12-envelope*/ 342, /*na-number-14-envelope*/ 360, + /*invite-envelope*/ 624, /*italy-envelope*/ 312, + /*monarch-envelope*/ 279, /*personal-envelope*/ 261 + }; + private static final int[] LENGTHS = { + /*iso-4a0*/ 6741, /*iso-2a0*/ 4768, /*iso-a0*/ 3370, /*iso-a1*/ 2384, + /*iso-a2*/ 1684, /*iso-a3*/ 1191, /*iso-a4*/ 842, /*iso-a5*/ 595, + /*iso-a6*/ 420, /*iso-a7*/ 298, /*iso-a8*/ 210, /*iso-a9*/ 147, + /*iso-a10*/ 105, /*iso-b0*/ 4008, /*iso-b1*/ 2835, /*iso-b2*/ 2004, + /*iso-b3*/ 1417, /*iso-b4*/ 1001, /*iso-b5*/ 729, /*iso-b6*/ 499, + /*iso-b7*/ 354, /*iso-b8*/ 249, /*iso-b9*/ 176, /*iso-b10*/ 125, + /*jis-b0*/ 4127, /*jis-b1*/ 2920, /*jis-b2*/ 2064, /*jis-b3*/ 1460, + /*jis-b4*/ 1032, /*jis-b5*/ 729, /*jis-b6*/ 516, /*jis-b7*/ 363, + /*jis-b8*/ 258, /*jis-b9*/ 181, /*jis-b10*/ 128, /*iso-c0*/ 3677, + /*iso-c1*/ 2599, /*iso-c2*/ 1837, /*iso-c3*/ 1298, /*iso-c4*/ 918, + /*iso-c5*/ 649, /*iso-c6*/ 459, /*iso-c7*/ 323, /*iso-c8*/ 230, + /*iso-c9*/ 162, /*iso-c10*/ 113, /*iso-designated-long*/ 624, + /*executive*/ 756, /*folio*/ 936, /*invoice*/ 612, /*ledger*/ 1224, + /*na-letter*/ 792, /*na-legal*/ 1008, /*quarto*/ 780, /*a*/ 792, + /*b*/ 1224, /*c*/ 1584, /*d*/ 2448, /*e*/ 3168, + /*na-10x15-envelope*/ 1080, /*na-10x14-envelope*/ 1008, + /*na-10x13-envelope*/ 936, /*na-9x12-envelope*/ 864, + /*na-9x11-envelope*/ 792, /*na-7x9-envelope*/ 648, + /*na-6x9-envelope*/ 648, /*na-number-9-envelope*/ 639, + /*na-number-10-envelope*/ 684, /*na-number-11-envelope*/ 747, + /*na-number-12-envelope*/ 792, /*na-number-14-envelope*/ 828, + /*invite-envelope*/ 624, /*italy-envelope*/ 652, + /*monarch-envelope*/ 540, /*personal-envelope*/ 468 + }; + + + private Frame frame; + private String docTitle = ""; + private JobAttributes jobAttributes; + private PageAttributes pageAttributes; + private PrintRequestAttributeSet attributes; + + /* + * Displays the native or cross-platform dialog and allows the + * user to update job & page attributes + */ + + /** + * The PrinterJob being uses to implement the PrintJob. + */ + private PrinterJob printerJob; + + /** + * The size of the page being used for the PrintJob. + */ + private PageFormat pageFormat; + + /** + * The PrinterJob and the application run on different + * threads and communicate through a pair of message + * queues. This queue is the list of Graphics that + * the PrinterJob has requested rendering for, but + * for which the application has not yet called getGraphics(). + * In practice the length of this message queue is always + * 0 or 1. + */ + private MessageQ graphicsToBeDrawn = new MessageQ("tobedrawn"); + + /** + * Used to communicate between the application's thread + * and the PrinterJob's thread this message queue holds + * the list of Graphics into which the application has + * finished drawing, but that have not yet been returned + * to the PrinterJob thread. Again, in practice, the + * length of this message queue is always 0 or 1. + */ + private MessageQ graphicsDrawn = new MessageQ("drawn"); + + /** + * The last Graphics returned to the application via + * getGraphics. This is the Graphics into which the + * application is currently drawing. + */ + private Graphics2D currentGraphics; + + /** + * The zero based index of the page currently being rendered + * by the application. + */ + private int pageIndex = -1; + + // The following Strings are maintained for backward-compatibility with + // Properties based print control. + private static final String DEST_PROP = "awt.print.destination"; + private static final String PRINTER = "printer"; + private static final String FILE = "file"; + + private static final String PRINTER_PROP = "awt.print.printer"; + + private static final String FILENAME_PROP = "awt.print.fileName"; + + private static final String NUMCOPIES_PROP = "awt.print.numCopies"; + + private static final String OPTIONS_PROP = "awt.print.options"; + + private static final String ORIENT_PROP = "awt.print.orientation"; + private static final String PORTRAIT = "portrait"; + private static final String LANDSCAPE = "landscape"; + + private static final String PAPERSIZE_PROP = "awt.print.paperSize"; + private static final String LETTER = "letter"; + private static final String LEGAL = "legal"; + private static final String EXECUTIVE = "executive"; + private static final String A4 = "a4"; + + private Properties props; + + private String options = ""; // REMIND: needs implementation + + /** + * The thread on which PrinterJob is running. + * This is different than the applications thread. + */ + private Thread printerJobThread; + + public PrintJobDelegate(Frame frame, String doctitle, + final Properties props) { + this.props = props; + this.jobAttributes = new JobAttributes(); + this.pageAttributes = new PageAttributes(); + translateInputProps(); + initPrintJob2D(frame, doctitle, + this.jobAttributes, this.pageAttributes); + } + + public PrintJobDelegate(Frame frame, String doctitle, + JobAttributes jobAttributes, + PageAttributes pageAttributes) { + initPrintJob2D(frame, doctitle, jobAttributes, pageAttributes); + } + + private void initPrintJob2D(Frame frame, String doctitle, + JobAttributes jobAttributes, + PageAttributes pageAttributes) { + + if (frame == null && + (jobAttributes == null || + jobAttributes.getDialog() == DialogType.NATIVE)) { + throw new NullPointerException("Frame must not be null"); + } + this.frame = frame; + + this.docTitle = (doctitle == null) ? "" : doctitle; + this.jobAttributes = (jobAttributes != null) + ? jobAttributes : new JobAttributes(); + this.pageAttributes = (pageAttributes != null) + ? pageAttributes : new PageAttributes(); + + // Currently, we always reduce page ranges to xxx or xxx-xxx + int[][] pageRanges = this.jobAttributes.getPageRanges(); + int first = pageRanges[0][0]; + int last = pageRanges[pageRanges.length - 1][1]; + this.jobAttributes.setPageRanges(new int[][] { + new int[] { first, last } + }); + this.jobAttributes.setToPage(last); + this.jobAttributes.setFromPage(first); + + + // Verify that the cross feed and feed resolutions are the same + int[] res = this.pageAttributes.getPrinterResolution(); + if (res[0] != res[1]) { + throw new IllegalArgumentException("Differing cross feed and feed"+ + " resolutions not supported."); + } + + // Verify that the app has access to the file system + DestinationType dest= this.jobAttributes.getDestination(); + if (dest == DestinationType.FILE) { + + // check if given filename is valid + String destStr = jobAttributes.getFileName(); + if ((destStr != null) && + (jobAttributes.getDialog() == JobAttributes.DialogType.NONE)) { + + File f = new File(destStr); + try { + // check if this is a new file and if filename chars are valid + // createNewFile returns false if file exists + if (f.createNewFile()) { + f.delete(); + } + } catch (IOException ioe) { + throw new IllegalArgumentException("Cannot write to file:"+ + destStr); + } + + File pFile = f.getParentFile(); + if ((f.exists() && + (!f.isFile() || !f.canWrite())) || + ((pFile != null) && + (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) { + throw new IllegalArgumentException("Cannot write to file:"+ + destStr); + } + } + } + } + + public boolean printDialog() { + + boolean proceedWithPrint = false; + + printerJob = PrinterJob.getPrinterJob(); + if (printerJob == null) { + return false; + } + DialogType d = this.jobAttributes.getDialog(); + PrintService pServ = printerJob.getPrintService(); + if ((pServ == null) && (d == DialogType.NONE)){ + return false; + } + copyAttributes(pServ); + + DefaultSelectionType select = + this.jobAttributes.getDefaultSelection(); + if (select == DefaultSelectionType.RANGE) { + attributes.add(SunPageSelection.RANGE); + } else if (select == DefaultSelectionType.SELECTION) { + attributes.add(SunPageSelection.SELECTION); + } else { + attributes.add(SunPageSelection.ALL); + } + + if (frame != null) { + attributes.add(new DialogOwner(frame)); + } + + if ( d == DialogType.NONE) { + proceedWithPrint = true; + } else { + if (d == DialogType.NATIVE) { + attributes.add(DialogTypeSelection.NATIVE); + } else { // (d == DialogType.COMMON) + attributes.add(DialogTypeSelection.COMMON); + } + if (proceedWithPrint = printerJob.printDialog(attributes)) { + if (pServ == null) { + // Windows gives an option to install a service + // when it detects there are no printers so + // we make sure we get the updated print service. + pServ = printerJob.getPrintService(); + if (pServ == null) { + return false; + } + } + updateAttributes(); + translateOutputProps(); + } + } + + if (proceedWithPrint) { + + JobName jname = (JobName)attributes.get(JobName.class); + if (jname != null) { + printerJob.setJobName(jname.toString()); + } + + pageFormat = new PageFormat(); + + Media media = (Media)attributes.get(Media.class); + MediaSize mediaSize = null; + if (media instanceof MediaSizeName msn) { + mediaSize = MediaSize.getMediaSizeForName(msn); + } + + Paper p = pageFormat.getPaper(); + if (mediaSize != null) { + p.setSize(mediaSize.getX(MediaSize.INCH)*72.0, + mediaSize.getY(MediaSize.INCH)*72.0); + } + + if (pageAttributes.getOrigin()==OriginType.PRINTABLE) { + // AWT uses 1/4" borders by default + p.setImageableArea(18.0, 18.0, + p.getWidth()-36.0, + p.getHeight()-36.0); + } else { + p.setImageableArea(0.0,0.0,p.getWidth(),p.getHeight()); + } + + pageFormat.setPaper(p); + + OrientationRequested orient = + (OrientationRequested)attributes.get(OrientationRequested.class); + if (orient!= null && + orient == OrientationRequested.REVERSE_LANDSCAPE) { + pageFormat.setOrientation(PageFormat.REVERSE_LANDSCAPE); + } else if (orient == OrientationRequested.LANDSCAPE) { + pageFormat.setOrientation(PageFormat.LANDSCAPE); + } else { + pageFormat.setOrientation(PageFormat.PORTRAIT); + } + + PageRanges pageRangesAttr + = (PageRanges) attributes.get(PageRanges.class); + if (pageRangesAttr != null) { + // Get the PageRanges from print dialog. + int[][] range = pageRangesAttr.getMembers(); + + int prevFromPage = this.jobAttributes.getFromPage(); + int prevToPage = this.jobAttributes.getToPage(); + + int currFromPage = range[0][0]; + int currToPage = range[range.length - 1][1]; + + // if from < to update fromPage first followed by toPage + // else update toPage first followed by fromPage + if (currFromPage < prevToPage) { + this.jobAttributes.setFromPage(currFromPage); + this.jobAttributes.setToPage(currToPage); + } else { + this.jobAttributes.setToPage(currToPage); + this.jobAttributes.setFromPage(currFromPage); + } + } + printerJob.setPrintable(this, pageFormat); + + } + + return proceedWithPrint; + } + + private void updateAttributes() { + Copies c = (Copies)attributes.get(Copies.class); + jobAttributes.setCopies(c.getValue()); + + SunPageSelection sel = + (SunPageSelection)attributes.get(SunPageSelection.class); + if (sel == SunPageSelection.RANGE) { + jobAttributes.setDefaultSelection(DefaultSelectionType.RANGE); + } else if (sel == SunPageSelection.SELECTION) { + jobAttributes.setDefaultSelection(DefaultSelectionType.SELECTION); + } else { + jobAttributes.setDefaultSelection(DefaultSelectionType.ALL); + } + + Destination dest = (Destination)attributes.get(Destination.class); + if (dest != null) { + jobAttributes.setDestination(DestinationType.FILE); + jobAttributes.setFileName(dest.getURI().getPath()); + } else { + jobAttributes.setDestination(DestinationType.PRINTER); + } + + PrintService serv = printerJob.getPrintService(); + if (serv != null) { + jobAttributes.setPrinter(serv.getName()); + } + + PageRanges range = (PageRanges)attributes.get(PageRanges.class); + int[][] members = range.getMembers(); + jobAttributes.setPageRanges(members); + + SheetCollate collation = + (SheetCollate)attributes.get(SheetCollate.class); + if (collation == SheetCollate.COLLATED) { + jobAttributes.setMultipleDocumentHandling( + MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES); + } else { + jobAttributes.setMultipleDocumentHandling( + MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES); + } + + Sides sides = (Sides)attributes.get(Sides.class); + if (sides == Sides.TWO_SIDED_LONG_EDGE) { + jobAttributes.setSides(SidesType.TWO_SIDED_LONG_EDGE); + } else if (sides == Sides.TWO_SIDED_SHORT_EDGE) { + jobAttributes.setSides(SidesType.TWO_SIDED_SHORT_EDGE); + } else { + jobAttributes.setSides(SidesType.ONE_SIDED); + } + + // PageAttributes + + Chromaticity color = + (Chromaticity)attributes.get(Chromaticity.class); + if (color == Chromaticity.COLOR) { + pageAttributes.setColor(ColorType.COLOR); + } else { + pageAttributes.setColor(ColorType.MONOCHROME); + } + + OrientationRequested orient = + (OrientationRequested)attributes.get(OrientationRequested.class); + if (orient == OrientationRequested.LANDSCAPE) { + pageAttributes.setOrientationRequested( + OrientationRequestedType.LANDSCAPE); + } else { + pageAttributes.setOrientationRequested( + OrientationRequestedType.PORTRAIT); + } + + PrintQuality qual = (PrintQuality)attributes.get(PrintQuality.class); + if (qual == PrintQuality.DRAFT) { + pageAttributes.setPrintQuality(PrintQualityType.DRAFT); + } else if (qual == PrintQuality.HIGH) { + pageAttributes.setPrintQuality(PrintQualityType.HIGH); + } else { // NORMAL + pageAttributes.setPrintQuality(PrintQualityType.NORMAL); + } + + Media media = (Media)attributes.get(Media.class); + if (media instanceof MediaSizeName msn) { + MediaType mType = unMapMedia(msn); + + if (mType != null) { + pageAttributes.setMedia(mType); + } + } + debugPrintAttributes(false, false); + } + + private void debugPrintAttributes(boolean ja, boolean pa ) { + if (ja) { + System.out.println("new Attributes\ncopies = "+ + jobAttributes.getCopies()+ + "\nselection = "+ + jobAttributes.getDefaultSelection()+ + "\ndest "+jobAttributes.getDestination()+ + "\nfile "+jobAttributes.getFileName()+ + "\nfromPage "+jobAttributes.getFromPage()+ + "\ntoPage "+jobAttributes.getToPage()+ + "\ncollation "+ + jobAttributes.getMultipleDocumentHandling()+ + "\nPrinter "+jobAttributes.getPrinter()+ + "\nSides2 "+jobAttributes.getSides() + ); + } + + if (pa) { + System.out.println("new Attributes\ncolor = "+ + pageAttributes.getColor()+ + "\norientation = "+ + pageAttributes.getOrientationRequested()+ + "\nquality "+pageAttributes.getPrintQuality()+ + "\nMedia2 "+pageAttributes.getMedia() + ); + } + } + + + /* From JobAttributes we will copy job name and duplex printing + * and destination. + * The majority of the rest of the attributes are reflected + * attributes. + * + * From PageAttributes we copy color, media size, orientation, + * origin type, resolution and print quality. + * We use the media, orientation in creating the page format, and + * the origin type to set its imageable area. + * + * REMIND: Interpretation of resolution, additional media sizes. + */ + private void copyAttributes(PrintService printServ) { + + attributes = new HashPrintRequestAttributeSet(); + attributes.add(new JobName(docTitle, null)); + PrintService pServ = printServ; + + String printerName = jobAttributes.getPrinter(); + if (printerName != null && printerName != "" + && pServ != null && !printerName.equals(pServ.getName())) { + + // Search for the given printerName in the list of PrintServices + PrintService []services = PrinterJob.lookupPrintServices(); + try { + for (int i=0; i queue = new ArrayList<>(); + + MessageQ(String id) { + qid = id; + } + + synchronized void closeWhenEmpty() { + + while (queue != null && queue.size() > 0) { + try { + wait(1000); + } catch (InterruptedException e) { + // do nothing. + } + } + + queue = null; + notifyAll(); + } + + synchronized void close() { + queue = null; + notifyAll(); + } + + synchronized boolean append(Graphics2D g) { + + boolean queued = false; + + if (queue != null) { + queue.add(g); + queued = true; + notify(); + } + + return queued; + } + + synchronized Graphics2D pop() { + Graphics2D g = null; + + while (g == null && queue != null) { + + if (queue.size() > 0) { + g = queue.remove(0); + notify(); + + } else { + try { + wait(2000); + } catch (InterruptedException e) { + // do nothing. + } + } + } + + return g; + } + + synchronized boolean isClosed() { + return queue == null; + } + + } + + + private static int[] getSize(MediaType mType) { + int []dim = new int[2]; + dim[0] = 612; + dim[1] = 792; + + for (int i=0; i < SIZES.length; i++) { + if (SIZES[i] == mType) { + dim[0] = WIDTHS[i]; + dim[1] = LENGTHS[i]; + break; + } + } + return dim; + } + + public static MediaSizeName mapMedia(MediaType mType) { + MediaSizeName media = null; + + // JAVAXSIZES.length and SIZES.length must be equal! + // Attempt to recover by getting the smaller size. + int length = Math.min(SIZES.length, JAVAXSIZES.length); + + for (int i=0; i < length; i++) { + if (SIZES[i] == mType) { + if ((JAVAXSIZES[i] != null) && + MediaSize.getMediaSizeForName(JAVAXSIZES[i]) != null) { + media = JAVAXSIZES[i]; + break; + } else { + /* create Custom Media */ + media = new CustomMediaSizeName(SIZES[i].toString()); + + float w = (float)Math.rint(WIDTHS[i] / 72.0); + float h = (float)Math.rint(LENGTHS[i] / 72.0); + if (w > 0.0 && h > 0.0) { + // add new created MediaSize to our static map + // so it will be found when we call findMedia + new MediaSize(w, h, Size2DSyntax.INCH, media); + } + + break; + } + } + } + return media; + } + + + public static MediaType unMapMedia(MediaSizeName mSize) { + MediaType media = null; + + // JAVAXSIZES.length and SIZES.length must be equal! + // Attempt to recover by getting the smaller size. + int length = Math.min(SIZES.length, JAVAXSIZES.length); + + for (int i=0; i < length; i++) { + if (JAVAXSIZES[i] == mSize) { + if (SIZES[i] != null) { + media = SIZES[i]; + break; + } + } + } + return media; + } + + private void translateInputProps() { + if (props == null) { + return; + } + + String str; + + str = props.getProperty(DEST_PROP); + if (str != null) { + if (str.equals(PRINTER)) { + jobAttributes.setDestination(DestinationType.PRINTER); + } else if (str.equals(FILE)) { + jobAttributes.setDestination(DestinationType.FILE); + } + } + str = props.getProperty(PRINTER_PROP); + if (str != null) { + jobAttributes.setPrinter(str); + } + str = props.getProperty(FILENAME_PROP); + if (str != null) { + jobAttributes.setFileName(str); + } + str = props.getProperty(NUMCOPIES_PROP); + if (str != null) { + jobAttributes.setCopies(Integer.parseInt(str)); + } + + this.options = props.getProperty(OPTIONS_PROP, ""); + + str = props.getProperty(ORIENT_PROP); + if (str != null) { + if (str.equals(PORTRAIT)) { + pageAttributes.setOrientationRequested( + OrientationRequestedType.PORTRAIT); + } else if (str.equals(LANDSCAPE)) { + pageAttributes.setOrientationRequested( + OrientationRequestedType.LANDSCAPE); + } + } + str = props.getProperty(PAPERSIZE_PROP); + if (str != null) { + if (str.equals(LETTER)) { + pageAttributes.setMedia(SIZES[MediaType.LETTER.hashCode()]); + } else if (str.equals(LEGAL)) { + pageAttributes.setMedia(SIZES[MediaType.LEGAL.hashCode()]); + } else if (str.equals(EXECUTIVE)) { + pageAttributes.setMedia(SIZES[MediaType.EXECUTIVE.hashCode()]); + } else if (str.equals(A4)) { + pageAttributes.setMedia(SIZES[MediaType.A4.hashCode()]); + } + } + } + + private void translateOutputProps() { + if (props == null) { + return; + } + + String str; + + props.setProperty(DEST_PROP, + (jobAttributes.getDestination() == DestinationType.PRINTER) ? + PRINTER : FILE); + str = jobAttributes.getPrinter(); + if (str != null && !str.isEmpty()) { + props.setProperty(PRINTER_PROP, str); + } + str = jobAttributes.getFileName(); + if (str != null && !str.isEmpty()) { + props.setProperty(FILENAME_PROP, str); + } + int copies = jobAttributes.getCopies(); + if (copies > 0) { + props.setProperty(NUMCOPIES_PROP, "" + copies); + } + str = this.options; + if (str != null && !str.isEmpty()) { + props.setProperty(OPTIONS_PROP, str); + } + props.setProperty(ORIENT_PROP, + (pageAttributes.getOrientationRequested() == + OrientationRequestedType.PORTRAIT) + ? PORTRAIT : LANDSCAPE); + MediaType media = SIZES[pageAttributes.getMedia().hashCode()]; + if (media == MediaType.LETTER) { + str = LETTER; + } else if (media == MediaType.LEGAL) { + str = LEGAL; + } else if (media == MediaType.EXECUTIVE) { + str = EXECUTIVE; + } else if (media == MediaType.A4) { + str = A4; + } else { + str = media.toString(); + } + props.setProperty(PAPERSIZE_PROP, str); + } + +} diff --git a/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c b/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c index 8e1211bda95..f78d3e09f72 100644 --- a/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c +++ b/src/java.desktop/share/native/common/java2d/opengl/OGLContext.c @@ -484,6 +484,7 @@ OGLContext_SetTransform(OGLContext *oglc, if (oglc->xformMatrix == NULL) { size_t arrsize = 16 * sizeof(GLdouble); oglc->xformMatrix = (GLdouble *)malloc(arrsize); + RETURN_IF_NULL(oglc->xformMatrix); memset(oglc->xformMatrix, 0, arrsize); oglc->xformMatrix[10] = 1.0; oglc->xformMatrix[15] = 1.0; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c index fad2cab1696..24469edd057 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c @@ -716,6 +716,7 @@ static void callbackScreenCastStart( DEBUG_SCREENCAST("Failed to start screencast: %u\n", status); startHelper->result = RESULT_DENIED; helper->isDone = TRUE; + callbackEnd(); return; } @@ -731,6 +732,7 @@ static void callbackScreenCastStart( DEBUG_SCREENCAST("No streams available with current token\n", NULL); startHelper->result = RESULT_NO_STREAMS; helper->isDone = TRUE; + callbackEnd(); return; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index 6b85a88e50c..f59a59a5125 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.awt.Graphics; import java.awt.Rectangle; import javax.swing.ButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.plaf.ComponentUI; @@ -73,6 +74,24 @@ public final class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { } super.paintBackground(g, menuItem, bgColor); } + + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, defaultTextIconGap, + menuItem, getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + /** * Method which renders the text of the current menu item. * diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index cf2fd119423..efa710391d5 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -880,6 +880,7 @@ public final class WindowsIconFactory implements Serializable } assert menuItem == null || c == menuItem; Icon icon = getIcon(); + if (type == JCheckBoxMenuItem.class || type == JRadioButtonMenuItem.class) { AbstractButton b = (AbstractButton) c; @@ -903,19 +904,18 @@ public final class WindowsIconFactory implements Serializable } XPStyle xp = XPStyle.getXP(); if (xp != null) { - Skin skin; - skin = xp.getSkin(c, backgroundPart); - skin.paintSkin(g, x, y, - getIconWidth(), getIconHeight(), backgroundState); - if (icon == null) { - skin = xp.getSkin(c, part); + Skin skin = xp.getSkin(c, part); + if (icon == null || icon.getIconHeight() <= 16) { skin.paintSkin(g, x + OFFSET, y + OFFSET, state); + } else { + skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state); } } } } if (icon != null) { - icon.paintIcon(c, g, x + OFFSET, y + OFFSET); + icon.paintIcon(c, g, x + VistaMenuItemCheckIconFactory.getIconWidth(), + y + OFFSET); } } private static WindowsMenuItemUIAccessor getAccessor( diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 2ee1b2d119f..a8bafc54c33 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,13 +26,20 @@ package com.sun.java.swing.plaf.windows; import java.awt.Color; +import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Enumeration; +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; import javax.swing.ButtonModel; +import javax.swing.DefaultButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuItem; @@ -41,6 +48,7 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicMenuItemUI; +import com.sun.java.swing.SwingUtilities3; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; import com.sun.java.swing.plaf.windows.XPStyle.Skin; @@ -59,6 +67,9 @@ public final class WindowsMenuItemUI extends BasicMenuItemUI { * The instance of {@code PropertyChangeListener}. */ private PropertyChangeListener changeListener; + private static Color disabledForeground; + private static Color acceleratorSelectionForeground; + private static Color acceleratorForeground; final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { @@ -123,6 +134,27 @@ public final class WindowsMenuItemUI extends BasicMenuItemUI { menuItem.addPropertyChangeListener(changeListener); } + protected void installDefaults() { + super.installDefaults(); + String prefix = getPropertyPrefix(); + + if (acceleratorSelectionForeground == null || + acceleratorSelectionForeground instanceof UIResource) { + acceleratorSelectionForeground = + UIManager.getColor(prefix + ".acceleratorSelectionForeground"); + } + if (acceleratorForeground == null || + acceleratorForeground instanceof UIResource) { + acceleratorForeground = + UIManager.getColor(prefix + ".acceleratorForeground"); + } + if (disabledForeground == null || + disabledForeground instanceof UIResource) { + disabledForeground = + UIManager.getColor(prefix + ".disabledForeground"); + } + } + /** * {@inheritDoc} */ @@ -135,6 +167,114 @@ public final class WindowsMenuItemUI extends BasicMenuItemUI { changeListener = null; } + private static void applyInsets(Rectangle rect, Insets insets) { + SwingUtilities3.applyInsets(rect, insets); + } + + private static void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color holdc, Color foreground) { + SwingUtilities3.paintCheckIcon(g, lh, lr, holdc, foreground); + } + + private static void paintIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc) { + SwingUtilities3.paintIcon(g, lh, lr, holdc); + } + + private static void paintAccText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + SwingUtilities3.setDisabledForeground(disabledForeground); + SwingUtilities3.setAcceleratorSelectionForeground( + acceleratorSelectionForeground); + SwingUtilities3.setAcceleratorForeground(acceleratorForeground); + SwingUtilities3.paintAccText(g, lh, lr); + } + + private static void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color foreground) { + SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); + } + + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, + defaultTextIconGap, menuItem, + getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + + static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g, + JComponent c, Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap, JMenuItem menuItem, String prefix) { + // Save original graphics font and color + Font holdf = g.getFont(); + Color holdc = g.getColor(); + + JMenuItem mi = (JMenuItem) c; + g.setFont(mi.getFont()); + + Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); + applyInsets(viewRect, mi.getInsets()); + + String acceleratorDelimiter = + UIManager.getString("MenuItem.acceleratorDelimiter"); + if (acceleratorDelimiter == null) { acceleratorDelimiter = "+"; } + Font acceleratorFont = UIManager.getFont("MenuItem.acceleratorFont"); + if (acceleratorFont == null) { + acceleratorFont = UIManager.getFont("MenuItem.font"); + } + + MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, + arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter, + mi.getComponentOrientation().isLeftToRight(), mi.getFont(), + acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem), + prefix); + MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); + + paintBackground(accessor, g, mi, background); + paintCheckIcon(g, lh, lr, holdc, foreground); + paintIcon(g, lh, lr, holdc); + + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getTextRect(); + + rect.x += lh.getAfterCheckIconGap(); + + lr.setTextRect(rect); + } + if (!lh.getText().isEmpty()) { + if (lh.getHtmlView() != null) { + // Text is HTML + lh.getHtmlView().paint(g, lr.getTextRect()); + } else { + // Text isn't HTML + paintText(accessor, g, lh.getMenuItem(), + lr.getTextRect(), lh.getText()); + } + } + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getAccRect(); + rect.x += lh.getAfterCheckIconGap(); + lr.setAccRect(rect); + } + paintAccText(g, lh, lr); + paintArrowIcon(g, lh, lr, foreground); + + // Restore original graphics font and color + g.setColor(holdc); + g.setFont(holdf); + } + /** * Method which renders the text of the current menu item. * diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 5562ce60388..81c01c11036 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -130,6 +130,25 @@ public final class WindowsMenuUI extends BasicMenuUI { hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true; } + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, arrowIcon, + background, foreground, + defaultTextIconGap, menuItem, + getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + + /** * Draws the background of the menu. * @since 1.4 diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index f6f3d4f06d1..385ab6b3634 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.awt.Graphics; import java.awt.Rectangle; import javax.swing.ButtonModel; +import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.plaf.ComponentUI; @@ -74,6 +75,23 @@ public final class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItem super.paintBackground(g, menuItem, bgColor); } + /** + * Paint MenuItem. + */ + protected void paintMenuItem(Graphics g, JComponent c, + Icon checkIcon, Icon arrowIcon, + Color background, Color foreground, + int defaultTextIconGap) { + if (WindowsMenuItemUI.isVistaPainting()) { + WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, + arrowIcon, background, foreground, defaultTextIconGap, + menuItem, getPropertyPrefix()); + return; + } + super.paintMenuItem(g, c, checkIcon, arrowIcon, background, + foreground, defaultTextIconGap); + } + /** * Method which renders the text of the current menu item. * diff --git a/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/dom/DOMSignContext.java b/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/dom/DOMSignContext.java index 4cb8eadcb25..46748166060 100644 --- a/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/dom/DOMSignContext.java +++ b/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/dom/DOMSignContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,10 @@ import javax.xml.crypto.dom.DOMCryptoContext; import javax.xml.crypto.dsig.XMLSignContext; import javax.xml.crypto.dsig.XMLSignature; import java.security.Key; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Signature; + import org.w3c.dom.Node; /** @@ -46,6 +50,17 @@ import org.w3c.dom.Node; * (for example, you should not use the same DOMSignContext * instance to sign two different {@link XMLSignature} objects). * + * @implNote + * The JDK implementation supports the following property that can be set + * using the {@link #setProperty setProperty} method. + *

      + *
    • jdk.xmldsig.SecureRandom: value must be a + * {@link SecureRandom}. If specified, this object will be + * used to initialize the underlying {@code Signature} during signing + * using the {@link Signature#initSign(PrivateKey, SecureRandom)} + * method. + *
    + * * @author Sean Mullan * @author JSR 105 Expert Group * @since 1.6 diff --git a/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMRSAPSSSignatureMethod.java b/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMRSAPSSSignatureMethod.java index c131ab689d7..253bca298ba 100644 --- a/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMRSAPSSSignatureMethod.java +++ b/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMRSAPSSSignatureMethod.java @@ -21,7 +21,7 @@ * under the License. */ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. */ package org.jcp.xml.dsig.internal.dom; @@ -33,6 +33,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; +import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.spec.AlgorithmParameterSpec; @@ -59,6 +60,7 @@ import org.w3c.dom.Text; public abstract class DOMRSAPSSSignatureMethod extends AbstractDOMSignatureMethod { private static final String DOM_SIGNATURE_PROVIDER = "org.jcp.xml.dsig.internal.dom.SignatureProvider"; + private static final String DOM_SIGNATURE_RANDOM = "jdk.xmldsig.SecureRandom"; private static final com.sun.org.slf4j.internal.Logger LOG = com.sun.org.slf4j.internal.LoggerFactory.getLogger(DOMRSAPSSSignatureMethod.class); @@ -324,7 +326,12 @@ public abstract class DOMRSAPSSSignatureMethod extends AbstractDOMSignatureMetho throw new XMLSignatureException(nsae); } } - signature.initSign((PrivateKey)key); + SecureRandom sr = (SecureRandom)context.getProperty(DOM_SIGNATURE_RANDOM); + if (sr != null) { + signature.initSign((PrivateKey) key, sr); + } else { + signature.initSign((PrivateKey) key); + } try { signature.setParameter(spec); } catch (InvalidAlgorithmParameterException e) { diff --git a/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMSignatureMethod.java b/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMSignatureMethod.java index 6523b93935d..984b2aa9cf4 100644 --- a/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMSignatureMethod.java +++ b/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMSignatureMethod.java @@ -21,7 +21,7 @@ * under the License. */ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. */ package org.jcp.xml.dsig.internal.dom; @@ -33,6 +33,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; +import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.interfaces.DSAKey; @@ -64,6 +65,7 @@ import sun.security.util.KeyUtil; public abstract class DOMSignatureMethod extends AbstractDOMSignatureMethod { private static final String DOM_SIGNATURE_PROVIDER = "org.jcp.xml.dsig.internal.dom.SignatureProvider"; + private static final String DOM_SIGNATURE_RANDOM = "jdk.xmldsig.SecureRandom"; private static final com.sun.org.slf4j.internal.Logger LOG = com.sun.org.slf4j.internal.LoggerFactory.getLogger(DOMSignatureMethod.class); @@ -390,7 +392,12 @@ public abstract class DOMSignatureMethod extends AbstractDOMSignatureMethod { throw new XMLSignatureException(nsae); } } - signature.initSign((PrivateKey)key); + SecureRandom sr = (SecureRandom)context.getProperty(DOM_SIGNATURE_RANDOM); + if (sr != null) { + signature.initSign((PrivateKey) key, sr); + } else { + signature.initSign((PrivateKey) key); + } LOG.debug("Signature provider: {}", signature.getProvider()); LOG.debug("JCA Algorithm: {}", getJCAAlgorithm()); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages_de.properties b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages_de.properties index 9126efd4674..654a313f6c9 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages_de.properties +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages_de.properties @@ -328,4 +328,4 @@ CatalogException=JAXP00090001: CatalogResolver ist mit dem Katalog "{0}" aktiviert, eine CatalogException wird jedoch zurückgegeben. # Implementation Property DTD - JDK_DTD_DENY = JAXP00010008: DOCTYPE ist nicht zulässig, wenn die DTD-Eigenschaft auf Ablehnen gesetzt wurde. Weitere Informationen: Eigenschaft jdk.xml.dtd.support in java.xml/module-summary. + JDK_DTD_DENY = JAXP00010008: DOCTYPE ist nicht zulässig, wenn die DTD-Eigenschaft auf "Ablehnen" gesetzt wurde. Weitere Informationen: Eigenschaft jdk.xml.dtd.support in java.xml/module-summary. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java index 1aea9f77a20..f45e8500000 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -240,7 +240,7 @@ public class Analyzer { @Override List rewrite(JCNewClass oldTree) { - if (oldTree.clazz.hasTag(TYPEAPPLY)) { + if (oldTree.clazz.hasTag(TYPEAPPLY) && !oldTree.type.isErroneous()) { JCNewClass nc = copier.copy(oldTree); ((JCTypeApply)nc.clazz).arguments = List.nil(); return List.of(nc); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java index e2ab9b1679c..420ef59a647 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,19 +25,19 @@ package com.sun.tools.javac.file; -import java.io.IOError; import java.io.IOException; import java.net.MalformedURLException; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.nio.file.FileSystems; import java.nio.file.Files; -import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.spi.FileSystemProvider; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.StringTokenizer; import java.util.jar.Attributes; import java.util.jar.JarFile; @@ -163,4 +163,30 @@ public class FSInfo { return null; } + // Must match the keys/values expected by ZipFileSystem.java. + private static final Map READ_ONLY_JARFS_ENV = Map.of( + // Jar files opened by Javac should always be read-only. + "accessMode", "readOnly", + // ignores timestamps not stored in ZIP central directory, reducing I/O. + "zipinfo-time", "false"); + + /** + * Returns a {@link java.nio.file.FileSystem FileSystem} environment map + * suitable for creating read-only JAR file-systems with default timestamp + * information via {@link FileSystemProvider#newFileSystem(Path, Map)} + * or {@link java.nio.file.FileSystems#newFileSystem(Path, Map)}. + * + * @param releaseVersion the release version to use when creating a + * file-system from a multi-release JAR (or + * {@code null} to ignore release versioning). + */ + public Map readOnlyJarFSEnv(String releaseVersion) { + if (releaseVersion == null) { + return READ_ONLY_JARFS_ENV; + } + // Multi-release JARs need an additional attribute. + Map env = new HashMap<>(READ_ONLY_JARFS_ENV); + env.put("releaseVersion", releaseVersion); + return Collections.unmodifiableMap(env); + } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java index 28274d26542..885d377e137 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java @@ -561,15 +561,10 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil public ArchiveContainer(Path archivePath) throws IOException, ProviderNotFoundException { this.archivePath = archivePath; - Map env = new HashMap<>(); - // ignores timestamps not stored in ZIP central directory, reducing I/O - // This key is handled by ZipFileSystem only. - env.put("zipinfo-time", "false"); - if (multiReleaseValue != null && archivePath.toString().endsWith(".jar")) { - env.put("multi-release", multiReleaseValue); FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider(); Assert.checkNonNull(jarFSProvider, "should have been caught before!"); + Map env = fsInfo.readOnlyJarFSEnv(multiReleaseValue); try { this.fileSystem = jarFSProvider.newFileSystem(archivePath, env); } catch (ZipException ze) { @@ -577,8 +572,11 @@ public class JavacFileManager extends BaseFileManager implements StandardJavaFil } } else { // Less common case is possible if the file manager was not initialized in JavacTask, - // or if non "*.jar" files are on the classpath. - this.fileSystem = FileSystems.newFileSystem(archivePath, env, (ClassLoader)null); + // or if non "*.jar" files are on the classpath. If this is not a ZIP/JAR file then it + // will ignore ZIP specific parameters in env, and may not end up being read-only. + // However, Javac should never attempt to write back to archives either way. + Map env = fsInfo.readOnlyJarFSEnv(null); + this.fileSystem = FileSystems.newFileSystem(archivePath, env); } packages = new HashMap<>(); for (Path root : fileSystem.getRootDirectories()) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java index 5ff55d4be3a..ff02de705fb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java @@ -81,14 +81,10 @@ import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import jdk.internal.jmod.JmodFile; -import com.sun.tools.javac.code.Lint; -import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.main.Option; import com.sun.tools.javac.resources.CompilerProperties.Errors; -import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; -import com.sun.tools.javac.util.JCDiagnostic.Warning; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.jvm.ModuleNameReader; @@ -141,7 +137,7 @@ public class Locations { Map fileSystems = new LinkedHashMap<>(); List closeables = new ArrayList<>(); - private Map fsEnv = Collections.emptyMap(); + private String releaseVersion = null; Locations() { initHandlers(); @@ -233,7 +229,8 @@ public class Locations { } public void setMultiReleaseValue(String multiReleaseValue) { - fsEnv = Collections.singletonMap("releaseVersion", multiReleaseValue); + // Null is implicitly allowed and unsets the value. + this.releaseVersion = multiReleaseValue; } private boolean contains(Collection searchPath, Path file) throws IOException { @@ -480,7 +477,7 @@ public class Locations { } /** - * @see JavaFileManager#getLocationForModule(Location, JavaFileObject, String) + * @see JavaFileManager#getLocationForModule(Location, JavaFileObject) */ Location getLocationForModule(Path file) throws IOException { return null; @@ -1387,7 +1384,7 @@ public class Locations { log.error(Errors.NoZipfsForArchive(p)); return null; } - try (FileSystem fs = jarFSProvider.newFileSystem(p, fsEnv)) { + try (FileSystem fs = jarFSProvider.newFileSystem(p, fsInfo.readOnlyJarFSEnv(releaseVersion))) { Path moduleInfoClass = fs.getPath("module-info.class"); if (Files.exists(moduleInfoClass)) { String moduleName = readModuleName(moduleInfoClass); @@ -1463,7 +1460,7 @@ public class Locations { log.error(Errors.LocnCantReadFile(p)); return null; } - fs = jarFSProvider.newFileSystem(p, Collections.emptyMap()); + fs = jarFSProvider.newFileSystem(p, fsInfo.readOnlyJarFSEnv(null)); try { Path moduleInfoClass = fs.getPath("classes/module-info.class"); String moduleName = readModuleName(moduleInfoClass); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java index ffb47504f24..4d679403ee7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java @@ -259,13 +259,19 @@ public final class SourceLauncher { } } catch (IllegalAccessException e) { throw new Fault(Errors.CantAccessMainMethod(mainClassName)); - } catch (InvocationTargetException e) { + } catch (InvocationTargetException exception) { // remove stack frames for source launcher - int invocationFrames = e.getStackTrace().length; - Throwable target = e.getCause(); - StackTraceElement[] targetTrace = target.getStackTrace(); - target.setStackTrace(Arrays.copyOfRange(targetTrace, 0, targetTrace.length - invocationFrames)); - throw e; + StackTraceElement[] invocationElements = exception.getStackTrace(); + if (invocationElements == null) throw exception; + Throwable cause = exception.getCause(); + if (cause == null) throw exception; + StackTraceElement[] causeElements = cause.getStackTrace(); + if (causeElements == null) throw exception; + int range = causeElements.length - invocationElements.length; + if (range >= 0) { + cause.setStackTrace(Arrays.copyOfRange(causeElements, 0, range)); + } + throw exception; } return mainClass; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java index 4c24f9892a6..2360fce1f75 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,10 +51,8 @@ import java.util.TreeSet; import javax.annotation.processing.Processor; import javax.tools.ForwardingJavaFileObject; import javax.tools.JavaFileManager; -import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; -import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import com.sun.source.util.Plugin; @@ -64,6 +62,7 @@ import com.sun.tools.javac.file.CacheFSInfo; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.StringUtils; @@ -96,6 +95,14 @@ public class JDKPlatformProvider implements PlatformProvider { private static final String[] symbolFileLocation = { "lib", "ct.sym" }; + // These must match attributes defined in ZipFileSystem.java. + private static final Map CT_SYM_ZIP_ENV = Map.of( + // Symbol file should always be opened read-only. + "accessMode", "readOnly", + // Uses less accurate, but faster, timestamp information + // (nobody should care about timestamps in the CT symbol file). + "zipinfo-time", "false"); + private static final Set SUPPORTED_JAVA_PLATFORM_VERSIONS; public static final Comparator NUMERICAL_COMPARATOR = (s1, s2) -> { int i1; @@ -117,7 +124,7 @@ public class JDKPlatformProvider implements PlatformProvider { SUPPORTED_JAVA_PLATFORM_VERSIONS = new TreeSet<>(NUMERICAL_COMPARATOR); Path ctSymFile = findCtSym(); if (Files.exists(ctSymFile)) { - try (FileSystem fs = FileSystems.newFileSystem(ctSymFile, (ClassLoader)null); + try (FileSystem fs = FileSystems.newFileSystem(ctSymFile, CT_SYM_ZIP_ENV); DirectoryStream dir = Files.newDirectoryStream(fs.getRootDirectories().iterator().next())) { for (Path section : dir) { @@ -249,7 +256,12 @@ public class JDKPlatformProvider implements PlatformProvider { try { FileSystem fs = ctSym2FileSystem.get(file); if (fs == null) { - ctSym2FileSystem.put(file, fs = FileSystems.newFileSystem(file, (ClassLoader)null)); + fs = FileSystems.newFileSystem(file, CT_SYM_ZIP_ENV); + // If for any reason this was not opened from a ZIP file, + // then the resulting file system would not be read-only. + // NOTE: This check is disabled until JDK 25 bootstrap! + // Assert.check(fs.isReadOnly()); + ctSym2FileSystem.put(file, fs); } Path root = fs.getRootDirectories().iterator().next(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties index 538b5a6c03b..0787c839cb4 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_de.properties @@ -1378,14 +1378,17 @@ compiler.warn.incubating.modules=Inkubatormodul(e) verwendet: {0} # 0: symbol, 1: symbol # lint: deprecation +# flags: aggregate compiler.warn.has.been.deprecated={0} in {1} ist veraltet # 0: symbol, 1: symbol # lint: removal +# flags: aggregate compiler.warn.has.been.deprecated.for.removal={0} in {1} ist veraltet und wurde zum Entfernen markiert # 0: symbol # lint: preview +# flags: aggregate compiler.warn.is.preview={0} ist eine Vorschau-API, die in einem zukünftigen Release entfernt werden kann. # 0: symbol @@ -1393,6 +1396,7 @@ compiler.err.is.preview={0} ist eine Vorschau-API, die standardmäßig deaktivie # 0: symbol # lint: preview +# flags: aggregate compiler.warn.is.preview.reflective={0} ist eine reflektive Vorschau-API, die in einem zukünftigen Release entfernt werden kann. # 0: symbol, 1: symbol @@ -1401,13 +1405,16 @@ compiler.warn.restricted.method={0}.{1} ist eine eingeschränkte Methode.\n(Eing # 0: symbol # lint: deprecation +# flags: aggregate compiler.warn.has.been.deprecated.module=Modul {0} ist veraltet # 0: symbol # lint: removal +# flags: aggregate compiler.warn.has.been.deprecated.for.removal.module=Modul {0} ist veraltet und wurde zum Entfernen markiert # 0: symbol +# flags: strict compiler.warn.sun.proprietary={0} ist eine interne proprietäre API, die in einem zukünftigen Release entfernt werden kann compiler.warn.illegal.char.for.encoding=Nicht zuordenbares Zeichen für Codierung {0} @@ -1704,10 +1711,12 @@ compiler.warn.unchecked.assign=Nicht geprüfte Zuweisung: {0} zu {1} # 0: symbol, 1: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.assign.to.var=Nicht geprüfte Zuweisung zu Variable {0} als Mitglied des Raw-Typs {1} # 0: symbol, 1: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.call.mbr.of.raw.type=Nicht geprüfter Aufruf von {0} als Mitglied des Raw-Typs {1} # lint: unchecked @@ -1715,14 +1724,17 @@ compiler.warn.unchecked.cast.to.type=Nicht geprüftes Casting zu Typ {0} # 0: kind name, 1: name, 2: object, 3: object, 4: kind name, 5: symbol # lint: unchecked +# flags: aggregate compiler.warn.unchecked.meth.invocation.applied=Nicht geprüfter Methodenaufruf: {0} {1} in {4} {5} wird auf die angegebenen Typen angewendet\nErforderlich: {2}\nErmittelt: {3} # 0: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.generic.array.creation=Nicht geprüfte Erstellung eines generischen Arrays für varargs-Parameter des Typs {0} # 0: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.varargs.non.reifiable.type=Möglich Heap-Beschädigung aus parametrisiertem vararg-Typ {0} # 0: symbol @@ -2011,6 +2023,7 @@ compiler.misc.prob.found.req=Inkompatible Typen: {0} # 0: message segment, 1: type, 2: type # lint: unchecked +# flags: aggregate compiler.warn.prob.found.req={0}\nErforderlich: {2}\nErmittelt: {1} # 0: type, 1: type @@ -2284,10 +2297,12 @@ compiler.err.override.incompatible.ret={0}\nRückgabetyp {1} ist nicht mit {2} k # 0: message segment, 1: type, 2: type # lint: unchecked +# flags: aggregate compiler.warn.override.unchecked.ret={0}\nRückgabetyp erfordert eine nicht geprüfte Konvertierung von {1} in {2} # 0: message segment, 1: type # lint: unchecked +# flags: aggregate compiler.warn.override.unchecked.thrown={0}\nAußer Kraft gesetzte Methode löst nicht {1} aus # 0: symbol @@ -2336,9 +2351,11 @@ compiler.misc.inapplicable.method={0} {1}.{2} ist nicht anwendbar\n({3}) ######################################## # 0: message segment (feature), 1: string (found version), 2: string (expected version) +# flags: source-level compiler.err.feature.not.supported.in.source={0} wird in -source {1} nicht unterstützt\n(Verwenden Sie -source {2} oder höher, um {0} zu aktivieren) # 0: message segment (feature), 1: string (found version), 2: string (expected version) +# flags: source-level compiler.err.feature.not.supported.in.source.plural={0} werden in -source {1} nicht unterstützt\n(Verwenden Sie -source {2} oder höher, um {0} zu aktivieren) # 0: message segment (feature), 1: string (found version), 2: string (expected version) @@ -2348,9 +2365,11 @@ compiler.misc.feature.not.supported.in.source={0} wird in -source {1} nicht unte compiler.misc.feature.not.supported.in.source.plural={0} werden in -source {1} nicht unterstützt\n(Verwenden Sie -source {2} oder höher, um {0} zu aktivieren) # 0: message segment (feature) +# flags: source-level compiler.err.preview.feature.disabled={0} ist ein Vorschaufeature, das standardmäßig deaktiviert ist.\n(Verwenden Sie --enable-preview, um {0} zu aktivieren) # 0: message segment (feature) +# flags: source-level compiler.err.preview.feature.disabled.plural={0} sind ein Vorschaufeature, das standardmäßig deaktiviert ist.\n(Verwenden Sie --enable-preview, um {0} zu aktivieren) # 0: file object (classfile), 1: string (expected version) @@ -2358,10 +2377,12 @@ compiler.err.preview.feature.disabled.classfile=Klassendatei für {0} verwendet # 0: message segment (feature) # lint: preview +# flags: aggregate compiler.warn.preview.feature.use={0} ist ein Vorschaufeature, das in einem zukünftigen Release entfernt werden kann. # 0: message segment (feature) # lint: preview +# flags: aggregate compiler.warn.preview.feature.use.plural={0} sind ein Vorschaufeature, das in einem zukünftigen Release entfernt werden kann. # 0: file object (classfile), 1: string (expected version) @@ -3039,6 +3060,7 @@ compiler.err.incorrect.number.of.nested.patterns=Falsche Anzahl verschachtelter # 0: kind name, 1: symbol # lint: preview +# flags: aggregate compiler.warn.declared.using.preview={0} {1} ist mit einem Vorschaufeature deklariert, das in einem zukünftigen Release entfernt werden kann. # lint: identity diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties index aa501fd6c11..291d8aeeec5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_ja.properties @@ -1378,14 +1378,17 @@ compiler.warn.incubating.modules=実験的なモジュールを使用してい # 0: symbol, 1: symbol # lint: deprecation +# flags: aggregate compiler.warn.has.been.deprecated={1}の{0}は推奨されません # 0: symbol, 1: symbol # lint: removal +# flags: aggregate compiler.warn.has.been.deprecated.for.removal={1}の{0}は推奨されておらず、削除用にマークされています # 0: symbol # lint: preview +# flags: aggregate compiler.warn.is.preview={0}はプレビューAPIであり、今後のリリースで削除される可能性があります。 # 0: symbol @@ -1393,6 +1396,7 @@ compiler.err.is.preview={0}はプレビューAPIであり、デフォルトで # 0: symbol # lint: preview +# flags: aggregate compiler.warn.is.preview.reflective={0}はリフレクティブ・プレビューAPIであり、今後のリリースで削除される可能性があります。 # 0: symbol, 1: symbol @@ -1401,13 +1405,16 @@ compiler.warn.restricted.method={0}.{1}は制限されたメソッドです。\n # 0: symbol # lint: deprecation +# flags: aggregate compiler.warn.has.been.deprecated.module=モジュール{0}は推奨されません # 0: symbol # lint: removal +# flags: aggregate compiler.warn.has.been.deprecated.for.removal.module=モジュール{0}は推奨されておらず、削除用にマークされています # 0: symbol +# flags: strict compiler.warn.sun.proprietary={0}は内部所有のAPIであり、今後のリリースで削除される可能性があります compiler.warn.illegal.char.for.encoding=この文字は、エンコーディング{0}にマップできません @@ -1704,10 +1711,12 @@ compiler.warn.unchecked.assign={0}から{1}への無検査代入です # 0: symbol, 1: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.assign.to.var=raw型{1}のメンバーとして変数{0}への無検査代入です # 0: symbol, 1: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.call.mbr.of.raw.type=raw型{1}のメンバーとしての{0}への無検査呼出しです # lint: unchecked @@ -1715,14 +1724,17 @@ compiler.warn.unchecked.cast.to.type=型{0}への無検査キャストです # 0: kind name, 1: name, 2: object, 3: object, 4: kind name, 5: symbol # lint: unchecked +# flags: aggregate compiler.warn.unchecked.meth.invocation.applied=無検査メソッド呼出し: {4} {5}の{0} {1}は指定された型に適用されます\n期待値: {2}\n検出値: {3} # 0: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.generic.array.creation=型{0}の可変引数パラメータに対する総称型配列の無検査作成です # 0: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.varargs.non.reifiable.type=パラメータ化された可変引数型{0}からのヒープ汚染の可能性があります # 0: symbol @@ -2011,6 +2023,7 @@ compiler.misc.prob.found.req=不適合な型: {0} # 0: message segment, 1: type, 2: type # lint: unchecked +# flags: aggregate compiler.warn.prob.found.req={0}\n期待値: {2}\n検出値: {1} # 0: type, 1: type @@ -2284,10 +2297,12 @@ compiler.err.override.incompatible.ret={0}\n戻り値の型{1}は{2}と互換性 # 0: message segment, 1: type, 2: type # lint: unchecked +# flags: aggregate compiler.warn.override.unchecked.ret={0}\n戻り値の型は{1}から{2}への無検査変換が必要です # 0: message segment, 1: type # lint: unchecked +# flags: aggregate compiler.warn.override.unchecked.thrown={0}\nオーバーライドされたメソッドは{1}をスローしません # 0: symbol @@ -2336,9 +2351,11 @@ compiler.misc.inapplicable.method={0} {1}.{2}は使用できません\n({3}) ######################################## # 0: message segment (feature), 1: string (found version), 2: string (expected version) +# flags: source-level compiler.err.feature.not.supported.in.source={0}は-source {1}でサポートされていません\n({0}を有効にするには-source {2}以上を使用してください) # 0: message segment (feature), 1: string (found version), 2: string (expected version) +# flags: source-level compiler.err.feature.not.supported.in.source.plural={0}は-source {1}でサポートされていません\n({0}を有効にするには-source {2}以上を使用してください) # 0: message segment (feature), 1: string (found version), 2: string (expected version) @@ -2348,9 +2365,11 @@ compiler.misc.feature.not.supported.in.source={0}は-source {1}でサポート compiler.misc.feature.not.supported.in.source.plural={0}は-source {1}でサポートされていません\n({0}を有効にするには-source {2}以上を使用してください) # 0: message segment (feature) +# flags: source-level compiler.err.preview.feature.disabled={0}はプレビュー機能であり、デフォルトで無効になっています。\n({0}を有効にするには--enable-previewを使用します) # 0: message segment (feature) +# flags: source-level compiler.err.preview.feature.disabled.plural={0}はプレビュー機能であり、デフォルトで無効になっています。\n({0}を有効にするには--enable-previewを使用します) # 0: file object (classfile), 1: string (expected version) @@ -2358,10 +2377,12 @@ compiler.err.preview.feature.disabled.classfile={0}のクラス・ファイル # 0: message segment (feature) # lint: preview +# flags: aggregate compiler.warn.preview.feature.use={0}はプレビュー機能であり、今後のリリースで削除される可能性があります。 # 0: message segment (feature) # lint: preview +# flags: aggregate compiler.warn.preview.feature.use.plural={0}はプレビュー機能であり、今後のリリースで削除される可能性があります。 # 0: file object (classfile), 1: string (expected version) @@ -3039,6 +3060,7 @@ compiler.err.incorrect.number.of.nested.patterns=ネスト・パターンの数 # 0: kind name, 1: symbol # lint: preview +# flags: aggregate compiler.warn.declared.using.preview={0} {1}はプレビュー機能を使用して宣言されており、今後のリリースで削除される可能性があります。 # lint: identity diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties index d25110f6532..268ce26bd49 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties @@ -1378,14 +1378,17 @@ compiler.warn.incubating.modules=使用 incubating 模块: {0} # 0: symbol, 1: symbol # lint: deprecation +# flags: aggregate compiler.warn.has.been.deprecated={1}中的{0}已过时 # 0: symbol, 1: symbol # lint: removal +# flags: aggregate compiler.warn.has.been.deprecated.for.removal={1} 中的 {0} 已过时, 且标记为待删除 # 0: symbol # lint: preview +# flags: aggregate compiler.warn.is.preview={0} 是预览 API,可能会在未来发行版中删除。 # 0: symbol @@ -1393,6 +1396,7 @@ compiler.err.is.preview={0} 是预览 API,默认情况下处于禁用状态。 # 0: symbol # lint: preview +# flags: aggregate compiler.warn.is.preview.reflective={0} 是反射预览 API,可能会在未来发行版中删除。 # 0: symbol, 1: symbol @@ -1401,13 +1405,16 @@ compiler.warn.restricted.method={0}.{1} 是受限制的方法。\n(受限制 # 0: symbol # lint: deprecation +# flags: aggregate compiler.warn.has.been.deprecated.module=模块 {0} 已过时 # 0: symbol # lint: removal +# flags: aggregate compiler.warn.has.been.deprecated.for.removal.module=模块 {0} 已过时, 且标记为待删除 # 0: symbol +# flags: strict compiler.warn.sun.proprietary={0}是内部专用 API, 可能会在未来发行版中删除 compiler.warn.illegal.char.for.encoding=编码{0}的不可映射字符 @@ -1704,10 +1711,12 @@ compiler.warn.unchecked.assign=未经检查的分配: 将{0}分配给{1} # 0: symbol, 1: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.assign.to.var=对作为原始类型{1}的成员的变量{0}的分配未经过检查 # 0: symbol, 1: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.call.mbr.of.raw.type=对作为原始类型{1}的成员的{0}的调用未经过检查 # lint: unchecked @@ -1715,14 +1724,17 @@ compiler.warn.unchecked.cast.to.type=向类型{0}的转换未经过检查 # 0: kind name, 1: name, 2: object, 3: object, 4: kind name, 5: symbol # lint: unchecked +# flags: aggregate compiler.warn.unchecked.meth.invocation.applied=方法调用未经过检查: 将{4} {5}中的{0} {1}应用到给定的类型\n需要: {2}\n找到: {3} # 0: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.generic.array.creation=对于类型为{0}的 varargs 参数, 泛型数组创建未经过检查 # 0: type # lint: unchecked +# flags: aggregate compiler.warn.unchecked.varargs.non.reifiable.type=参数化 vararg 类型{0}的堆可能已受污染 # 0: symbol @@ -2011,6 +2023,7 @@ compiler.misc.prob.found.req=不兼容的类型: {0} # 0: message segment, 1: type, 2: type # lint: unchecked +# flags: aggregate compiler.warn.prob.found.req={0}\n需要: {2}\n找到: {1} # 0: type, 1: type @@ -2284,10 +2297,12 @@ compiler.err.override.incompatible.ret={0}\n返回类型{1}与{2}不兼容 # 0: message segment, 1: type, 2: type # lint: unchecked +# flags: aggregate compiler.warn.override.unchecked.ret={0}\n返回类型需要从{1}到{2}的未经检查的转换 # 0: message segment, 1: type # lint: unchecked +# flags: aggregate compiler.warn.override.unchecked.thrown={0}\n被覆盖的方法未抛出{1} # 0: symbol @@ -2336,9 +2351,11 @@ compiler.misc.inapplicable.method={0} {1}.{2}不适用\n({3}) ######################################## # 0: message segment (feature), 1: string (found version), 2: string (expected version) +# flags: source-level compiler.err.feature.not.supported.in.source=-source {1} 中不支持 {0}\n(请使用 -source {2} 或更高版本以启用 {0}) # 0: message segment (feature), 1: string (found version), 2: string (expected version) +# flags: source-level compiler.err.feature.not.supported.in.source.plural=-source {1} 中不支持 {0}\n(请使用 -source {2} 或更高版本以启用 {0}) # 0: message segment (feature), 1: string (found version), 2: string (expected version) @@ -2348,9 +2365,11 @@ compiler.misc.feature.not.supported.in.source=-source {1} 中不支持 {0}\n(请 compiler.misc.feature.not.supported.in.source.plural=-source {1} 中不支持 {0}\n(请使用 -source {2} 或更高版本以启用 {0}) # 0: message segment (feature) +# flags: source-level compiler.err.preview.feature.disabled={0} 是预览功能,默认情况下禁用。\n(请使用 --enable-preview 以启用 {0}) # 0: message segment (feature) +# flags: source-level compiler.err.preview.feature.disabled.plural={0} 是预览功能,默认情况下禁用。\n(请使用 --enable-preview 以启用 {0}) # 0: file object (classfile), 1: string (expected version) @@ -2358,10 +2377,12 @@ compiler.err.preview.feature.disabled.classfile={0} 的类文件使用 Java SE { # 0: message segment (feature) # lint: preview +# flags: aggregate compiler.warn.preview.feature.use={0} 是预览功能,可能会在未来发行版中删除。 # 0: message segment (feature) # lint: preview +# flags: aggregate compiler.warn.preview.feature.use.plural={0} 是预览功能,可能会在未来发行版中删除。 # 0: file object (classfile), 1: string (expected version) @@ -3039,6 +3060,7 @@ compiler.err.incorrect.number.of.nested.patterns=嵌套模式数不正确\n需 # 0: kind name, 1: symbol # lint: preview +# flags: aggregate compiler.warn.declared.using.preview={0} {1} 是使用预览功能声明的,可能会在未来发行版中删除。 # lint: identity diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_de.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_de.properties index 18a43abe14a..1a00fad1dd0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_de.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_de.properties @@ -175,11 +175,8 @@ javac.opt.Xlint.desc.preview=Warnt vor Verwendung von Vorschausprachfeatures. javac.opt.Xlint.desc.restricted=Warnt vor der Verwendung eingeschränkter Methoden. -javac.opt.Xlint.desc.synchronization=\ - Warnt vor Synchronisierungsversuchen mit Instanzen wertbasierter Klassen.\n\ -\ Dieser Schlüssel ist ein veralteter Alias für die Kategorie ''identity'', die dieselben Verwendungen und\n\ -\ Effekte hat. Benutzern wird empfohlen, die Kategorie ''identity'' für alle zukünftigen\n\ -\ und vorhandenen Verwendungen von ''synchronization'' zu verwenden. +# L10N: do not localize: identity synchronization +javac.opt.Xlint.desc.synchronization=Warnt vor Synchronisierungsversuchen mit Instanzen wertbasierter Klassen.\n Dieser Schlüssel ist ein veralteter Alias für die Kategorie "identity", die dieselben Verwendungen und\n Effekte hat. Benutzern wird empfohlen, die Kategorie "identity" für alle zukünftigen\n und vorhandenen Verwendungen von "synchronization" zu verwenden. javac.opt.Xlint.desc.identity=Warnt vor Verwendungen wertbasierter Klassen, wenn eine Identitätsklasse erwartet wird. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties index 66e2e3dce39..3b967d368ae 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties @@ -175,6 +175,7 @@ javac.opt.Xlint.desc.preview=プレビュー言語機能の使用について警 javac.opt.Xlint.desc.restricted=制限されたメソッドの使用について警告します。 +# L10N: do not localize: identity synchronization javac.opt.Xlint.desc.synchronization=値ベース・クラスのインスタンスでの同期の試行について警告します。\n このキーは、''identity''の非推奨のエイリアスであり、同じ使用方法と効果を\n 持ちます。ユーザーには、今後および既存の''synchronization''の使用に対して''identity''カテゴリを\n 使用することをお薦めします。 javac.opt.Xlint.desc.identity=アイデンティティ・クラスが必要な場所での値ベース・クラスの使用について警告します。 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties index efc6e73494e..a24b5511c9c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties @@ -175,6 +175,7 @@ javac.opt.Xlint.desc.preview=有关使用预览语言功能的警告。 javac.opt.Xlint.desc.restricted=有关使用受限制方法的警告。 +# L10N: do not localize: identity synchronization javac.opt.Xlint.desc.synchronization=有关尝试在基于值的类的实例上同步的警告。\n 此密钥是 ''identity'' 的已过时别名,具有相同的用法和\n 效果。建议用户在 ''synchronization'' 的所有未来和现有\n 用法中使用 ''identity'' 类别。 javac.opt.Xlint.desc.identity=有关在需要身份类的情况下使用基于值的类的警告。 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_de.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_de.properties index 7f12decb225..1edda5da22f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_de.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher_de.properties @@ -100,7 +100,7 @@ launcher.err.main.not.void=Methode "main" ist nicht mit Rückgabetyp "void" dekl launcher.err.cant.find.class=Klasse nicht gefunden: {0} # 0: string -launcher.err.cant.find.main.method=Konnte keine main(String[])- oder main()-Methode in der Klasse: {0} finden. +launcher.err.cant.find.main.method=main(String[])- oder main()-Methode nicht gefunden in Klasse: {0} # 0: string launcher.err.cant.instantiate=Abstrakte Klasse: {0} kann nicht instanziiert werden diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c index 9c5d6414e4c..463818626dd 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_util.c @@ -1209,7 +1209,7 @@ CK_VOID_PTR jObjectToPrimitiveCKObjectPtr(JNIEnv *env, jobject jObject, CK_ULONG jclass jBooleanArrayClass, jIntArrayClass, jLongArrayClass; jclass jStringClass; jclass jObjectClass, jClassClass; - CK_VOID_PTR ckpObject; + CK_VOID_PTR ckpObject = NULL; jmethodID jMethod; jobject jClassObject; jstring jClassNameString; 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 c788b4e1617..08406fef518 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 @@ -2876,6 +2876,8 @@ public abstract class ByteVector extends AbstractVector { toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (byte) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (byte) VectorMath.maxUnsigned(a, b))); + case VECTOR_OP_SUADD: return (v, m) -> + toBits(v.rOp((byte)0, m, (i, a, b) -> (byte) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> toBits(v.rOp((byte)-1, m, (i, a, b) -> (byte)(a & b))); case VECTOR_OP_OR: return (v, m) -> 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 85aac460f8b..43356b9ea6c 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 @@ -2861,6 +2861,8 @@ public abstract class IntVector extends AbstractVector { toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (int) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (int) VectorMath.maxUnsigned(a, b))); + case VECTOR_OP_SUADD: return (v, m) -> + toBits(v.rOp((int)0, m, (i, a, b) -> (int) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> toBits(v.rOp((int)-1, m, (i, a, b) -> (int)(a & b))); case VECTOR_OP_OR: return (v, m) -> 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 abd86863165..8947343ff30 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 @@ -2727,6 +2727,8 @@ public abstract class LongVector extends AbstractVector { toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (long) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (long) VectorMath.maxUnsigned(a, b))); + case VECTOR_OP_SUADD: return (v, m) -> + toBits(v.rOp((long)0, m, (i, a, b) -> (long) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> toBits(v.rOp((long)-1, m, (i, a, b) -> (long)(a & b))); case VECTOR_OP_OR: return (v, m) -> 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 464b792e0f7..e222c6d25f3 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 @@ -2877,6 +2877,8 @@ public abstract class ShortVector extends AbstractVector { toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (short) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (short) VectorMath.maxUnsigned(a, b))); + case VECTOR_OP_SUADD: return (v, m) -> + toBits(v.rOp((short)0, m, (i, a, b) -> (short) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> toBits(v.rOp((short)-1, m, (i, a, b) -> (short)(a & b))); case VECTOR_OP_OR: return (v, m) -> 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 3fa7d1dab6c..f7d987fd280 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 @@ -3451,6 +3451,8 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> ($type$) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> ($type$) VectorMath.maxUnsigned(a, b))); + case VECTOR_OP_SUADD: return (v, m) -> + toBits(v.rOp(($type$)0, m, (i, a, b) -> ($type$) VectorMath.addSaturatingUnsigned(a, b))); #end[!FP] #if[BITWISE] case VECTOR_OP_AND: return (v, m) -> diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMetaspaceConstantImpl.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMetaspaceConstantImpl.java index 55d5492c2f8..fd08361f682 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMetaspaceConstantImpl.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotMetaspaceConstantImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,7 +95,7 @@ final class HotSpotMetaspaceConstantImpl implements HotSpotMetaspaceConstant, VM } private boolean canBeStoredInCompressibleMetaSpace() { - if (metaspaceObject instanceof HotSpotResolvedJavaType t && !t.isArray()) { + if (!HotSpotVMConfig.config().useClassMetaspaceForAllClasses && metaspaceObject instanceof HotSpotResolvedJavaType t && !t.isArray()) { // As of JDK-8338526, interface and abstract types are not stored // in compressible metaspace. return !t.isInterface() && !t.isAbstract(); diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java index bc2e121fe90..449b315e467 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,6 +67,8 @@ class HotSpotVMConfig extends HotSpotVMConfigAccess { final boolean useCompressedOops = getFlag("UseCompressedOops", Boolean.class); + final boolean useClassMetaspaceForAllClasses = getFlag("UseClassMetaspaceForAllClasses", Boolean.class); + final int objectAlignment = getFlag("ObjectAlignmentInBytes", Integer.class); final int klassOffsetInBytes = getFieldValue("CompilerToVM::Data::oopDesc_klass_offset_in_bytes", Integer.class, "int"); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties index a725d415406..2669aa9bdc0 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_de.properties @@ -136,8 +136,7 @@ doclet.Preview_API_Checkbox_Toggle_All=Alle umschalten doclet.Preview_JEP_URL=https://openjdk.org/jeps/{0} doclet.Preview_Label=Vorschau doclet.Preview_Mark=PREVIEW -doclet.Preview_Notes=Hinweise zur Vorschau-API -doclet.Preview_Notes_Elements=Elemente mit Vorschauhinweisen +doclet.Preview_Notes=Von Vorschaufeatures betroffene permanente APIs doclet.Restricted_Methods=Eingeschränkte Methoden doclet.Restricted_Mark=RESTRICTED doclet.searchTag=Suchtag @@ -345,6 +344,7 @@ doclet.UsesDeclaredUsingPreview={0} bezieht sich auf mindestens einen Typ, der m doclet.PreviewTrailingNote1=Programme können {0} nur verwenden, wenn Vorschaufeatures aktiviert sind. doclet.PreviewTrailingNote2=Vorschaufeatures können in künftigen Releases entfernt oder zu permanenten Features der Java-Plattform hochgestuft werden. doclet.PreviewJavaSERequiresTransitiveJavaBase=Indirekte Exporte aus dem Modul java.base sind mit der Direktive requires transitive java.base verknüpft. Das ist ein Vorschaufeature der Java-Sprache.
    Programme können requires transitive java.base nur verwenden, wenn Vorschaufeatures aktiviert sind.
    Vorschaufeatures können in einem zukünftigen Release entfernt oder zu permanenten Features der Java-Plattform hochgestuft werden.
    +doclet.PreviewMultipleNotes=Mehrere Vorschauhinweise in {0}. doclet.RestrictedMethod=eingeschränkte Methode doclet.RestrictedLeadingNote={0} ist eine {1} der Java-Plattform. doclet.RestrictedTrailingNote1=Programme können {0} nur verwenden, wenn der Zugriff auf eingeschränkte Methoden aktiviert ist. @@ -432,7 +432,7 @@ doclet.usage.excludedocfilessubdir.parameters=,,... doclet.usage.excludedocfilessubdir.description=Schließen Sie alle "doc-files"-Unterverzeichnisse mit einem angegebenen Namen aus.\n":" kann überall im Argument als Trennzeichen verwendet werden. doclet.usage.group.parameters= ,... -doclet.usage.group.description=Angegebene Elemente auf Überblickseite gruppieren.\n":" kann überall im Argument als Trennzeichen verwendet werden. +doclet.usage.group.description=Angegebene Packages oder Module auf Überblickseite gruppieren.\n":" kann überall im Argument als Trennzeichen verwendet werden. doclet.usage.legal-notices.parameters='default' | 'none' | doclet.usage.legal-notices.description=Steuert die rechtlichen Hinweise in der generierten Ausgabe diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties index 749ef5856b8..1694dc980bc 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_ja.properties @@ -136,8 +136,7 @@ doclet.Preview_API_Checkbox_Toggle_All=すべて設定 doclet.Preview_JEP_URL=https://openjdk.org/jeps/{0} doclet.Preview_Label=プレビュー doclet.Preview_Mark=PREVIEW -doclet.Preview_Notes=プレビューAPIのノート -doclet.Preview_Notes_Elements=プレビュー・ノートを含む要素 +doclet.Preview_Notes=プレビュー機能によって影響を受ける永続的なAPI doclet.Restricted_Methods=制限されたメソッド doclet.Restricted_Mark=RESTRICTED doclet.searchTag=検索タグ @@ -345,6 +344,7 @@ doclet.UsesDeclaredUsingPreview={0}は、Java言語のプレビュー機能を doclet.PreviewTrailingNote1=プログラムは、プレビュー機能が有効になっている場合にのみ{0}を使用できます。 doclet.PreviewTrailingNote2=プレビュー機能は将来のリリースで削除されるか、Javaプラットフォームの永続的な機能にアップグレードされる可能性があります。 doclet.PreviewJavaSERequiresTransitiveJavaBase=java.baseモジュールからの間接的エクスポートは、Java言語のプレビュー機能であるrequires transitive java.baseディレクティブに関連付けられています。
    プログラムは、プレビュー機能が有効になっている場合にのみrequires transitive java.baseを使用できます。
    プレビュー機能は将来のリリースで削除されるか、Javaプラットフォームの永続的な機能にアップグレードされる可能性があります。
    +doclet.PreviewMultipleNotes={0}の複数のプレビュー・ノート。 doclet.RestrictedMethod=制限されたメソッド doclet.RestrictedLeadingNote={0}は、Javaプラットフォームの{1}です。 doclet.RestrictedTrailingNote1=プログラムは、制限されたメソッドへのアクセスが有効になっている場合にのみ{0}を使用できます。 @@ -432,7 +432,7 @@ doclet.usage.excludedocfilessubdir.parameters=,,... doclet.usage.excludedocfilessubdir.description=指定された名前の'doc-files'サブディレクトリをすべて除外します。\n':'も、セパレータとして引数の任意の場所に使用できます。 doclet.usage.group.parameters= ,... -doclet.usage.group.description=指定する要素を概要ページにおいてグループ化します。\n':'も、セパレータとして引数の任意の場所に使用できます。 +doclet.usage.group.description=指定するパッケージまたはモジュールを概要ページにおいてグループ化します。\n':'も、セパレータとして引数の任意の場所に使用できます。 doclet.usage.legal-notices.parameters='default' | 'none' | doclet.usage.legal-notices.description=生成された出力の法律上の注意点を制御します diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties index 4bb154065b6..8881171351e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_zh_CN.properties @@ -136,8 +136,7 @@ doclet.Preview_API_Checkbox_Toggle_All=全部切换 doclet.Preview_JEP_URL=https://openjdk.org/jeps/{0} doclet.Preview_Label=预览 doclet.Preview_Mark=PREVIEW -doclet.Preview_Notes=预览 API 注释 -doclet.Preview_Notes_Elements=包含预览注释的元素 +doclet.Preview_Notes=受预览功能影响的永久 API doclet.Restricted_Methods=受限制的方法 doclet.Restricted_Mark=RESTRICTED doclet.searchTag=搜索标记 @@ -345,6 +344,7 @@ doclet.UsesDeclaredUsingPreview={0} 引用一个或多个类型,这些类型 doclet.PreviewTrailingNote1=只有在启用了预览功能时,程序才能使用 {0}。 doclet.PreviewTrailingNote2=预览功能可能会在未来发行版中删除,也可能会升级为 Java 平台的永久功能。 doclet.PreviewJavaSERequiresTransitiveJavaBase=来自 java.base 模块的间接导出项与 requires transitive java.base 指令关联,这是 Java 语言的预览功能。
    仅在启用了预览功能时,程序才能使用 requires transitive java.base
    预览功能可能会在未来发行版中删除,也可能会升级为 Java 平台的永久功能。
    +doclet.PreviewMultipleNotes={0} 中有多个预览注释。 doclet.RestrictedMethod=受限制方法 doclet.RestrictedLeadingNote={0} 是 Java 平台的 {1}。 doclet.RestrictedTrailingNote1=只有在启用了对受限制方法的访问时,程序才能使用 {0}。 @@ -432,7 +432,7 @@ doclet.usage.excludedocfilessubdir.parameters=,,... doclet.usage.excludedocfilessubdir.description=排除包含给定名称的所有 'doc-files' 子目录。\n还可以将 ':' 作为分隔符用于参数中的任何位置。 doclet.usage.group.parameters= ,... -doclet.usage.group.description=在概览页面上将指定元素归到一组。\n还可以将 ':' 作为分隔符用于参数中的任何位置。 +doclet.usage.group.description=在概览页面中将指定程序包或模块归到一组。\n还可以将 ':' 作为分隔符用于参数中的任何位置。 doclet.usage.legal-notices.parameters='default' | 'none' | doclet.usage.legal-notices.description=控制所生成输出中的法律声明 diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java index 8c60903d48c..9668fb27e07 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -247,6 +247,7 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet { Set alreadyDocumentedExceptions = new HashSet<>(); List exceptionTags = utils.getThrowsTrees(executable); for (ThrowsTree t : exceptionTags) { + config.tagletManager.checkTags(holder, t.getDescription()); Element exceptionElement = getExceptionType(t, executable); outputAnExceptionTagDeeply(exceptionSection, exceptionElement, t, executable, alreadyDocumentedExceptions, typeSubstitutions); } @@ -364,9 +365,10 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet { alreadyDocumentedExceptions.add(exceptionType); var description = tag.getDescription(); int i = indexOfInheritDoc(tag, holder); - if (i == -1) { - // Since the description does not contain {@inheritDoc}, we either add a new entry, or - // append to the current one. Here's an example of when we add a new entry: + // Constructors do not support {@inheritDoc}, but we still want to render the remaining tag. + if (i == -1 || holder.getKind() != ElementKind.METHOD) { + // Since the description does not contain or support {@inheritDoc}, we either add a new entry, + // or append to the current one. Here's an example of when we add a new entry: // // ... -> {@inheritDoc} -> // @@ -387,7 +389,6 @@ public class ThrowsTaglet extends BaseTaglet implements InheritableTaglet { exceptionSection.endEntry(); } } else { // expand a single {@inheritDoc} - assert holder.getKind() == ElementKind.METHOD : holder.getKind(); // only methods can use {@inheritDoc} // Is the {@inheritDoc} that we found standalone (i.e. without preceding and following text)? boolean loneInheritDoc = description.size() == 1; assert !loneInheritDoc || i == 0 : i; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties index 9836c58843b..4b02a720c78 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties @@ -124,7 +124,7 @@ doclet.inheritDocNoDoc=overridden methods do not document exception type {0} doclet.throwsInheritDocUnsupported=@inheritDoc is not supported for exception-type type parameters \ that are not declared by a method; document such exception types directly doclet.noInheritedDoc=@inheritDoc used but {0} does not override or implement any method. -doclet.tag_misuse=Tag {0} cannot be used in {1} documentation. It can only be used in the following types of documentation: {2}. +doclet.tag_misuse=Tag {0} cannot be used in {1} documentation. It can only be used in the following types of documentation: {2}. doclet.Package_Summary=Package Summary doclet.Requires_Summary=Requires doclet.Indirect_Requires_Summary=Indirect Requires diff --git a/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c b/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c index a0f2687a88c..3a66e451519 100644 --- a/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c +++ b/src/jdk.jdwp.agent/unix/native/libjdwp/exec_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ #include #include #include +#include #include "sys.h" #include "util.h" #include "error_messages.h" @@ -48,6 +49,21 @@ static char *skipNonWhitespace(char *p) { return p; } +static int +markCloseOnExec(int fd) +{ + const int flags = fcntl(fd, F_GETFD); + if (flags < 0) { + return -1; + } + if ((flags & FD_CLOEXEC) == 0) { + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { + return -1; + } + } + return 0; +} + #if defined(_AIX) /* AIX does not understand '/proc/self' - it requires the real process ID */ #define FD_DIR aix_fd_dir @@ -57,70 +73,60 @@ static char *skipNonWhitespace(char *p) { #define FD_DIR "/proc/self/fd" #endif -// Closes every file descriptor that is listed as a directory -// entry in "/proc/self/fd" (or its equivalent). Standard -// input/output/error file descriptors will not be closed -// by this function. This function returns 0 on failure -// and 1 on success. +// Marks all file descriptors found in /proc/self/fd with the +// FD_CLOEXEC flag to ensure they are automatically closed +// upon execution of a new program via exec(). This function +// returns -1 on failure and 0 on success. static int -closeDescriptors(void) +markDescriptorsCloseOnExec(void) { DIR *dp; struct dirent *dirp; - /* leave out standard input/output/error descriptors */ - int from_fd = STDERR_FILENO + 1; - - /* We're trying to close all file descriptors, but opendir() might - * itself be implemented using a file descriptor, and we certainly - * don't want to close that while it's in use. We assume that if - * opendir() is implemented using a file descriptor, then it uses - * the lowest numbered file descriptor, just like open(). So - * before calling opendir(), we close a couple explicitly, so that - * opendir() can then use these lowest numbered closed file - * descriptors afresh. */ - - close(from_fd); /* for possible use by opendir() */ - close(from_fd + 1); /* another one for good luck */ - from_fd += 2; /* leave out the 2 we just closed, which the opendir() may use */ + const int from_fd = STDERR_FILENO; #if defined(_AIX) - /* set FD_DIR for AIX which does not understand '/proc/self' - it - * requires the real process ID */ + /* AIX does not understand '/proc/self' - it requires the real process ID */ char aix_fd_dir[32]; /* the pid has at most 19 digits */ snprintf(aix_fd_dir, 32, "/proc/%d/fd", getpid()); #endif if ((dp = opendir(FD_DIR)) == NULL) { ERROR_MESSAGE(("failed to open dir %s while determining" - " file descriptors to close for process %d", + " file descriptors to mark or close for process %d", FD_DIR, getpid())); - return 0; // failure + return -1; // failure } + int dir_fd = dirfd(dp); + while ((dirp = readdir(dp)) != NULL) { if (!isdigit(dirp->d_name[0])) { continue; } - const long fd = strtol(dirp->d_name, NULL, 10); - if (fd <= INT_MAX && fd >= from_fd) { - (void)close((int)fd); + int fd = strtol(dirp->d_name, NULL, 10); + if (fd <= INT_MAX && fd > from_fd && fd != dir_fd) { + if (markCloseOnExec(fd) == -1) { + (void)close((int)fd); + } } } (void)closedir(dp); - return 1; // success + return 0; // success } -// Does necessary housekeeping of a forked child process -// (like closing copied file descriptors) before -// execing the child process. This function never returns. +// Performs necessary housekeeping in the forked child process, +// such as marking copied file descriptors (except standard input/output/error) +// with FD_CLOEXEC to ensure they are closed during exec(). +// This function never returns. static void forkedChildProcess(const char *file, char *const argv[]) { - /* Close all file descriptors that have been copied over - * from the parent process due to fork(). */ - if (closeDescriptors() == 0) { /* failed, close the old way */ + /* Mark all file descriptors (except standard input/output/error) + * copied from the parent process with FD_CLOEXEC, so they are + * closed automatically upon exec(). */ + if (markDescriptorsCloseOnExec() < 0) { /* failed, close the old way */ /* Find max allowed file descriptors for a process * and assume all were opened for the parent process and * copied over to this child process. We close them all. */ @@ -132,7 +138,7 @@ forkedChildProcess(const char *file, char *const argv[]) rlim_t i = STDERR_FILENO + 1; ERROR_MESSAGE(("failed to close file descriptors of" " child process optimally, falling back to closing" - " %d file descriptors sequentially", (max_fd - i + 1))); + " %d file descriptors sequentially", (max_fd - i))); for (; i < max_fd; i++) { (void)close(i); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Contextual.java b/src/jdk.jfr/share/classes/jdk/jfr/Contextual.java index 92deeb8d562..e1cf0544e6c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Contextual.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Contextual.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + package jdk.jfr; import java.lang.annotation.ElementType; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java index 4c8913e96a0..3e95932b197 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java @@ -49,7 +49,9 @@ final class ChunkInputStream extends InputStream { } this.chunks = l.iterator(); - nextStream(); + if (!nextStream()) { + throw new IOException("Recording data missing on disk."); + } } @Override @@ -62,13 +64,18 @@ final class ChunkInputStream extends InputStream { } private boolean nextStream() throws IOException { - if (!nextChunk()) { - return false; + while (nextChunk()) { + try { + stream = new BufferedInputStream(Files.newInputStream(currentChunk.getFile())); + unstreamedSize -= currentChunk.getSize(); + return true; + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.INFO, "Could not open chunk file for stream: " + e.getMessage() + ". Skipping."); + // Release chunk if it can't be found/accessed. + closeChunk(); + } } - - stream = new BufferedInputStream(Files.newInputStream(currentChunk.getFile())); - unstreamedSize -= currentChunk.getSize(); - return true; + return false; } private boolean nextChunk() { @@ -126,11 +133,14 @@ final class ChunkInputStream extends InputStream { } private void closeStream() throws IOException { - if (stream != null) { - stream.close(); - stream = null; + try { + if (stream != null) { + stream.close(); + stream = null; + } + } finally { + closeChunk(); } - closeChunk(); } private void closeChunk() { @@ -143,11 +153,9 @@ final class ChunkInputStream extends InputStream { @Override public void close() throws IOException { closeStream(); - while (currentChunk != null) { - closeChunk(); - if (!nextChunk()) { - return; - } + while (chunks.hasNext()) { + RepositoryChunk c = chunks.next(); + c.release(); } } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/ClassInspector.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/ClassInspector.java index 3646162e8f7..3cb13fb66ad 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/ClassInspector.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/ClassInspector.java @@ -140,7 +140,7 @@ final class ClassInspector { return true; } - boolean isThrottled() { + boolean isThrottled(MethodDesc staticThrottleMethod) { String result = annotationValue(ANNOTATION_THROTTLE, String.class, "off"); if (result != null) { return true; @@ -151,6 +151,9 @@ final class ClassInspector { return true; } } + if (isJDK()) { + return hasStaticMethod(staticThrottleMethod); + } return false; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java index 02775b7707a..7a8d490d6e5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -285,20 +285,25 @@ public final class EventControl { private SettingControl instantiateSettingControl(Class settingControlClass) throws IllegalAccessException, InstantiationException { SecuritySupport.makeVisibleToJFR(settingControlClass); - final Constructor cc; - try { - cc = settingControlClass.getDeclaredConstructors()[0]; - } catch (Exception e) { - throw (Error) new InternalError("Could not get constructor for " + settingControlClass.getName()).initCause(e); - } - cc.setAccessible(true); try { + Constructor cc = findDefaultConstructor(settingControlClass); + cc.setAccessible(true); return (SettingControl) cc.newInstance(); } catch (IllegalArgumentException | InvocationTargetException e) { throw new InternalError("Could not instantiate setting for class " + settingControlClass.getName()); } } + private Constructor findDefaultConstructor(Class settingControlClass) { + for (Constructor c : settingControlClass.getDeclaredConstructors()) { + if (c.getParameterCount() == 0) { + return c; + } + } + // Programming error by user, fail fast + throw new InstantiationError("Could not find default constructor for " + settingControlClass.getName()); + } + private static Control defineEnabled(PlatformEventType type) { // Java events are enabled by default, // JVM events are not, maybe they should be? Would lower learning curve 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 032ccb5f4b5..b7ee6fb5fe0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventInstrumentation.java @@ -124,11 +124,7 @@ public final class EventInstrumentation { this.eventClassDesc = inspector.getClassDesc(); this.staticCommitMethod = inspector.findStaticCommitMethod(); this.untypedEventConfiguration = hasUntypedConfiguration(); - if (inspector.isJDK()) { - this.throttled = inspector.hasStaticMethod(METHOD_EVENT_SHOULD_THROTTLE_COMMIT_LONG_LONG); - } else { - this.throttled = inspector.isThrottled(); - } + this.throttled = inspector.isThrottled(METHOD_EVENT_SHOULD_THROTTLE_COMMIT_LONG_LONG); } byte[] buildInstrumented() { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java index 8521cd4303f..84caf7cb460 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SettingsManager.java @@ -194,7 +194,7 @@ final class SettingsManager { String key = entry.getKey(); String value = entry.getValue(); int index = key.indexOf("#"); - if (index > 1 && index < key.length() - 2) { + if (index > 0 && index < key.length() - 1) { String eventName = key.substring(0, index); eventName = Utils.upgradeLegacyJDKEvent(eventName); InternalSetting s = internals.get(eventName); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java index 7ede3de9d15..80edba97e59 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/Type.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -149,7 +149,7 @@ public class Type implements Comparable { return null; } - static boolean isKnownType(Class type) { + public static boolean isKnownType(Class type) { if (type.isPrimitive()) { return true; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java index bf26294ef57..de7584d3e3f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java @@ -330,7 +330,7 @@ public final class TypeLibrary { dynamicFieldSet.put(dynamicField.getName(), dynamicField); } List newTypes = new ArrayList<>(); - for (Field field : Utils.getVisibleEventFields(clazz)) { + for (Field field : Utils.getEventFields(clazz)) { ValueDescriptor vd = dynamicFieldSet.get(field.getName()); if (vd != null) { if (!vd.getTypeName().equals(field.getType().getName())) { @@ -343,9 +343,7 @@ public final class TypeLibrary { } else { vd = createField(field); } - if (vd != null) { - type.add(vd); - } + type.add(vd); } addTypes(newTypes); } @@ -384,17 +382,7 @@ public final class TypeLibrary { } private static ValueDescriptor createField(Field field) { - int mod = field.getModifiers(); - if (Modifier.isTransient(mod)) { - return null; - } - if (Modifier.isStatic(mod)) { - return null; - } Class fieldType = field.getType(); - if (!Type.isKnownType(fieldType)) { - return null; - } boolean constantPool = Thread.class == fieldType || fieldType == Class.class; Type type = createType(fieldType); String fieldName = field.getName(); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantMap.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantMap.java index daf6ad7278c..3b6859304b8 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantMap.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ConstantMap.java @@ -77,7 +77,7 @@ final class ConstantMap { if (id != 0) { String msg = "Missing object ID " + id + " in pool " + getName() + ". All IDs should reference an object"; Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, msg); - // assert false : msg; + assert false : msg; } return null; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java index 7de6617f806..db832626ded 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java @@ -211,13 +211,15 @@ public final class Utils { return sanitized; } - public static List getVisibleEventFields(Class clazz) { + public static List getEventFields(Class clazz) { List fields = new ArrayList<>(); for (Class c = clazz; !Utils.isEventBaseClass(c); c = c.getSuperclass()) { for (Field field : c.getDeclaredFields()) { - // skip private field in base classes - if (c == clazz || !Modifier.isPrivate(field.getModifiers())) { - fields.add(field); + if (isSupportedField(field)) { + // skip private field in base classes + if (c == clazz || !Modifier.isPrivate(field.getModifiers())) { + fields.add(field); + } } } } @@ -311,48 +313,34 @@ public final class Utils { } public static void verifyMirror(Class mirror, Class real) { - Class cMirror = Objects.requireNonNull(mirror); - Class cReal = Objects.requireNonNull(real); - Map mirrorFields = new HashMap<>(); - while (cMirror != null) { - for (Field f : cMirror.getDeclaredFields()) { - if (isSupportedType(f.getType())) { - mirrorFields.put(f.getName(), f); - } - } - cMirror = cMirror.getSuperclass(); + for (Field f : mirror.getDeclaredFields()) { + mirrorFields.put(f.getName(), f); } - while (cReal != null) { - for (Field realField : cReal.getDeclaredFields()) { - if (isSupportedType(realField.getType()) && !realField.isSynthetic()) { - String fieldName = realField.getName(); - Field mirrorField = mirrorFields.get(fieldName); - if (mirrorField == null) { - throw new InternalError("Missing mirror field for " + cReal.getName() + "#" + fieldName); - } - if (realField.getType() != mirrorField.getType()) { - throw new InternalError("Incorrect type for mirror field " + fieldName); - } - if (realField.getModifiers() != mirrorField.getModifiers()) { - throw new InternalError("Incorrect modifier for mirror field " + fieldName); - } - mirrorFields.remove(fieldName); - } + for (Field realField : Utils.getEventFields(real)) { + String fieldName = realField.getName(); + Field mirrorField = mirrorFields.remove(fieldName); + if (mirrorField == null) { + throw new InternalError("Missing mirror field for " + real.getName() + "#" + fieldName); + } + if (realField.getType() != mirrorField.getType()) { + throw new InternalError("Incorrect type for mirror field " + fieldName); + } + if (realField.getModifiers() != mirrorField.getModifiers()) { + throw new InternalError("Incorrect modifier for mirror field " + fieldName); } - cReal = cReal.getSuperclass(); } - if (!mirrorFields.isEmpty()) { throw new InternalError("Found additional fields in mirror class " + mirrorFields.keySet()); } } - private static boolean isSupportedType(Class type) { - if (Modifier.isTransient(type.getModifiers()) || Modifier.isStatic(type.getModifiers())) { + public static boolean isSupportedField(Field field) { + int modifiers = field.getModifiers(); + if (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers)) { return false; } - return Type.isValidJavaFieldType(type.getName()); + return Type.isKnownType(field.getType()); } public static void notifyFlush() { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java index 8de462abac7..476ca3201ce 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -24,10 +24,10 @@ */ package jdk.jpackage.internal; -import jdk.jpackage.internal.model.LinuxPackage; -import jdk.jpackage.internal.model.LinuxLauncher; -import jdk.jpackage.internal.model.Package; -import jdk.jpackage.internal.model.Launcher; +import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource; +import static jdk.jpackage.internal.model.LauncherShortcut.toRequest; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; + import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -45,12 +45,13 @@ import java.util.stream.Stream; import javax.imageio.ImageIO; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; -import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource; import jdk.jpackage.internal.model.FileAssociation; +import jdk.jpackage.internal.model.LinuxLauncher; +import jdk.jpackage.internal.model.LinuxPackage; +import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.util.CompositeProxy; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.XmlUtils; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; /** * Helper to create files for desktop integration. @@ -77,7 +78,7 @@ final class DesktopIntegration extends ShellCustomAction { // Need desktop and icon files if one of conditions is met: // - there are file associations configured // - user explicitly requested to create a shortcut - boolean withDesktopFile = !associations.isEmpty() || launcher.shortcut().orElse(false); + boolean withDesktopFile = !associations.isEmpty() || toRequest(launcher.shortcut()).orElse(false); var curIconResource = createLauncherIconResource(pkg.app(), launcher, env::createResource); @@ -132,7 +133,7 @@ final class DesktopIntegration extends ShellCustomAction { nestedIntegrations = pkg.app().additionalLaunchers().stream().map(v -> { return (LinuxLauncher)v; }).filter(l -> { - return l.shortcut().orElse(true); + return toRequest(l.shortcut()).orElse(true); }).map(toFunction(l -> { return new DesktopIntegration(env, pkg, l); })).toList(); @@ -225,6 +226,9 @@ final class DesktopIntegration extends ShellCustomAction { } private Map createDataForDesktopFile() { + + var installedLayout = pkg.asInstalledPackageApplicationLayout().orElseThrow(); + Map data = new HashMap<>(); data.put("APPLICATION_NAME", launcher.name()); data.put("APPLICATION_DESCRIPTION", launcher.description()); @@ -232,8 +236,7 @@ final class DesktopIntegration extends ShellCustomAction { f -> f.installPath().toString()).orElse(null)); data.put("DEPLOY_BUNDLE_CATEGORY", pkg.menuGroupName()); data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo( - pkg.asInstalledPackageApplicationLayout().orElseThrow().launchersDirectory().resolve( - launcher.executableNameWithSuffix()).toString())); + installedLayout.launchersDirectory().resolve(launcher.executableNameWithSuffix()).toString())); return data; } @@ -481,7 +484,7 @@ final class DesktopIntegration extends ShellCustomAction { private final BuildEnv env; private final LinuxPackage pkg; - private final Launcher launcher; + private final LinuxLauncher launcher; private final List associations; diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java index 7dff3cd73ae..6967dea111e 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java @@ -29,16 +29,14 @@ import static jdk.jpackage.internal.FromParams.createApplicationBuilder; import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam; import static jdk.jpackage.internal.FromParams.createPackageBuilder; import static jdk.jpackage.internal.FromParams.createPackageBundlerParam; +import static jdk.jpackage.internal.FromParams.findLauncherShortcut; import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT; -import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT; import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB; import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LinuxApplication; import jdk.jpackage.internal.model.LinuxLauncher; @@ -51,11 +49,10 @@ final class LinuxFromParams { private static LinuxApplication createLinuxApplication( Map params) throws ConfigException, IOException { final var launcherFromParams = new LauncherFromParams(); + final var app = createApplicationBuilder(params, toFunction(launcherParams -> { final var launcher = launcherFromParams.create(launcherParams); - final var shortcut = Stream.of(SHORTCUT_HINT, LINUX_SHORTCUT_HINT).map(param -> { - return param.findIn(launcherParams); - }).filter(Optional::isPresent).map(Optional::get).findFirst(); + final var shortcut = findLauncherShortcut(LINUX_SHORTCUT_HINT, params, launcherParams); return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(shortcut)); }), APPLICATION_LAYOUT).create(); return LinuxApplication.create(app); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java index 8970f2198c2..c84b5e3bbf5 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java @@ -24,6 +24,7 @@ */ package jdk.jpackage.internal.model; +import java.util.HashMap; import java.util.Map; import jdk.jpackage.internal.util.CompositeProxy; @@ -36,9 +37,11 @@ public interface LinuxLauncher extends Launcher, LinuxLauncherMixin { @Override default Map extraAppImageFileData() { - return shortcut().map(v -> { - return Map.of("shortcut", Boolean.toString(v)); - }).orElseGet(Map::of); + Map map = new HashMap<>(); + shortcut().ifPresent(shortcut -> { + shortcut.store(SHORTCUT_ID, map::put); + }); + return map; } /** @@ -52,4 +55,6 @@ public interface LinuxLauncher extends Launcher, LinuxLauncherMixin { public static LinuxLauncher create(Launcher launcher, LinuxLauncherMixin mixin) { return CompositeProxy.create(LinuxLauncher.class, launcher, mixin); } + + public static final String SHORTCUT_ID = "linux-shortcut"; } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncherMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncherMixin.java index d5e15101c7e..e8ff61ca239 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncherMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncherMixin.java @@ -32,24 +32,20 @@ import java.util.Optional; public interface LinuxLauncherMixin { /** - * Gets the start menu shortcut setting of this application launcher. + * Gets the start menu shortcut of this application launcher. *

    - * Returns true if this application launcher was requested to have - * the start menu shortcut. - *

    - * Returns false if this application launcher was requested not to - * have the start menu shortcut. - *

    - * Returns an empty {@link Optional} instance if there was no request about the - * start menu shortcut for this application launcher. + * Returns a non-empty {@link Optional} instance if a request about the start + * menu shortcut for this application launcher was made and an empty + * {@link Optional} instance if there was no request about the start menu + * shortcut for this application launcher. * - * @return the start menu shortcut setting of this application launcher + * @return the start menu shortcut of this application launcher */ - Optional shortcut(); + Optional shortcut(); /** * Default implementation of {@link LinuxLauncherMixin} interface. */ - record Stub(Optional shortcut) implements LinuxLauncherMixin { + record Stub(Optional shortcut) implements LinuxLauncherMixin { } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java index d9946075c4f..93d037c6a45 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java @@ -36,8 +36,6 @@ import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.Arguments.CLIOptions; import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA; import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME; -import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT; -import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT; /* * AddLauncherArguments @@ -135,16 +133,16 @@ class AddLauncherArguments { Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_CONSOLE_HINT.getId(), getOptionValue(CLIOptions.WIN_CONSOLE_HINT)); - Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(), + Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_SHORTCUT_HINT.getId(), getOptionValue(CLIOptions.WIN_SHORTCUT_HINT)); - Arguments.putUnlessNull(bundleParams, MENU_HINT.getID(), + Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_MENU_HINT.getId(), getOptionValue(CLIOptions.WIN_MENU_HINT)); } if (OperatingSystem.isLinux()) { Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_CATEGORY.getId(), getOptionValue(CLIOptions.LINUX_CATEGORY)); - Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(), + Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_SHORTCUT_HINT.getId(), getOptionValue(CLIOptions.LINUX_SHORTCUT_HINT)); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java index cc9b490b188..24ae249297e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java @@ -25,6 +25,7 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; +import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; import static jdk.jpackage.internal.StandardBundlerParam.TEMP_ROOT; import static jdk.jpackage.internal.StandardBundlerParam.VERBOSE; @@ -46,9 +47,9 @@ final class BuildEnvFromParams { final var pkg = FromParams.getCurrentPackage(params); if (app.isRuntime()) { - builder.appImageDir(PREDEFINED_RUNTIME_IMAGE.fetchFrom(params)); + PREDEFINED_RUNTIME_IMAGE.copyInto(params, builder::appImageDir); } else if (StandardBundlerParam.hasPredefinedAppImage(params)) { - builder.appImageDir(StandardBundlerParam.getPredefinedAppImage(params)); + PREDEFINED_APP_IMAGE.copyInto(params, builder::appImageDir); } else if (pkg.isPresent()) { builder.appImageDirForPackage(); } else { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java index 5e940aba18b..34818fafc94 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java @@ -24,6 +24,9 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.Arguments.CLIOptions.LINUX_SHORTCUT_HINT; +import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_MENU_HINT; +import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_SHORTCUT_HINT; import static jdk.jpackage.internal.StandardBundlerParam.ABOUT_URL; import static jdk.jpackage.internal.StandardBundlerParam.ADD_LAUNCHERS; import static jdk.jpackage.internal.StandardBundlerParam.ADD_MODULES; @@ -63,6 +66,8 @@ import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo; import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.LauncherShortcut; +import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.function.ThrowingFunction; @@ -165,6 +170,32 @@ final class FromParams { jdk.jpackage.internal.model.Package.class.getName())); } + static Optional findLauncherShortcut( + BundlerParamInfo shortcutParam, + Map mainParams, + Map launcherParams) { + + Optional launcherValue; + if (launcherParams == mainParams) { + // The main launcher + launcherValue = Optional.empty(); + } else { + launcherValue = shortcutParam.findIn(launcherParams); + } + + return launcherValue.map(withShortcut -> { + if (withShortcut) { + return Optional.of(LauncherShortcutStartupDirectory.DEFAULT); + } else { + return Optional.empty(); + } + }).or(() -> { + return shortcutParam.findIn(mainParams).map(_ -> { + return Optional.of(LauncherShortcutStartupDirectory.DEFAULT); + }); + }).map(LauncherShortcut::new); + } + private static ApplicationLaunchers createLaunchers( Map params, Function, Launcher> launcherMapper) { @@ -195,8 +226,9 @@ final class FromParams { // mainParams), APP_NAME.fetchFrom(launcherParams))); launcherParams.put(DESCRIPTION.getID(), DESCRIPTION.fetchFrom(mainParams)); } - return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(), ADD_LAUNCHERS - .getID(), FILE_ASSOCIATIONS.getID()); + return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(), + ADD_LAUNCHERS.getID(), FILE_ASSOCIATIONS.getID(), WIN_MENU_HINT.getId(), + WIN_SHORTCUT_HINT.getId(), LINUX_SHORTCUT_HINT.getId()); } static final BundlerParamInfo APPLICATION = createApplicationBundlerParam(null); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index 10590a7aa8b..276bed4f31c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java @@ -55,19 +55,49 @@ import jdk.jpackage.internal.util.function.ExceptionBox; final class PackagingPipeline { + /** + * Runs the pipeline for the given application. + * + * @param env the build environment + * @param app the application + */ void execute(BuildEnv env, Application app) throws PackagerException { execute(appContextMapper.apply(createTaskContext(env, app))); } + /** + * Runs the pipeline for the given package. + *

    + * Building a package may require a directory where the app image bits will be + * accumulated or the existing app image may be used. The decision is made based + * on the properties of the given package. A new build environment will be + * created if an intermediate directory is required. To access the build + * environment that will be used by the pipeline before running the pipeline + * create {@link StartupParameters} instance using + * {@link Builder#createStartupParameters(BuildEnv, Package, Path)} method. + * + * @param env the build environment + * @param pkg the package + * @param outputDir the output directory for the package file + */ void execute(BuildEnv env, Package pkg, Path outputDir) throws PackagerException { execute((StartupParameters)createPackagingTaskContext(env, pkg, outputDir, taskConfig, appImageLayoutForPackaging.apply(pkg))); } + /** + * Runs the pipeline using the startup parameters created with + * {@link Builder#createStartupParameters(BuildEnv, Package, Path)} call. + * + * @param startupParameters the pipeline startup parameters + */ void execute(StartupParameters startupParameters) throws PackagerException { execute(pkgContextMapper.apply(createTaskContext((PackagingTaskContext)startupParameters))); } + /** + * The way to access packaging build environment before building a package in a pipeline. + */ interface StartupParameters { BuildEnv packagingEnv(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java index ec27b1fac5c..7b3f936c5a3 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RuntimeBuilderBuilder.java @@ -24,13 +24,11 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.I18N.buildConfigException; import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.util.List; @@ -116,15 +114,6 @@ final class RuntimeBuilderBuilder { private static RuntimeBuilder createCopyingRuntimeBuilder(Path runtimeDir, Path... modulePath) throws ConfigException { - if (!Files.exists(runtimeDir)) { - throw buildConfigException() - .message("message.runtime-image-dir-does-not-exist", - "--runtime-image", runtimeDir) - .advice("message.runtime-image-dir-does-not-exist.advice", - "--runtime-image") - .create(); - } - return appImageLayout -> { try { // copy whole runtime, skipping jmods and src.zip diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java index 93d84fd79b6..6b89bb3ee65 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/StandardBundlerParam.java @@ -94,10 +94,17 @@ final class StandardBundlerParam { null ); + static final BundlerParamInfo PREDEFINED_APP_IMAGE = + new BundlerParamInfo<>( + Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(), + Path.class, + params -> null, + (s, p) -> Path.of(s)); + static final BundlerParamInfo PREDEFINED_APP_IMAGE_FILE = BundlerParamInfo.createBundlerParam( ExternalApplication.class, params -> { if (hasPredefinedAppImage(params)) { - var appImage = getPredefinedAppImage(params); + var appImage = PREDEFINED_APP_IMAGE.fetchFrom(params); return AppImageFile.load(appImage, PLATFORM_APPLICATION_LAYOUT); } else { return null; @@ -127,13 +134,6 @@ final class StandardBundlerParam { (s, p) -> Path.of(s) ); - static final BundlerParamInfo PREDEFINED_APP_IMAGE = - new BundlerParamInfo<>( - Arguments.CLIOptions.PREDEFINED_APP_IMAGE.getId(), - Path.class, - params -> null, - (s, p) -> Path.of(s)); - // this is the raw --app-name arg - used in APP_NAME and INSTALLER_NAME static final BundlerParamInfo NAME = new BundlerParamInfo<>( @@ -307,24 +307,6 @@ final class StandardBundlerParam { true : Boolean.valueOf(s) ); - static final BundlerParamInfo SHORTCUT_HINT = - new BundlerParamInfo<>( - "shortcut-hint", // not directly related to a CLI option - Boolean.class, - params -> true, // defaults to true - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - true : Boolean.valueOf(s) - ); - - static final BundlerParamInfo MENU_HINT = - new BundlerParamInfo<>( - "menu-hint", // not directly related to a CLI option - Boolean.class, - params -> true, // defaults to true - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? - true : Boolean.valueOf(s) - ); - static final BundlerParamInfo RESOURCE_DIR = new BundlerParamInfo<>( Arguments.CLIOptions.RESOURCE_DIR.getId(), @@ -533,18 +515,6 @@ final class StandardBundlerParam { return params.containsKey(PREDEFINED_APP_IMAGE.getID()); } - static Path getPredefinedAppImage(Map params) { - Path applicationImage = PREDEFINED_APP_IMAGE.fetchFrom(params); - if (applicationImage != null && !IOUtils.exists(applicationImage)) { - throw new RuntimeException( - MessageFormat.format(I18N.getString( - "message.app-image-dir-does-not-exist"), - PREDEFINED_APP_IMAGE.getID(), - applicationImage.toString())); - } - return applicationImage; - } - private static String getDefaultAppVersion(Map params) { String appVersion = DEFAULT_VERSION; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java index f164daf3eb0..943a42712c8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java @@ -222,10 +222,10 @@ public interface Application extends BundleSpec { /** * Gets the additional properties of this application for the application entry - * in the app image (".jpackage") file. + * in the app image (".jpackage.xml") file. * * @return the additional properties of this application for the application - * entry in ".jpackage" file + * entry in ".jpackage.xml" file */ Map extraAppImageFileData(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java index 6e36949190e..4275b84ba80 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DefaultLauncherIcon.java @@ -29,7 +29,7 @@ import java.util.Optional; /** * Default application launcher icon. *

    - * Default icon is either loaded from the resources of {@link jdk.jpackage/} module or picked from the resource directory. + * Default icon is either loaded from the resources of {@link jdk.jpackage} module or picked from the resource directory. *

    * Use {@link #INSTANCE} field to get an instance of this type. */ diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java index 9c20d5432ee..ac60b503fe4 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Launcher.java @@ -173,10 +173,10 @@ public interface Launcher { /** * Gets the additional properties for application launcher entries in the app - * image (".jpackage") file. + * image (".jpackage.xml") file. * * @return the additional properties for application launcher entries in - * ".jpackage" file + * ".jpackage.xml" file */ Map extraAppImageFileData(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcut.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcut.java new file mode 100644 index 00000000000..4188bccb073 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcut.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.model; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; + +/** + * A shortcut to launch an application launcher. + */ +public record LauncherShortcut(Optional startupDirectory) { + + public LauncherShortcut { + Objects.requireNonNull(startupDirectory); + } + + public LauncherShortcut(LauncherShortcutStartupDirectory startupDirectory) { + this(Optional.of(startupDirectory)); + } + + public LauncherShortcut() { + this(Optional.empty()); + } + + void store(String propertyName, BiConsumer sink) { + Objects.requireNonNull(propertyName); + Objects.requireNonNull(sink); + if (startupDirectory.isEmpty()) { + sink.accept(propertyName, Boolean.FALSE.toString()); + } else { + startupDirectory.ifPresent(v -> { + sink.accept(propertyName, v.asStringValue()); + }); + } + } + + /** + * Converts the given shortcut into a shortcut request. + *

    + * Returns true if shortcut was explicitly requested. + *

    + * Returns false if no shortcut was explicitly requested. + *

    + * Returns an empty {@link Optional} instance if there was no shortcut request. + * + * @return shortcut request + */ + public static Optional toRequest(Optional shortcut) { + return shortcut.map(v -> v.startupDirectory().isPresent()); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java new file mode 100644 index 00000000000..c604b00c3e2 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.model; + +import java.util.Objects; + +/** + * The directory in which to run an application launcher when it is started from + * a shortcut. + */ +public enum LauncherShortcutStartupDirectory { + + /** + * Platform-specific default value. + *

    + * On Windows, it indicates that the startup directory should be the package's + * installation directory. + *

    + * On Linux, it indicates that a shortcut doesn't have the startup directory + * configured explicitly. + */ + DEFAULT("true"); + + LauncherShortcutStartupDirectory(String stringValue) { + this.stringValue = Objects.requireNonNull(stringValue); + } + + public String asStringValue() { + return stringValue; + } + + private final String stringValue; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties index 1221ae747de..2426b97c7e2 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_de.properties @@ -86,7 +86,6 @@ MSG_BundlerFailed=Fehler: Bundler "{1}" ({0}) konnte kein Package generieren MSG_BundlerConfigException=Bundler {0} aufgrund eines Konfigurationsproblems übersprungen: {1} \nEmpfehlung zur Behebung: {2} MSG_BundlerConfigExceptionNoAdvice=Bundler {0} aufgrund eines Konfigurationsproblems übersprungen: {1} MSG_BundlerRuntimeException=Bundler {0} nicht erfolgreich. Grund: {1} -MSG_BundlerFailed=Fehler: Bundler "{1}" ({0}) konnte kein Package generieren ERR_NoMainClass=Fehler: Hauptanwendungsklasse fehlt ERR_UnsupportedOption=Fehler: Option [{0}] ist auf dieser Plattform ungültig diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties index 5a04af8a7bc..e0faf346973 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_ja.properties @@ -86,7 +86,6 @@ MSG_BundlerFailed=エラー: バンドラ"{1}" ({0})がパッケージの生成 MSG_BundlerConfigException=構成の問題のため、バンドラ{0}がスキップされました: {1} \n次の修正を行ってください: {2} MSG_BundlerConfigExceptionNoAdvice=構成の問題のため、バンドラ{0}がスキップされました: {1} MSG_BundlerRuntimeException={1}のため、バンドラ{0}が失敗しました -MSG_BundlerFailed=エラー: バンドラ"{1}" ({0})がパッケージの生成に失敗しました ERR_NoMainClass=エラー: メイン・アプリケーション・クラスがありません ERR_UnsupportedOption=エラー: オプション[{0}]は、このプラットフォームでは無効です diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties index 5546abef09f..0236ef34077 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources_zh_CN.properties @@ -86,7 +86,6 @@ MSG_BundlerFailed=错误:打包程序 "{1}" ({0}) 无法生成程序包 MSG_BundlerConfigException=由于配置问题, 跳过了打包程序{0}: {1} \n修复建议: {2} MSG_BundlerConfigExceptionNoAdvice=由于配置问题, 跳过了打包程序{0}: {1} MSG_BundlerRuntimeException=由于{1}, 打包程序{0}失败 -MSG_BundlerFailed=错误:打包程序 "{1}" ({0}) 无法生成程序包 ERR_NoMainClass=错误:缺少主应用程序类 ERR_UnsupportedOption=错误:选项 [{0}] 在此平台上无效 diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java index 95f16d09575..15d8d2f83b0 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java @@ -24,24 +24,19 @@ */ package jdk.jpackage.internal; -import static java.util.stream.Collectors.toSet; import static jdk.jpackage.internal.BundlerParamInfo.createBooleanBundlerParam; import static jdk.jpackage.internal.BundlerParamInfo.createStringBundlerParam; import static jdk.jpackage.internal.FromParams.createApplicationBuilder; import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam; import static jdk.jpackage.internal.FromParams.createPackageBuilder; import static jdk.jpackage.internal.FromParams.createPackageBundlerParam; -import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT; +import static jdk.jpackage.internal.FromParams.findLauncherShortcut; import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; -import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT; import static jdk.jpackage.internal.WinPackagingPipeline.APPLICATION_LAYOUT; import static jdk.jpackage.internal.model.StandardPackageType.WIN_MSI; -import static jdk.jpackage.internal.model.WinLauncherMixin.WinShortcut.WIN_SHORTCUT_DESKTOP; -import static jdk.jpackage.internal.model.WinLauncherMixin.WinShortcut.WIN_SHORTCUT_START_MENU; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; -import java.util.List; import java.util.Map; import java.util.UUID; import jdk.jpackage.internal.model.ConfigException; @@ -63,18 +58,11 @@ final class WinFromParams { final boolean isConsole = CONSOLE_HINT.findIn(launcherParams).orElse(false); - final var shortcuts = Map.of(WIN_SHORTCUT_DESKTOP, List.of(SHORTCUT_HINT, - WIN_SHORTCUT_HINT), WIN_SHORTCUT_START_MENU, List.of(MENU_HINT, - WIN_MENU_HINT)).entrySet().stream().filter(e -> { + final var startMenuShortcut = findLauncherShortcut(WIN_MENU_HINT, params, launcherParams); - final var shortcutParams = e.getValue(); + final var desktopShortcut = findLauncherShortcut(WIN_SHORTCUT_HINT, params, launcherParams); - return shortcutParams.get(0).findIn(launcherParams).orElseGet(() -> { - return shortcutParams.get(1).findIn(launcherParams).orElse(false); - }); - }).map(Map.Entry::getKey).collect(toSet()); - - return WinLauncher.create(launcher, new WinLauncherMixin.Stub(isConsole, shortcuts)); + return WinLauncher.create(launcher, new WinLauncherMixin.Stub(isConsole, startMenuShortcut, desktopShortcut)); }), APPLICATION_LAYOUT).create(); diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java index 7e400c5be29..ea4d9eee19a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java @@ -25,12 +25,9 @@ package jdk.jpackage.internal; -import jdk.jpackage.internal.model.WinLauncher; -import jdk.jpackage.internal.model.WinMsiPackage; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.DottedVersion; -import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.util.PathGroup; +import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.util.CollectionUtils.toCollection; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; @@ -50,7 +47,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; -import static java.util.stream.Collectors.toMap; import java.util.stream.Stream; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; @@ -60,15 +56,19 @@ import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import static jdk.jpackage.internal.util.CollectionUtils.toCollection; -import jdk.jpackage.internal.model.WinLauncherMixin.WinShortcut; import jdk.jpackage.internal.WixToolset.WixToolsetType; import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.FileAssociation; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.LauncherShortcut; +import jdk.jpackage.internal.model.WinLauncher; +import jdk.jpackage.internal.model.WinMsiPackage; +import jdk.jpackage.internal.util.PathGroup; import jdk.jpackage.internal.util.PathUtils; -import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.internal.util.XmlConsumer; -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import jdk.jpackage.internal.util.XmlUtils; import org.w3c.dom.NodeList; /** @@ -352,7 +352,7 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { private final Config cfg; private final Id id; - }; + } private static void addComponentGroup(XMLStreamWriter xml, String id, List componentIds) throws XMLStreamException, IOException { @@ -469,7 +469,18 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { launcher.executableNameWithSuffix()); if (folder.isRequestedFor(launcher)) { - String componentId = addShortcutComponent(xml, launcherPath, folder); + var workDirectory = folder.shortcut(launcher).startupDirectory().map(v -> { + switch (v) { + case DEFAULT -> { + return INSTALLDIR; + } + default -> { + throw new AssertionError(); + } + } + }).orElseThrow(); + + String componentId = addShortcutComponent(xml, launcherPath, folder, workDirectory); if (componentId != null) { Path folderPath = folder.getPath(this); @@ -499,23 +510,26 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { } private String addShortcutComponent(XMLStreamWriter xml, Path launcherPath, - ShortcutsFolder folder) throws XMLStreamException, IOException { + ShortcutsFolder folder, Path shortcutWorkDir) throws XMLStreamException, IOException { Objects.requireNonNull(folder); if (!INSTALLDIR.equals(launcherPath.getName(0))) { throw throwInvalidPathException(launcherPath); } + if (!INSTALLDIR.equals(shortcutWorkDir.getName(0))) { + throw throwInvalidPathException(shortcutWorkDir); + } + String launcherBasename = PathUtils.replaceSuffix( IOUtils.getFileName(launcherPath), "").toString(); Path shortcutPath = folder.getPath(this).resolve(launcherBasename); return addComponent(xml, shortcutPath, Component.Shortcut, unused -> { xml.writeAttribute("Name", launcherBasename); - xml.writeAttribute("WorkingDirectory", INSTALLDIR.toString()); + xml.writeAttribute("WorkingDirectory", Id.Folder.of(shortcutWorkDir)); xml.writeAttribute("Advertise", "no"); - xml.writeAttribute("Target", String.format("[#%s]", - Component.File.idOf(launcherPath))); + xml.writeAttribute("Target", String.format("[#%s]", Id.File.of(launcherPath))); }); } @@ -906,15 +920,15 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { } enum ShortcutsFolder { - ProgramMenu(PROGRAM_MENU_PATH, WinShortcut.WIN_SHORTCUT_START_MENU, + ProgramMenu(PROGRAM_MENU_PATH, WinLauncher::startMenuShortcut, "JP_INSTALL_STARTMENU_SHORTCUT", "JpStartMenuShortcutPrompt"), - Desktop(DESKTOP_PATH, WinShortcut.WIN_SHORTCUT_DESKTOP, + Desktop(DESKTOP_PATH, WinLauncher::desktopShortcut, "JP_INSTALL_DESKTOP_SHORTCUT", "JpDesktopShortcutPrompt"); - private ShortcutsFolder(Path root, WinShortcut shortcutId, + private ShortcutsFolder(Path root, Function> shortcut, String property, String wixVariableName) { this.root = root; - this.shortcutId = shortcutId; + this.shortcut = shortcut; this.wixVariableName = wixVariableName; this.property = property; } @@ -927,7 +941,11 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { } boolean isRequestedFor(WinLauncher launcher) { - return launcher.shortcuts().contains(shortcutId); + return LauncherShortcut.toRequest(shortcut.apply(launcher)).orElse(false); + } + + LauncherShortcut shortcut(WinLauncher launcher) { + return shortcut.apply(launcher).orElseThrow(); } String getWixVariableName() { @@ -947,7 +965,7 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { private final Path root; private final String property; private final String wixVariableName; - private final WinShortcut shortcutId; + private final Function> shortcut; } private boolean systemWide; diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java index a00eb16a4a9..4a2a0756dbd 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java @@ -120,7 +120,7 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder { super.addFilesToConfigRoot(); if (withCustomActionsDll) { - String fname = "wixhelper.dll"; // CA dll + String fname = "msica.dll"; // CA dll try (InputStream is = ResourceLocator.class.getResourceAsStream(fname)) { Files.copy(is, getConfigRoot().resolve(fname)); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncher.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncher.java index b10ca99d483..3052029a33c 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncher.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncher.java @@ -24,9 +24,8 @@ */ package jdk.jpackage.internal.model; -import static java.util.stream.Collectors.toMap; - import java.io.InputStream; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import jdk.jpackage.internal.resources.ResourceLocator; @@ -47,10 +46,20 @@ public interface WinLauncher extends Launcher, WinLauncherMixin { @Override default Map extraAppImageFileData() { - return shortcuts().stream().collect(toMap(WinShortcut::name, v -> Boolean.toString(true))); + Map map = new HashMap<>(); + desktopShortcut().ifPresent(shortcut -> { + shortcut.store(SHORTCUT_DESKTOP_ID, map::put); + }); + startMenuShortcut().ifPresent(shortcut -> { + shortcut.store(SHORTCUT_START_MENU_ID, map::put); + }); + return map; } public static WinLauncher create(Launcher launcher, WinLauncherMixin mixin) { return CompositeProxy.create(WinLauncher.class, launcher, mixin); } + + public static final String SHORTCUT_START_MENU_ID = "win-menu"; + public static final String SHORTCUT_DESKTOP_ID = "win-shortcut"; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncherMixin.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncherMixin.java index 65b6a1bab46..1762678434b 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncherMixin.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinLauncherMixin.java @@ -24,30 +24,37 @@ */ package jdk.jpackage.internal.model; -import java.util.Set; +import java.util.Optional; public interface WinLauncherMixin { boolean isConsole(); - enum WinShortcut { - WIN_SHORTCUT_DESKTOP("shortcut"), - WIN_SHORTCUT_START_MENU("menu"), - ; + /** + * Gets the start menu shortcut of this application launcher. + *

    + * Returns a non-empty {@link Optional} instance if a request about the start + * menu shortcut for this application launcher was made and an empty + * {@link Optional} instance if there was no request about the start menu + * shortcut for this application launcher. + * + * @return the start menu shortcut of this application launcher + */ + Optional startMenuShortcut(); - WinShortcut(String name) { - this.name = name; - } + /** + * Gets the desktop shortcut of this application launcher. + *

    + * Returns a non-empty {@link Optional} instance if a request about the desktop + * shortcut for this application launcher was made and an empty {@link Optional} + * instance if there was no request about the desktop shortcut for this + * application launcher. + * + * @return the start menu shortcut of this application launcher + */ + Optional desktopShortcut(); - public String getName() { - return name; - } - - private final String name; - } - - Set shortcuts(); - - record Stub(boolean isConsole, Set shortcuts) implements WinLauncherMixin { + record Stub(boolean isConsole, Optional startMenuShortcut, + Optional desktopShortcut) implements WinLauncherMixin { } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs index 2a3ea3743e2..f1e8f594164 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/main.wxs @@ -65,7 +65,7 @@ - + diff --git a/src/jdk.jpackage/windows/native/libwixhelper/Version.cpp b/src/jdk.jpackage/windows/native/libmsica/Version.cpp similarity index 100% rename from src/jdk.jpackage/windows/native/libwixhelper/Version.cpp rename to src/jdk.jpackage/windows/native/libmsica/Version.cpp diff --git a/src/jdk.jpackage/windows/native/libwixhelper/Version.h b/src/jdk.jpackage/windows/native/libmsica/Version.h similarity index 100% rename from src/jdk.jpackage/windows/native/libwixhelper/Version.h rename to src/jdk.jpackage/windows/native/libmsica/Version.h diff --git a/src/jdk.jpackage/windows/native/libwixhelper/libwixhelper.cpp b/src/jdk.jpackage/windows/native/libmsica/libmsica.cpp similarity index 100% rename from src/jdk.jpackage/windows/native/libwixhelper/libwixhelper.cpp rename to src/jdk.jpackage/windows/native/libmsica/libmsica.cpp diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/RemoteRecordingStream.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/RemoteRecordingStream.java index 862781802fc..a8752e159ad 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/RemoteRecordingStream.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/RemoteRecordingStream.java @@ -98,10 +98,12 @@ public final class RemoteRecordingStream implements EventStream { private final FlightRecorderMXBean mbean; private final long recordingId; + private final String identifier; - RemoteSettings(FlightRecorderMXBean mbean, long recordingId) { + RemoteSettings(FlightRecorderMXBean mbean, long recordingId, String identifier) { this.mbean = mbean; this.recordingId = recordingId; + this.identifier = identifier; } @Override @@ -111,7 +113,7 @@ public final class RemoteRecordingStream implements EventStream { // FlightRecorderMXBean implementation always returns // new instance of Map so no need to create new here. Map newSettings = getEventSettings(); - newSettings.put(name, value); + newSettings.put(identifier + "#" + name, value); mbean.setRecordingSettings(recordingId, newSettings); } @@ -340,9 +342,9 @@ public final class RemoteRecordingStream implements EventStream { */ public EventSettings disable(String name) { Objects.requireNonNull(name, "name"); - EventSettings s = ManagementSupport.newEventSettings(new RemoteSettings(mbean, recordingId)); + EventSettings s = ManagementSupport.newEventSettings(new RemoteSettings(mbean, recordingId, name)); try { - return s.with(name + "#" + ENABLED, "false"); + return s.with(ENABLED, "false"); } catch (Exception e) { ManagementSupport.logDebug(e.getMessage()); close(); @@ -364,9 +366,9 @@ public final class RemoteRecordingStream implements EventStream { */ public EventSettings enable(String name) { Objects.requireNonNull(name, "name"); - EventSettings s = ManagementSupport.newEventSettings(new RemoteSettings(mbean, recordingId)); + EventSettings s = ManagementSupport.newEventSettings(new RemoteSettings(mbean, recordingId, name)); try { - return s.with(name + "#" + ENABLED, "true"); + return s.with(ENABLED, "true"); } catch (Exception e) { ManagementSupport.logDebug(e.getMessage()); close(); diff --git a/test/hotspot/gtest/aarch64/aarch64-asmtest.py b/test/hotspot/gtest/aarch64/aarch64-asmtest.py index 5b2c18b0a2b..62274e2c10f 100644 --- a/test/hotspot/gtest/aarch64/aarch64-asmtest.py +++ b/test/hotspot/gtest/aarch64/aarch64-asmtest.py @@ -2087,6 +2087,10 @@ generate(SpecialCases, [["ccmn", "__ ccmn(zr, zr, 3u, Assembler::LE);", ["index", "__ sve_index(z7, __ D, r5, 5);", "index\tz7.d, x5, #5"], ["cpy", "__ sve_cpy(z7, __ H, p3, r5);", "cpy\tz7.h, p3/m, w5"], ["tbl", "__ sve_tbl(z16, __ S, z17, z18);", "tbl\tz16.s, {z17.s}, z18.s"], + ["tbl", "__ sve_tbl(z16, __ B, z17, z18, z16);", "tbl\tz16.b, {z17.b, z18.b}, z16.b"], + ["tbl", "__ sve_tbl(z16, __ H, z17, z18, z16);", "tbl\tz16.h, {z17.h, z18.h}, z16.h"], + ["tbl", "__ sve_tbl(z16, __ S, z17, z18, z16);", "tbl\tz16.s, {z17.s, z18.s}, z16.s"], + ["tbl", "__ sve_tbl(z16, __ D, z17, z18, z16);", "tbl\tz16.d, {z17.d, z18.d}, z16.d"], ["ld1w", "__ sve_ld1w_gather(z15, p0, r5, z16);", "ld1w\t{z15.s}, p0/z, [x5, z16.s, uxtw #2]"], ["ld1d", "__ sve_ld1d_gather(z15, p0, r5, z16);", "ld1d\t{z15.d}, p0/z, [x5, z16.d, uxtw #3]"], ["st1w", "__ sve_st1w_scatter(z15, p0, r5, z16);", "st1w\t{z15.s}, p0, [x5, z16.s, uxtw #2]"], diff --git a/test/hotspot/gtest/aarch64/asmtest.out.h b/test/hotspot/gtest/aarch64/asmtest.out.h index d90c2479995..f08c69a27dd 100644 --- a/test/hotspot/gtest/aarch64/asmtest.out.h +++ b/test/hotspot/gtest/aarch64/asmtest.out.h @@ -1100,6 +1100,10 @@ __ sve_index(z7, __ D, r5, 5); // index z7.d, x5, #5 __ sve_cpy(z7, __ H, p3, r5); // cpy z7.h, p3/m, w5 __ sve_tbl(z16, __ S, z17, z18); // tbl z16.s, {z17.s}, z18.s + __ sve_tbl(z16, __ B, z17, z18, z16); // tbl z16.b, {z17.b, z18.b}, z16.b + __ sve_tbl(z16, __ H, z17, z18, z16); // tbl z16.h, {z17.h, z18.h}, z16.h + __ sve_tbl(z16, __ S, z17, z18, z16); // tbl z16.s, {z17.s, z18.s}, z16.s + __ sve_tbl(z16, __ D, z17, z18, z16); // tbl z16.d, {z17.d, z18.d}, z16.d __ sve_ld1w_gather(z15, p0, r5, z16); // ld1w {z15.s}, p0/z, [x5, z16.s, uxtw #2] __ sve_ld1d_gather(z15, p0, r5, z16); // ld1d {z15.d}, p0/z, [x5, z16.d, uxtw #3] __ sve_st1w_scatter(z15, p0, r5, z16); // st1w {z15.s}, p0, [x5, z16.s, uxtw #2] @@ -1438,30 +1442,30 @@ 0x9101a1a0, 0xb10a5cc8, 0xd10810aa, 0xf10fd061, 0x120cb166, 0x321764bc, 0x52174681, 0x720c0227, 0x9241018e, 0xb25a2969, 0xd278b411, 0xf26aad01, - 0x14000000, 0x17ffffd7, 0x140004b0, 0x94000000, - 0x97ffffd4, 0x940004ad, 0x3400000a, 0x34fffa2a, - 0x3400954a, 0x35000008, 0x35fff9c8, 0x350094e8, - 0xb400000b, 0xb4fff96b, 0xb400948b, 0xb500001d, - 0xb5fff91d, 0xb500943d, 0x10000013, 0x10fff8b3, - 0x100093d3, 0x90000013, 0x36300016, 0x3637f836, - 0x36309356, 0x3758000c, 0x375ff7cc, 0x375892ec, + 0x14000000, 0x17ffffd7, 0x140004b4, 0x94000000, + 0x97ffffd4, 0x940004b1, 0x3400000a, 0x34fffa2a, + 0x340095ca, 0x35000008, 0x35fff9c8, 0x35009568, + 0xb400000b, 0xb4fff96b, 0xb400950b, 0xb500001d, + 0xb5fff91d, 0xb50094bd, 0x10000013, 0x10fff8b3, + 0x10009453, 0x90000013, 0x36300016, 0x3637f836, + 0x363093d6, 0x3758000c, 0x375ff7cc, 0x3758936c, 0x128313a0, 0x528a32c7, 0x7289173b, 0x92ab3acc, 0xd2a0bf94, 0xf2c285e8, 0x9358722f, 0x330e652f, 0x53067f3b, 0x93577c53, 0xb34a1aac, 0xd35a4016, 0x13946c63, 0x93c3dbc8, 0x54000000, 0x54fff5a0, - 0x540090c0, 0x54000001, 0x54fff541, 0x54009061, - 0x54000002, 0x54fff4e2, 0x54009002, 0x54000002, - 0x54fff482, 0x54008fa2, 0x54000003, 0x54fff423, - 0x54008f43, 0x54000003, 0x54fff3c3, 0x54008ee3, - 0x54000004, 0x54fff364, 0x54008e84, 0x54000005, - 0x54fff305, 0x54008e25, 0x54000006, 0x54fff2a6, - 0x54008dc6, 0x54000007, 0x54fff247, 0x54008d67, - 0x54000008, 0x54fff1e8, 0x54008d08, 0x54000009, - 0x54fff189, 0x54008ca9, 0x5400000a, 0x54fff12a, - 0x54008c4a, 0x5400000b, 0x54fff0cb, 0x54008beb, - 0x5400000c, 0x54fff06c, 0x54008b8c, 0x5400000d, - 0x54fff00d, 0x54008b2d, 0x5400000e, 0x54ffefae, - 0x54008ace, 0x5400000f, 0x54ffef4f, 0x54008a6f, + 0x54009140, 0x54000001, 0x54fff541, 0x540090e1, + 0x54000002, 0x54fff4e2, 0x54009082, 0x54000002, + 0x54fff482, 0x54009022, 0x54000003, 0x54fff423, + 0x54008fc3, 0x54000003, 0x54fff3c3, 0x54008f63, + 0x54000004, 0x54fff364, 0x54008f04, 0x54000005, + 0x54fff305, 0x54008ea5, 0x54000006, 0x54fff2a6, + 0x54008e46, 0x54000007, 0x54fff247, 0x54008de7, + 0x54000008, 0x54fff1e8, 0x54008d88, 0x54000009, + 0x54fff189, 0x54008d29, 0x5400000a, 0x54fff12a, + 0x54008cca, 0x5400000b, 0x54fff0cb, 0x54008c6b, + 0x5400000c, 0x54fff06c, 0x54008c0c, 0x5400000d, + 0x54fff00d, 0x54008bad, 0x5400000e, 0x54ffefae, + 0x54008b4e, 0x5400000f, 0x54ffef4f, 0x54008aef, 0xd40658e1, 0xd4014d22, 0xd4046543, 0xd4273f60, 0xd44cad80, 0xd503201f, 0xd503203f, 0xd503205f, 0xd503209f, 0xd50320bf, 0xd503219f, 0xd50323bf, @@ -1668,7 +1672,8 @@ 0x65d8a801, 0x65dcac01, 0x655cb241, 0x0520a1e0, 0x0521a601, 0x052281e0, 0x05238601, 0x04a14026, 0x042244a6, 0x046344a6, 0x04a444a6, 0x04e544a7, - 0x0568aca7, 0x05b23230, 0x853040af, 0xc5b040af, + 0x0568aca7, 0x05b23230, 0x05302a30, 0x05702a30, + 0x05b02a30, 0x05f02a30, 0x853040af, 0xc5b040af, 0xe57080af, 0xe5b080af, 0x25034440, 0x254054c4, 0x25034640, 0x25415a05, 0x25834440, 0x25c54489, 0x250b5d3a, 0x2550dc20, 0x2518e3e1, 0x2518e021, diff --git a/test/hotspot/gtest/gc/z/test_zPageAge.cpp b/test/hotspot/gtest/gc/z/test_zPageAge.cpp index 1c93a380db7..b8881b7f9b9 100644 --- a/test/hotspot/gtest/gc/z/test_zPageAge.cpp +++ b/test/hotspot/gtest/gc/z/test_zPageAge.cpp @@ -44,4 +44,8 @@ TEST(ZPageAgeRangeTest, test) { ZPageAgeRange rangeOld = ZPageAgeRangeOld; EXPECT_EQ(rangeOld.first(), ZPageAge::old); EXPECT_EQ(rangeOld.last(), ZPageAge::old); + + ZPageAgeRange rangeAll = ZPageAgeRangeAll; + EXPECT_EQ(rangeAll.first(), ZPageAge::eden); + EXPECT_EQ(rangeAll.last(), ZPageAge::old); } diff --git a/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp b/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp index 34410cf6c84..49ab816c4a0 100644 --- a/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp +++ b/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp @@ -25,6 +25,7 @@ #include "memory/metaspace.hpp" #include "memory/metaspaceUtils.hpp" +#include "oops/compressedKlass.hpp" #include "unittest.hpp" TEST_VM(metaspace, MetaspaceUtils_reserved) { @@ -37,15 +38,13 @@ TEST_VM(metaspace, MetaspaceUtils_reserved) { } TEST_VM(metaspace, MetaspaceUtils_reserved_compressed_class_pointers) { - if (!UseCompressedClassPointers) { - return; + if (UseCompressedClassPointers && CompressedKlassPointers::needs_class_space()) { + size_t reserved = MetaspaceUtils::reserved_bytes(); + EXPECT_GT(reserved, 0UL); + size_t reserved_class = MetaspaceUtils::reserved_bytes(Metaspace::ClassType); + EXPECT_GT(reserved_class, 0UL); + EXPECT_LE(reserved_class, reserved); } - size_t reserved = MetaspaceUtils::reserved_bytes(); - EXPECT_GT(reserved, 0UL); - - size_t reserved_class = MetaspaceUtils::reserved_bytes(Metaspace::ClassType); - EXPECT_GT(reserved_class, 0UL); - EXPECT_LE(reserved_class, reserved); } TEST_VM(metaspace, MetaspaceUtils_committed) { @@ -61,15 +60,13 @@ TEST_VM(metaspace, MetaspaceUtils_committed) { } TEST_VM(metaspace, MetaspaceUtils_committed_compressed_class_pointers) { - if (!UseCompressedClassPointers) { - return; + if (UseCompressedClassPointers && CompressedKlassPointers::needs_class_space()) { + size_t committed = MetaspaceUtils::committed_bytes(); + EXPECT_GT(committed, 0UL); + size_t committed_class = MetaspaceUtils::committed_bytes(Metaspace::ClassType); + EXPECT_GT(committed_class, 0UL); + EXPECT_LE(committed_class, committed); } - size_t committed = MetaspaceUtils::committed_bytes(); - EXPECT_GT(committed, 0UL); - - size_t committed_class = MetaspaceUtils::committed_bytes(Metaspace::ClassType); - EXPECT_GT(committed_class, 0UL); - EXPECT_LE(committed_class, committed); } TEST_VM(metaspace, MetaspaceUtils_non_compressed_class_pointers) { @@ -105,7 +102,7 @@ TEST_VM(MetaspaceUtils, MetaspaceUtils_get_statistics) { check_metaspace_stats_are_not_null(combined_stats.non_class_space_stats()); check_metaspace_stats_are_consistent(combined_stats.non_class_space_stats()); - if (UseCompressedClassPointers) { + if (CompressedKlassPointers::needs_class_space() && UseCompressedClassPointers) { check_metaspace_stats_are_not_null(combined_stats.class_space_stats()); check_metaspace_stats_are_consistent(combined_stats.class_space_stats()); } else { diff --git a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp index f987d83755c..ae3f4e74516 100644 --- a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp @@ -35,7 +35,7 @@ // This prefix shows up on any c heap corruption NMT detects. If unsure which assert will // come, just use this one. -#define COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX "NMT corruption" +#define COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX "NMT has detected a memory corruption bug." #define DEFINE_TEST(test_function, expected_assertion_message) \ TEST_VM_FATAL_ERROR_MSG(NMT, test_function, ".*" expected_assertion_message ".*") { \ diff --git a/test/hotspot/gtest/nmt/test_nmt_treap.cpp b/test/hotspot/gtest/nmt/test_nmt_treap.cpp deleted file mode 100644 index bc8c24b592f..00000000000 --- a/test/hotspot/gtest/nmt/test_nmt_treap.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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 "memory/resourceArea.hpp" -#include "nmt/nmtTreap.hpp" -#include "nmt/virtualMemoryTracker.hpp" -#include "runtime/os.hpp" -#include "unittest.hpp" -class NMTTreapTest : public testing::Test { -public: - struct Cmp { - static int cmp(int a, int b) { - return a - b; - } - }; - - struct CmpInverse { - static int cmp(int a, int b) { - return b - a; - } - }; - - struct FCmp { - static int cmp(float a, float b) { - if (a < b) return -1; - if (a == b) return 0; - return 1; - } - }; - -#ifdef ASSERT - template - void verify_it(Treap& t) { - t.verify_self(); - } -#endif // ASSERT - -public: - void inserting_duplicates_results_in_one_value() { - constexpr const int up_to = 10; - GrowableArrayCHeap nums_seen(up_to, up_to, 0); - TreapCHeap treap; - - for (int i = 0; i < up_to; i++) { - treap.upsert(i, i); - treap.upsert(i, i); - treap.upsert(i, i); - treap.upsert(i, i); - treap.upsert(i, i); - } - - treap.visit_in_order([&](TreapCHeap::TreapNode* node) { - nums_seen.at(node->key())++; - return true; - }); - for (int i = 0; i < up_to; i++) { - EXPECT_EQ(1, nums_seen.at(i)); - } - } - - void treap_ought_not_leak() { - struct LeakCheckedAllocator { - int allocations; - - LeakCheckedAllocator() - : allocations(0) { - } - - void* allocate(size_t sz) { - void* allocation = os::malloc(sz, mtTest); - if (allocation == nullptr) { - vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR, "treap failed allocation"); - } - ++allocations; - return allocation; - } - - void free(void* ptr) { - --allocations; - os::free(ptr); - } - }; - - constexpr const int up_to = 10; - { - Treap treap; - for (int i = 0; i < up_to; i++) { - treap.upsert(i, i); - } - EXPECT_EQ(up_to, treap._allocator.allocations); - for (int i = 0; i < up_to; i++) { - treap.remove(i); - } - EXPECT_EQ(0, treap._allocator.allocations); - EXPECT_EQ(nullptr, treap._root); - } - - { - Treap treap; - for (int i = 0; i < up_to; i++) { - treap.upsert(i, i); - } - treap.remove_all(); - EXPECT_EQ(0, treap._allocator.allocations); - EXPECT_EQ(nullptr, treap._root); - } - } - - void test_find() { - struct Empty {}; - TreapCHeap treap; - using Node = TreapCHeap::TreapNode; - - Node* n = nullptr; - auto test = [&](float f) { - EXPECT_EQ(nullptr, treap.find(treap._root, f)); - treap.upsert(f, Empty{}); - Node* n = treap.find(treap._root, f); - EXPECT_NE(nullptr, n); - EXPECT_EQ(f, n->key()); - }; - - test(1.0f); - test(5.0f); - test(0.0f); - } -}; - -TEST_VM_F(NMTTreapTest, InsertingDuplicatesResultsInOneValue) { - this->inserting_duplicates_results_in_one_value(); -} - -TEST_VM_F(NMTTreapTest, TreapOughtNotLeak) { - this->treap_ought_not_leak(); -} - -TEST_VM_F(NMTTreapTest, TestVisitors) { - { // Tests with 'default' ordering (ascending) - TreapCHeap treap; - using Node = TreapCHeap::TreapNode; - - treap.visit_range_in_order(0, 100, [&](Node* x) { - EXPECT_TRUE(false) << "Empty treap has no nodes to visit"; - return true; - }); - - // Single-element set - treap.upsert(1, 0); - int count = 0; - treap.visit_range_in_order(0, 100, [&](Node* x) { - count++; - return true; - }); - EXPECT_EQ(1, count); - - count = 0; - treap.visit_in_order([&](Node* x) { - count++; - return true; - }); - EXPECT_EQ(1, count); - - // Add an element outside of the range that should not be visited on the right side and - // one on the left side. - treap.upsert(101, 0); - treap.upsert(-1, 0); - count = 0; - treap.visit_range_in_order(0, 100, [&](Node* x) { - count++; - return true; - }); - EXPECT_EQ(1, count); - - count = 0; - treap.visit_in_order([&](Node* x) { - count++; - return true; - }); - EXPECT_EQ(3, count); - - // Visiting empty range [0, 0) == {} - treap.upsert(0, 0); // This node should not be visited. - treap.visit_range_in_order(0, 0, [&](Node* x) { - EXPECT_TRUE(false) << "Empty visiting range should not visit any node"; - return true; - }); - - treap.remove_all(); - for (int i = 0; i < 11; i++) { - treap.upsert(i, 0); - } - - ResourceMark rm; - GrowableArray seen; - treap.visit_range_in_order(0, 10, [&](Node* x) { - seen.push(x->key()); - return true; - }); - EXPECT_EQ(10, seen.length()); - for (int i = 0; i < 10; i++) { - EXPECT_EQ(i, seen.at(i)); - } - - seen.clear(); - treap.visit_in_order([&](Node* x) { - seen.push(x->key()); - return true; - }); - EXPECT_EQ(11, seen.length()); - for (int i = 0; i < 10; i++) { - EXPECT_EQ(i, seen.at(i)); - } - - seen.clear(); - treap.visit_range_in_order(10, 12, [&](Node* x) { - seen.push(x->key()); - return true; - }); - EXPECT_EQ(1, seen.length()); - EXPECT_EQ(10, seen.at(0)); - } - { // Test with descending ordering - TreapCHeap treap; - using Node = TreapCHeap::TreapNode; - - for (int i = 0; i < 10; i++) { - treap.upsert(i, 0); - } - ResourceMark rm; - GrowableArray seen; - treap.visit_range_in_order(9, -1, [&](Node* x) { - seen.push(x->key()); - return true; - }); - EXPECT_EQ(10, seen.length()); - for (int i = 0; i < 10; i++) { - EXPECT_EQ(10-i-1, seen.at(i)); - } - seen.clear(); - - treap.visit_in_order([&](Node* x) { - seen.push(x->key()); - return true; - }); - EXPECT_EQ(10, seen.length()); - for (int i = 0; i < 10; i++) { - EXPECT_EQ(10 - i - 1, seen.at(i)); - } - } -} - -TEST_VM_F(NMTTreapTest, TestFind) { - test_find(); -} - -TEST_VM_F(NMTTreapTest, TestClosestLeq) { - using Node = TreapCHeap::TreapNode; - { - TreapCHeap treap; - Node* n = treap.closest_leq(0); - EXPECT_EQ(nullptr, n); - - treap.upsert(0, 0); - n = treap.closest_leq(0); - EXPECT_EQ(0, n->key()); - - treap.upsert(-1, -1); - n = treap.closest_leq(0); - EXPECT_EQ(0, n->key()); - - treap.upsert(6, 0); - n = treap.closest_leq(6); - EXPECT_EQ(6, n->key()); - - n = treap.closest_leq(-2); - EXPECT_EQ(nullptr, n); - } -} - -#ifdef ASSERT - -TEST_VM_F(NMTTreapTest, VerifyItThroughStressTest) { - { // Repeatedly verify a treap of moderate size - TreapCHeap treap; - constexpr const int ten_thousand = 10000; - for (int i = 0; i < ten_thousand; i++) { - int r = os::random(); - if (r % 2 == 0) { - treap.upsert(i, i); - } else { - treap.remove(i); - } - if (i % 100 == 0) { - verify_it(treap); - } - } - for (int i = 0; i < ten_thousand; i++) { - int r = os::random(); - if (r % 2 == 0) { - treap.upsert(i, i); - } else { - treap.remove(i); - } - if (i % 100 == 0) { - verify_it(treap); - } - } - } - { // Make a very large treap and verify at the end - struct Nothing {}; - TreapCHeap treap; - constexpr const int one_hundred_thousand = 100000; - for (int i = 0; i < one_hundred_thousand; i++) { - treap.upsert(i, Nothing()); - } - verify_it(treap); - } -} -struct NTD { - static bool has_run_destructor; - ~NTD() { - has_run_destructor = true; - } -}; - -bool NTD::has_run_destructor = false; - -TEST_VM_F(NMTTreapTest, ValueDestructorsAreRun) { - TreapCHeap treap; - NTD ntd; - treap.upsert(0, ntd); - treap.remove(0); - EXPECT_TRUE(NTD::has_run_destructor); - NTD::has_run_destructor = false; - { - TreapCHeap treap; - NTD ntd; - treap.upsert(0, ntd); - } - EXPECT_TRUE(NTD::has_run_destructor); -} - -#endif // ASSERT diff --git a/test/hotspot/gtest/nmt/test_vmatree.cpp b/test/hotspot/gtest/nmt/test_vmatree.cpp index b43bfc58c69..cc4493a0e0f 100644 --- a/test/hotspot/gtest/nmt/test_vmatree.cpp +++ b/test/hotspot/gtest/nmt/test_vmatree.cpp @@ -31,7 +31,7 @@ #include "unittest.hpp" using Tree = VMATree; -using TNode = Tree::TreapNode; +using TNode = Tree::TNode; using NCS = NativeCallStackStorage; class NMTVMATreeTest : public testing::Test { @@ -54,16 +54,16 @@ public: // Utilities - VMATree::TreapNode* treap_root(VMATree& tree) { - return tree._tree._root; + VMATree::TNode* rbtree_root(VMATree& tree) { + return static_cast(tree._tree._root); } - VMATree::VMATreap& treap(VMATree& tree) { + VMATree::VMARBTree& rbtree(VMATree& tree) { return tree._tree; } - VMATree::TreapNode* find(VMATree::VMATreap& treap, const VMATree::position key) { - return treap.find(treap._root, key); + VMATree::TNode* find(VMATree::VMARBTree& rbtree, const VMATree::position key) { + return rbtree.find_node(key); } NativeCallStack make_stack(size_t a) { @@ -71,17 +71,17 @@ public: return stack; } - VMATree::StateType in_type_of(VMATree::TreapNode* x) { + VMATree::StateType in_type_of(VMATree::TNode* x) { return x->val().in.type(); } - VMATree::StateType out_type_of(VMATree::TreapNode* x) { + VMATree::StateType out_type_of(VMATree::TNode* x) { return x->val().out.type(); } int count_nodes(Tree& tree) { int count = 0; - treap(tree).visit_in_order([&](TNode* x) { + rbtree(tree).visit_in_order([&](TNode* x) { ++count; return true; }); @@ -123,14 +123,14 @@ public: for (int i = 0; i < 10; i++) { tree.release_mapping(i * 100, 100); } - EXPECT_EQ(nullptr, treap_root(tree)); + EXPECT_EQ(nullptr, rbtree_root(tree)); // Other way around tree.reserve_mapping(0, 100 * 10, rd); for (int i = 9; i >= 0; i--) { tree.release_mapping(i * 100, 100); } - EXPECT_EQ(nullptr, treap_root(tree)); + EXPECT_EQ(nullptr, rbtree_root(tree)); } // Committing in a whole reserved range results in 2 nodes @@ -140,7 +140,7 @@ public: for (int i = 0; i < 10; i++) { tree.commit_mapping(i * 100, 100, rd); } - treap(tree).visit_in_order([&](TNode* x) { + rbtree(tree).visit_in_order([&](TNode* x) { VMATree::StateType in = in_type_of(x); VMATree::StateType out = out_type_of(x); EXPECT_TRUE((in == VMATree::StateType::Released && out == VMATree::StateType::Committed) || @@ -166,7 +166,7 @@ public: }; int i = 0; - treap(tree).visit_in_order([&](TNode* x) { + rbtree(tree).visit_in_order([&](TNode* x) { if (i < 16) { found[i] = x->key(); } @@ -199,7 +199,7 @@ public: }; void call_update_region(const UpdateCallInfo upd) { - VMATree::TreapNode n1{upd.req.A, {}, 0}, n2{upd.req.B, {}, 0}; + VMATree::TNode n1{upd.req.A, {}}, n2{upd.req.B, {}}; n1.val().out= upd.ex_st; n2.val().in = n1.val().out; Tree tree; @@ -264,7 +264,7 @@ public: template void check_tree(Tree& tree, const ExpectedTree& et, int line_no) { - using Node = VMATree::TreapNode; + using Node = VMATree::TNode; auto left_released = [&](Node n) -> bool { return n.val().in.type() == VMATree::StateType::Released and n.val().in.mem_tag() == mtNone; @@ -274,7 +274,7 @@ public: n.val().out.mem_tag() == mtNone; }; for (int i = 0; i < N; i++) { - VMATree::VMATreap::Range r = tree.tree().find_enclosing_range(et.nodes[i]); + VMATree::VMARBTree::Range r = tree.tree().find_enclosing_range(et.nodes[i]); ASSERT_TRUE(r.start != nullptr); Node node = *r.start; ASSERT_EQ(node.key(), (VMATree::position)et.nodes[i]) << "at line " << line_no; @@ -354,7 +354,7 @@ TEST_VM_F(NMTVMATreeTest, DuplicateReserve) { tree.reserve_mapping(100, 100, rd); tree.reserve_mapping(100, 100, rd); EXPECT_EQ(2, count_nodes(tree)); - VMATree::VMATreap::Range r = tree.tree().find_enclosing_range(110); + VMATree::VMARBTree::Range r = tree.tree().find_enclosing_range(110); EXPECT_EQ(100, (int)(r.end->key() - r.start->key())); } @@ -369,7 +369,7 @@ TEST_VM_F(NMTVMATreeTest, UseTagInplace) { // post-cond: 0---20**30--40**70----100 tree.commit_mapping(20, 50, rd_None_cs1, true); tree.uncommit_mapping(30, 10, rd_None_cs1); - tree.visit_in_order([&](TNode* node) { + tree.visit_in_order([&](const TNode* node) { if (node->key() != 100) { EXPECT_EQ(mtTest, node->val().out.mem_tag()) << "failed at: " << node->key(); if (node->key() != 20 && node->key() != 40) { @@ -410,9 +410,9 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { VMATree::RegionData rd_NMT_cs1{si[1], mtNMT}; tree.commit_mapping(50, 50, rd_NMT_cs1); tree.reserve_mapping(0, 100, rd_Test_cs0); - treap(tree).visit_in_order([&](TNode* x) { + rbtree(tree).visit_in_order([&](const TNode* x) { EXPECT_TRUE(x->key() == 0 || x->key() == 100); - if (x->key() == 0) { + if (x->key() == 0UL) { EXPECT_EQ(x->val().out.reserved_regiondata().mem_tag, mtTest); } return true; @@ -438,7 +438,7 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { tree.reserve_mapping(0, 500000, rd_NMT_cs0); tree.release_mapping(0, 500000); - EXPECT_EQ(nullptr, treap_root(tree)); + EXPECT_EQ(nullptr, rbtree_root(tree)); } { // A committed region inside of/replacing a reserved region @@ -448,7 +448,7 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { Tree tree; tree.reserve_mapping(0, 100, rd_NMT_cs0); tree.commit_mapping(0, 100, rd_Test_cs1); - treap(tree).visit_range_in_order(0, 99999, [&](TNode* x) { + rbtree(tree).visit_range_in_order(0, 99999, [&](TNode* x) { if (x->key() == 0) { EXPECT_EQ(mtTest, x->val().out.reserved_regiondata().mem_tag); } @@ -463,9 +463,9 @@ TEST_VM_F(NMTVMATreeTest, LowLevel) { Tree tree; VMATree::RegionData rd_NMT_cs0{si[0], mtNMT}; tree.reserve_mapping(0, 0, rd_NMT_cs0); - EXPECT_EQ(nullptr, treap_root(tree)); + EXPECT_EQ(nullptr, rbtree_root(tree)); tree.commit_mapping(0, 0, rd_NMT_cs0); - EXPECT_EQ(nullptr, treap_root(tree)); + EXPECT_EQ(nullptr, rbtree_root(tree)); } } @@ -483,14 +483,14 @@ TEST_VM_F(NMTVMATreeTest, SetTag) { auto expect_equivalent_form = [&](auto& expected, VMATree& tree, int line_no) { // With auto& our arrays do not deteriorate to pointers but are kept as testrange[N] // so this actually works! - int len = sizeof(expected) / sizeof(testrange); + size_t len = sizeof(expected) / sizeof(testrange); VMATree::position previous_to = 0; - for (int i = 0; i < len; i++) { + for (size_t i = 0; i < len; i++) { testrange expect = expected[i]; assert(previous_to == 0 || previous_to <= expect.from, "the expected list must be sorted"); previous_to = expect.to; - VMATree::VMATreap::Range found = tree.tree().find_enclosing_range(expect.from); + VMATree::VMARBTree::Range found = tree.tree().find_enclosing_range(expect.from); ASSERT_NE(nullptr, found.start); ASSERT_NE(nullptr, found.end); // Same region @@ -993,10 +993,10 @@ TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { ASSERT_LE(end, SimpleVMATracker::num_pages); SimpleVMATracker::Info endi = tr->pages[end]; - VMATree::VMATreap& treap = this->treap(tree); - VMATree::TreapNode* startn = find(treap, start * page_size); + VMATree::VMARBTree& rbtree = this->rbtree(tree); + VMATree::TNode* startn = find(rbtree, start * page_size); ASSERT_NE(nullptr, startn); - VMATree::TreapNode* endn = find(treap, (end * page_size) + page_size); + VMATree::TNode* endn = find(rbtree, (end * page_size) + page_size); ASSERT_NE(nullptr, endn); const NativeCallStack& start_stack = ncss.get(startn->val().out.reserved_stack()); diff --git a/test/hotspot/gtest/oops/test_compressedKlass.cpp b/test/hotspot/gtest/oops/test_compressedKlass.cpp index 56bfd29782f..c2ae7719776 100644 --- a/test/hotspot/gtest/oops/test_compressedKlass.cpp +++ b/test/hotspot/gtest/oops/test_compressedKlass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, 2025, Red Hat, Inc. All rights reserved. * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -22,6 +22,7 @@ * questions. */ +#include "classfile/vmClasses.hpp" #include "oops/compressedKlass.inline.hpp" #include "utilities/globalDefinitions.hpp" @@ -36,6 +37,7 @@ TEST_VM(CompressedKlass, basics) { ASSERT_LT(CompressedKlassPointers::klass_range_start(), CompressedKlassPointers::klass_range_end()); ASSERT_LE(CompressedKlassPointers::klass_range_end(), CompressedKlassPointers::encoding_range_end()); +#ifdef _LP64 switch (CompressedKlassPointers::shift()) { case 0: ASSERT_EQ(CompressedKlassPointers::encoding_range_end() - CompressedKlassPointers::base(), (ptrdiff_t)(4 * G)); @@ -47,6 +49,10 @@ TEST_VM(CompressedKlass, basics) { const size_t expected_size = nth_bit(CompressedKlassPointers::narrow_klass_pointer_bits() + CompressedKlassPointers::shift()); ASSERT_EQ(CompressedKlassPointers::encoding_range_end() - CompressedKlassPointers::base(), (ptrdiff_t)expected_size); } +#else + ASSERT_EQ(CompressedKlassPointers::base(), (address)0); + ASSERT_EQ(CompressedKlassPointers::encoding_range_end(), (address)(UINT_MAX)); +#endif // _LP64 } TEST_VM(CompressedKlass, ccp_off) { @@ -107,3 +113,15 @@ TEST_VM(CompressedKlass, test_good_address) { addr = CompressedKlassPointers::klass_range_end() - alignment; ASSERT_TRUE(CompressedKlassPointers::is_encodable(addr)); } + +TEST_VM(CompressedKlass, test_is_valid_narrow_klass) { + if (!UseCompressedClassPointers) { + return; + } + ASSERT_FALSE(CompressedKlassPointers::is_valid_narrow_klass_id(0)); + narrowKlass nk_jlC = CompressedKlassPointers::encode((Klass*)vmClasses::Class_klass()); + ASSERT_TRUE(CompressedKlassPointers::is_valid_narrow_klass_id(nk_jlC)); + if (CompressedClassSpaceSize < 4 * G && CompressedKlassPointers::base() != nullptr) { + ASSERT_FALSE(CompressedKlassPointers::is_valid_narrow_klass_id(0xFFFFFFFF)); + } +} diff --git a/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp b/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp index 9c0599fb513..b7ef0663c8a 100644 --- a/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp +++ b/test/hotspot/gtest/runtime/test_virtualMemoryTracker.cpp @@ -50,14 +50,14 @@ namespace { }; } -#define check(rmr, regions) check_inner((rmr), (regions), ARRAY_SIZE(regions), __FILE__, __LINE__) +#define check(vmt, rmr, regions) check_inner((vmt), (rmr), (regions), ARRAY_SIZE(regions), __FILE__, __LINE__) -#define check_empty(rmr) \ +#define check_empty(vmt, rmr) \ do { \ - check_inner((rmr), nullptr, 0, __FILE__, __LINE__); \ + check_inner((vmt), (rmr), nullptr, 0, __FILE__, __LINE__); \ } while (false) -static void diagnostic_print(const ReservedMemoryRegion& rmr) { +static void diagnostic_print(VirtualMemoryTracker& vmt, const ReservedMemoryRegion& rmr) { LOG("In reserved region " PTR_FORMAT ", size %X:", p2i(rmr.base()), rmr.size()); VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) { LOG(" committed region: " PTR_FORMAT ", size %X", p2i(region.base()), region.size()); @@ -65,16 +65,16 @@ static void diagnostic_print(const ReservedMemoryRegion& rmr) { }); } -static void check_inner(const ReservedMemoryRegion& rmr, R* regions, size_t regions_size, const char* file, int line) { +static void check_inner(VirtualMemoryTracker& vmt, const ReservedMemoryRegion& rmr, R* regions, size_t regions_size, const char* file, int line) { size_t i = 0; size_t size = 0; // Helpful log - diagnostic_print(rmr); + diagnostic_print(vmt, rmr); #define WHERE " from " << file << ":" << line - VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) { + vmt.tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) { EXPECT_LT(i, regions_size) << WHERE; EXPECT_EQ(region.base(), regions[i]._addr) << WHERE; EXPECT_EQ(region.size(), regions[i]._size) << WHERE; @@ -84,16 +84,18 @@ static void check_inner(const ReservedMemoryRegion& rmr, R* regions, size_t regi }); EXPECT_EQ(i, regions_size) << WHERE; - EXPECT_EQ(size, rmr.committed_size()) << WHERE; + EXPECT_EQ(size, vmt.committed_size(&rmr)) << WHERE; } class VirtualMemoryTrackerTest { public: static void test_add_committed_region_adjacent() { + VirtualMemoryTracker vmt(true); + RegionsTree* rtree = vmt.tree(); size_t size = 0x01000000; - ReservedSpace rs = MemoryReserver::reserve(size, mtTest); + const address addr = (address)0x0000A000; - address addr = (address)rs.base(); + vmt.add_reserved_region(addr, size, CALLER_PC, mtTest); address frame1 = (address)0x1234; address frame2 = (address)0x1235; @@ -102,9 +104,6 @@ public: NativeCallStack stack2(&frame2, 1); // Fetch the added RMR for the space - RegionsTree* rtree = VirtualMemoryTracker::Instance::tree(); - MemTracker::NmtVirtualMemoryLocker nvml; - ReservedMemoryRegion rmr = rtree->find_reserved_region(addr); ASSERT_EQ(rmr.size(), size); @@ -118,24 +117,24 @@ public: { // Commit one region rtree->commit_region(addr + cs, cs, stack); R r[] = { {addr + cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit adjacent - lower address rtree->commit_region(addr, cs, stack); R r[] = { {addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit adjacent - higher address rtree->commit_region(addr + 2 * cs, cs, stack); R r[] = { {addr, 3 * cs} }; - check(rmr, r); + check(vmt,rmr, r); } // Cleanup rtree->uncommit_region(addr, 3 * cs); - ASSERT_EQ(rmr.committed_size(), 0u); + ASSERT_EQ(vmt.committed_size(&rmr), 0u); // Commit adjacent regions with different stacks @@ -143,14 +142,14 @@ public: { // Commit one region rtree->commit_region(addr + cs, cs, stack); R r[] = { {addr + cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit adjacent - lower address rtree->commit_region(addr, cs, stack2); R r[] = { {addr, cs}, {addr + cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit adjacent - higher address @@ -158,25 +157,21 @@ public: R r[] = { {addr, cs}, {addr + cs, cs}, {addr + 2 * cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } // Cleanup rtree->uncommit_region(addr, 3 * cs); - ASSERT_EQ(rmr.committed_size(), 0u); - - rtree->tree().remove_all(); + ASSERT_EQ(vmt.committed_size(&rmr), 0u); } static void test_add_committed_region_adjacent_overlapping() { + VirtualMemoryTracker vmt(true); + RegionsTree* rtree = vmt.tree(); size_t size = 0x01000000; - ReservedSpace rs = MemoryReserver::reserve(size, mtTest); - - RegionsTree* rtree = VirtualMemoryTracker::Instance::tree(); - MemTracker::NmtVirtualMemoryLocker nvml; - - address addr = (address)rs.base(); + const address addr = (address)0x0000A000; + vmt.add_reserved_region(addr, size, CALLER_PC, mtTest); address frame1 = (address)0x1234; address frame2 = (address)0x1235; @@ -199,28 +194,28 @@ public: rtree->commit_region(addr + 3 * cs, 2 * cs, stack); R r[] = { {addr, 2 * cs}, {addr + 3 * cs, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit adjacent and overlapping rtree->commit_region(addr + 2 * cs, 2 * cs, stack); R r[] = { {addr, 5 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } // revert to two non-adjacent regions rtree->uncommit_region(addr + 2 * cs, cs); - ASSERT_EQ(rmr.committed_size(), 4 * cs); + ASSERT_EQ(vmt.committed_size(&rmr), 4 * cs); { // Commit overlapping and adjacent rtree->commit_region(addr + cs, 2 * cs, stack); R r[] = { {addr, 5 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } // Cleanup rtree->uncommit_region(addr, 5 * cs); - ASSERT_EQ(rmr.committed_size(), 0u); + ASSERT_EQ(vmt.committed_size(&rmr), 0u); // Commit adjacent and overlapping regions with different stacks @@ -230,7 +225,7 @@ public: rtree->commit_region(addr + 3 * cs, 2 * cs, stack); R r[] = { {addr, 2 * cs}, {addr + 3 * cs, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit adjacent and overlapping @@ -238,33 +233,32 @@ public: R r[] = { {addr, 2 * cs}, {addr + 2 * cs, 2 * cs}, {addr + 4 * cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } // revert to two non-adjacent regions rtree->commit_region(addr, 5 * cs, stack); rtree->uncommit_region(addr + 2 * cs, cs); - ASSERT_EQ(rmr.committed_size(), 4 * cs); + ASSERT_EQ(vmt.committed_size(&rmr), 4 * cs); { // Commit overlapping and adjacent rtree->commit_region(addr + cs, 2 * cs, stack2); R r[] = { {addr, cs}, {addr + cs, 2 * cs}, {addr + 3 * cs, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } rtree->tree().remove_all(); } static void test_add_committed_region_overlapping() { + VirtualMemoryTracker vmt(true); + RegionsTree* rtree = vmt.tree(); size_t size = 0x01000000; - ReservedSpace rs = MemoryReserver::reserve(size, mtTest); + const address addr = (address)0x0000A000; - RegionsTree* rtree = VirtualMemoryTracker::Instance::tree(); - MemTracker::NmtVirtualMemoryLocker nvml; - - address addr = (address)rs.base(); + vmt.add_reserved_region(addr, size, CALLER_PC, mtTest); address frame1 = (address)0x1234; address frame2 = (address)0x1235; @@ -287,54 +281,54 @@ public: { // Commit one region rtree->commit_region(addr, cs, stack); R r[] = { {addr, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit the same region rtree->commit_region(addr, cs, stack); R r[] = { {addr, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit a succeeding region rtree->commit_region(addr + cs, cs, stack); R r[] = { {addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit over two regions rtree->commit_region(addr, 2 * cs, stack); R r[] = { {addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } {// Commit first part of a region rtree->commit_region(addr, cs, stack); R r[] = { {addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit second part of a region rtree->commit_region(addr + cs, cs, stack); R r[] = { {addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit a third part rtree->commit_region(addr + 2 * cs, cs, stack); R r[] = { {addr, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit in the middle of a region rtree->commit_region(addr + 1 * cs, cs, stack); R r[] = { {addr, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } // Cleanup rtree->uncommit_region(addr, 3 * cs); - ASSERT_EQ(rmr.committed_size(), 0u); + ASSERT_EQ(vmt.committed_size(&rmr), 0u); // With preceding region @@ -345,71 +339,71 @@ public: { R r[] = { {addr, cs}, {addr + 2 * cs, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } rtree->commit_region(addr + 3 * cs, cs, stack); { R r[] = { {addr, cs}, {addr + 2 * cs, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } rtree->commit_region(addr + 4 * cs, cs, stack); { R r[] = { {addr, cs}, {addr + 2 * cs, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } // Cleanup rtree->uncommit_region(addr, 5 * cs); - ASSERT_EQ(rmr.committed_size(), 0u); + ASSERT_EQ(vmt.committed_size(&rmr), 0u); // With different stacks { // Commit one region rtree->commit_region(addr, cs, stack); R r[] = { {addr, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit the same region rtree->commit_region(addr, cs, stack2); R r[] = { {addr, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit a succeeding region rtree->commit_region(addr + cs, cs, stack); R r[] = { {addr, cs}, {addr + cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit over two regions rtree->commit_region(addr, 2 * cs, stack); R r[] = { {addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } {// Commit first part of a region rtree->commit_region(addr, cs, stack2); R r[] = { {addr, cs}, {addr + cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit second part of a region rtree->commit_region(addr + cs, cs, stack2); R r[] = { {addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit a third part rtree->commit_region(addr + 2 * cs, cs, stack2); R r[] = { {addr, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); } { // Commit in the middle of a region @@ -417,7 +411,7 @@ public: R r[] = { {addr, cs}, {addr + cs, cs}, {addr + 2 * cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } rtree->tree().remove_all(); @@ -435,14 +429,12 @@ public: } static void test_remove_uncommitted_region() { + VirtualMemoryTracker vmt(true); + RegionsTree* rtree = vmt.tree(); size_t size = 0x01000000; - ReservedSpace rs = MemoryReserver::reserve(size, mtTest); - - RegionsTree* rtree = VirtualMemoryTracker::Instance::tree(); - MemTracker::NmtVirtualMemoryLocker nvml; - - address addr = (address)rs.base(); + const address addr = (address)0x0000A000; + vmt.add_reserved_region(addr, size, CALLER_PC, mtTest); address frame1 = (address)0x1234; address frame2 = (address)0x1235; @@ -461,11 +453,11 @@ public: { // Commit regions rtree->commit_region(addr, 3 * cs, stack); R r[] = { {addr, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); // Remove only existing rtree->uncommit_region(addr, 3 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } { @@ -477,7 +469,7 @@ public: rtree->uncommit_region(addr, cs); R r[] = { {addr + 2 * cs, cs}, {addr + 4 * cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } // add back @@ -487,7 +479,7 @@ public: rtree->uncommit_region(addr + 2 * cs, cs); R r[] = { {addr + 0 * cs, cs}, {addr + 4 * cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } // add back @@ -497,17 +489,17 @@ public: rtree->uncommit_region(addr + 4 * cs, cs); R r[] = { {addr + 0 * cs, cs}, {addr + 2 * cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); } rtree->uncommit_region(addr, 5 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } { // Remove larger region rtree->commit_region(addr + 1 * cs, cs, stack); rtree->uncommit_region(addr, 3 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } { // Remove smaller region - in the middle @@ -515,50 +507,50 @@ public: rtree->uncommit_region(addr + 1 * cs, cs); R r[] = { { addr + 0 * cs, cs}, { addr + 2 * cs, cs} }; - check(rmr, r); + check(vmt, rmr, r); rtree->uncommit_region(addr, 3 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } { // Remove smaller region - at the beginning rtree->commit_region(addr, 3 * cs, stack); rtree->uncommit_region(addr + 0 * cs, cs); R r[] = { { addr + 1 * cs, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); rtree->uncommit_region(addr, 3 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } { // Remove smaller region - at the end rtree->commit_region(addr, 3 * cs, stack); rtree->uncommit_region(addr + 2 * cs, cs); R r[] = { { addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); rtree->uncommit_region(addr, 3 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } { // Remove smaller, overlapping region - at the beginning rtree->commit_region(addr + 1 * cs, 4 * cs, stack); rtree->uncommit_region(addr, 2 * cs); R r[] = { { addr + 2 * cs, 3 * cs} }; - check(rmr, r); + check(vmt, rmr, r); rtree->uncommit_region(addr + 1 * cs, 4 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } { // Remove smaller, overlapping region - at the end rtree->commit_region(addr, 3 * cs, stack); rtree->uncommit_region(addr + 2 * cs, 2 * cs); R r[] = { { addr, 2 * cs} }; - check(rmr, r); + check(vmt, rmr, r); rtree->uncommit_region(addr, 3 * cs); - check_empty(rmr); + check_empty(vmt, rmr); } rtree->tree().remove_all(); diff --git a/test/hotspot/gtest/utilities/test_rbtree.cpp b/test/hotspot/gtest/utilities/test_rbtree.cpp index c47e6cb2d58..e1be83bfd58 100644 --- a/test/hotspot/gtest/utilities/test_rbtree.cpp +++ b/test/hotspot/gtest/utilities/test_rbtree.cpp @@ -129,6 +129,7 @@ public: rbtree_const.visit_in_order([&](const RBTreeIntNode* node) { nums_seen.at(node->key())++; + return true; }); for (int i = 0; i < up_to; i++) { EXPECT_EQ(1, nums_seen.at(i)); @@ -210,6 +211,7 @@ public: rbtree_const.visit_range_in_order(0, 100, [&](const Node* x) { EXPECT_TRUE(false) << "Empty rbtree has no nodes to visit"; + return true; }); // Single-element set @@ -217,14 +219,21 @@ public: int count = 0; rbtree_const.visit_range_in_order(0, 100, [&](const Node* x) { count++; + return true; }); EXPECT_EQ(1, count); count = 0; rbtree_const.visit_in_order([&](const Node* x) { count++; + return true; }); EXPECT_EQ(1, count); + rbtree.visit_in_order([&](const Node* x) { + count++; + return true; + }); + EXPECT_EQ(2, count); // Add an element outside of the range that should not be visited on the right side and // one on the left side. @@ -233,21 +242,62 @@ public: count = 0; rbtree_const.visit_range_in_order(0, 100, [&](const Node* x) { count++; + return true; }); EXPECT_EQ(1, count); + rbtree.visit_range_in_order(0, 100, [&](const Node* x) { + count++; + return true; + }); + EXPECT_EQ(2, count); count = 0; rbtree_const.visit_in_order([&](const Node* x) { count++; + return true; }); EXPECT_EQ(3, count); + rbtree.visit_in_order([&](const Node* x) { + count++; + return true; + }); + EXPECT_EQ(6, count); count = 0; rbtree.upsert(0, 0); rbtree_const.visit_range_in_order(0, 0, [&](const Node* x) { count++; + return true; }); EXPECT_EQ(1, count); + rbtree.visit_range_in_order(0, 0, [&](const Node* x) { + count++; + return true; + }); + EXPECT_EQ(2, count); + + // Test exiting visit early + rbtree.remove_all(); + for (int i = 0; i < 11; i++) { + rbtree.upsert(i, 0); + } + + count = 0; + rbtree_const.visit_in_order([&](const Node* x) { + if (x->key() >= 6) return false; + count++; + return true; + }); + EXPECT_EQ(6, count); + + count = 0; + rbtree_const.visit_range_in_order(6, 10, [&](const Node* x) { + if (x->key() >= 6) return false; + count++; + return true; + }); + + EXPECT_EQ(0, count); rbtree.remove_all(); for (int i = 0; i < 11; i++) { @@ -258,6 +308,7 @@ public: GrowableArray seen; rbtree_const.visit_range_in_order(0, 9, [&](const Node* x) { seen.push(x->key()); + return true; }); EXPECT_EQ(10, seen.length()); for (int i = 0; i < 10; i++) { @@ -267,6 +318,7 @@ public: seen.clear(); rbtree_const.visit_in_order([&](const Node* x) { seen.push(x->key()); + return true; }); EXPECT_EQ(11, seen.length()); for (int i = 0; i < 10; i++) { @@ -276,6 +328,7 @@ public: seen.clear(); rbtree_const.visit_range_in_order(10, 12, [&](const Node* x) { seen.push(x->key()); + return true; }); EXPECT_EQ(1, seen.length()); EXPECT_EQ(10, seen.at(0)); @@ -292,6 +345,7 @@ public: GrowableArray seen; rbtree_const.visit_range_in_order(9, -1, [&](const Node* x) { seen.push(x->key()); + return true; }); EXPECT_EQ(10, seen.length()); for (int i = 0; i < 10; i++) { @@ -301,6 +355,7 @@ public: rbtree_const.visit_in_order([&](const Node* x) { seen.push(x->key()); + return true; }); EXPECT_EQ(10, seen.length()); for (int i = 0; i < 10; i++) { @@ -320,9 +375,12 @@ public: {4, 4}, {6, 6}, {6, 7}, {7, 7}}; for (const int (&test_case)[2] : test_cases) { - rbtree.visit_range_in_order(test_case[0], test_case[1], [&](const Node* x) { - FAIL() << "Range should not visit nodes"; + bool visited = false; + rbtree.visit_range_in_order(test_case[0], test_case[1], [&](const Node* x) -> bool { + visited = true; + return true; }); + EXPECT_FALSE(visited); } } @@ -515,6 +573,7 @@ public: // After deleting, values should have remained consistant rbtree.visit_in_order([&](const Node* node) { EXPECT_EQ(node, node->val()); + return true; }); } diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 7692c962bdf..7026b6f79d6 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -75,9 +75,6 @@ compiler/codecache/CodeCacheFullCountTest.java 8332954 generic-all compiler/interpreter/Test6833129.java 8335266 generic-i586 -compiler/ciReplay/TestInliningProtectionDomain.java 8349191 generic-all -compiler/ciReplay/TestIncrementalInlining.java 8349191 generic-all - compiler/c2/TestVerifyConstraintCasts.java 8355574 generic-all compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 @@ -87,7 +84,6 @@ compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 # :hotspot_gc gc/epsilon/TestMemoryMXBeans.java 8206434 generic-all -gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java 8156755 generic-all gc/g1/logging/TestG1LoggingFailure.java 8169634 generic-all gc/g1/humongousObjects/TestHeapCounters.java 8178918 generic-all gc/TestAllocHumongousFragment.java#adaptive 8298781 generic-all diff --git a/test/hotspot/jtreg/applications/jcstress/README b/test/hotspot/jtreg/applications/jcstress/README index 2aaf1210f9b..b85b4ddbbe3 100644 --- a/test/hotspot/jtreg/applications/jcstress/README +++ b/test/hotspot/jtreg/applications/jcstress/README @@ -1,4 +1,4 @@ -Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,13 @@ The tests located under this directory run tests from the Java Concurrency Stress test suite[1] (a.k.a. jcstress). This suite aims to verify the correctness of concurrency support in the JDK. -All the tests are run through the test driver class -- JcstressRunner, which -downloads the specified build of org.openjdk.jcstress:jcstress-tests-all, +All the tests are run through the test driver class -- JcstressRunner, spawns a new JVM to run one jcstress test and checks that it finishes -successfully. +successfully. These tests require a build of org.openjdk.jcstress:jcstress-tests-all. +If this artifact could not be found automatically, you can build it using jcstress +guide[1], and then pass the JAR location with JDK option such as +-Djdk.test.lib.artifacts.jcstress-tests-all=jcstress-tests-all.jar, +either with explicit -javaoption: or TEST_VM_OPTS test variable. When the used version of org.openjdk.jcstress:jcstress-tests-all needs to be changed, one should make corresponding changes to the artifact description in diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestIllegalArrayCopyBeforeInfiniteLoop.java b/test/hotspot/jtreg/compiler/arraycopy/TestIllegalArrayCopyBeforeInfiniteLoop.java index 0b3572d6afa..af166571909 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestIllegalArrayCopyBeforeInfiniteLoop.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestIllegalArrayCopyBeforeInfiniteLoop.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex1.java b/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex1.java index 2f35a11e948..b7da2119bd3 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex1.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex1.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex2.java b/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex2.java index da8f0936cae..b101254d189 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex2.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestNegArrayLengthAsIndex2.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestNegativeArrayCopyAfterLoop.java b/test/hotspot/jtreg/compiler/arraycopy/TestNegativeArrayCopyAfterLoop.java index e28cfb871f7..901ff78347d 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestNegativeArrayCopyAfterLoop.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestNegativeArrayCopyAfterLoop.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/c1/TestRangeCheckEliminated.java b/test/hotspot/jtreg/compiler/c1/TestRangeCheckEliminated.java index 9da8a5390da..0f5b8860195 100644 --- a/test/hotspot/jtreg/compiler/c1/TestRangeCheckEliminated.java +++ b/test/hotspot/jtreg/compiler/c1/TestRangeCheckEliminated.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * diff --git a/test/hotspot/jtreg/compiler/c2/TestDuplicateSimpleLoopBackedge.java b/test/hotspot/jtreg/compiler/c2/TestDuplicateSimpleLoopBackedge.java index 0b102b4c9fd..414a351e53f 100644 --- a/test/hotspot/jtreg/compiler/c2/TestDuplicateSimpleLoopBackedge.java +++ b/test/hotspot/jtreg/compiler/c2/TestDuplicateSimpleLoopBackedge.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/c2/TestEliminateRedundantConversionSequences.java b/test/hotspot/jtreg/compiler/c2/TestEliminateRedundantConversionSequences.java new file mode 100644 index 00000000000..b452b8f67dd --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestEliminateRedundantConversionSequences.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8359603 + * @summary Redundant ConvX2Y->ConvY2X->ConvX2Y sequences should be + * simplified to a single ConvX2Y operation when applicable + * VerifyIterativeGVN checks that this optimization was applied + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions + * -XX:CompileCommand=compileonly,compiler.c2.TestEliminateRedundantConversionSequences::test* + * -XX:-TieredCompilation -Xbatch -XX:VerifyIterativeGVN=1110 compiler.c2.TestEliminateRedundantConversionSequences + * @run main compiler.c2.TestEliminateRedundantConversionSequences + * + */ + +package compiler.c2; + +public class TestEliminateRedundantConversionSequences { + static long instanceCountD2L; + static int instanceCoundF2I; + static long instanceCountF2L; + static float instanceCountI2F; + + // ConvD2L->ConvL2D->ConvD2L + static void testD2L(double d) { + int i = 1; + int j = 1; + while (++i < 3) { + for (; 8 > j; ++j) { + instanceCountD2L = (long)d; + d = instanceCountD2L; + } + } + } + + // ConvF2I->ConvI2F->ConvF2I + static void testF2I(float d) { + int i = 1; + int j = 1; + while (++i < 3) { + for (; 8 > j; ++j) { + instanceCoundF2I = (int)d; + d = instanceCoundF2I; + } + } + } + + // ConvF2L->ConvL2F->ConvF2L + static void testF2L(float d) { + int i = 1; + int j = 1; + while (++i < 3) { + for (; 8 > j; ++j) { + instanceCountF2L = (long)d; + d = instanceCountF2L; + } + } + } + + // ConvI2F->ConvF2I->ConvI2F + static void testI2F(int d) { + int i = 1; + int j = 1; + while (++i < 3) { + for (; 8 > j; ++j) { + instanceCountI2F = d; + d = (int)instanceCountI2F; + } + } + } + + public static void main(String[] strArr) { + for (int i = 0; i < 1550; ++i) { + testD2L(1); + } + for (int i = 0; i < 1550; ++i) { + testF2I(1); + } + for (int i = 0; i < 1550; ++i) { + testF2L(1); + } + for (int i = 0; i < 1550; ++i) { + testI2F(1); + } + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/c2/cr6865031/Test.java b/test/hotspot/jtreg/compiler/c2/cr6865031/Test.java index af23628577b..beb41943183 100644 --- a/test/hotspot/jtreg/compiler/c2/cr6865031/Test.java +++ b/test/hotspot/jtreg/compiler/c2/cr6865031/Test.java @@ -1,6 +1,6 @@ /* * Copyright 2009 Goldman Sachs International. All Rights Reserved. - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/c2/irTests/InvolutionIdentityTests.java b/test/hotspot/jtreg/compiler/c2/irTests/InvolutionIdentityTests.java deleted file mode 100644 index e1945c8a519..00000000000 --- a/test/hotspot/jtreg/compiler/c2/irTests/InvolutionIdentityTests.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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; - -import compiler.lib.generators.Generator; -import compiler.lib.generators.Generators; -import compiler.lib.generators.RestrictableGenerator; -import compiler.lib.ir_framework.DontCompile; -import compiler.lib.ir_framework.IR; -import compiler.lib.ir_framework.IRNode; -import compiler.lib.ir_framework.Run; -import compiler.lib.ir_framework.Test; -import compiler.lib.ir_framework.TestFramework; -import jdk.test.lib.Asserts; - -/* - * @test - * @bug 8350988 - * @summary Test that Identity simplifications of Involution nodes are being performed as expected. - * @library /test/lib / - * @run driver compiler.c2.irTests.InvolutionIdentityTests - */ -public class InvolutionIdentityTests { - - public static final RestrictableGenerator GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE); - public static final RestrictableGenerator GEN_SHORT = Generators.G.safeRestrict(Generators.G.ints(), Short.MIN_VALUE, Short.MAX_VALUE); - public static final RestrictableGenerator GEN_LONG = Generators.G.longs(); - public static final RestrictableGenerator GEN_INT = Generators.G.ints(); - public static final Generator GEN_FLOAT = Generators.G.floats(); - public static final Generator GEN_DOUBLE = Generators.G.doubles(); - - public static void main(String[] args) { - TestFramework.run(); - } - - @Run(test = { - "testI1", "testI2", - "testL1", "testL2", - "testS1", - "testUS1", - "testF1", - "testD1" - }) - public void runMethod() { - int ai = GEN_INT.next(); - - int mini = Integer.MIN_VALUE; - int maxi = Integer.MAX_VALUE; - - assertResultI(0); - assertResultI(ai); - assertResultI(mini); - assertResultI(maxi); - - long al = GEN_LONG.next(); - - long minl = Long.MIN_VALUE; - long maxl = Long.MAX_VALUE; - - assertResultL(0); - assertResultL(al); - assertResultL(minl); - assertResultL(maxl); - - short as = GEN_SHORT.next().shortValue(); - - short mins = Short.MIN_VALUE; - short maxs = Short.MAX_VALUE; - - assertResultS((short) 0); - assertResultS(as); - assertResultS(mins); - assertResultS(maxs); - - char ac = (char) GEN_CHAR.next().intValue(); - - char minc = Character.MIN_VALUE; - char maxc = Character.MAX_VALUE; - - assertResultUS((char) 0); - assertResultUS(ac); - assertResultUS(minc); - assertResultUS(maxc); - - float af = GEN_FLOAT.next(); - float inf = Float.POSITIVE_INFINITY; - float nanf = Float.NaN; - - assertResultF(0f); - assertResultF(-0f); - assertResultF(af); - assertResultF(inf); - assertResultF(nanf); - - double ad = GEN_DOUBLE.next(); - double ind = Double.POSITIVE_INFINITY; - double nand = Double.NaN; - - assertResultD(0d); - assertResultD(-0d); - assertResultD(ad); - assertResultD(ind); - assertResultD(nand); - - } - - @DontCompile - public void assertResultI(int a) { - Asserts.assertEQ(Integer.reverseBytes(Integer.reverseBytes(a)), testI1(a)); - Asserts.assertEQ(Integer.reverse(Integer.reverse(a)) , testI2(a)); - } - - @DontCompile - public void assertResultL(long a) { - Asserts.assertEQ(Long.reverseBytes(Long.reverseBytes(a)), testL1(a)); - Asserts.assertEQ(Long.reverse(Long.reverse(a)) , testL2(a)); - } - - @DontCompile - public void assertResultS(short a) { - Asserts.assertEQ(Short.reverseBytes(Short.reverseBytes(a)), testS1(a)); - } - - @DontCompile - public void assertResultUS(char a) { - Asserts.assertEQ(Character.reverseBytes(Character.reverseBytes(a)), testUS1(a)); - } - - @DontCompile - public void assertResultF(float a) { - Asserts.assertEQ(Float.floatToRawIntBits(-(-a)), Float.floatToRawIntBits(testF1(a))); - } - - @DontCompile - public void assertResultD(double a) { - Asserts.assertEQ(Double.doubleToRawLongBits(-(-a)), Double.doubleToRawLongBits(testD1(a))); - } - - @Test - @IR(failOn = {IRNode.REVERSE_BYTES_I}) - public int testI1(int x) { - return Integer.reverseBytes(Integer.reverseBytes(x)); - } - - @Test - @IR(failOn = {IRNode.REVERSE_I}) - public int testI2(int x) { - return Integer.reverse(Integer.reverse(x)); - } - - @Test - @IR(failOn = {IRNode.REVERSE_BYTES_L}) - public long testL1(long x) { - return Long.reverseBytes(Long.reverseBytes(x)); - } - - @Test - @IR(failOn = {IRNode.REVERSE_L}) - public long testL2(long x) { - return Long.reverse(Long.reverse(x)); - } - - @Test - @IR(failOn = {IRNode.REVERSE_BYTES_S}) - public short testS1(short x) { - return Short.reverseBytes(Short.reverseBytes(x)); - } - - @Test - @IR(failOn = {IRNode.REVERSE_BYTES_US}) - public char testUS1(char x) { - return Character.reverseBytes(Character.reverseBytes(x)); - } - - @Test - @IR(failOn = {IRNode.NEG_F}) - public float testF1(float x) { - return -(-x); - } - - @Test - @IR(failOn = {IRNode.NEG_D}) - public double testD1(double x) { - return -(-x); - } -} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestAutoVectorization2DArray.java b/test/hotspot/jtreg/compiler/c2/irTests/TestAutoVectorization2DArray.java index ac622e3bcc3..5b1d6f51bb3 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestAutoVectorization2DArray.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestAutoVectorization2DArray.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/compilercontrol/TestConflictInlineCommands.java b/test/hotspot/jtreg/compiler/compilercontrol/TestConflictInlineCommands.java index 9c12ea6b8a7..c7e20157e7e 100644 --- a/test/hotspot/jtreg/compiler/compilercontrol/TestConflictInlineCommands.java +++ b/test/hotspot/jtreg/compiler/compilercontrol/TestConflictInlineCommands.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/debug/TestStress.java b/test/hotspot/jtreg/compiler/debug/TestStress.java index 2046488ac40..f43818e8bd8 100644 --- a/test/hotspot/jtreg/compiler/debug/TestStress.java +++ b/test/hotspot/jtreg/compiler/debug/TestStress.java @@ -77,7 +77,17 @@ public class TestStress { static void sum(int n) { int acc = 0; - for (int i = 0; i < n; i++) acc += i; + int[] arr1 = new int[n]; + int[] arr2 = new int[n]; + int[] arr3 = new int[n]; + int[] arr4 = new int[n]; + for (int i = 0; i < n; i++) { + acc += i; + arr1[i] = i; + arr2[i] = acc; + arr3[i] = i * n; + arr4[i] = acc * n; + } System.out.println(acc); } diff --git a/test/hotspot/jtreg/compiler/debug/TestStressDistinctSeed.java b/test/hotspot/jtreg/compiler/debug/TestStressDistinctSeed.java new file mode 100644 index 00000000000..3b29daf2ce6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/debug/TestStressDistinctSeed.java @@ -0,0 +1,131 @@ +/* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please 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 jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Asserts; +import java.util.Set; +import java.util.HashSet; +import java.util.Arrays; + +/* + * @test + * @key stress randomness + * @requires vm.debug == true & vm.compiler2.enabled & vm.flagless + * @summary Tests that stress compilations with the N different seeds yield different + * IGVN, CCP, macro elimination, and macro expansion traces. + * @library /test/lib / + * @run driver compiler.debug.TestStressDistinctSeed + */ + +public class TestStressDistinctSeed { + + private static int counter = 0; + + static String phaseTrace(String stressOption, String traceOption, + int stressSeed) throws Exception { + String className = TestStressDistinctSeed.class.getName(); + String[] procArgs = { + "-Xcomp", "-XX:-TieredCompilation", "-XX:-Inline", "-XX:+CICountNative", + "-XX:CompileOnly=" + className + "::sum", "-XX:" + traceOption, + "-XX:+" + stressOption, "-XX:StressSeed=" + stressSeed, + className, "5" }; + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(procArgs); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + out.shouldHaveExitValue(0); + return out.getStdout(); + } + + static String igvnTrace(int stressSeed) throws Exception { + return phaseTrace("StressIGVN", "+TraceIterativeGVN", stressSeed); + } + + static String ccpTrace(int stressSeed) throws Exception { + return phaseTrace("StressCCP", "+TracePhaseCCP", stressSeed); + } + + static String macroExpansionTrace(int stressSeed) throws Exception { + return phaseTrace("StressMacroExpansion", + "CompileCommand=PrintIdealPhase,*::*,AFTER_MACRO_EXPANSION_STEP", + stressSeed); + } + + static String macroEliminationTrace(int stressSeed) throws Exception { + return phaseTrace("StressMacroElimination", + "CompileCommand=PrintIdealPhase,*::*,AFTER_MACRO_ELIMINATION_STEP", + stressSeed); + } + + static void sum(int n) { + int[] arr1 = new int[n]; + for (int i = 0; i < n; i++) { + synchronized (TestStressDistinctSeed.class) { + counter += i; + arr1[i] = counter; + } + } + System.out.println(counter); + } + + public static void main(String[] args) throws Exception { + Set igvnTraceSet = new HashSet<>(); + Set ccpTraceSet = new HashSet<>(); + Set macroExpansionTraceSet = new HashSet<>(); + Set macroEliminationTraceSet = new HashSet<>(); + String igvnTraceOutput, ccpTraceOutput, macroExpansionTraceOutput, macroEliminationTraceOutput; + if (args.length == 0) { + for (int s = 0; s < 5; s++) { + igvnTraceOutput = igvnTrace(s); + ccpTraceOutput = ccpTrace(s); + macroExpansionTraceOutput = macroExpansionTrace(s); + macroEliminationTraceOutput = macroEliminationTrace(s); + // Test same seed produce same result to test that different traces come from different seed and + // not indeterminism with the test. + Asserts.assertEQ(igvnTraceOutput, igvnTrace(s), + "got different IGVN traces for the same seed"); + Asserts.assertEQ(ccpTraceOutput, ccpTrace(s), + "got different CCP traces for the same seed"); + Asserts.assertEQ(macroExpansionTraceOutput, macroExpansionTrace(s), + "got different macro expansion traces for the same seed"); + Asserts.assertEQ(macroEliminationTraceOutput, macroEliminationTrace(s), + "got different macro elimination traces for the same seed"); + + igvnTraceSet.add(igvnTraceOutput); + ccpTraceSet.add(ccpTraceOutput); + macroExpansionTraceSet.add(macroExpansionTraceOutput); + macroEliminationTraceSet.add(macroEliminationTraceOutput); + } + Asserts.assertGT(igvnTraceSet.size(), 1, + "got same IGVN traces for 5 different seeds"); + Asserts.assertGT(ccpTraceSet.size(), 1, + "got same CCP traces for 5 different seeds"); + Asserts.assertGT(macroExpansionTraceSet.size(), 1, + "got same macro expansion traces for 5 different seeds"); + Asserts.assertGT(macroEliminationTraceSet.size(), 1, + "got same macro elimination traces for 5 different seeds"); + } else if (args.length > 0) { + sum(Integer.parseInt(args[0])); + } + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java b/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java index 59d3de0d58e..8e6169f07dc 100644 --- a/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java +++ b/test/hotspot/jtreg/compiler/debug/TraceIterativeGVN.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/intrinsics/math/TestPow0Dot5Opt.java b/test/hotspot/jtreg/compiler/intrinsics/math/TestPow0Dot5Opt.java index a0560870d2a..9854eb8c386 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/math/TestPow0Dot5Opt.java +++ b/test/hotspot/jtreg/compiler/intrinsics/math/TestPow0Dot5Opt.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/intrinsics/math/TestPow2Opt.java b/test/hotspot/jtreg/compiler/intrinsics/math/TestPow2Opt.java index 2bf48407a36..db05baf9683 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/math/TestPow2Opt.java +++ b/test/hotspot/jtreg/compiler/intrinsics/math/TestPow2Opt.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnSupportedCPU.java b/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnSupportedCPU.java index 3706f3abfd8..d3c0a4a8da7 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnSupportedCPU.java +++ b/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnSupportedCPU.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Huawei Technologies Co., Ltd. All rights reserved. - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnUnsupportedCPU.java b/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnUnsupportedCPU.java index 633ceb1d9ab..c0045ae2922 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnUnsupportedCPU.java +++ b/test/hotspot/jtreg/compiler/intrinsics/sha/cli/TestUseSHA3IntrinsicsOptionOnUnsupportedCPU.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Huawei Technologies Co., Ltd. All rights reserved. - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/jvmci/errors/TestInvalidTieredStopAtLevel.java b/test/hotspot/jtreg/compiler/jvmci/errors/TestInvalidTieredStopAtLevel.java index 3527dd891d6..0a00987f3d8 100644 --- a/test/hotspot/jtreg/compiler/jvmci/errors/TestInvalidTieredStopAtLevel.java +++ b/test/hotspot/jtreg/compiler/jvmci/errors/TestInvalidTieredStopAtLevel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2020, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 1b843e27587..7fb1eeb800c 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -585,6 +585,21 @@ public class IRNode { beforeMatchingNameRegex(CMP_P, "CmpP"); } + public static final String CMP_LT_MASK = PREFIX + "CMP_LT_MASK" + POSTFIX; + static { + beforeMatchingNameRegex(CMP_LT_MASK, "CmpLTMask"); + } + + public static final String ROUND_F = PREFIX + "ROUND_F" + POSTFIX; + static { + beforeMatchingNameRegex(ROUND_F, "RoundF"); + } + + public static final String ROUND_D = PREFIX + "ROUND_D" + POSTFIX; + static { + beforeMatchingNameRegex(ROUND_D, "RoundD"); + } + public static final String COMPRESS_BITS = PREFIX + "COMPRESS_BITS" + POSTFIX; static { beforeMatchingNameRegex(COMPRESS_BITS, "CompressBits"); @@ -1387,6 +1402,21 @@ public class IRNode { vectorNode(UMAX_VL, "UMaxV", TYPE_LONG); } + public static final String MASK_ALL = PREFIX + "MASK_ALL" + POSTFIX; + static { + beforeMatchingNameRegex(MASK_ALL, "MaskAll"); + } + + public static final String VECTOR_LONG_TO_MASK = PREFIX + "VECTOR_LONG_TO_MASK" + POSTFIX; + static { + beforeMatchingNameRegex(VECTOR_LONG_TO_MASK, "VectorLongToMask"); + } + + public static final String VECTOR_MASK_TO_LONG = PREFIX + "VECTOR_MASK_TO_LONG" + POSTFIX; + static { + beforeMatchingNameRegex(VECTOR_MASK_TO_LONG, "VectorMaskToLong"); + } + // Can only be used if avx512_vnni is available. public static final String MUL_ADD_VS2VI_VNNI = PREFIX + "MUL_ADD_VS2VI_VNNI" + POSTFIX; static { @@ -1483,16 +1513,6 @@ public class IRNode { superWordNodes(MAX_REDUCTION_V, "MaxReductionV"); } - public static final String NEG_F = PREFIX + "NEG_F" + POSTFIX; - static { - beforeMatchingNameRegex(NEG_F, "NegF"); - } - - public static final String NEG_D = PREFIX + "NEG_D" + POSTFIX; - static { - beforeMatchingNameRegex(NEG_D, "NegD"); - } - public static final String NEG_VF = VECTOR_PREFIX + "NEG_VF" + POSTFIX; static { vectorNode(NEG_VF, "NegVF", TYPE_FLOAT); @@ -2846,6 +2866,36 @@ public class IRNode { fromBeforeRemoveUselessToFinalCode(BLACKHOLE, "Blackhole"); } + public static final String SELECT_FROM_TWO_VECTOR_VB = VECTOR_PREFIX + "SELECT_FROM_TWO_VECTOR_VB" + POSTFIX; + static { + vectorNode(SELECT_FROM_TWO_VECTOR_VB, "SelectFromTwoVector", TYPE_BYTE); + } + + public static final String SELECT_FROM_TWO_VECTOR_VS = VECTOR_PREFIX + "SELECT_FROM_TWO_VECTOR_VS" + POSTFIX; + static { + vectorNode(SELECT_FROM_TWO_VECTOR_VS, "SelectFromTwoVector", TYPE_SHORT); + } + + public static final String SELECT_FROM_TWO_VECTOR_VI = VECTOR_PREFIX + "SELECT_FROM_TWO_VECTOR_VI" + POSTFIX; + static { + vectorNode(SELECT_FROM_TWO_VECTOR_VI, "SelectFromTwoVector", TYPE_INT); + } + + public static final String SELECT_FROM_TWO_VECTOR_VF = VECTOR_PREFIX + "SELECT_FROM_TWO_VECTOR_VF" + POSTFIX; + static { + vectorNode(SELECT_FROM_TWO_VECTOR_VF, "SelectFromTwoVector", TYPE_FLOAT); + } + + public static final String SELECT_FROM_TWO_VECTOR_VD = VECTOR_PREFIX + "SELECT_FROM_TWO_VECTOR_VD" + POSTFIX; + static { + vectorNode(SELECT_FROM_TWO_VECTOR_VD, "SelectFromTwoVector", TYPE_DOUBLE); + } + + public static final String SELECT_FROM_TWO_VECTOR_VL = VECTOR_PREFIX + "SELECT_FROM_TWO_VECTOR_VL" + POSTFIX; + static { + vectorNode(SELECT_FROM_TWO_VECTOR_VL, "SelectFromTwoVector", TYPE_LONG); + } + /* * Utility methods to set up IR_NODE_MAPPINGS. */ diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java index 6662acf8e9e..16b4654013a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java @@ -105,6 +105,7 @@ public class IREncodingPrinter { "avx512f", "avx512_fp16", "avx512_vnni", + "avx512_vbmi", "bmi2", // AArch64 "sha3", diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopEndNodeEliminate.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopEndNodeEliminate.java index a4683db4f70..e3b5bdff12f 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestLoopEndNodeEliminate.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopEndNodeEliminate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopPredicateDep.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopPredicateDep.java index ca651a20750..bb945342c0d 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestLoopPredicateDep.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopPredicateDep.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java b/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java index 38a82139553..eae00ca3522 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/MinMaxRed_Long.java b/test/hotspot/jtreg/compiler/loopopts/superword/MinMaxRed_Long.java index 9c3e2c7bc5d..f1e68fb95fb 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/MinMaxRed_Long.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/MinMaxRed_Long.java @@ -31,16 +31,21 @@ package compiler.loopopts.superword; -import compiler.lib.ir_framework.*; -import jdk.test.lib.Utils; +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.Run; +import compiler.lib.ir_framework.RunMode; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; import java.util.Arrays; -import java.util.Random; -import java.util.stream.LongStream; public class MinMaxRed_Long { - private static final Random random = Utils.getRandomInstance(); + private static final int SIZE = 1024; + private static final Generator GEN_LONG = Generators.G.longs(); public static void main(String[] args) throws Exception { TestFramework framework = new TestFramework(); @@ -53,97 +58,41 @@ public class MinMaxRed_Long { @Run(test = {"maxReductionImplement"}, mode = RunMode.STANDALONE) public void runMaxTest() { - runMaxTest(50); - runMaxTest(80); - runMaxTest(100); - } + long[] longs = new long[SIZE]; + Generators.G.fill(GEN_LONG, longs); - private static void runMaxTest(int probability) { - long[] longs = reductionInit(probability); long res = 0; for (int j = 0; j < 2000; j++) { res = maxReductionImplement(longs, res); } - if (res == 11 * Arrays.stream(longs).max().getAsLong()) { + + final long expected = Arrays.stream(longs).map(l -> l * 11).max().getAsLong(); + if (res == expected) { System.out.println("Success"); } else { - throw new AssertionError("Failed"); + throw new AssertionError("Failed, got result " + res + " but expected " + expected); } } @Run(test = {"minReductionImplement"}, mode = RunMode.STANDALONE) public void runMinTest() { - runMinTest(50); - runMinTest(80); - runMinTest(100); - } + long[] longs = new long[SIZE]; + Generators.G.fill(GEN_LONG, longs); - private static void runMinTest(int probability) { - long[] longs = reductionInit(probability); - // Negating the values generated for controlling max branching - // allows same logic to be used for min tests. - longs = negate(longs); long res = 0; for (int j = 0; j < 2000; j++) { res = minReductionImplement(longs, res); } - if (res == 11 * Arrays.stream(longs).min().getAsLong()) { + + final long expected = Arrays.stream(longs).map(l -> l * 11).min().getAsLong(); + if (res == expected) { System.out.println("Success"); } else { - throw new AssertionError("Failed"); + throw new AssertionError("Failed, got result " + res + " but expected " + expected); } } - static long[] negate(long[] nums) { - return LongStream.of(nums).map(l -> -l).toArray(); - } - - public static long[] reductionInit(int probability) { - int aboveCount, abovePercent; - long[] longs = new long[1024]; - - // Generates an array of numbers such that as the array is iterated - // there is P probability of finding a new max value, - // and 100-P probability of not finding a new max value. - // The algorithm loops around if the distribution does not match the probability, - // but it approximates the probability as the array sizes increase. - // The worst case of this algorithm is when the desired array size is 100 - // and the aim is to get 50% of probability, which can only be satisfied - // with 50 elements being a new max. This situation can take 15 rounds. - // As sizes increase, say 10'000 elements, - // the number of elements that have to satisfy 50% increases, - // so the algorithm will stop as an example when 5027 elements are a new max values. - // Also, probability values in the edges will achieve their objective quicker, - // with 0% or 100% probability doing it in a single loop. - // To support the same algorithm for min calculations, - // negating the array elements achieves the same objective. - do { - long max = random.nextLong(10); - longs[0] = max; - - aboveCount = 0; - for (int i = 1; i < longs.length; i++) { - long value; - if (random.nextLong(101) <= probability) { - long increment = random.nextLong(10); - value = max + increment; - aboveCount++; - } else { - // Decrement by at least 1 - long diffToMax = random.nextLong(10) + 1; - value = max - diffToMax; - } - longs[i] = value; - max = Math.max(max, value); - } - - abovePercent = ((aboveCount + 1) * 100) / longs.length; - } while (abovePercent != probability); - - return longs; - } - @Test @IR(applyIfAnd = {"SuperWordReductions", "true", "MaxVectorSize", ">=32"}, applyIfCPUFeatureOr = {"avx512", "true", "asimd" , "true"}, diff --git a/test/hotspot/jtreg/compiler/oracle/TestInvalidCompileCommand.java b/test/hotspot/jtreg/compiler/oracle/TestInvalidCompileCommand.java index 791b1d042e3..a64e5f2b8bb 100644 --- a/test/hotspot/jtreg/compiler/oracle/TestInvalidCompileCommand.java +++ b/test/hotspot/jtreg/compiler/oracle/TestInvalidCompileCommand.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java b/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java index 65aa6bcb2d0..52a7aba1a7e 100644 --- a/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java +++ b/test/hotspot/jtreg/compiler/print/TestTraceOptoParse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/regalloc/TestGCMRecalcPressureNodes.java b/test/hotspot/jtreg/compiler/regalloc/TestGCMRecalcPressureNodes.java index fe2fd7e4444..c99bdca2cb1 100644 --- a/test/hotspot/jtreg/compiler/regalloc/TestGCMRecalcPressureNodes.java +++ b/test/hotspot/jtreg/compiler/regalloc/TestGCMRecalcPressureNodes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,4 +50,3 @@ public class TestGCMRecalcPressureNodes { } } } - diff --git a/test/hotspot/jtreg/compiler/unsafe/TestMisalignedUnsafeAccess.java b/test/hotspot/jtreg/compiler/unsafe/TestMisalignedUnsafeAccess.java index 84752a0ccf0..f4b1e3f0a53 100644 --- a/test/hotspot/jtreg/compiler/unsafe/TestMisalignedUnsafeAccess.java +++ b/test/hotspot/jtreg/compiler/unsafe/TestMisalignedUnsafeAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2020, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestIntrinsicBailOut.java b/test/hotspot/jtreg/compiler/vectorapi/TestIntrinsicBailOut.java index 72e1c3aa30f..b6ddadad49b 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestIntrinsicBailOut.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestIntrinsicBailOut.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021, 2022, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, 2022, Tencent. 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. * diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorOp.java b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorOp.java new file mode 100644 index 00000000000..3746578266c --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromTwoVectorOp.java @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2025, 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. + */ + +package compiler.vectorapi; + +import compiler.lib.generators.*; +import compiler.lib.ir_framework.*; + +import jdk.incubator.vector.*; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.VectorSpecies; + +import java.util.Random; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +/** + * @test + * @bug 8348868 + * @library /test/lib / + * @summary Verify that SelectFromTwoVector IR node is correctly being + * generated on aarch64 and x86 + * @modules jdk.incubator.vector + * @run driver compiler.vectorapi.TestSelectFromTwoVectorOp + */ + +public class TestSelectFromTwoVectorOp { + private static final int SIZE = 1024; + private static final Generators random = Generators.G; + + private static byte[] ba; + private static byte[] bb; + private static byte[] bres; + private static byte[][] bindex; + + private static short[] sa; + private static short[] sb; + private static short[] sres; + private static short[][] sindex; + + private static int[] ia; + private static int[] ib; + private static int[] ires; + private static int[][] iindex; + + private static float[] fa; + private static float[] fb; + private static float[] fres; + private static float[][] findex; + + private static long[] la; + private static long[] lb; + private static long[] lres; + private static long[][] lindex; + + private static double[] da; + private static double[] db; + private static double[] dres; + private static double[][] dindex; + + // Stores the possible number of elements that can be + // held in various vector sizes/shapes + private static int [] nums = {2, 4, 8, 16, 32, 64}; + + static { + ba = new byte[SIZE]; + bb = new byte[SIZE]; + bres = new byte[SIZE]; + bindex = new byte[4][SIZE]; + + sa = new short[SIZE]; + sb = new short[SIZE]; + sres = new short[SIZE]; + sindex = new short[4][SIZE]; + + ia = new int[SIZE]; + ib = new int[SIZE]; + ires = new int[SIZE]; + iindex = new int[4][SIZE]; + + fa = new float[SIZE]; + fb = new float[SIZE]; + fres = new float[SIZE]; + findex = new float[4][SIZE]; + + la = new long[SIZE]; + lb = new long[SIZE]; + lres = new long[SIZE]; + lindex = new long[3][SIZE]; + + da = new double[SIZE]; + db = new double[SIZE]; + dres = new double[SIZE]; + dindex = new double[3][SIZE]; + + // Populate the indices + for (int i = 0; i < bindex.length; i++) { + bindex[i] = new byte[SIZE]; + sindex[i] = new short[SIZE]; + iindex[i] = new int[SIZE]; + findex[i] = new float[SIZE]; + + // The index array contains indices in the range of [0, vector_length * 2) + Generator byteGen1 = random.uniformInts(0, (nums[i + 2] * 2) - 1); + Generator shortGen1 = random.uniformInts(0, (nums[i + 1] * 2) - 1); + + for (int j = 0; j < SIZE; j++) { + bindex[i][j] = byteGen1.next().byteValue(); + sindex[i][j] = shortGen1.next().shortValue(); + } + + if (i < dindex.length) { + dindex[i] = new double[SIZE]; + lindex[i] = new long[SIZE]; + + random.fill(random.uniformDoubles(0, (double) ((nums[i] * 2) - 1)), dindex[i]); + random.fill(random.uniformLongs(0, (long) ((nums[i] * 2) - 1)), lindex[i]); + } + + random.fill(random.uniformInts(0, (nums[i] * 2) - 1), iindex[i]); + random.fill(random.uniformFloats(0, (float)((nums[i] * 2) - 1)), findex[i]); + } + + // Populate the sources + Generator byteGen = random.uniformInts(Byte.MIN_VALUE, Byte.MAX_VALUE); + Generator shortGen = random.uniformInts(Short.MIN_VALUE, Short.MAX_VALUE); + + for (int i = 0; i < SIZE; i++) { + ba[i] = byteGen.next().byteValue(); + bb[i] = byteGen.next().byteValue(); + + sa[i] = shortGen.next().shortValue(); + sb[i] = shortGen.next().shortValue(); + } + + random.fill(random.ints(), ia); + random.fill(random.ints(), ib); + random.fill(random.floats(), fa); + random.fill(random.floats(), fb); + random.fill(random.longs(), la); + random.fill(random.longs(), lb); + random.fill(random.doubles(), da); + random.fill(random.doubles(), db); + } + + // Test SelectFromTwoVector operation for Bytes + @ForceInline + public static void ByteSelectFromTwoVectorKernel(VectorSpecies SPECIES, byte[] ba, + byte[] bb, byte[] bindex) { + for (int i = 0; i < SPECIES.loopBound(ba.length); i += SPECIES.length()) { + ByteVector.fromArray(SPECIES, bindex, i) + .selectFrom(ByteVector.fromArray(SPECIES, ba, i), + ByteVector.fromArray(SPECIES, bb, i)) + .intoArray(bres, i); + } + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeature = {"asimd", "true"}, + applyIf = {"MaxVectorSize", ">=8"}) + public static void selectFromTwoVector_Byte64() { + ByteSelectFromTwoVectorKernel(ByteVector.SPECIES_64, ba, bb, bindex[0]); + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_16, ">0"}, + applyIfCPUFeature = {"asimd", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_16, ">0"}, + applyIfCPUFeatureAnd = {"avx512_vbmi", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + public static void selectFromTwoVector_Byte128() { + ByteSelectFromTwoVectorKernel(ByteVector.SPECIES_128, ba, bb, bindex[1]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_32}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_32, ">0"}, + applyIfCPUFeature = {"sve2", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_32, ">0"}, + applyIfCPUFeatureAnd = {"avx512_vbmi", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + public static void selectFromTwoVector_Byte256() { + ByteSelectFromTwoVectorKernel(ByteVector.SPECIES_256, ba, bb, bindex[2]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_64}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_64, ">0"}, + applyIfCPUFeature = {"sve2", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VB, IRNode.VECTOR_SIZE_64, ">0"}, + applyIfCPUFeatureAnd = {"avx512_vbmi", "true", "avx512f", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public static void selectFromTwoVector_Byte512() { + ByteSelectFromTwoVectorKernel(ByteVector.SPECIES_512, ba, bb, bindex[3]); + } + + // Test SelectFromTwoVector operation for Shorts + @ForceInline + public static void ShortSelectFromTwoVectorKernel(VectorSpecies SPECIES, short[] sa, + short[] sb, short[] sindex) { + for (int i = 0; i < SPECIES.loopBound(sa.length); i += SPECIES.length()) { + ShortVector.fromArray(SPECIES, sindex, i) + .selectFrom(ShortVector.fromArray(SPECIES, sa, i), + ShortVector.fromArray(SPECIES, sb, i)) + .intoArray(sres, i); + } + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_4, ">0"}, + applyIfCPUFeature = {"asimd", "true"}, + applyIf = {"MaxVectorSize", ">=8"}) + public static void selectFromTwoVector_Short64() { + ShortSelectFromTwoVectorKernel(ShortVector.SPECIES_64, sa, sb, sindex[0]); + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=16"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeature = {"sve2", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeatureAnd = {"avx512bw", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + public static void selectFromTwoVector_Short128() { + ShortSelectFromTwoVectorKernel(ShortVector.SPECIES_128, sa, sb, sindex[1]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_16}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_16, ">0"}, + applyIfCPUFeature = {"sve2", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_16, ">0"}, + applyIfCPUFeatureAnd = {"avx512bw", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + public static void selectFromTwoVector_Short256() { + ShortSelectFromTwoVectorKernel(ShortVector.SPECIES_256, sa, sb, sindex[2]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_32}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_32, ">0"}, + applyIfCPUFeature = {"sve2", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VS, IRNode.VECTOR_SIZE_32, ">0"}, + applyIfCPUFeatureAnd = {"avx512bw", "true", "avx512f", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public static void selectFromTwoVector_Short512() { + ShortSelectFromTwoVectorKernel(ShortVector.SPECIES_512, sa, sb, sindex[3]); + } + + // Test SelectFromTwoVector operation for Ints + @ForceInline + public static void IntSelectFromTwoVectorKernel(VectorSpecies SPECIES, int[] ia, + int[] ib, int[] iindex) { + for (int i = 0; i < SPECIES.loopBound(ia.length); i += SPECIES.length()) { + IntVector.fromArray(SPECIES, iindex, i) + .selectFrom(IntVector.fromArray(SPECIES, ia, i), + IntVector.fromArray(SPECIES, ib, i)) + .intoArray(ires, i); + } + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VI, IRNode.VECTOR_SIZE_2, ">0"}, + applyIfCPUFeatureOr = {"asimd", "true"}, + applyIf = {"MaxVectorSize", ">=8"}) + public static void selectFromTwoVector_Int64() { + IntSelectFromTwoVectorKernel(IntVector.SPECIES_64, ia, ib, iindex[0]); + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VI, IRNode.VECTOR_SIZE_4, ">0"}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=16"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VI, IRNode.VECTOR_SIZE_4, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + public static void selectFromTwoVector_Int128() { + IntSelectFromTwoVectorKernel(IntVector.SPECIES_128, ia, ib, iindex[1]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VI, IRNode.VECTOR_SIZE_8}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VI, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + public static void selectFromTwoVector_Int256() { + IntSelectFromTwoVectorKernel(IntVector.SPECIES_256, ia, ib, iindex[2]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VI, IRNode.VECTOR_SIZE_16}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VI, IRNode.VECTOR_SIZE_16, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512f", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public static void selectFromTwoVector_Int512() { + IntSelectFromTwoVectorKernel(IntVector.SPECIES_512, ia, ib, iindex[3]); + } + + // Test SelectFromTwoVector operation for Floats + @ForceInline + public static void FloatSelectFromTwoVectorKernel(VectorSpecies SPECIES, float[] fa, + float[] fb, float[] findex) { + for (int i = 0; i < SPECIES.loopBound(ia.length); i += SPECIES.length()) { + FloatVector.fromArray(SPECIES, findex, i) + .selectFrom(FloatVector.fromArray(SPECIES, fa, i), + FloatVector.fromArray(SPECIES, fb, i)) + .intoArray(fres, i); + } + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VF, IRNode.VECTOR_SIZE_2, ">0"}, + applyIfCPUFeatureOr = {"asimd", "true"}, + applyIf = {"MaxVectorSize", ">=8"}) + public static void selectFromTwoVector_Float64() { + FloatSelectFromTwoVectorKernel(FloatVector.SPECIES_64, fa, fb, findex[0]); + } + + @Test + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VF, IRNode.VECTOR_SIZE_4, ">0"}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=16"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VF, IRNode.VECTOR_SIZE_4, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + public static void selectFromTwoVector_Float128() { + FloatSelectFromTwoVectorKernel(FloatVector.SPECIES_128, fa, fb, findex[1]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VF, IRNode.VECTOR_SIZE_8}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VF, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + public static void selectFromTwoVector_Float256() { + FloatSelectFromTwoVectorKernel(FloatVector.SPECIES_256, fa, fb, findex[2]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VF, IRNode.VECTOR_SIZE_16}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VF, IRNode.VECTOR_SIZE_16, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512f", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public static void selectFromTwoVector_Float512() { + FloatSelectFromTwoVectorKernel(FloatVector.SPECIES_512, fa, fb, findex[3]); + } + + // Test SelectFromTwoVector operation for Doubles + @ForceInline + public static void DoubleSelectFromTwoVectorKernel(VectorSpecies SPECIES, double[] da, + double[] db, double[] dindex) { + for (int i = 0; i < SPECIES.loopBound(ia.length); i += SPECIES.length()) { + DoubleVector.fromArray(SPECIES, dindex, i) + .selectFrom(DoubleVector.fromArray(SPECIES, da, i), + DoubleVector.fromArray(SPECIES, db, i)) + .intoArray(dres, i); + } + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VD, IRNode.VECTOR_SIZE_2}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=16"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VD, IRNode.VECTOR_SIZE_2, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + public static void selectFromTwoVector_Double128() { + DoubleSelectFromTwoVectorKernel(DoubleVector.SPECIES_128, da, db, dindex[0]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VD, IRNode.VECTOR_SIZE_4}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VD, IRNode.VECTOR_SIZE_4, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + public static void selectFromTwoVector_Double256() { + DoubleSelectFromTwoVectorKernel(DoubleVector.SPECIES_256, da, db, dindex[1]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VD, IRNode.VECTOR_SIZE_8}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VD, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512f", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public static void selectFromTwoVector_Double512() { + DoubleSelectFromTwoVectorKernel(DoubleVector.SPECIES_512, da, db, dindex[2]); + } + + // Test SelectFromTwoVector operation for Longs + @ForceInline + public static void LongSelectFromTwoVectorKernel(VectorSpecies SPECIES, long[] la, + long[] lb, long[] lindex) { + for (int i = 0; i < SPECIES.loopBound(ia.length); i += SPECIES.length()) { + LongVector.fromArray(SPECIES, lindex, i) + .selectFrom(LongVector.fromArray(SPECIES, la, i), + LongVector.fromArray(SPECIES, lb, i)) + .intoArray(lres, i); + } + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VL, IRNode.VECTOR_SIZE_2}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=16"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VL, IRNode.VECTOR_SIZE_2, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=16"}) + public static void selectFromTwoVector_Long128() { + LongSelectFromTwoVectorKernel(LongVector.SPECIES_128, la, lb, lindex[0]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VL, IRNode.VECTOR_SIZE_4}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=32"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VL, IRNode.VECTOR_SIZE_4, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512vl", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) + public static void selectFromTwoVector_Long256() { + LongSelectFromTwoVectorKernel(LongVector.SPECIES_256, la, lb, lindex[1]); + } + + @Test + @IR(failOn = {IRNode.SELECT_FROM_TWO_VECTOR_VL, IRNode.VECTOR_SIZE_8}, + applyIfCPUFeatureAnd = {"asimd", "true", "sve2", "false"}, + applyIf = {"MaxVectorSize", ">=64"}) + @IR(counts = {IRNode.SELECT_FROM_TWO_VECTOR_VL, IRNode.VECTOR_SIZE_8, ">0"}, + applyIfCPUFeatureOr = {"sve2", "true", "avx512f", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public static void selectFromTwoVector_Long512() { + LongSelectFromTwoVectorKernel(LongVector.SPECIES_512, la, lb, lindex[2]); + } + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorErgonomics.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorErgonomics.java index 4f9f8ca3bd2..0fd586f8d6e 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorErgonomics.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorErgonomics.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java new file mode 100644 index 00000000000..e66b16f053b --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCastIdentityTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8356760 +* @library /test/lib / +* @summary Optimize VectorMask.fromLong for all-true/all-false cases +* @modules jdk.incubator.vector +* +* @run driver compiler.vectorapi.VectorMaskCastIdentityTest +*/ + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import java.util.Random; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +public class VectorMaskCastIdentityTest { + private static final boolean[] mr = new boolean[128]; // 128 is large enough + private static final Random rd = Utils.getRandomInstance(); + static { + for (int i = 0; i < mr.length; i++) { + mr[i] = rd.nextBoolean(); + } + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"asimd", "true"}) + public static int testTwoCastToDifferentType() { + // The types before and after the two casts are not the same, so the cast cannot be eliminated. + VectorMask mFloat64 = VectorMask.fromArray(FloatVector.SPECIES_64, mr, 0); + VectorMask mDouble128 = mFloat64.cast(DoubleVector.SPECIES_128); + VectorMask mInt64 = mDouble128.cast(IntVector.SPECIES_64); + return mInt64.trueCount(); + } + + @Run(test = "testTwoCastToDifferentType") + public static void testTwoCastToDifferentType_runner() { + int count = testTwoCastToDifferentType(); + VectorMask mFloat64 = VectorMask.fromArray(FloatVector.SPECIES_64, mr, 0); + Asserts.assertEquals(count, mFloat64.trueCount()); + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 2" }, applyIfCPUFeatureOr = {"avx2", "true"}) + public static int testTwoCastToDifferentType2() { + // The types before and after the two casts are not the same, so the cast cannot be eliminated. + VectorMask mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0); + VectorMask mDouble256 = mInt128.cast(DoubleVector.SPECIES_256); + VectorMask mShort64 = mDouble256.cast(ShortVector.SPECIES_64); + return mShort64.trueCount(); + } + + @Run(test = "testTwoCastToDifferentType2") + public static void testTwoCastToDifferentType2_runner() { + int count = testTwoCastToDifferentType2(); + VectorMask mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0); + Asserts.assertEquals(count, mInt128.trueCount()); + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 0" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + public static int testTwoCastToSameType() { + // The types before and after the two casts are the same, so the cast will be eliminated. + VectorMask mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0); + VectorMask mFloat128 = mInt128.cast(FloatVector.SPECIES_128); + VectorMask mInt128_2 = mFloat128.cast(IntVector.SPECIES_128); + return mInt128_2.trueCount(); + } + + @Run(test = "testTwoCastToSameType") + public static void testTwoCastToSameType_runner() { + int count = testTwoCastToSameType(); + VectorMask mInt128 = VectorMask.fromArray(IntVector.SPECIES_128, mr, 0); + Asserts.assertEquals(count, mInt128.trueCount()); + } + + @Test + @IR(counts = { IRNode.VECTOR_MASK_CAST, "= 1" }, applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + public static int testOneCastToDifferentType() { + // The types before and after the only cast are different, the cast will not be eliminated. + VectorMask mFloat128 = VectorMask.fromArray(FloatVector.SPECIES_128, mr, 0).not(); + VectorMask mInt128 = mFloat128.cast(IntVector.SPECIES_128); + return mInt128.trueCount(); + } + + @Run(test = "testOneCastToDifferentType") + public static void testOneCastToDifferentType_runner() { + int count = testOneCastToDifferentType(); + VectorMask mInt128 = VectorMask.fromArray(FloatVector.SPECIES_128, mr, 0).not(); + Asserts.assertEquals(count, mInt128.trueCount()); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskFromLongTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskFromLongTest.java new file mode 100644 index 00000000000..a97ce2f9162 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskFromLongTest.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8356760 +* @library /test/lib / +* @summary Optimize VectorMask.fromLong for all-true/all-false cases +* @modules jdk.incubator.vector +* +* @run driver compiler.vectorapi.VectorMaskFromLongTest +*/ + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +public class VectorMaskFromLongTest { + static final VectorSpecies B_SPECIES = ByteVector.SPECIES_MAX; + static final VectorSpecies S_SPECIES = ShortVector.SPECIES_MAX; + static final VectorSpecies I_SPECIES = IntVector.SPECIES_MAX; + static final VectorSpecies F_SPECIES = FloatVector.SPECIES_MAX; + static final VectorSpecies L_SPECIES = LongVector.SPECIES_MAX; + static final VectorSpecies D_SPECIES = DoubleVector.SPECIES_MAX; + + static boolean[] mr = new boolean[B_SPECIES.length()]; + + @ForceInline + public static void maskFromLongKernel(VectorSpecies species, long inputLong) { + VectorMask.fromLong(species, inputLong).intoArray(mr, 0); + } + + @DontInline + public static void verifyMaskFromLong(VectorSpecies species, long inputLong) { + for (int i = 0; i < species.length(); i++) { + long expectedValue = (inputLong >>> i) & 1L; + if (mr[i] != (expectedValue == 1L)) { + Asserts.fail("Mask bit " + i + " is expected to be " + expectedValue + + " but was " + mr[i] + " for long " + inputLong); + } + } + } + + @ForceInline + public static void testMaskFromLong(VectorSpecies species, long inputLong ) { + maskFromLongKernel(species, inputLong); + verifyMaskFromLong(species, inputLong); + } + + @ForceInline + public static void testMaskFromLongMaskAll(VectorSpecies species) { + int vlen = species.length(); + long inputLong = 0L; + testMaskFromLong(species, inputLong); + + inputLong = vlen >= 64 ? 0L : (0x1L << vlen); + testMaskFromLong(species, inputLong); + + inputLong = -1L; + testMaskFromLong(species, inputLong); + + inputLong = (-1L >>> (64 - vlen)); + testMaskFromLong(species, inputLong); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_B, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_B, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongMaskAllByte() { + testMaskFromLongMaskAll(B_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_S, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_S, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongMaskAllShort() { + testMaskFromLongMaskAll(S_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_I, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_I, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongMaskAllInt() { + testMaskFromLongMaskAll(I_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_L, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_L, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongMaskAllLong() { + testMaskFromLongMaskAll(L_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_I, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_I, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongMaskAllFloat() { + testMaskFromLongMaskAll(F_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_L, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_L, "> 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongMaskAllDouble() { + testMaskFromLongMaskAll(D_SPECIES); + } + + // Tests for general input long values + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_B, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_B, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongByte() { + // Test the case where some but not all bits are set. + testMaskFromLong(B_SPECIES, (-1L >>> (64 - B_SPECIES.length()))-1); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_S, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_S, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongShort() { + // Test the case where some but not all bits are set. + testMaskFromLong(S_SPECIES, (-1L >>> (64 - S_SPECIES.length()))-1); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongInt() { + // Test the case where some but not all bits are set. + testMaskFromLong(I_SPECIES, (-1L >>> (64 - I_SPECIES.length()))-1); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongLong() { + // Test the case where some but not all bits are set. + testMaskFromLong(L_SPECIES, (-1L >>> (64 - L_SPECIES.length()))-1); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongFloat() { + // Test the case where some but not all bits are set. + testMaskFromLong(F_SPECIES, (-1L >>> (64 - F_SPECIES.length()))-1); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "> 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskFromLongDouble() { + // Test the case where some but not all bits are set. + testMaskFromLong(D_SPECIES, (-1L >>> (64 - D_SPECIES.length()))-1); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskToLongTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskToLongTest.java new file mode 100644 index 00000000000..3201d593efe --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskToLongTest.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8356760 +* @library /test/lib / +* @summary Optimize VectorMask.fromLong for all-true/all-false cases +* @modules jdk.incubator.vector +* +* @run driver compiler.vectorapi.VectorMaskToLongTest +*/ + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +public class VectorMaskToLongTest { + static final VectorSpecies B_SPECIES = ByteVector.SPECIES_MAX; + static final VectorSpecies S_SPECIES = ShortVector.SPECIES_MAX; + static final VectorSpecies I_SPECIES = IntVector.SPECIES_MAX; + static final VectorSpecies F_SPECIES = FloatVector.SPECIES_MAX; + static final VectorSpecies L_SPECIES = LongVector.SPECIES_MAX; + static final VectorSpecies D_SPECIES = DoubleVector.SPECIES_MAX; + + @DontInline + public static void verifyMaskToLong(VectorSpecies species, long inputLong, long got) { + long expected = inputLong & (-1L >>> (64 - species.length())); + Asserts.assertEquals(expected, got, "for input long " + inputLong); + } + + @ForceInline + public static void testMaskAllToLong(VectorSpecies species) { + int vlen = species.length(); + long inputLong = 0L; + // fromLong is expected to be converted to maskAll. + long got = VectorMask.fromLong(species, inputLong).toLong(); + verifyMaskToLong(species, inputLong, got); + + inputLong = vlen >= 64 ? 0 : (0x1L << vlen); + got = VectorMask.fromLong(species, inputLong).toLong(); + verifyMaskToLong(species, inputLong, got); + + inputLong = -1L; + got = VectorMask.fromLong(species, inputLong).toLong(); + verifyMaskToLong(species, inputLong, got); + + inputLong = (-1L >>> (64 - vlen)); + got = VectorMask.fromLong(species, inputLong).toLong(); + verifyMaskToLong(species, inputLong, got); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_B, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_B, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskAllToLongByte() { + testMaskAllToLong(B_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_S, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_S, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskAllToLongShort() { + testMaskAllToLong(S_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskAllToLongInt() { + testMaskAllToLong(I_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskAllToLongLong() { + testMaskAllToLong(L_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_I, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskAllToLongFloat() { + testMaskAllToLong(F_SPECIES); + } + + @Test + @IR(counts = { IRNode.MASK_ALL, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve", "true", "avx512", "true", "rvv", "true" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + @IR(counts = { IRNode.REPLICATE_L, "= 0", + IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureAnd = { "avx2", "true", "avx512", "false" }) + public static void testMaskAllToLongDouble() { + testMaskAllToLong(D_SPECIES); + } + + // General cases for (VectorMaskToLong (VectorLongToMask (x))) => x. + + @Test + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx2", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static void testFromLongToLongByte() { + // Test the case where some but not all bits are set. + long inputLong = (-1L >>> (64 - B_SPECIES.length()))-1; + long got = VectorMask.fromLong(B_SPECIES, inputLong).toLong(); + verifyMaskToLong(B_SPECIES, inputLong, got); + } + + @Test + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx2", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static void testFromLongToLongShort() { + // Test the case where some but not all bits are set. + long inputLong = (-1L >>> (64 - S_SPECIES.length()))-1; + long got = VectorMask.fromLong(S_SPECIES, inputLong).toLong(); + verifyMaskToLong(S_SPECIES, inputLong, got); + } + + @Test + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx2", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static void testFromLongToLongInt() { + // Test the case where some but not all bits are set. + long inputLong = (-1L >>> (64 - I_SPECIES.length()))-1; + long got = VectorMask.fromLong(I_SPECIES, inputLong).toLong(); + verifyMaskToLong(I_SPECIES, inputLong, got); + } + + @Test + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 0" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx2", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static void testFromLongToLongLong() { + // Test the case where some but not all bits are set. + long inputLong = (-1L >>> (64 - L_SPECIES.length()))-1; + long got = VectorMask.fromLong(L_SPECIES, inputLong).toLong(); + verifyMaskToLong(L_SPECIES, inputLong, got); + } + + @Test + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 1", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx2", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static void testFromLongToLongFloat() { + // Test the case where some but not all bits are set. + long inputLong = (-1L >>> (64 - F_SPECIES.length()))-1; + long got = VectorMask.fromLong(F_SPECIES, inputLong).toLong(); + verifyMaskToLong(F_SPECIES, inputLong, got); + } + + @Test + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 1", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureOr = { "sve2", "true", "avx2", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_LONG_TO_MASK, "= 0", + IRNode.VECTOR_MASK_TO_LONG, "= 1" }, + applyIfCPUFeatureAnd = { "asimd", "true", "sve", "false" }) + public static void testFromLongToLongDouble() { + // Test the case where some but not all bits are set. + long inputLong = (-1L >>> (64 - D_SPECIES.length()))-1; + long got = VectorMask.fromLong(D_SPECIES, inputLong).toLong(); + verifyMaskToLong(D_SPECIES, inputLong, got); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorReinterpretTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorReinterpretTest.java index b453311f857..2f9b97b56d7 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorReinterpretTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorReinterpretTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java index 5985367b265..2256fd90e35 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java @@ -389,6 +389,45 @@ public class TestSubwordTruncation { } } + @Test + @IR(counts = { IRNode.CMP_LT_MASK, ">0" }) + @Arguments(setup = "setupByteArray") + public Object[] testCmpLTMask(byte[] in) { + char[] res = new char[SIZE]; + + for (int i = 0; i < SIZE; i++) { + res[i] = (char) (in[i] >= 0 ? in[i] : 256 + in[i]); + } + + return new Object[] { in, res }; + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = { IRNode.ROUND_F, ">0" }) + @Arguments(setup = "setupByteArray") + public Object[] testRoundF(byte[] in) { + short[] res = new short[SIZE]; + + for (int i = 0; i < SIZE; i++) { + res[i] = (short) Math.round(in[i] * 10.F); + } + + return new Object[] { in, res }; + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = { IRNode.ROUND_D, ">0" }) + @Arguments(setup = "setupByteArray") + public Object[] testRoundD(byte[] in) { + short[] res = new short[SIZE]; + + for (int i = 0; i < SIZE; i++) { + res[i] = (short) Math.round(in[i] * 10.0); + } + + return new Object[] { in, res }; + } + public static void main(String[] args) { TestFramework.run(); } diff --git a/test/hotspot/jtreg/containers/docker/TestMemoryWithCgroupV1.java b/test/hotspot/jtreg/containers/docker/TestMemoryWithCgroupV1.java index f80a83842c9..3340f9de03c 100644 --- a/test/hotspot/jtreg/containers/docker/TestMemoryWithCgroupV1.java +++ b/test/hotspot/jtreg/containers/docker/TestMemoryWithCgroupV1.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java b/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java index 5a705ffe8ed..3aec113990c 100644 --- a/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java +++ b/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2025, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java index 94f65a4328f..a4fadc185d2 100644 --- a/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java +++ b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java @@ -147,6 +147,7 @@ class TestCodeCacheUnloadDuringConcCycleRunner { } public static void main(String[] args) throws Exception { + System.out.println("Running to breakpoint: " + args[0]); try { WB.concurrentGCAcquireControl(); WB.concurrentGCRunTo(args[0]); @@ -157,9 +158,12 @@ class TestCodeCacheUnloadDuringConcCycleRunner { WB.concurrentGCRunToIdle(); } finally { + // Make sure that the marker we use to find the expected log message is printed + // before we release whitebox control, i.e. before the expected garbage collection + // can start. + System.out.println(TestCodeCacheUnloadDuringConcCycle.AFTER_FIRST_CYCLE_MARKER); WB.concurrentGCReleaseControl(); } - System.out.println(TestCodeCacheUnloadDuringConcCycle.AFTER_FIRST_CYCLE_MARKER); Thread.sleep(1000); triggerCodeCacheGC(); } diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/GC.java b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/GC.java deleted file mode 100644 index 3a479612bba..00000000000 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/GC.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.g1.humongousObjects.objectGraphTest; - -import gc.testlibrary.Helpers; -import jdk.test.lib.Asserts; -import jdk.test.whitebox.WhiteBox; - -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; - -/** - * Provides methods to initiate GC of requested type and - * checks for states of humongous and non-humongous soft/weak externally - * referenced objects after GCs - */ -public enum GC { - CMC { - @Override - public Runnable get() { - return () -> { - WHITE_BOX.g1RunConcurrentGC(); - }; - } - - public Consumer> getChecker() { - return getCheckerImpl(false, false, true, false); - } - - @Override - public List shouldContain() { - return Arrays.asList(GCTokens.WB_INITIATED_CMC); - } - - @Override - public List shouldNotContain() { - return Arrays.asList(GCTokens.WB_INITIATED_YOUNG_GC, GCTokens.WB_INITIATED_MIXED_GC, - GCTokens.FULL_GC, GCTokens.YOUNG_GC); - } - }, - - CMC_NO_SURV_ROOTS { - @Override - public Runnable get() { - return () -> { - WHITE_BOX.concurrentGCAcquireControl(); - try { - WHITE_BOX.youngGC(); - WHITE_BOX.youngGC(); - WHITE_BOX.concurrentGCRunTo(WHITE_BOX.AFTER_MARKING_STARTED); - WHITE_BOX.concurrentGCRunToIdle(); - } finally { - WHITE_BOX.concurrentGCReleaseControl(); - } - }; - } - - public Consumer> getChecker() { - return getCheckerImpl(true, false, true, false); - } - - @Override - public List shouldContain() { - return Arrays.asList(GCTokens.WB_INITIATED_CMC); - } - - @Override - public List shouldNotContain() { - return Arrays.asList(GCTokens.WB_INITIATED_MIXED_GC, - GCTokens.FULL_GC, GCTokens.YOUNG_GC); - } - }, - - YOUNG_GC { - @Override - public Runnable get() { - return WHITE_BOX::youngGC; - } - - public Consumer> getChecker() { - return getCheckerImpl(false, false, true, false); - } - - @Override - public List shouldContain() { - return Arrays.asList(GCTokens.WB_INITIATED_YOUNG_GC); - } - - @Override - public List shouldNotContain() { - return Arrays.asList(GCTokens.WB_INITIATED_MIXED_GC, GCTokens.FULL_GC, GCTokens.WB_INITIATED_CMC, - GCTokens.CMC, GCTokens.YOUNG_GC); - } - }, - - FULL_GC { - @Override - public Runnable get() { - return System::gc; - } - - public Consumer> getChecker() { - return getCheckerImpl(true, false, true, false); - } - - @Override - public List shouldContain() { - return Arrays.asList(GCTokens.FULL_GC); - } - - @Override - public List shouldNotContain() { - return Arrays.asList(GCTokens.WB_INITIATED_YOUNG_GC, GCTokens.WB_INITIATED_MIXED_GC, - GCTokens.WB_INITIATED_CMC, GCTokens.CMC, GCTokens.YOUNG_GC); - } - }, - - MIXED_GC { - @Override - public Runnable get() { - return () -> { - WHITE_BOX.concurrentGCAcquireControl(); - try { - WHITE_BOX.youngGC(); - WHITE_BOX.youngGC(); - WHITE_BOX.concurrentGCRunTo(WHITE_BOX.AFTER_MARKING_STARTED); - WHITE_BOX.concurrentGCRunToIdle(); - WHITE_BOX.youngGC(); - // Provoking Mixed GC - WHITE_BOX.youngGC();// second evacuation pause will be mixed - } finally { - WHITE_BOX.concurrentGCReleaseControl(); - } - }; - } - - public Consumer> getChecker() { - return getCheckerImpl(true, false, true, false); - } - - @Override - public List shouldContain() { - return Arrays.asList(GCTokens.WB_INITIATED_CMC); - } - - @Override - public List shouldNotContain() { - return Arrays.asList(GCTokens.YOUNG_GC); - } - }, - - FULL_GC_MEMORY_PRESSURE { - @Override - public Runnable get() { - return WHITE_BOX::fullGC; - } - - public Consumer> getChecker() { - return getCheckerImpl(true, true, true, true); - } - - @Override - public List shouldContain() { - return Arrays.asList(GCTokens.FULL_GC_MEMORY_PRESSURE); - } - - @Override - public List shouldNotContain() { - return Arrays.asList(GCTokens.WB_INITIATED_YOUNG_GC, GCTokens.WB_INITIATED_MIXED_GC, - GCTokens.WB_INITIATED_CMC, GCTokens.CMC, GCTokens.YOUNG_GC, GCTokens.FULL_GC); - } - }; - - protected String getErrorMessage(ReferenceInfo ref, boolean expectedNull, String gcType) { - return String.format("Externally effectively %s referenced %shumongous object was%s deleted after %s", - (ref.softlyReachable ? "soft" : "weak"), (ref.effectiveHumongous ? "" : "non-"), - (expectedNull ? " not" : ""), gcType); - } - - protected Consumer> getCaseCheck(boolean expectedNull) { - return expectedNull - ? r -> Asserts.assertNull(r.reference.get(), getErrorMessage(r, true, name())) - : r -> Asserts.assertNotNull(r.reference.get(), getErrorMessage(r, false, name())); - } - - protected Consumer> getCheckerImpl(boolean weakH, boolean softH, - boolean weakS, boolean softS) { - return new Checker(getCaseCheck(weakH), getCaseCheck(softH), getCaseCheck(weakS), getCaseCheck(softS)); - } - - protected String getGcLogName(String prefix) { - return prefix + "_" + name() + ".gc.log"; - } - - private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); - - /** - * @return method to initiate GC - */ - public abstract Runnable get(); - - /** - * @return checker for objects' states after GC - */ - public abstract Consumer> getChecker(); - - /** - * @return list of tokens that should be contained in gc log after gc of specified type - */ - public abstract List shouldContain(); - - /** - * @return list of tokens that should not be contained in gc log after gc of specified type - */ - public abstract List shouldNotContain(); - - - /** - * Checks object' state after gc - * Contains 4 Consumers which are called depending on humongous/non-humongous and - * external weak/soft referenced objects - */ - private static class Checker implements Consumer> { - // 4 consumers with checks for (humongous /simple objects)*(weak/soft referenced) - final Consumer> weakHumongousCheck; - final Consumer> softHumongousCheck; - final Consumer> weakSimpleCheck; - final Consumer> softSimpleCheck; - - public Checker(Consumer> weakHumongousCheck, - Consumer> softHumongousCheck, - Consumer> weakSimpleCheck, - Consumer> softSimpleCheck) { - this.weakHumongousCheck = weakHumongousCheck; - this.softHumongousCheck = softHumongousCheck; - this.weakSimpleCheck = weakSimpleCheck; - this.softSimpleCheck = softSimpleCheck; - } - - public void accept(ReferenceInfo ref) { - - System.out.println("reference.get() returned " + ref.reference.get()); - if (ref.effectiveHumongous && ref.softlyReachable) { - System.out.println("soft and humongous"); - softHumongousCheck.accept(ref); - } - - if (ref.effectiveHumongous && !ref.softlyReachable) { - System.out.println("weak and humongous"); - weakHumongousCheck.accept(ref); - - } - - if (!ref.effectiveHumongous && ref.softlyReachable) { - System.out.println("soft and non-humongous"); - softSimpleCheck.accept(ref); - } - - if (!ref.effectiveHumongous && !ref.softlyReachable) { - System.out.println("weak and non-humongous"); - weakSimpleCheck.accept(ref); - } - } - } - -} diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/ObjectGraph.java b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/ObjectGraph.java deleted file mode 100644 index 8bffda16a1c..00000000000 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/ObjectGraph.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.g1.humongousObjects.objectGraphTest; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public class ObjectGraph { - - private ObjectGraph() { - } - - public enum ReferenceType { - NONE, - WEAK, - SOFT, - STRONG; - } - - /** - * Performs operation on all nodes that are reachable from initial ones - * - * @param nodes initial nodes - * @param operation operation - */ - public static void propagateTransitiveProperty(Set nodes, Consumer operation) { - Deque roots = new ArrayDeque<>(); - nodes.stream().forEach(roots::push); - ObjectGraph.enumerateAndMark(roots, operation); - } - - /** - * Connects graph's vertexes with single-directed (vertex -> neighbour) link - * - * @param vertex who is connected - * @param neighbour connected to whom - */ - private static void connectVertexes(Object[] vertex, Object[] neighbour) { - - // check if vertex array is full - if (vertex[vertex.length - 1] != null) { - throw new Error("Array is full and no connections could be added"); - } - int i = 0; - while (vertex[i] != null) { - ++i; - } - vertex[i] = neighbour; - } - - - /** - * Builds object graph using description from list of parsed nodes. Graph uses Object[] as nodes, first n elements - * of array are links to connected nodes, others are null. Then runs visitors on generated graph - * - * @param parsedNodes list of nodes' description - * @param visitors visitors that will visit each node of generated graph - * @param humongousAllocationSize size of humongous node - * @param simpleAllocationSize size of simple (non-humongous) node - * @return root reference to generated graph - */ - public static Object[] generateObjectNodes(List parsedNodes, - Map, - BiConsumer> visitors, - int humongousAllocationSize, int simpleAllocationSize) { - - Object[][] objectNodes = new Object[parsedNodes.size()][]; - - // Allocating nodes on Object[] - for (int i = 0; i < parsedNodes.size(); ++i) { - objectNodes[i] = new Object[(parsedNodes.get(i).isHumongous ? - humongousAllocationSize : simpleAllocationSize)]; - } - - // Connecting nodes on allocated on Object[] - for (int i = 0; i < parsedNodes.size(); ++i) { - for (int j = 0; j < parsedNodes.get(i).getConnectedTo().size(); ++j) { - connectVertexes(objectNodes[i], objectNodes[parsedNodes.get(i).getConnectedTo().get(j)]); - } - } - - // Calling visitors - visitors.entrySet() - .stream() - .forEach( - entry -> parsedNodes.stream() - .filter(parsedNode -> entry.getKey().test(parsedNode)) - .forEach(node -> entry.getValue().accept(node, objectNodes)) - ); - - return objectNodes[0]; - } - - /** - * Enumerates graph starting with provided vertexes. All vertexes that are reachable from the provided ones are - * marked - * - * @param markedParents provided vertexes - * @param markVertex lambda which marks vertexes - */ - public static void enumerateAndMark(Deque markedParents, - Consumer markVertex) { - Map isVisited = new HashMap<>(); - while (!markedParents.isEmpty()) { - Object[] vertex = markedParents.pop(); - if (vertex == null || isVisited.containsKey(vertex)) { - continue; - } - isVisited.put(vertex, true); - markVertex.accept(vertex); - - for (int i = 0; i < vertex.length; ++i) { - if (vertex[i] == null) { - break; - } - markedParents.add((Object[]) vertex[i]); - } - } - } - -} diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/README b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/README deleted file mode 100644 index 5366fc876be..00000000000 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/README +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -The test checks that after different type of GC unreachable objects behave as expected: - -1. Young GC - weakly referenced non-humongous objects are collected, other objects are not collected. - -2. Full GC - weakly referenced non-humongous and humongous objects are collected, softly referenced non-humongous and - humongous objects are not collected. - -3. Full GC with memory pressure - weakly and softly referenced non-humongous and humongous objects are collected. - -4. CMC - weakly referenced non-humongous objects are collected, other objects are not collected since weak references - from Young Gen is handled as strong during CMC. - -5. CMC_NO_SURV_ROOTS - weakly referenced non-humongous and humongous objects are collected, softly referenced - non-humongous and humongous objects are not collected since we make 2 Young GC to promote all - weak references to Old Gen. - -6. Mixed GC - weakly referenced non-humongous and humongous objects are collected, softly referenced non-humongous and - humongous objects are not collected. - -The test gets gc type as a command line argument. -Then the test allocates object graph in heap (currently testing scenarios are pre-generated and stored in -TestcaseData.getPregeneratedTestcases()) with TestObjectGraphAfterGC::allocateObjectGraph. - -Since we are testing humongous objects we need pretty unusual nodes - arrays of Object. -We need this since only large enough array could be Humongous object (in fact class with huge amount of fields is -humongous too but it's for other tests). -ObjectGraph class generates object graph with Object[] nodes. It also provides a way to collect -information about each node using "visitor" pattern. - -Using visitors we build Set of ReferenceInfo instances which contains the following information: -reference - external weak/soft reference to graph's node -graphId and nodeId - graph's and node's ids - we need this for error handling -softlyReachable - is node effectively referenced by external soft reference. It could be when external -soft reference or when this node is reachable from node that exteranally referenced by soft reference -effectiveHumongous - if node behaves effectively humongous. It could be when node is humongous -or when this node is reachable from humongous node. - -When we leave TestObjectGraphAfterGC::allocateObjectGraph we make graph reachable only with references from Set of -ReferenceInfo instances. - -We run specified gc and check that each instance of ReferenceInfo set behaves as expected. -Then we check that gc log file contains expected tokens and doesn't contain tokens that it should not contain. diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/ReferenceInfo.java b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/ReferenceInfo.java deleted file mode 100644 index 2eb02b8b217..00000000000 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/ReferenceInfo.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.g1.humongousObjects.objectGraphTest; - -import java.lang.ref.Reference; - -/** - * Immutable structure that holds the following information about graph's node - * reference - weak/soft reference to graph's node - * graphId and nodeId - graph's and node's ids - we need this for error handling - * softlyReachable - is node effectively referenced by external soft reference. It could be when external - * soft reference or when this node is reachable from node that externally referenced by soft reference - * effectiveHumongous - if node behaves effectively humongous. It could be when node is humongous - * or when this node is reachable from humongous node. - * - * @param - actual type of node - */ -public class ReferenceInfo { - public final Reference reference; - public final String graphId; - public final String nodeId; - public final boolean softlyReachable; - public final boolean effectiveHumongous; - - public ReferenceInfo(Reference reference, String graphId, String nodeId, boolean softlyReachable, - boolean effectiveHumongous) { - this.reference = reference; - this.graphId = graphId; - this.nodeId = nodeId; - this.softlyReachable = softlyReachable; - this.effectiveHumongous = effectiveHumongous; - } - - @Override - public String toString() { - return String.format("Node %s is effectively %shumongous and effectively %ssoft referenced\n" - + "\tReference type is %s and it points to %s", nodeId, - (effectiveHumongous ? "" : "non-"), (softlyReachable ? "" : "non-"), - reference.getClass().getSimpleName(), reference.get()); - } -} diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java deleted file mode 100644 index 88633309690..00000000000 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.g1.humongousObjects.objectGraphTest; - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.whitebox.WhiteBox; - -import java.io.File; -import java.io.IOException; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; -import java.nio.file.Files; -import java.util.Map; -import java.util.HashMap; -import java.util.List; -import java.util.HashSet; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; - - -/** - * @test TestObjectGraphAfterGC - * @summary Checks that objects' graph behave as expected after gc - * @requires vm.gc.G1 - * @requires vm.opt.ExplicitGCInvokesConcurrent != true - * @library /test/lib / - * @modules java.management java.base/jdk.internal.misc - * @build jdk.test.whitebox.WhiteBox - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30000 -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 - * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_MIXED_GC.gc.log -XX:MaxTenuringThreshold=1 - * -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0 - * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC - * - * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log - * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC - * - * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC.gc.log - * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC - * - * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC_MEMORY_PRESSURE.gc.log - * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC_MEMORY_PRESSURE - * - * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC.gc.log -XX:MaxTenuringThreshold=16 - * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC - * - * @run main/othervm -Xms200M -Xmx200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC_NO_SURV_ROOTS.gc.log -XX:MaxTenuringThreshold=1 - * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC_NO_SURV_ROOTS - * - */ - -/** - * Checks that objects' graph behave as expected after gc - * See README file for detailed info on test's logic - */ -public class TestObjectGraphAfterGC { - - private static final int simpleAllocationSize = 1024; - - /** - * Entry point - * - * @param args - first argument - gc name - */ - public static void main(String[] args) { - - if (args.length < 1) { - throw new Error("Expected gc name wasn't provided as command line argument"); - } - - GC gcType = GC.valueOf(args[0].toUpperCase()); - - System.out.println("Testing " + gcType.name()); - - TestcaseData.getPregeneratedTestcases().stream().forEach(testcase -> { - System.out.println("Testcase: " + testcase); - - try { - TestObjectGraphAfterGC.doTesting(testcase, gcType.get(), gcType.getChecker(), - gcType.getGcLogName(TestObjectGraphAfterGC.class.getSimpleName()), gcType.shouldContain(), - gcType.shouldNotContain()); - } catch (IOException e) { - throw new Error("Problems trying to find or open " + TestObjectGraphAfterGC.class.getSimpleName() - + ".gc.log", e); - } - System.out.println(" Passed"); - }); - } - - /** - * Implements testing with 3 methods - allocateObjectGraph, checkResults and checkGCLog - * - * @param testcaseData testcase in the following notation: - * H - humongous node - * S - non-humongous node - * s - external soft reference - * w - external weak reference - * Hs->Sw - 1st node is humongous, externally soft referenced and strong references to - * non-humongous node 2 which is externally weak referenced - * H->1 - humongous node connects to the first node of chain - * @param doGC method that initiates gc - * @param checker consumer that checks node's state after gc and throws Error if it's wrong - * @param gcLogName name of gc log - * @param shouldContain list of tokens that should be contained in gc log - * @param shouldNotContain list of tokens that should not be contained in gc log - * @throws IOException if there are some issues with gc log - */ - private static void doTesting(String testcaseData, Runnable doGC, Consumer> checker, - String gcLogName, List shouldContain, List shouldNotContain) - throws IOException { - Set> nodeData = allocateObjectGraph(testcaseData); - doGC.run(); - checkResults(nodeData, checker); - checkGCLog(gcLogName, shouldContain, shouldNotContain); - } - - /** - * Allocates a number of objects of humongous and regular size and links then with strong references. - * How many objects to create, their size and links between them is encoded in the given parameters. - * As the result an object graph will be created. - * For the testing purpose for each created object (a graph node) an extra ReferenceInfo object will be created. - * The ReferenceInfo instances will contain either weak or soft reference to the graph node. - * - * @param testcaseData testcase in the - *

    - * H - humongous node - * S - non-humongous node - * s - external soft reference - * w - external weak reference - * Hs->Sw - 1st node is humongous, externally soft referenced and strong references to - * non-humongous node 2 which is externally weak referenced - * H->1 - humongous node connects to the first node of chain - * @return set of ReferenceInfo objects containing weak/soft reference to the graph node and other data on how - * objects should behave after gc - */ - private static Set> allocateObjectGraph(String testcaseData) { - Map nodeIds = new HashMap<>(); - Set humongousNodes = new HashSet<>(); - Set externalSoftReferenced = new HashSet<>(); - Set externalWeakReferenced = new HashSet<>(); - - Map, BiConsumer> visitors - = new HashMap<>(); - - visitors.put((parsedNode -> true), - (parsedNode, objects) -> nodeIds.put(objects[Integer.valueOf(parsedNode.id)], parsedNode.id) - ); - - visitors.put((parsedNode -> parsedNode.isHumongous), - (parsedNode, objects) -> humongousNodes.add(objects[Integer.valueOf(parsedNode.id)]) - ); - - visitors.put(parsedNode -> parsedNode.getReferencesTypes().stream(). - anyMatch(referenceType -> referenceType == ObjectGraph.ReferenceType.SOFT), - (parsedNode, objects) -> externalSoftReferenced.add(objects[Integer.valueOf(parsedNode.id)]) - ); - - visitors.put(parsedNode -> parsedNode.getReferencesTypes().stream(). - anyMatch(referenceType -> referenceType == ObjectGraph.ReferenceType.WEAK), - (parsedNode, objects) -> externalWeakReferenced.add(objects[Integer.valueOf(parsedNode.id)]) - ); - - List internalParsedNodes = TestcaseData.parse(testcaseData); - - Object[] root = ObjectGraph.generateObjectNodes(internalParsedNodes, visitors, - WhiteBox.getWhiteBox().g1RegionSize(), simpleAllocationSize); - - ObjectGraph.propagateTransitiveProperty(humongousNodes, humongousNodes::add); - Set effectiveSoftReferenced = new HashSet<>(); - ObjectGraph.propagateTransitiveProperty(externalSoftReferenced, effectiveSoftReferenced::add); - - // Create external references - ReferenceQueue referenceQueue = new ReferenceQueue<>(); - Set> externalRefs = new HashSet<>(); - - externalWeakReferenced.stream() - .forEach(objects -> externalRefs.add(new WeakReference<>(objects, referenceQueue))); - externalSoftReferenced.stream() - .forEach(objects -> externalRefs.add(new SoftReference<>(objects, referenceQueue))); - - return externalRefs.stream() - .map(ref -> new ReferenceInfo<>(ref, testcaseData, nodeIds.get(ref.get()), - effectiveSoftReferenced.contains(ref.get()), humongousNodes.contains(ref.get()))) - .collect(Collectors.toSet()); - - } - - /** - * Checks that object' state after gc is as expected - * - * @param nodeData array with information about nodes - * @param checker consumer that checks node's state after gc and throws Error if it's wrong - */ - private static void checkResults(Set> nodeData, Consumer> checker) { - nodeData.stream().forEach(checker::accept); - } - - /** - * Checks that gc log contains what we expected and does not contain what we didn't expect - * - * @param gcLogName gc log name - * @param shouldContain list of tokens that should be contained in gc log - * @param shouldNotContain list of tokens that should not be contained in gc log - * @throws IOException if there are some issues with gc log - */ - private static void checkGCLog(String gcLogName, List shouldContain, List shouldNotContain) - throws IOException { - - if (gcLogName == null) { - return; - } - String gcLog = new String(Files.readAllBytes(new File(gcLogName).toPath())); - - OutputAnalyzer outputAnalyzer = new OutputAnalyzer(gcLog, ""); - - shouldContain.stream().forEach(outputAnalyzer::shouldContain); - shouldNotContain.stream().forEach(outputAnalyzer::shouldNotContain); - } - -} diff --git a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestcaseData.java b/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestcaseData.java deleted file mode 100644 index 1e20a205068..00000000000 --- a/test/hotspot/jtreg/gc/g1/humongousObjects/objectGraphTest/TestcaseData.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.g1.humongousObjects.objectGraphTest; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -public final class TestcaseData { - /** - * Temporary node description used during parsing - */ - private static class InternalParsedNode { - public String id; - public final ArrayList connectedTo = new ArrayList<>(); - public final ArrayList connectedFrom = new ArrayList<>(); - public final List referencesTypes = new ArrayList<>(); - public boolean isHumongous; - } - - /** - * Immutable node description. - * Contains: - * Node id - * Humongous flag - * List of external references' types - * List of nodes connected to - */ - public static class FinalParsedNode { - public final String id; - public final boolean isHumongous; - private final List referencesTypes; - private final ArrayList connectedTo; - - - public FinalParsedNode(InternalParsedNode internalParsedNode) { - referencesTypes = internalParsedNode.referencesTypes; - connectedTo = internalParsedNode.connectedTo; - id = internalParsedNode.id; - isHumongous = internalParsedNode.isHumongous; - } - - public List getReferencesTypes() { - return Collections.unmodifiableList(referencesTypes); - } - - public List getConnectedTo() { - return Collections.unmodifiableList(connectedTo); - } - } - - /** - * @param testcaseDesc testcase in the following notation: - * H - humongous node - * S - non-humongous node - * s - external soft reference - * w - external weak reference - * Hs->Sw - 1st node is humongous, externally soft referenced and strong references to non-humongous node 2 which is - * externally weak referenced - * H->1 - humongous node connects to the first node of chain - * @return list of nodes description in FinalParsedNode structure - */ - public static List parse(String testcaseDesc) { - String[] nodes = testcaseDesc.split("-"); - List internalParsedNodeList = new ArrayList<>(); - - for (int i = 0; i < nodes.length; ++i) { - String node = nodes[i]; - InternalParsedNode nd; - if (node.contains("1")) { - nd = internalParsedNodeList.get(0); - - } else { - nd = new InternalParsedNode(); - internalParsedNodeList.add(nd); - nd.id = String.valueOf(i); - } - - if (node.startsWith(">")) { - nd.connectedFrom.add(i - 1); - } - if (node.endsWith("<")) { - nd.connectedFrom.add(i + 1); - } - if (node.contains("w")) { - nd.referencesTypes.add(ObjectGraph.ReferenceType.WEAK); - } - - if (node.contains("s")) { - nd.referencesTypes.add(ObjectGraph.ReferenceType.SOFT); - } - if (node.contains("H")) { - nd.isHumongous = true; - } - - if (node.contains("S")) { - nd.isHumongous = false; - } - } - - // we have connectedFrom but we need to get connectedTo - for (int i = 0; i < internalParsedNodeList.size(); ++i) { - for (Integer reference : internalParsedNodeList.get(i).connectedFrom) { - internalParsedNodeList.get(reference).connectedTo.add(i); - } - } - - List finalParsedNodes = internalParsedNodeList.stream().map(FinalParsedNode::new) - .collect(Collectors.toList()); - - return finalParsedNodes; - } - - /** - * @return List of pregenerated testing cases - */ - public static List getPregeneratedTestcases() { - return Arrays.asList( - "Hw", - "Sw", - "Sw->Hw", - "Hw->Sw", - "Sw<->Hw", - "Sw<->Sw", - "Hw->Sw->Sw", - "Hw->Sw->Sw", - "Sw->Hw->Sw", - "Hw->Sw->Sw->1", - "Sw->Hw->Sw->1", - "Sw->Hw->Hw->1", - "Sw<->Hw<->Hw->1", - "Sw<->Hw<->Sw->1", - "Sw->Hw<->Sw", - "Hs", - "Ss", - "Ss->Hs", - "Hs->Ss", - "Ss<->Hs", - "Ss<->Ss", - "Hs->Ss->Ss", - "Hs->Ss->Ss", - "Ss->Hs->Ss", - "Hs->Ss->Ss->1", - "Ss->Hs->Ss->1", - "Ss->Hs->Hs->1", - "Ss<->Hs<->Hs->1", - "Ss<->Hs<->Ss->1", - "Ss->Hs<->Ss", - "Ss->Hw", - "Sw->Hs", - "Hs->Sw", - "Hw->Ss", - "Ss<->Hw", - "Sw<->Hs", - "Ss<->Sw", - "Sw<->Ss", - "Hs->Sw->Sw", - "Hw->Ss->Sw", - "Hw->Sw->Ss", - "Ss->Hw->Sw", - "Sw->Hs->Sw", - "Sw->Hw->Ss", - "Hs->Sw->Sw->1", - "Hw->Ss->Sw->1", - "Hw->Sw->Ss->1", - "Ss->Hw->Sw->1", - "Ss->Hs->Sw->1", - "Sw->Hw->Ss->1", - "Ss->Hw->Hw->1", - "Sw->Hs->Hw->1", - "Sw->Hw->Hs->1", - "Ss<->Hw<->Hw->1", - "Sw<->Hs<->Hw->1", - "Sw<->Hw<->Hs->1", - "Ss<->Hw<->Sw->1", - "Sw<->Hs<->Sw->1", - "Sw<->Hw<->Ss->1", - "Ss->Hw<->Sw", - "Sw->Hs<->Sw", - "Sw->Hw<->Ss" - ); - } -} diff --git a/test/hotspot/jtreg/gc/z/TestUncommit.java b/test/hotspot/jtreg/gc/z/TestUncommit.java index 0ba4c55f22a..afb736501dd 100644 --- a/test/hotspot/jtreg/gc/z/TestUncommit.java +++ b/test/hotspot/jtreg/gc/z/TestUncommit.java @@ -108,7 +108,16 @@ public class TestUncommit { throw new Exception("Uncommitted too fast"); } + // In typical conditions (system not over-provisioned or slow), + // uncommitting is expected to complete within 3 * ZUncommitDelay after + // the last commit. To accommodate less ideal environments, only + // durations exceeding 5 * ZUncommitDelay are flagged as errors. + if (actualDelay > delay * 3) { + log(" *** Uncommit slower than expected. ***"); + } + + if (actualDelay > delay * 5) { throw new Exception("Uncommitted too slow"); } diff --git a/test/hotspot/jtreg/gtest/CompressedKlassGtest.java b/test/hotspot/jtreg/gtest/CompressedKlassGtest.java index 8ad200842d4..450203357a4 100644 --- a/test/hotspot/jtreg/gtest/CompressedKlassGtest.java +++ b/test/hotspot/jtreg/gtest/CompressedKlassGtest.java @@ -33,6 +33,7 @@ /* @test id=use-zero-based-encoding * @library /test/lib + * @requires vm.bits == "64" * @modules java.base/jdk.internal.misc * java.xml * @run main/native GTestWrapper --gtest_filter=CompressedKlass* -XX:-UseCompactObjectHeaders -Xlog:metaspace* -Xmx6g -Xms128m -Xshare:off -XX:CompressedClassSpaceSize=128m @@ -40,6 +41,7 @@ /* @test id=ccp_off * @library /test/lib + * @requires vm.bits == "64" * @modules java.base/jdk.internal.misc * java.xml * @run main/native GTestWrapper --gtest_filter=CompressedKlass* -XX:-UseCompressedClassPointers -Xlog:metaspace* -Xmx6g -Xms128m @@ -47,6 +49,7 @@ /* @test id=use-zero-based-encoding-coh * @library /test/lib + * @requires vm.bits == "64" * @modules java.base/jdk.internal.misc * java.xml * @run main/native GTestWrapper --gtest_filter=CompressedKlass* -XX:+UseCompactObjectHeaders -Xlog:metaspace* -Xmx6g -Xms128m -Xshare:off -XX:CompressedClassSpaceSize=128m @@ -54,7 +57,20 @@ /* @test id=use-zero-based-encoding-coh-large-class-space * @library /test/lib + * @requires vm.bits == "64" * @modules java.base/jdk.internal.misc * java.xml * @run main/native GTestWrapper --gtest_filter=CompressedKlass* -XX:+UseCompactObjectHeaders -Xlog:metaspace* -Xmx6g -Xms128m -Xshare:off -XX:CompressedClassSpaceSize=4g */ + +/* Very basic test on 32-bit, where we only support a pro-forma Compressed Class Pointers implementation without + * class space. + */ + +/* @test id=32-bit + * @library /test/lib + * @requires vm.bits == "32" + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=CompressedKlass* -Xlog:metaspace* -Xmx128m -Xms128m -Xshare:off + */ diff --git a/test/hotspot/jtreg/gtest/GTestWrapper.java b/test/hotspot/jtreg/gtest/GTestWrapper.java index 50998c2748a..1bd9734e48c 100644 --- a/test/hotspot/jtreg/gtest/GTestWrapper.java +++ b/test/hotspot/jtreg/gtest/GTestWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,6 +82,7 @@ public class GTestWrapper { command.add(execPath.toAbsolutePath().toString()); command.add("-jdk"); command.add(Utils.TEST_JDK); + command.add("-Xmx200m"); command.add("--gtest_output=xml:" + resultFile); command.add("--gtest_catch_exceptions=0"); for (String a : args) { diff --git a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java index 687bdf2ef57..ee3531868e6 100644 --- a/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java +++ b/test/hotspot/jtreg/runtime/ClassFile/ClassAccessFlagsRawTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ */ import java.lang.reflect.*; +import java.util.Set; public class ClassAccessFlagsRawTest { @@ -48,28 +49,53 @@ public class ClassAccessFlagsRawTest { public static void main(String argv[]) throws Throwable { Class cl = java.lang.Class.class; - m = cl.getDeclaredMethod("getClassAccessFlagsRaw", new Class[0]); + m = cl.getDeclaredMethod("getClassFileAccessFlags", new Class[0]); m.setAccessible(true); testIt("SUPERset", 0x21); // ACC_SUPER 0x20 + ACC_PUBLIC 0x1 testIt("SUPERnotset", Modifier.PUBLIC); - // test primitive array. should return ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC. - int flags = (int)m.invoke((new int[3]).getClass()); + // Test that primitive should return ACC_ABSTRACT | ACC_FINAL | ACC_PUBLIC. + int[] arr = new int[3]; + if (!arr.getClass().getComponentType().isPrimitive()) { + throw new RuntimeException("not primitive"); + } + int flags = (int)m.invoke(arr.getClass().getComponentType()); if (flags != (Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC)) { throw new RuntimeException( - "expected 0x411, got 0x" + Integer.toHexString(flags) + " for primitive array"); + "expected 0x411, got 0x" + Integer.toHexString(flags) + " for primitive type"); } - // test object array. should return flags of component. - flags = (int)m.invoke((new SUPERnotset[2]).getClass()); - if (flags != Modifier.PUBLIC) { + // Test that primitive array raw access flags return 0. + flags = (int)m.invoke(arr.getClass()); + if (flags != 0) { throw new RuntimeException( - "expected 0x1, got 0x" + Integer.toHexString(flags) + " for object array"); + "expected 0x0 got 0x" + Integer.toHexString(flags) + " for primitive array"); } - // test multi-dimensional object array. should return flags of component. - flags = (int)m.invoke((new SUPERnotset[4][2]).getClass()); + // Test that the modifier flags return element type flags. + flags = (int)arr.getClass().getModifiers(); + if (flags != (Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC)) { + throw new RuntimeException( + "expected 0x411, got 0x" + Integer.toHexString(flags) + " for primitive type"); + } + + // Test that AccessFlags set will return element type access flags. + Set aacc = arr.getClass().accessFlags(); + if (!aacc.containsAll(Set.of(AccessFlag.FINAL, AccessFlag.ABSTRACT, AccessFlag.PUBLIC))) { + throw new RuntimeException( + "AccessFlags should contain FINAL, ABSTRACT and PUBLIC for primitive type"); + } + + // Test object array. Raw access flags are 0 for arrays. + flags = (int)m.invoke((new SUPERnotset[2]).getClass()); + if (flags != 0) { + throw new RuntimeException( + "expected 0x0, got 0x" + Integer.toHexString(flags) + " for object array"); + } + + // Test object array component type. + flags = (int)m.invoke((new SUPERnotset[2]).getClass().getComponentType()); if (flags != Modifier.PUBLIC) { throw new RuntimeException( "expected 0x1, got 0x" + Integer.toHexString(flags) + " for object array"); diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ShowEventsOnCrashTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/ShowEventsOnCrashTest.java index 0c8646a4345..d0660d61894 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/ShowEventsOnCrashTest.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/ShowEventsOnCrashTest.java @@ -30,11 +30,15 @@ * @requires vm.flagless * @requires vm.debug == true & (os.family == "linux" | os.family == "windows") * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver ShowEventsOnCrashTest */ // Note: this test can only run on debug since it relies on VMError::controlled_crash() which // only exists in debug builds. + +import jdk.test.whitebox.WhiteBox; import java.io.File; import java.util.regex.Pattern; @@ -45,10 +49,17 @@ public class ShowEventsOnCrashTest { public static void main(String[] args) throws Exception { + if (args.length > 0 && args[0].equals("test")) { + Thread.sleep(2000); // Wait to accumulate log entries + WhiteBox.getWhiteBox().controlledCrash(2); + throw new RuntimeException("Still alive?"); + } + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", "-Xmx100M", "-XX:-CreateCoredumpOnCrash", - "-XX:ErrorHandlerTest=2", - "-version"); + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-Xmx100M", "-XX:-CreateCoredumpOnCrash", + ShowEventsOnCrashTest.class.getName(), "test"); OutputAnalyzer output_detail = new OutputAnalyzer(pb.start()); diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java b/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java index f2f7e4697a3..1e168221726 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java @@ -23,14 +23,26 @@ /* - * @test + * @test id=coh-off * @summary Test that we see VM configs reported correctly in hs_err file * @library /test/lib * @requires vm.flagless * @requires vm.debug * @modules java.base/jdk.internal.misc * java.management - * @run driver TestVMConfigInHsErrFile + * @run driver TestVMConfigInHsErrFile coh-off + */ + +/* + * @test id=coh-on + * @summary Test that we see VM configs reported correctly in hs_err file + * @library /test/lib + * @requires vm.bits == "64" + * @requires vm.flagless + * @requires vm.debug + * @modules java.base/jdk.internal.misc + * java.management + * @run driver TestVMConfigInHsErrFile coh-on */ import jdk.test.lib.process.OutputAnalyzer; @@ -42,8 +54,10 @@ import java.util.regex.Pattern; public class TestVMConfigInHsErrFile { public static void main(String[] args) throws Exception { - testCompactObjectHeaders(); - testCompressedClassPointers(); + switch (args[0]) { + case "coh-on" -> testCompactObjectHeaders(); + case "coh-off" -> testCompressedClassPointers(); + } } private static void testCompactObjectHeaders() throws Exception { diff --git a/test/hotspot/jtreg/runtime/Monitor/MonitorWithDeadObjectTest.java b/test/hotspot/jtreg/runtime/Monitor/MonitorWithDeadObjectTest.java index 31aeaccf07a..7f9b44a4a76 100644 --- a/test/hotspot/jtreg/runtime/Monitor/MonitorWithDeadObjectTest.java +++ b/test/hotspot/jtreg/runtime/Monitor/MonitorWithDeadObjectTest.java @@ -32,20 +32,20 @@ */ /* - * @requires os.family != "windows" & os.family != "aix" * @test id=DetachThread + * @requires os.family != "windows" & os.family != "aix" * @run main/othervm/native MonitorWithDeadObjectTest 0 */ /* - * @requires os.family != "windows" & os.family != "aix" * @test id=DumpThreadsBeforeDetach + * @requires os.family != "windows" & os.family != "aix" * @run main/othervm/native MonitorWithDeadObjectTest 1 */ /* - * @requires os.family != "windows" & os.family != "aix" * @test id=DumpThreadsAfterDetach + * @requires os.family != "windows" & os.family != "aix" * @run main/othervm/native MonitorWithDeadObjectTest 2 */ diff --git a/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java b/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java index c85dd657338..e28dd7f4d2a 100644 --- a/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java +++ b/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java @@ -88,7 +88,7 @@ public class TestAbortVMOnSafepointTimeout { } else { output.shouldContain("SIGILL"); if (Platform.isLinux()) { - output.shouldContain("(sent by kill)"); + output.shouldContain("(sent by safepoint timeout handler"); } } output.shouldNotHaveExitValue(0); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java b/test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java index 79d377ca876..8538155375c 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java index 44957f5d2ed..0d03313bec2 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java @@ -484,6 +484,7 @@ public class AOTFlags { testEmptyValue("AOTCache"); testEmptyValue("AOTConfiguration"); testEmptyValue("AOTMode"); + testEmptyValue("AOTCacheOutput"); } static void testEmptyValue(String option) throws Exception { diff --git a/test/hotspot/jtreg/runtime/jni/checked/TestCharArrayReleasing.java b/test/hotspot/jtreg/runtime/jni/checked/TestCharArrayReleasing.java index 7cb427cebbb..c26fb2bea4c 100644 --- a/test/hotspot/jtreg/runtime/jni/checked/TestCharArrayReleasing.java +++ b/test/hotspot/jtreg/runtime/jni/checked/TestCharArrayReleasing.java @@ -25,6 +25,8 @@ * @test * @bug 8357601 * @requires vm.flagless + * @comment The check of the array allocated with raw malloc triggers ASAN as we peek into the malloc header space. + * @requires !vm.asan * @library /test/lib * @run main/othervm/native TestCharArrayReleasing 0 0 * @run main/othervm/native TestCharArrayReleasing 1 0 diff --git a/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java b/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java index 2def134b9d3..7c0dfb2f3bb 100644 --- a/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java +++ b/test/hotspot/jtreg/runtime/os/TestHugePageDecisionsAtVMStartup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, 2024, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -56,6 +56,7 @@ import jdk.test.lib.os.linux.HugePageConfiguration; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; +import jtreg.SkippedException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -123,6 +124,9 @@ public class TestHugePageDecisionsAtVMStartup { out.shouldContain(warningNoTHP); } else if (useLP && !useTHP && configuration.supportsExplicitHugePages() && haveUsableExplicitHugePages) { + if (configuration.getExplicitAvailableHugePageNumber() == 0) { + throw new SkippedException("No usable explicit hugepages configured on the system, skipping test"); + } out.shouldContain("[info][pagesize] Using the default large page size: " + buildSizeString(configuration.getExplicitDefaultHugePageSize())); out.shouldContain("[info][pagesize] UseLargePages=1, UseTransparentHugePages=0"); out.shouldContain("[info][pagesize] Large page support enabled"); diff --git a/test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java b/test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java index ce4572199d1..7980b5ae28d 100644 --- a/test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java +++ b/test/hotspot/jtreg/serviceability/HeapDump/UnmountedVThreadNativeMethodAtTop.java @@ -27,7 +27,9 @@ * @requires vm.continuations * @modules jdk.management * @library /test/lib - * @run junit/othervm/native --enable-native-access=ALL-UNNAMED UnmountedVThreadNativeMethodAtTop + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run junit/othervm/native -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI --enable-native-access=ALL-UNNAMED UnmountedVThreadNativeMethodAtTop */ import java.lang.management.ManagementFactory; @@ -45,9 +47,12 @@ import static org.junit.jupiter.api.Assertions.*; import jdk.test.lib.hprof.model.Snapshot; import jdk.test.lib.hprof.model.ThreadObject; import jdk.test.lib.hprof.parser.Reader; +import jdk.test.whitebox.WhiteBox; public class UnmountedVThreadNativeMethodAtTop { + static WhiteBox wb = WhiteBox.getWhiteBox(); + boolean done; /** @@ -56,7 +61,7 @@ public class UnmountedVThreadNativeMethodAtTop { */ @BeforeEach void doGC() { - System.gc(); + wb.fullGC(); } /** diff --git a/test/hotspot/jtreg/serviceability/dcmd/thread/PrintTest.java b/test/hotspot/jtreg/serviceability/dcmd/thread/PrintTest.java index 29c462a56b3..a4a5eb5721b 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/thread/PrintTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/thread/PrintTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -168,6 +168,9 @@ public class PrintTest { output.shouldNotMatch(jucLockPattern1); output.shouldNotMatch(jucLockPattern2); } + + /* Check for presence of version string */ + output.shouldContain("JDK version:"); } @Test diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java index 4693040fa6b..e130c3c7e6b 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/StopThreadTest/StopThreadTest.java @@ -211,7 +211,6 @@ public class StopThreadTest { log("TestTask.run: caught expected AssertionError from method A()"); seenExceptionFromA = true; } - Thread.interrupted(); if (!seenExceptionFromA && !preemptableVirtualThread()) { StopThreadTest.setFailed("TestTask.run: expected AssertionError from method A()"); } @@ -224,7 +223,6 @@ public class StopThreadTest { log("TestTask.run: caught expected AssertionError from method B()"); seenExceptionFromB = true; } - Thread.interrupted(); if (!seenExceptionFromB) { StopThreadTest.setFailed("TestTask.run: expected AssertionError from method B()"); } @@ -237,7 +235,6 @@ public class StopThreadTest { log("TestTask.run: caught expected AssertionError from method C()"); seenExceptionFromC = true; } - Thread.interrupted(); if (!seenExceptionFromC) { StopThreadTest.setFailed("TestTask.run: expected AssertionError from method C()"); } diff --git a/test/hotspot/jtreg/sources/TestIncludesAreSorted.java b/test/hotspot/jtreg/sources/TestIncludesAreSorted.java index 222b97d6056..072784cda9d 100644 --- a/test/hotspot/jtreg/sources/TestIncludesAreSorted.java +++ b/test/hotspot/jtreg/sources/TestIncludesAreSorted.java @@ -43,12 +43,20 @@ public class TestIncludesAreSorted { * can be checked). */ private static final String[] HOTSPOT_SOURCES_TO_CHECK = { + "share/adlc", "share/c1", + "share/cds", "share/ci", + "share/classfile", + "share/code", "share/compiler", "share/jvmci", + "share/libadt", + "share/metaprogramming", "share/oops", - "share/opto" + "share/opto", + "share/services", + "share/utilities" }; /** diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/lexical.xml b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/lexical.xml deleted file mode 100644 index e7ea712cdd9..00000000000 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/lexical.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - Publishers of the Music of New York Women Composers - The Publishers <![CDATA[<?xml>]]> - - - ACA - info@composers.com - http://www.composers.com/ -

    - 170 West 74th St. - NY - NY - 10023 -
    - 212-362-8900 - 212-874-8605 - - &familytree; - - - diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/doctypeGF.out b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/doctypeGF.out deleted file mode 100644 index d4a9d98475f..00000000000 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/out/doctypeGF.out +++ /dev/null @@ -1,21 +0,0 @@ - - - - Publishers of the Music of New York Women Composers - The Publishers - - ACA - info@composers.com - http://www.composers.com/ -
    - 170 West 74th St. - NY - NY - 10023 -
    - 212-362-8900 - 212-874-8605 - - -
    -
    diff --git a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/publish2.xml b/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/publish2.xml deleted file mode 100644 index 789983f79f3..00000000000 --- a/test/jaxp/javax/xml/jaxp/functional/javax/xml/transform/xmlfiles/publish2.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Publishers of the Music of New York Women Composers - The Publishers - - ACA - info@composers.com - http://www.composers.com/ -
    - 170 West 74th St. - NY - NY - 10023 -
    - 212-362-8900 - 212-874-8605 - - &familytree; -
    -
    - diff --git a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/xmlfiles/out/DTDHandlerGF.out b/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/xmlfiles/out/DTDHandlerGF.out deleted file mode 100644 index f4b4241dd39..00000000000 --- a/test/jaxp/javax/xml/jaxp/functional/org/xml/sax/xmlfiles/out/DTDHandlerGF.out +++ /dev/null @@ -1,2 +0,0 @@ -In unparsedEntityDecl... name:logo publicId:null systemId:http://sc11152338.us.oracle.com:8080/xmlsqe/jaxp/web/testfiles/JAXPREP/images/tool.gif notationName:gif -In notationDecl... name:gif publicId:null systemId:http://sardinia/ diff --git a/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java b/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java index 5356464334e..0fc27ea3de0 100644 --- a/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java +++ b/test/jdk/java/awt/font/FontNames/LocaleFamilyNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,29 +26,32 @@ * @bug 4935798 6521210 6901159 * @summary Tests that all family names that are reported in all locales * correspond to some font returned from getAllFonts(). - * @run main LocaleFamilyNames + * @run main/othervm/timeout=360 LocaleFamilyNames */ import java.awt.*; import java.util.*; public class LocaleFamilyNames { public static void main(String[] args) throws Exception { + System.out.println("Start time: " + java.time.LocalDateTime.now()); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Font[] all_fonts = ge.getAllFonts(); - Locale[] all_locales = Locale.getAvailableLocales(); + System.out.println("Number of fonts: " + all_fonts.length); + System.out.println("Number of locales: " + all_locales.length); + HashSet all_families = new HashSet(); for (int i=0; i s.endsWith(".xbm")); + + for (File file : files) { + String fileName = file.getName(); + validCase = fileName.startsWith("valid"); + + System.out.println("--- Testing " + fileName + " ---"); + try (FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream errContent = new ByteArrayOutputStream()) { + System.setErr(new PrintStream(errContent)); + + ImageIcon icon = new ImageIcon(fis.readAllBytes()); + boolean isErrEmpty = errContent.toString().isEmpty(); + if (!isErrEmpty) { + System.out.println("Expected ImageFormatException occurred."); + System.out.print(errContent); + } + + if (validCase && !isErrEmpty) { + throw new RuntimeException("Test failed: Error stream not empty"); + } else if (!validCase && isErrEmpty) { + throw new RuntimeException("Test failed: ImageFormatException" + + " expected but not thrown"); + } + System.out.println("PASSED\n"); + } finally { + System.setErr(originalErr); + } + } + } +} diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid.xbm new file mode 100644 index 00000000000..8a8cfc27632 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid.xbm @@ -0,0 +1,2 @@ +#define k_ht 3 +h` k[] = { 01x0, 42222222222236319330:: diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm new file mode 100644 index 00000000000..c6f819582d0 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm @@ -0,0 +1,3 @@ +#define k_wt 16 +#define k_ht 1 +k[] = { 0x10, 1234567890}; diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_ht.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_ht.xbm new file mode 100644 index 00000000000..5244651a4cb --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_ht.xbm @@ -0,0 +1,3 @@ +#define k_wt 16 +#define k_ht 0 +k[] = { 0x10, 0x12}; diff --git a/test/jdk/java/awt/image/XBMDecoder/valid.xbm b/test/jdk/java/awt/image/XBMDecoder/valid.xbm new file mode 100644 index 00000000000..23b57b2c811 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/valid.xbm @@ -0,0 +1,6 @@ +#define test_width 16 +#define test_height 3 +#define ht_x 1 +#define ht_y 2 +static unsigned char test_bits[] = { +0x13, 0x11, 0x15, 0x00, 0xAB, 0xcd }; diff --git a/test/jdk/java/awt/image/XBMDecoder/valid_hex.xbm b/test/jdk/java/awt/image/XBMDecoder/valid_hex.xbm new file mode 100644 index 00000000000..e365d802447 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/valid_hex.xbm @@ -0,0 +1,4 @@ +#define test_width 16 +#define test_height 2 +static unsigned char test_bits[] = { 0x13, 0x11, + 0xAB, 0xff }; diff --git a/test/jdk/java/io/File/MaxPathLength.java b/test/jdk/java/io/File/MaxPathLength.java index 1110b469759..02b60f124d1 100644 --- a/test/jdk/java/io/File/MaxPathLength.java +++ b/test/jdk/java/io/File/MaxPathLength.java @@ -24,30 +24,31 @@ /* @test @bug 4759207 4403166 4165006 4403166 6182812 6274272 7160013 @summary Test to see if win32 path length can be greater than 260 + @library .. /test/lib */ import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.DirectoryNotEmptyException; +import jdk.test.lib.Platform; public class MaxPathLength { private static String sep = File.separator; private static String pathComponent = sep + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; private static String fileName = - "areallylongfilenamethatsforsur"; - private static boolean isWindows = false; + "areallylongfilenamethatsforsur"; private static final int MAX_LENGTH = 256; + private static final int FILE_EXISTS_SLEEP = 100; + + private static final int MAX_PATH_COMPONENTS_WINDOWS = 20; + private static int counter = 0; public static void main(String[] args) throws Exception { - String osName = System.getProperty("os.name"); - if (osName.startsWith("Windows")) { - isWindows = true; - } for (int i = 4; i < 7; i++) { String name = fileName; @@ -59,8 +60,10 @@ public class MaxPathLength { } // test long paths on windows - // And these long pathes cannot be handled on Solaris and Mac platforms - testLongPathOnWindows(); + // And these long paths cannot be handled on Linux and Mac platforms + if (Platform.isWindows()) { + testLongPath(); + } } private static String getNextName(String fName) { @@ -139,7 +142,7 @@ public class MaxPathLength { if (flist == null || !fn.equals(flist[0].getName())) throw new RuntimeException ("File.listFiles() failed"); - if (isWindows && + if (Platform.isWindows() && !fu.getCanonicalPath().equals(f.getCanonicalPath())) throw new RuntimeException ("getCanonicalPath() failed"); @@ -155,7 +158,7 @@ public class MaxPathLength { String abPath = f.getAbsolutePath(); if (!abPath.startsWith("\\\\") || abPath.length() < 1093) { - throw new RuntimeException ("File.renameTo() failed for lenth=" + throw new RuntimeException ("File.renameTo() failed for length=" + abPath.length()); } } else { @@ -182,7 +185,7 @@ public class MaxPathLength { Files.deleteIfExists(p); // Test if the file is really deleted and wait for 1 second at most for (int j = 0; j < 10 && Files.exists(p); j++) { - Thread.sleep(100); + Thread.sleep(FILE_EXISTS_SLEEP); } } catch (DirectoryNotEmptyException ex) { // Give up the clean-up, let jtreg handle it. @@ -193,14 +196,12 @@ public class MaxPathLength { } } - private static void testLongPathOnWindows () throws Exception { - if (isWindows) { - String name = fileName; - while (name.length() < MAX_LENGTH) { - testLongPath (20, name, false); - testLongPath (20, name, true); - name = getNextName(name); - } + private static void testLongPath () throws Exception { + String name = fileName; + while (name.length() < MAX_LENGTH) { + testLongPath(MAX_PATH_COMPONENTS_WINDOWS, name, false); + testLongPath(MAX_PATH_COMPONENTS_WINDOWS, name, true); + name = getNextName(name); } } -} +} \ No newline at end of file diff --git a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java index 70b73884d92..2b23d866af4 100644 --- a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java +++ b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/java/lang/invoke/ClassValueTest.java b/test/jdk/java/lang/invoke/ClassValueTest.java index 856653b3f92..34f7af1a8a0 100644 --- a/test/jdk/java/lang/invoke/ClassValueTest.java +++ b/test/jdk/java/lang/invoke/ClassValueTest.java @@ -23,23 +23,23 @@ /* * @test - * @bug 8351045 8351996 - * @enablePreview - * @comment Remove preview if ScopedValue is finalized + * @bug 8351045 8351996 8358535 * @summary tests for class-specific values + * @modules java.base/java.lang:+open * @library /test/lib * @run junit ClassValueTest */ import java.lang.classfile.ClassFile; import java.lang.constant.ClassDesc; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.ref.WeakReference; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; @@ -49,9 +49,7 @@ import java.util.concurrent.atomic.AtomicReference; import jdk.test.lib.util.ForceGC; import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; import static org.junit.jupiter.api.Assertions.*; @@ -479,4 +477,67 @@ final class ClassValueTest { awaitThreads(t); assertEquals(42, clv.get(int.class), "slow computation reinstalled value"); } + + // ClassValue cache invalidated and not reinstated when another + // unrelated entry is removed + @Test + public void testCacheRefresh() throws Throwable { + // Setup + var lookup = MethodHandles.privateLookupIn(ClassValue.class, MethodHandles.lookup()); + var classValueEntryClass = Class.forName("java.lang.ClassValue$Entry"); + MethodHandle getCacheCarefully = lookup.findStatic(ClassValue.class, "getCacheCarefully", + MethodType.methodType(classValueEntryClass.arrayType(), Class.class)); + var classValueMapClass = Class.forName("java.lang.ClassValue$ClassValueMap"); + MethodHandle probeHomeLocation = lookup.findStatic(classValueMapClass, "probeHomeLocation", + MethodType.methodType(classValueEntryClass, classValueEntryClass.arrayType(), ClassValue.class)); + MethodHandle match = lookup.findVirtual(ClassValue.class, "match", + MethodType.methodType(boolean.class, classValueEntryClass)); + + // Work + ClassValue clv = new ClassValue<>() { + @Override + protected String computeValue(Class type) { + return ""; + } + }; + // A class that shouldn't have arbitrary values stuffing the cache + var cleanClass = clv.getClass(); + clv.get(cleanClass); // create cache on clean class + assertTrue(checkDirectCacheMatch( + getCacheCarefully, + probeHomeLocation, + match, + clv, + cleanClass + )); + clv.get(int.class); + clv.remove(int.class); // invalidate cache on clean class + assertFalse(checkDirectCacheMatch( + getCacheCarefully, + probeHomeLocation, + match, + clv, + cleanClass + )); + clv.get(cleanClass); + assertTrue(checkDirectCacheMatch( + getCacheCarefully, + probeHomeLocation, + match, + clv, + cleanClass + )); + } + + private static boolean checkDirectCacheMatch( + MethodHandle getCacheCarefully, + MethodHandle probeHomeLocation, + MethodHandle match, + ClassValue clv, + Class cl + ) throws Throwable { + Object cache = getCacheCarefully.invoke(cl); + Object entry = probeHomeLocation.invoke(cache, clv); + return (boolean) match.invoke(clv, entry); + } } diff --git a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java index 8ee5179ea81..45cebdb552b 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java @@ -148,7 +148,8 @@ public class ModuleSetAccessibleTest { // non-public constructor Constructor ctor - = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, ProtectionDomain.class, boolean.class); + = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, + ProtectionDomain.class, boolean.class, char.class); AccessibleObject[] ctors = { ctor }; try { diff --git a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java index 9574afee407..774ee4d1dad 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java @@ -194,7 +194,8 @@ public class TrySetAccessibleTest { // non-public constructor Constructor ctor - = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, ProtectionDomain.class, boolean.class); + = Class.class.getDeclaredConstructor(ClassLoader.class, Class.class, char.class, + ProtectionDomain.class, boolean.class, char.class); AccessibleObject[] ctors = { ctor }; assertFalse(ctor.trySetAccessible()); diff --git a/test/jdk/java/net/CookieStoreTest.java b/test/jdk/java/net/CookieStoreTest.java new file mode 100644 index 00000000000..50a5ab3fac1 --- /dev/null +++ b/test/jdk/java/net/CookieStoreTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.CookieManager; +import java.net.CookieStore; +import java.net.HttpCookie; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/* + * @test + * @bug 8365086 + * @summary verify that the implementation of java.net.CookieStore works + * as expected + * @run junit CookieStoreTest + */ +class CookieStoreTest { + + // neither the scheme, host nor the port matters in this test + private static final URI COOKIE_TEST_URI = URI.create("https://127.0.0.1:12345"); + + static List cookieStores() { + final List params = new ArrayList<>(); + // empty CookieStore + params.add(Arguments.of(new CookieManager().getCookieStore(), true)); + + final CookieStore cookieStore = new CookieManager().getCookieStore(); + cookieStore.add(COOKIE_TEST_URI, new HttpCookie("foo", "bar")); + // non-empty CookieStore + params.add(Arguments.of(cookieStore, false)); + + return params; + } + + /* + * Verify that the List returned by CookieStore.getURIs() is immutable. + */ + @ParameterizedTest + @MethodSource("cookieStores") + void testImmutableGetURIs(final CookieStore cookieStore, final boolean expectEmpty) { + final List uris = cookieStore.getURIs(); + assertNotNull(uris, "CookieStore.getURIs() returned null"); + assertEquals(expectEmpty, uris.isEmpty(), "CookieStore.getURIs() returned: " + uris); + assertImmutableList(uris, COOKIE_TEST_URI); + } + + /* + * Verify that the List returned by CookieStore.getCookies() is immutable. + */ + @ParameterizedTest + @MethodSource("cookieStores") + void testImmutableGetCookies(final CookieStore cookieStore, final boolean expectEmpty) { + final List cookies = cookieStore.getCookies(); + assertNotNull(cookies, "CookieStore.getCookies() returned null"); + assertEquals(expectEmpty, cookies.isEmpty(), "CookieStore.getCookies() returned: " + cookies); + assertImmutableList(cookies, new HttpCookie("hello", "world")); + } + + /* + * Verify that the List returned by CookieStore.get(URI) is immutable. + */ + @ParameterizedTest + @MethodSource("cookieStores") + void testImmutableGetCookiesForURI(final CookieStore cookieStore, final boolean expectEmpty) { + final List cookies = cookieStore.get(COOKIE_TEST_URI); + assertNotNull(cookies, "CookieStore.get(URI) returned null"); + assertEquals(expectEmpty, cookies.isEmpty(), "CookieStore.get(URI) returned: " + cookies); + assertImmutableList(cookies, new HttpCookie("hello", "world")); + } + + /* + * Verifies that the attempt to modify the contents of the list will fail + * due to the list being immutable. + */ + private static void assertImmutableList(final List list, T elementToAddOrRemove) { + // the list is expected to be immutable, so each of these operations must fail + assertThrows(UnsupportedOperationException.class, () -> list.add(elementToAddOrRemove)); + assertThrows(UnsupportedOperationException.class, () -> list.remove(elementToAddOrRemove)); + assertThrows(UnsupportedOperationException.class, list::clear); + // even try the replace operation when the list isn't empty + if (!list.isEmpty()) { + assertThrows(UnsupportedOperationException.class, () -> list.set(0, elementToAddOrRemove)); + } + } +} diff --git a/test/jdk/java/net/HttpURLConnection/HttpUrlConnectionExpectContinueResponseMessageTest.java b/test/jdk/java/net/HttpURLConnection/HttpUrlConnectionExpectContinueResponseMessageTest.java new file mode 100644 index 00000000000..06aa9319161 --- /dev/null +++ b/test/jdk/java/net/HttpURLConnection/HttpUrlConnectionExpectContinueResponseMessageTest.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 that Response Message gets set even if a non 100 response + * gets return when Expect Continue is set + * @bug 8352502 + * @library /test/lib + * @run junit/othervm -Djdk.internal.httpclient.debug=true + * -Djdk.httpclient.HttpClient.log=all + * HttpUrlConnectionExpectContinueResponseMessageTest + */ + +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.Socket; +import java.net.ServerSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URL; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class HttpUrlConnectionExpectContinueResponseMessageTest { + class Control { + volatile ServerSocket serverSocket = null; + volatile boolean stop = false; + volatile String response = null; + volatile Socket acceptingSocket = null; + volatile String testPath = null; + } + + private Thread serverThread = null; + private volatile Control control; + static final Logger logger; + + static { + logger = Logger.getLogger("sun.net.www.protocol.http.HttpURLConnection"); + logger.setLevel(Level.ALL); + Logger.getLogger("").getHandlers()[0].setLevel(Level.ALL); + } + + public Object[][] args() { + return new Object[][]{ + // Expected Status Code, Status Line, Expected responseMessage + { 404, "HTTP/1.1 404 Not Found", "Not Found" }, + { 405, "HTTP/1.1 405 Method Not Allowed", "Method Not Allowed" }, + { 401, "HTTP/1.1 401 Unauthorized", "Unauthorized"} + }; + } + + @BeforeAll + public void startServerSocket() throws Exception { + Control control = this.control = new Control(); + + control.serverSocket = new ServerSocket(); + control.serverSocket.setReuseAddress(true); + control.serverSocket.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + Runnable runnable = () -> { + while (!control.stop) { + try { + Socket socket = control.serverSocket.accept(); + String path = getPath(socket); + + OutputStream outputStream; + if (path.equals(control.testPath)) { + control.acceptingSocket = socket; + outputStream = control.acceptingSocket.getOutputStream(); + + // send a wrong response and then shutdown + outputStream.write(control.response.getBytes()); + outputStream.flush(); + control.acceptingSocket.shutdownOutput(); + } else { + // stray request showed up, return 500 and close socket + outputStream = socket.getOutputStream(); + outputStream.write("HTTP/1.1 500 Internal Server Error\r\n".getBytes()); + outputStream.write("Connection: close\r\n".getBytes()); + outputStream.write("Content-Length: 0\r\n".getBytes()); + outputStream.write("\r\n".getBytes()); + outputStream.flush(); + socket.close(); + } + } catch (Exception e) { + // Any exceptions will be ignored + } + } + }; + serverThread = new Thread(runnable); + serverThread.start(); + } + + private static String getPath(Socket socket) throws IOException { + InputStream inputStream = socket.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + + StringBuilder reqBuilder = new StringBuilder(); + String line = null; + while (!(line = reader.readLine()).isEmpty()) { + reqBuilder.append(line + "\r\n"); + } + String req = reqBuilder.toString(); + StringTokenizer tokenizer = new StringTokenizer(req); + String method = tokenizer.nextToken(); + String path = tokenizer.nextToken(); + return path; + } + + @AfterAll + public void stopServerSocket() throws Exception { + Control control = this.control; + control.stop = true; + control.serverSocket.close(); + serverThread.join(); + } + + @ParameterizedTest + @MethodSource("args") + public void test(int expectedCode, String statusLine, String expectedMessage) throws Exception { + String body = "Testing: " + expectedCode; + Control control = this.control; + control.response = statusLine + "\r\n" + + "Content-Length: 0\r\n" + + "\r\n"; + control.testPath = "/ContinueResponseMessageTest/" + expectedCode; + + URL url = URIBuilder.newBuilder() + .scheme("http") + .loopback() + .port(control.serverSocket.getLocalPort()) + .path(control.testPath) + .toURL(); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Connection", "Close"); + connection.setRequestProperty("Expect", "100-Continue"); + + try { + connection.setFixedLengthStreamingMode(body.getBytes().length); + OutputStream outputStream = connection.getOutputStream(); + outputStream.write(body.getBytes()); + outputStream.close(); + } catch (Exception ex) { + // server returning 4xx responses can result in exceptions + // but we can just swallow them + } + + int responseCode = connection.getResponseCode(); + String responseMessage = connection.getResponseMessage(); + assertTrue(responseCode == expectedCode, + String.format("Expected %s response, instead received %s", expectedCode, responseCode)); + assertTrue(expectedMessage.equals(responseMessage), + String.format("Expected Response Message %s, instead received %s", + expectedMessage, responseMessage)); + control.acceptingSocket.close(); + } +} diff --git a/test/jdk/java/net/NetworkInterface/IPv4Only.java b/test/jdk/java/net/NetworkInterface/IPv4Only.java index efa59aa7691..3d12b3282bd 100644 --- a/test/jdk/java/net/NetworkInterface/IPv4Only.java +++ b/test/jdk/java/net/NetworkInterface/IPv4Only.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,12 +29,18 @@ */ -import java.net.*; -import java.util.*; + import jdk.test.lib.net.IPSupport; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + public class IPv4Only { public static void main(String[] args) throws Exception { + IPSupport.printPlatformSupport(System.out); if (IPSupport.hasIPv4()) { System.out.println("Testing IPv4"); Enumeration nifs = NetworkInterface.getNetworkInterfaces(); @@ -43,7 +49,7 @@ public class IPv4Only { Enumeration addrs = nif.getInetAddresses(); while (addrs.hasMoreElements()) { InetAddress hostAddr = addrs.nextElement(); - if ( hostAddr instanceof Inet6Address ){ + if (hostAddr instanceof Inet6Address){ throw new RuntimeException( "NetworkInterfaceV6List failed - found v6 address " + hostAddr.getHostAddress() ); } } diff --git a/test/jdk/java/net/Socket/DeadlockTest.java b/test/jdk/java/net/Socket/DeadlockTest.java index 40b001171f1..3e2d38dc142 100644 --- a/test/jdk/java/net/Socket/DeadlockTest.java +++ b/test/jdk/java/net/Socket/DeadlockTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,12 +31,19 @@ * @run main/othervm -Djava.net.preferIPv4Stack=true DeadlockTest */ -import java.net.*; -import java.io.*; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; + import jdk.test.lib.net.IPSupport; public class DeadlockTest { - public static void main(String [] argv) throws Exception { + public static void main(String[] argv) throws Exception { IPSupport.throwSkippedExceptionIfNonOperational(); ServerSocket ss = new ServerSocket(0, 0, InetAddress.getLoopbackAddress()); @@ -52,16 +59,9 @@ public class DeadlockTest { Thread c1 = new Thread(ct); c1.start(); - // Wait for the client thread to finish - c1.join(20000); - - // If timeout, we assume there is a deadlock - if (c1.isAlive() == true) { - // Close the socket to force the server thread - // terminate too - s1.stop(); - throw new Exception("Takes too long. Dead lock"); - } + // Wait for the client thread to finish. + // If it doesn't finish then it's a sign of a deadlock + c1.join(); } finally { ss.close(); clientSocket.close(); @@ -73,7 +73,7 @@ class ServerThread implements Runnable { private static boolean dbg = false; - ObjectInputStream in; + ObjectInputStream in; ObjectOutputStream out; ServerSocket server; diff --git a/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java b/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java index 83961453f05..c6b2c323693 100644 --- a/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java +++ b/test/jdk/java/net/httpclient/HttpsTunnelAuthTest.java @@ -37,12 +37,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.test.lib.net.SimpleSSLContext; import static java.lang.System.out; -/** +/* * @test * @bug 8262027 * @summary Verify that it's possible to handle proxy authentication manually @@ -62,7 +61,7 @@ import static java.lang.System.out; //-Djdk.internal.httpclient.debug=true -Dtest.debug=true public class HttpsTunnelAuthTest implements HttpServerAdapters, AutoCloseable { - static final String data[] = { + static final String[] data = { "Lorem ipsum", "dolor sit amet", "consectetur adipiscing elit, sed do eiusmod tempor", @@ -150,7 +149,7 @@ public class HttpsTunnelAuthTest implements HttpServerAdapters, AutoCloseable { @Override public void close() throws Exception { - if (proxy != null) close(proxy::stop); + if (proxy != null) close(proxy); if (http1Server != null) close(http1Server::stop); if (https1Server != null) close(https1Server::stop); if (https2Server != null) close(https2Server::stop); @@ -160,7 +159,8 @@ public class HttpsTunnelAuthTest implements HttpServerAdapters, AutoCloseable { try { closeable.close(); } catch (Exception x) { - // OK. + // OK to ignore and just log + System.err.println("ignoring failure during close() of " + closeable + " due to: " + x); } } diff --git a/test/jdk/java/net/ipv6tests/TcpTest.java b/test/jdk/java/net/ipv6tests/TcpTest.java index 0ca35737a76..e75397b5181 100644 --- a/test/jdk/java/net/ipv6tests/TcpTest.java +++ b/test/jdk/java/net/ipv6tests/TcpTest.java @@ -31,6 +31,7 @@ * @library /test/lib * @build jdk.test.lib.NetworkConfiguration * jdk.test.lib.Platform + * jtreg.SkippedException * @run main TcpTest -d */ @@ -38,6 +39,8 @@ import java.net.*; import java.io.*; import java.util.concurrent.TimeUnit; +import jtreg.SkippedException; + public class TcpTest extends Tests { static ServerSocket server, server1, server2; static Socket c1, c2, c3, s1, s2, s3; @@ -62,12 +65,10 @@ public class TcpTest extends Tests { public static void main (String[] args) throws Exception { checkDebug(args); if (ia4addr == null) { - System.out.println ("No IPV4 addresses: exiting test"); - return; + throw new SkippedException("No IPV4 addresses: exiting test"); } if (ia6addr == null) { - System.out.println ("No IPV6 addresses: exiting test"); - return; + throw new SkippedException("No IPV6 addresses: exiting test"); } dprintln ("Local Addresses"); dprintln (ia4addr.toString()); diff --git a/test/jdk/java/net/vthread/HttpALot.java b/test/jdk/java/net/vthread/HttpALot.java index c016824a92a..6418e103a35 100644 --- a/test/jdk/java/net/vthread/HttpALot.java +++ b/test/jdk/java/net/vthread/HttpALot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,7 @@ * questions. */ -/** +/* * @test * @bug 8284161 * @summary Stress test the HTTP protocol handler and HTTP server @@ -44,6 +44,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URL; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; @@ -52,6 +53,8 @@ import jdk.test.lib.net.URIBuilder; public class HttpALot { + private static final String HELLO = "Hello"; + public static void main(String[] args) throws Exception { int requests = 25_000; if (args.length > 0) { @@ -65,12 +68,20 @@ public class HttpALot { InetAddress lb = InetAddress.getLoopbackAddress(); HttpServer server = HttpServer.create(new InetSocketAddress(lb, 0), 1024); ThreadFactory factory = Thread.ofVirtual().factory(); - server.setExecutor(Executors.newThreadPerTaskExecutor(factory)); + final ExecutorService serverExecutor = Executors.newThreadPerTaskExecutor(factory); + server.setExecutor(serverExecutor); server.createContext("/hello", e -> { - byte[] response = "Hello".getBytes("UTF-8"); - e.sendResponseHeaders(200, response.length); - try (OutputStream out = e.getResponseBody()) { - out.write(response); + try { + byte[] response = HELLO.getBytes("UTF-8"); + e.sendResponseHeaders(200, response.length); + try (OutputStream out = e.getResponseBody()) { + out.write(response); + } + } catch (Throwable t) { + System.err.println("failed to handle request " + e.getRequestURI() + + " due to: " + t); + t.printStackTrace(); + throw t; // let it propagate } requestsHandled.incrementAndGet(); }); @@ -85,15 +96,21 @@ public class HttpALot { // go server.start(); - try { - factory = Thread.ofVirtual().name("fetcher-", 0).factory(); - try (var executor = Executors.newThreadPerTaskExecutor(factory)) { - for (int i = 1; i <= requests; i++) { - executor.submit(() -> fetch(url)).get(); + try (serverExecutor) { + try { + factory = Thread.ofVirtual().name("fetcher-", 0).factory(); + try (var executor = Executors.newThreadPerTaskExecutor(factory)) { + for (int i = 1; i <= requests; i++) { + final String actual = executor.submit(() -> fetch(url)).get(); + if (!HELLO.equals(actual)) { + throw new RuntimeException("unexpected response: \"" + actual + + "\" for request " + i); + } + } } + } finally { + server.stop(1); } - } finally { - server.stop(1); } if (requestsHandled.get() < requests) { diff --git a/test/jdk/java/nio/Buffer/CharBufferAsCharSequenceTest.java b/test/jdk/java/nio/Buffer/CharBufferAsCharSequenceTest.java new file mode 100644 index 00000000000..ae5a6122712 --- /dev/null +++ b/test/jdk/java/nio/Buffer/CharBufferAsCharSequenceTest.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.PrimitiveIterator.OfInt; +import java.util.Random; + +import jdk.test.lib.RandomFactory; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/* + * @test + * @bug 8343110 8361299 8364345 + * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @summary Tests CharBuffer implementations of CharSequence + * @run junit CharBufferAsCharSequenceTest + */ +public class CharBufferAsCharSequenceTest { + + private static final Random RAND = RandomFactory.getRandom(); + private static final int SIZE = RAND.nextInt(128, 1153); + + private static char[] randomChars() { + char[] chars = new char[SIZE]; + for (int i=0; i>> 1; + int start = RAND.nextInt(mid >> 1); // from 0 to 1/4 + int end = RAND.nextInt(mid + 1, cb.capacity()); // from mid to capacity + cb.position(start); + cb.limit(end); + return cb; + } + + /** + * Generates random content to use for populating cb then calling through + * to {@code addCases(String, char[], CharBuffer, List)} + * + * @param type String description of the type of CharBuffer under test. + * @param cb CharBuffer instance to populate as base of creating cases. + * @param cases The {@code List} to populate with the cases for use from + * {@link #charBufferArguments()}. + */ + private static void populateAndAddCases(String type, CharBuffer cb, List cases) { + assert cb.position() == 0 && cb.limit() == cb.capacity(); + char[] buf = randomChars(); + cb.put(buf); + cb.clear(); + addCases(type, buf, cb, cases); + } + + /** + * Adds 4 cases to cases. + *
      + *
    • Full use of cb
    • . + *
    • A duplicate of cb with a randomized position and limit. See + * {@code randomizeRange(CharBuffer)} + *
    • + *
    • A {@link CharBuffer#slice() sliced} copy of randomized range.
    • + *
    • A {@link CharBuffer#slice() sliced} copy of randomized range with a + * randomized position and limit.
    • + *
    + */ + private static void addCases(String type, char[] buf, CharBuffer cb, List cases) { + assert cb.position() == 0 && cb.limit() == cb.capacity(); + cases.add(Arguments.of(cb, buf, 0, buf.length, type + " full")); + + CharBuffer rndRange = randomizeRange(cb.duplicate()); + cases.add(Arguments.of(rndRange, buf, rndRange.position(), rndRange.limit(), + type + " at " + rndRange.position() + " through " + rndRange.limit())); + cases.add(Arguments.of(rndRange.slice(), buf, rndRange.position(), rndRange.limit(), + type + " sliced at " + rndRange.position() + " through " + rndRange.limit())); + + CharBuffer rndSlicedRange = randomizeRange(rndRange.slice()); + cases.add(Arguments.of(rndSlicedRange, + buf, + rndRange.position() + rndSlicedRange.position(), + rndRange.position() + rndSlicedRange.limit(), + type + " sliced at " + rndRange.position() + " with position " + + rndSlicedRange.position() + " and limit " + rndSlicedRange.limit())); + } + + /** + * Returns a {@code List} of {@link Arguments}, with each entry representing a + * test case scenario. + *
      + *
    • CharBuffer - the instance to be tested
    • + *
    • char[] - the data expected to be backing the current state of the CharBuffer
    • + *
    • int start - index (inclusive) into char[] where the CharBuffer should be positioned
    • + *
    • int stop - index (exclusive) into char[] where the CharBuffer should be limited
    • + *
    • String - description of the test scenario
    • + *
    + * + * Generates the following sets of arguments/test cases. + *
      + *
    • See {@code populateAndAddCases(String, CharBuffer, List)} for the + * following types: + *
        + *
      • HeapCharBuffer + *
      • HeapByteBuffer Big Endian + *
      • HeapByteBuffer Little Endian + *
      • DirectByteBuffer Big Endian + *
      • DirectByteBuffer Little Endian + *
      + *
    • + *
    • Randomly generated content into {@link CharBuffer#wrap(CharSequence) + * StringCharBuffer} - see {@code addCases(String, char[], CharBuffer, List)}. + *
        + *
      • StringCharBuffer wrapping a {@code CharBuffer} created from + * {@link CharBuffer#wrap(char[])}
      • + *
      • StringCharBuffer wrapping a {@code String}
      • + *
      + *
    • + *
    + */ + static List charBufferArguments() { + List args = new ArrayList<>(); + + populateAndAddCases("HeapCharBuffer", CharBuffer.allocate(SIZE), args); + populateAndAddCases("HeapByteBuffer BE", + ByteBuffer.allocate(SIZE * 2).order(ByteOrder.BIG_ENDIAN).asCharBuffer(), args); + populateAndAddCases("HeapByteBuffer LE", + ByteBuffer.allocate(SIZE * 2).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(), args); + populateAndAddCases("DirectByteBuffer BE", + ByteBuffer.allocateDirect(SIZE * 2).order(ByteOrder.BIG_ENDIAN).asCharBuffer(), args); + populateAndAddCases("DirectByteBuffer LE", + ByteBuffer.allocateDirect(SIZE * 2).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(), args); + + char[] randomChars = randomChars(); + CharBuffer cb = CharBuffer.wrap(randomChars); + addCases("StringCharBuffer over CharBuffer", randomChars, CharBuffer.wrap(cb), args); + + addCases("StringCharBuffer over String", randomChars, CharBuffer.wrap(new String(randomChars)), args); + + return args; + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testToString(CharSequence actual, char[] expected, int start, int stop, String description) { + assertEquals(new String(expected, start, stop - start), actual.toString(), description); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testLength(CharSequence actual, char[] expected, int start, int stop, String description) { + assertEquals(stop - start, actual.length(), description); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsRange(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[16]; + int length = Math.min(10, stop - start - 1); + actual.getChars(1, length + 1, val, 3); + + for (int i = 0; i < length; ++i) { + assertEquals(expected[i + start + 1], val[i + 3], "val at offset of " + i + " from " + description); + } + // test that calling getChars did not move the position + assertEquals(expected[start], actual.charAt(0), "first char after calling getChars: " + description); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsAll(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[stop - start]; + actual.getChars(0, val.length, val, 0); + + for (int i = 0; i < val.length; ++i) { + assertEquals(expected[i + start], val[i], "val at offset of " + i + " from " + description); + } + // test that calling getChars did not move the position + assertEquals(expected[start], actual.charAt(0), "first char after calling getChars: " + description); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsNegativeSrcBegin(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[16]; + assertThrows(IndexOutOfBoundsException.class, () -> actual.getChars(-1, 4, val, 1)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsNegativeSrcEnd(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[16]; + assertThrows(IndexOutOfBoundsException.class, () -> actual.getChars(0, -4, val, 1)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsSrcEndBeforeBegin(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[16]; + assertThrows(IndexOutOfBoundsException.class, () -> actual.getChars(3, 2, val, 1)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsNegativeDstBegin(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[16]; + assertThrows(IndexOutOfBoundsException.class, () -> actual.getChars(1, 3, val, -1)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsDstBeginOOB(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[16]; + assertThrows(IndexOutOfBoundsException.class, () -> actual.getChars(1, 4, val, val.length + 1)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsDstLengthOOB(CharSequence actual, char[] expected, int start, int stop, String description) { + char[] val = new char[16]; + assertThrows(IndexOutOfBoundsException.class, () -> actual.getChars(1, 4, val, val.length - 2)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testGetCharsNullDst(CharSequence actual, char[] expected, int start, int stop, String description) { + assertThrows(NullPointerException.class, () -> actual.getChars(0, 1, null, 0)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testCharAt(CharSequence actual, char[] expected, int start, int stop, String description) { + for (int i = 0, j = stop - start; i < j; ++i) { + assertEquals(expected[start + i], actual.charAt(i), "chart at " + i + ": " + description); + } + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testCharAtNegativePos(CharSequence actual, char[] expected, int start, int stop, String description) { + assertThrows(IndexOutOfBoundsException.class, () -> actual.charAt(-1)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testCharAtPosOOB(CharSequence actual, char[] expected, int start, int stop, String description) { + assertThrows(IndexOutOfBoundsException.class, () -> actual.charAt(stop - start + 1)); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testChars(CharSequence actual, char[] expected, int start, int stop, String description) { + OfInt chars = actual.chars().iterator(); + for (int i = 0, j = stop - start; i < j; ++i) { + assertEquals(expected[start + i], (char) chars.nextInt(), "chart at " + i + ": " + description); + } + assertFalse(chars.hasNext(), "chars has more elements than expected " + description); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testCodePoints(CharSequence actual, char[] expected, int start, int stop, String description) { + OfInt codePoints = actual.codePoints().iterator(); + for (int i = 0, j = stop - start; i < j; ++i) { + char c1 = expected[start + i]; + int expectedCodePoint = c1; + if (Character.isHighSurrogate(c1) && (i + 1) < j) { + char c2 = expected[start + i + 1]; + if (Character.isLowSurrogate(c2)) { + expectedCodePoint = Character.toCodePoint(c1, c2); + ++i; + } + } + assertEquals(expectedCodePoint, codePoints.nextInt(), "code point at " + i + ": " + description); + } + assertFalse(codePoints.hasNext(), "codePoints has more elements than expected " + description); + } + + @ParameterizedTest(name="{4}") + @MethodSource("charBufferArguments") + void testSubSequence(CharSequence actual, char[] expected, int start, int stop, String description) { + int maxTests = Math.min(7, ((stop - start) >> 1) - 1); + for (int i = 0; i < maxTests; ++i) { + assertEquals(new String(expected, start + i, stop - start - (2 * i)), + actual.subSequence(i, actual.length() - i).toString(), + "subsequence at index " + i + " for " + description); + } + } +} diff --git a/test/jdk/java/nio/Buffer/GetChars.java b/test/jdk/java/nio/Buffer/GetChars.java deleted file mode 100644 index 65aeca70328..00000000000 --- a/test/jdk/java/nio/Buffer/GetChars.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.CharBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertEquals; - -/** - * @test - * @bug 8343110 8361299 - * @summary Check for expected behavior of CharBuffer.getChars(). - * @run testng GetChars - * @key randomness - */ -public class GetChars { - private static CharBuffer CB = CharBuffer.wrap("Test"); - - @Test - public void testExactCopy() { - var dst = new char[4]; - CB.getChars(0, 4, dst, 0); - Assert.assertEquals(dst, new char[] {'T', 'e', 's', 't'}); - } - - @Test - public void testPartialCopy() { - var dst = new char[2]; - CB.getChars(1, 3, dst, 0); - Assert.assertEquals(dst, new char[] {'e', 's'}); - } - - @Test - public void testPositionedCopy() { - var dst = new char[] {1, 2, 3, 4, 5, 6}; - CB.getChars(0, 4, dst, 1); - Assert.assertEquals(dst, new char[] {1, 'T', 'e', 's', 't', 6}); - } - - @Test - public void testSrcBeginIsNegative() { - Assert.assertThrows(IndexOutOfBoundsException.class, - () -> CB.getChars(-1, 3, new char[4], 0)); - } - - @Test - public void testSrcBeginIsNegationOfPosition() { - CB.position(1); - Assert.assertThrows(IndexOutOfBoundsException.class, - () -> { - try { - CB.getChars(-1, 3, new char[4], 0); - } finally { - CB.position(0); - } - }); - } - - @Test - public void testDstBeginIsNegative() { - Assert.assertThrows(IndexOutOfBoundsException.class, - () -> CB.getChars(0, 4, new char[4], -1)); - } - - @Test - public void testSrcBeginIsGreaterThanSrcEnd() { - Assert.assertThrows(IndexOutOfBoundsException.class, - () -> CB.getChars(4, 0, new char[4], 0)); - } - - @Test - public void testSrcEndIsGreaterThanSequenceLength() { - Assert.assertThrows(IndexOutOfBoundsException.class, - () -> CB.getChars(0, 5, new char[4], 0)); - } - - @Test - public void testRequestedLengthIsGreaterThanDstLength() { - Assert.assertThrows(IndexOutOfBoundsException.class, - () -> CB.getChars(0, 4, new char[3], 0)); - } - - @Test - public void testDstIsNull() { - Assert.assertThrows(NullPointerException.class, - () -> CB.getChars(0, 4, null, 0)); - } - - private static final Random RAND = new Random(); - private static final int SIZE = 128 + RAND.nextInt(1024); - - /** - * Randomize the char buffer's position and limit. - */ - private static CharBuffer randomizeRange(CharBuffer cb) { - int mid = cb.capacity() >>> 1; - int start = RAND.nextInt(mid + 1); // from 0 to mid - int end = mid + RAND.nextInt(cb.capacity() - mid + 1); // from mid to capacity - cb.position(start); - cb.limit(end); - return cb; - } - - /** - * Randomize the char buffer's contents, position and limit. - */ - private static CharBuffer randomize(CharBuffer cb) { - while (cb.hasRemaining()) { - cb.put((char)RAND.nextInt()); - } - return randomizeRange(cb); - } - - /** - * Sums the remaining chars in the char buffer. - */ - private static int intSum(CharBuffer cb) { - int sum = 0; - cb.mark(); - while (cb.hasRemaining()) { - sum += cb.get(); - } - cb.reset(); - return sum; - } - - /** - * Sums the chars in the char array. - */ - private static int intSum(char[] ca) { - int sum = 0; - for (int i = 0; i < ca.length; i++) - sum += ca[i]; - return sum; - } - - /** - * Creates char buffers to test, adding them to the given list. - */ - private static void addCases(CharBuffer cb, List buffers) { - randomize(cb); - buffers.add(cb); - - buffers.add(cb.slice()); - buffers.add(cb.duplicate()); - buffers.add(cb.asReadOnlyBuffer()); - - buffers.add(randomizeRange(cb.slice())); - buffers.add(randomizeRange(cb.duplicate())); - buffers.add(randomizeRange(cb.asReadOnlyBuffer())); - } - - @DataProvider(name = "charbuffers") - public Object[][] createCharBuffers() { - List buffers = new ArrayList<>(); - - // heap - addCases(CharBuffer.allocate(SIZE), buffers); - addCases(CharBuffer.wrap(new char[SIZE]), buffers); - addCases(ByteBuffer.allocate(SIZE*2).order(ByteOrder.BIG_ENDIAN).asCharBuffer(), - buffers); - addCases(ByteBuffer.allocate(SIZE*2).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(), - buffers); - - // direct - addCases(ByteBuffer.allocateDirect(SIZE*2).order(ByteOrder.BIG_ENDIAN).asCharBuffer(), - buffers); - addCases(ByteBuffer.allocateDirect(SIZE*2).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(), - buffers); - - // read-only buffer backed by a CharSequence - buffers.add(CharBuffer.wrap(randomize(CharBuffer.allocate(SIZE)))); - - Object[][] params = new Object[buffers.size()][]; - for (int i = 0; i < buffers.size(); i++) { - CharBuffer cb = buffers.get(i); - params[i] = new Object[] { cb.getClass().getName(), cb }; - } - - return params; - } - - @Test(dataProvider = "charbuffers") - public void testGetChars(String type, CharBuffer cb) { - System.out.format("%s position=%d, limit=%d%n", type, cb.position(), cb.limit()); - int expected = intSum(cb); - var dst = new char[cb.remaining()]; - cb.getChars(0, cb.remaining(), dst, 0); - int actual = intSum(dst); - assertEquals(actual, expected); - } -} diff --git a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/Basic.java b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/Basic.java index 1605910a2dd..c988d89a2c7 100644 --- a/test/jdk/java/nio/file/attribute/BasicFileAttributeView/Basic.java +++ b/test/jdk/java/nio/file/attribute/BasicFileAttributeView/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,9 +22,12 @@ */ /* @test - * @bug 4313887 6838333 + * @bug 4313887 6838333 8364277 * @summary Unit test for java.nio.file.attribute.BasicFileAttributeView - * @library ../.. + * @library ../.. /test/lib + * @build jdk.test.lib.Platform + * jdk.test.lib.util.FileUtils + * @run main/othervm --enable-native-access=ALL-UNNAMED Basic */ import java.nio.file.*; @@ -33,6 +36,9 @@ import java.util.*; import java.util.concurrent.TimeUnit; import java.io.*; +import jdk.test.lib.Platform; +import jdk.test.lib.util.FileUtils; + public class Basic { static void check(boolean okay, String msg) { @@ -97,6 +103,17 @@ public class Basic { check(!attrs.isOther(), "is not other"); } + static void checkAttributesOfJunction(Path junction) + throws IOException + { + BasicFileAttributes attrs = + Files.readAttributes(junction, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + check(!attrs.isSymbolicLink(), "is a link"); + check(!attrs.isDirectory(), "is a directory"); + check(!attrs.isRegularFile(), "is not a regular file"); + check(attrs.isOther(), "is other"); + } + static void attributeReadWriteTests(Path dir) throws IOException { @@ -114,12 +131,18 @@ public class Basic { Path link = dir.resolve("link"); try { Files.createSymbolicLink(link, file); - } catch (UnsupportedOperationException x) { - return; - } catch (IOException x) { - return; + checkAttributesOfLink(link); + } catch (IOException | UnsupportedOperationException x) { + if (!Platform.isWindows()) + return; + } + + // NTFS junctions are Windows-only + if (Platform.isWindows()) { + Path junction = dir.resolve("junction"); + FileUtils.createWinDirectoryJunction(junction, dir); + checkAttributesOfJunction(junction); } - checkAttributesOfLink(link); } public static void main(String[] args) throws IOException { diff --git a/test/jdk/java/security/PKCS12Attribute/HashCode.java b/test/jdk/java/security/PKCS12Attribute/HashCode.java index 21d2fd1f7f2..58217214321 100644 --- a/test/jdk/java/security/PKCS12Attribute/HashCode.java +++ b/test/jdk/java/security/PKCS12Attribute/HashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,18 +23,98 @@ /* * @test - * @bug 8200792 - * @summary PKCS12Attribute#hashCode is always constant -1 + * @bug 8200792 8355379 + * @summary Tests PKCS12Attribute#hashCode correctness, stability, and caching after Stable annotation fix */ - import java.security.PKCS12Attribute; +import java.util.HashSet; +import java.util.Set; public class HashCode { public static void main(String[] args) throws Exception { int h1 = new PKCS12Attribute("1.2.3.4", "AA").hashCode(); int h2 = new PKCS12Attribute("2.3.4.5", "BB,CC").hashCode(); if (h1 == -1 || h2 == -1 || h1 == h2) { - throw new Exception("I see " + h1 + " and " + h2); + throw new Exception("Unexpected hashCodes: " + h1 + " and " + h2); + } + + PKCS12Attribute attr1 = new PKCS12Attribute("1.2.3.4", "AA"); + + // Equality and hash code consistency + testHashCode(attr1); + + // Inequality and hash code difference + testInequality(new PKCS12Attribute("1.2.3.4", "AA"), + new PKCS12Attribute("1.2.3.4", "BB")); + testInequality(new PKCS12Attribute("1.2.3.4", "AA"), + new PKCS12Attribute("2.3.4.5", "AA")); + + // Repeated hashCode consistency + testRepeatHashCode(attr1); + + // Hash code uniqueness stress test + testHashCodeUniqueness(); + } + + private static void testHashCode(PKCS12Attribute attr) throws Exception { + int originalHash = attr.hashCode(); + + PKCS12Attribute reconstructed = new PKCS12Attribute(attr.getName(), attr.getValue()); + + if (!attr.equals(reconstructed)) { + throw new Exception("Equality failed for: name=" + attr.getName() + + ", value=" + attr.getValue()); + } + + int newHash = reconstructed.hashCode(); + if (originalHash != newHash) { + throw new Exception("Hash code mismatch for: name=" + attr.getName() + + ", original=" + originalHash + ", new=" + newHash); + } + + System.out.println("Pass: name=" + attr.getName() + ", value=" + attr.getValue() + + ", hashCode=" + originalHash); + } + + private static void testInequality(PKCS12Attribute a1, PKCS12Attribute a2) throws Exception { + if (a1.equals(a2)) { + throw new Exception("Unexpected equality: name=" + a1.getName() + + ", values=" + a1.getValue() + " vs " + a2.getValue()); + } + + if (a1.hashCode() == a2.hashCode()) { + System.out.println("Warning: Different attributes share hashCode: " + a1.hashCode()); + } else { + System.out.println("Pass: name=" + a1.getName() + + ", values differ and hashCodes differ"); } } + + private static void testRepeatHashCode(PKCS12Attribute attr) throws Exception { + int h1 = attr.hashCode(); + int h2 = attr.hashCode(); + + if (h1 != h2) { + throw new Exception("Inconsistent hashCode for: name=" + attr.getName() + + ", value=" + attr.getValue()); + } + + System.out.println("Pass: repeat hashCode consistency for name=" + attr.getName()); + } + + private static void testHashCodeUniqueness() { + Set seen = new HashSet<>(); + int collisions = 0; + + for (int i = 0; i < 1000; i++) { + PKCS12Attribute attr = new PKCS12Attribute("1.2.3." + i, "V" + i); + if (!seen.add(attr.hashCode())) { + System.out.println("Hash collision: name=" + attr.getName() + + ", value=" + attr.getValue()); + collisions++; + } + } + + System.out.println("Hash uniqueness test complete. Collisions: " + collisions); + } } diff --git a/test/jdk/java/security/cert/URICertStoreParameters/TestBasic.java b/test/jdk/java/security/cert/URICertStoreParameters/TestBasic.java index ed96d32a5c7..e62c1d469bd 100644 --- a/test/jdk/java/security/cert/URICertStoreParameters/TestBasic.java +++ b/test/jdk/java/security/cert/URICertStoreParameters/TestBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,12 @@ import java.security.cert.CertStore; import java.security.cert.URICertStoreParameters; import java.net.URI; +import java.util.HashSet; +import java.util.Set; /* * @test - * @bug 8038084 + * @bug 8038084 8355379 * @summary Basic testing for URICertStoreParameters * @run main TestBasic */ @@ -37,6 +39,7 @@ public class TestBasic { String str1 = "ldap://myownhost:5000/"; String str2 = "ldap://myownhost:5000/cn=foo"; test(str1, str2); + testRepeatedHashCode(str1); System.out.println("Test passed"); } @@ -68,4 +71,15 @@ public class TestBasic { throw new Exception("Error: p1/p1Too should NOT equal p2"); } } + + private static void testRepeatedHashCode(String str) throws Exception { + System.out.println("Testing repeated hashCode consistency"); + URICertStoreParameters p = new URICertStoreParameters(new URI(str)); + int h1 = p.hashCode(); + int h2 = p.hashCode(); + if (h1 != h2) { + throw new Exception("hashCode inconsistent across calls"); + } + System.out.println("hashCode consistency verified"); + } } diff --git a/test/jdk/java/text/Format/DecimalFormat/CloneTest.java b/test/jdk/java/text/Format/DecimalFormat/CloneTest.java index fa46b09d6ac..f11d576a39a 100644 --- a/test/jdk/java/text/Format/DecimalFormat/CloneTest.java +++ b/test/jdk/java/text/Format/DecimalFormat/CloneTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8354522 + * @bug 8354522 8358880 * @summary Check for cloning interference * @library /test/lib * @run junit/othervm --add-opens=java.base/java.text=ALL-UNNAMED CloneTest @@ -95,11 +95,6 @@ public class CloneTest { assertNotSame(data, valFromDigitList(dfClone, "data")); } - Object tempBuilder = valFromDigitList(original, "tempBuilder"); - if (tempBuilder != null) { - assertNotSame(data, valFromDigitList(dfClone, "data")); - } - assertEquals(digitListField.get(original), digitListField.get(dfClone)); } catch (ReflectiveOperationException e) { throw new SkippedException("reflective access in white-box test failed", e); diff --git a/test/jdk/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java b/test/jdk/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java index edbf0f3ae86..91900e37b6a 100644 --- a/test/jdk/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java +++ b/test/jdk/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java @@ -44,6 +44,7 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase { private volatile int privateField; int w; float z; + static volatile int q; public static void main(String[] args) { main(suite(), args); } @@ -88,6 +89,16 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase { } catch (IllegalArgumentException success) {} } + /** + * construction with static field throws IllegalArgumentException + */ + public void testConstructor4() { + try { + updaterFor("q"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + /** * construction using private field from subclass throws RuntimeException */ diff --git a/test/jdk/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java b/test/jdk/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java index 17689061426..5edcf871f42 100644 --- a/test/jdk/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java +++ b/test/jdk/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java @@ -44,6 +44,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { private volatile long privateField; long w; float z; + static volatile long q; public static void main(String[] args) { main(suite(), args); } @@ -88,6 +89,16 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { } catch (IllegalArgumentException success) {} } + /** + * construction with static field throws IllegalArgumentException + */ + public void testConstructor4() { + try { + updaterFor("q"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + /** * construction using private field from subclass throws RuntimeException */ diff --git a/test/jdk/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java b/test/jdk/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java index 6df68c4c634..313f40f7918 100644 --- a/test/jdk/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java +++ b/test/jdk/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java @@ -45,6 +45,7 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase { Object z; Item w; volatile int i; + static volatile Item q; public static void main(String[] args) { main(suite(), args); @@ -100,6 +101,17 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase { } catch (ClassCastException success) {} } + /** + * construction with static field throws IllegalArgumentException + */ + public void testConstructor5() { + try { + updaterFor("q"); + shouldThrow(); + } catch (IllegalArgumentException success) {} + } + + /** * construction using private field from subclass throws RuntimeException */ diff --git a/test/jdk/javax/management/security/SecurityTest.java b/test/jdk/javax/management/security/SecurityTest.java index 835c340dd99..7212aea883f 100644 --- a/test/jdk/javax/management/security/SecurityTest.java +++ b/test/jdk/javax/management/security/SecurityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,6 +142,9 @@ public class SecurityTest { * map (argName, value) format, then calls original test's run method. */ public static void main(String args[]) throws Exception { + // Disable default KeyManager's certificate checking so we can use + // a certificate signed with MD5withRSA algorithm. + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", "false"); System.out.println("================================================="); @@ -529,6 +532,10 @@ public class SecurityTest { private MBeanServerConnection mbsc = null; public static void main(String args[]) throws Exception { + // Disable default KeyManager's certificate checking so we can use + // a certificate signed with MD5withRSA algorithm. + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", + "false"); // Parses parameters Utils.parseDebugProperties(); diff --git a/test/jdk/javax/net/ssl/DTLS/DTLSNamedGroups.java b/test/jdk/javax/net/ssl/DTLS/DTLSNamedGroups.java index e9165fa391c..1fc4f962675 100644 --- a/test/jdk/javax/net/ssl/DTLS/DTLSNamedGroups.java +++ b/test/jdk/javax/net/ssl/DTLS/DTLSNamedGroups.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,4 +140,3 @@ public class DTLSNamedGroups extends DTLSOverDatagram { } } } - diff --git a/test/jdk/javax/net/ssl/DTLS/DTLSSignatureSchemes.java b/test/jdk/javax/net/ssl/DTLS/DTLSSignatureSchemes.java index 5dd897b1bd7..9c1c64f7fa0 100644 --- a/test/jdk/javax/net/ssl/DTLS/DTLSSignatureSchemes.java +++ b/test/jdk/javax/net/ssl/DTLS/DTLSSignatureSchemes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,4 +134,3 @@ public class DTLSSignatureSchemes extends DTLSOverDatagram { } } } - diff --git a/test/jdk/javax/net/ssl/SSLException/CheckSSLHandshakeException.java b/test/jdk/javax/net/ssl/SSLException/CheckSSLHandshakeException.java index 4c8aba3de44..4ba3e906597 100644 --- a/test/jdk/javax/net/ssl/SSLException/CheckSSLHandshakeException.java +++ b/test/jdk/javax/net/ssl/SSLException/CheckSSLHandshakeException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/SSLException/CheckSSLKeyException.java b/test/jdk/javax/net/ssl/SSLException/CheckSSLKeyException.java index dcd62fcf8e7..2d271236de1 100644 --- a/test/jdk/javax/net/ssl/SSLException/CheckSSLKeyException.java +++ b/test/jdk/javax/net/ssl/SSLException/CheckSSLKeyException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/SSLException/CheckSSLPeerUnverifiedException.java b/test/jdk/javax/net/ssl/SSLException/CheckSSLPeerUnverifiedException.java index 04184e99306..1d37271ed53 100644 --- a/test/jdk/javax/net/ssl/SSLException/CheckSSLPeerUnverifiedException.java +++ b/test/jdk/javax/net/ssl/SSLException/CheckSSLPeerUnverifiedException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/SSLException/CheckSSLProtocolException.java b/test/jdk/javax/net/ssl/SSLException/CheckSSLProtocolException.java index 3f62fac8f77..c1bd53e21e9 100644 --- a/test/jdk/javax/net/ssl/SSLException/CheckSSLProtocolException.java +++ b/test/jdk/javax/net/ssl/SSLException/CheckSSLProtocolException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java index fc5001e89b8..25f73606b96 100644 --- a/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java +++ b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/SSLParameters/NamedGroupsSpec.java b/test/jdk/javax/net/ssl/SSLParameters/NamedGroupsSpec.java index 9146e7b5f7b..0d910ccfb67 100644 --- a/test/jdk/javax/net/ssl/SSLParameters/NamedGroupsSpec.java +++ b/test/jdk/javax/net/ssl/SSLParameters/NamedGroupsSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/SSLParameters/SignatureSchemes.java b/test/jdk/javax/net/ssl/SSLParameters/SignatureSchemes.java index 7dadeff5703..7f41cb1af79 100644 --- a/test/jdk/javax/net/ssl/SSLParameters/SignatureSchemes.java +++ b/test/jdk/javax/net/ssl/SSLParameters/SignatureSchemes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java b/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java index bac110aa033..5438f429455 100644 --- a/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java +++ b/test/jdk/javax/net/ssl/ServerName/EndingDotHostname.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -248,4 +248,3 @@ public class EndingDotHostname { sslIS.read(); } } - diff --git a/test/jdk/javax/net/ssl/templates/SSLExampleCert.java b/test/jdk/javax/net/ssl/templates/SSLExampleCert.java index 0b82aed3b7b..46f69a62e41 100644 --- a/test/jdk/javax/net/ssl/templates/SSLExampleCert.java +++ b/test/jdk/javax/net/ssl/templates/SSLExampleCert.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java index 6da32894587..3aa7a98c394 100644 --- a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java +++ b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,8 @@ /* * @test * @bug 5016500 - * @library /test/lib/ + * @library /javax/net/ssl/templates + * /test/lib/ * @summary Test SslRmi[Client|Server]SocketFactory SSL socket parameters. * @run main/othervm SSLSocketParametersTest 1 * @run main/othervm SSLSocketParametersTest 2 @@ -36,8 +37,6 @@ */ import jdk.test.lib.Asserts; -import java.io.IOException; -import java.io.File; import java.io.Serializable; import java.lang.ref.Reference; import java.rmi.ConnectIOException; @@ -49,13 +48,17 @@ import javax.net.ssl.SSLContext; import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.rmi.ssl.SslRMIServerSocketFactory; -public class SSLSocketParametersTest implements Serializable { +public class SSLSocketParametersTest extends SSLContextTemplate { + + public SSLSocketParametersTest() throws Exception { + SSLContext.setDefault(createServerSSLContext()); + } public interface Hello extends Remote { String sayHello() throws RemoteException; } - public class HelloImpl implements Hello { + public static class HelloImpl implements Hello { public String sayHello() { return "Hello World!"; } @@ -134,23 +137,7 @@ public class SSLSocketParametersTest implements Serializable { } public static void main(String[] args) throws Exception { - // Set keystore properties (server-side) - // - final String keystore = System.getProperty("test.src") + - File.separator + "keystore"; - System.out.println("KeyStore = " + keystore); - System.setProperty("javax.net.ssl.keyStore", keystore); - System.setProperty("javax.net.ssl.keyStorePassword", "password"); - - // Set truststore properties (client-side) - // - final String truststore = System.getProperty("test.src") + - File.separator + "truststore"; - System.out.println("TrustStore = " + truststore); - System.setProperty("javax.net.ssl.trustStore", truststore); - System.setProperty("javax.net.ssl.trustStorePassword", "trustword"); - SSLSocketParametersTest test = new SSLSocketParametersTest(); test.runTest(Integer.parseInt(args[0])); } -} \ No newline at end of file +} diff --git a/test/jdk/javax/rmi/ssl/keystore b/test/jdk/javax/rmi/ssl/keystore deleted file mode 100644 index 05f53564582..00000000000 Binary files a/test/jdk/javax/rmi/ssl/keystore and /dev/null differ diff --git a/test/jdk/javax/rmi/ssl/truststore b/test/jdk/javax/rmi/ssl/truststore deleted file mode 100644 index 2f5ba3433dc..00000000000 Binary files a/test/jdk/javax/rmi/ssl/truststore and /dev/null differ diff --git a/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java b/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java index 8f68773398c..6d500fea656 100644 --- a/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java +++ b/test/jdk/javax/security/auth/callback/PasswordCallback/CheckCleanerBound.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,4 +55,3 @@ public final class CheckCleanerBound { } } } - diff --git a/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java b/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java index ea8b1d1c145..64db7de2b18 100644 --- a/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java +++ b/test/jdk/javax/security/auth/callback/PasswordCallback/PasswordCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * 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,4 +49,3 @@ public final class PasswordCleanup { } } } - diff --git a/test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java b/test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java new file mode 100644 index 00000000000..15b1c7fe217 --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/TestRadioAndCheckMenuItemWithIcon.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8348760 + * @summary Verify if RadioButtonMenuItem bullet and + * JCheckboxMenuItem checkmark is shown if + * JRadioButtonMenuItem and JCheckboxMenuItem + * is rendered with ImageIcon in WindowsLookAndFeel + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual TestRadioAndCheckMenuItemWithIcon + */ + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; +import javax.swing.UIManager; + +public class TestRadioAndCheckMenuItemWithIcon { + + private static final String INSTRUCTIONS = """ + A top level Menu will be shown. + + Clicking on the Menu will show a + JRadioButtonMenuItem group with 3 radiobutton menuitems + and a JCheckBoxMenuItem group with 3 checkbox menuitems. + + First radiobutton menuitem is selected with imageicon of a red square. + Second radiobutton menuitem is unselected with imageicon. + Third radiobutton menuItem is unselected without imageicon. + + First checkbox menuitem is selected with imageicon. + Second checkbox menuitem is unselected with imageicon. + Third checkbox menuItem is unselected without imageicon. + + Verify that for first JRadioButtonMenuItem with imageicon, + a bullet is shown alongside the imageicon and + for first JCheckBoxMenuItem with imageicon + a checkmark is shown alongside the imageicon. + + If bullet and checkmark is shown, test passes else fails."""; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + PassFailJFrame.builder() + .title("JRadioButtonMenuItem Instructions") + .instructions(INSTRUCTIONS) + .columns(60) + .testUI(TestRadioAndCheckMenuItemWithIcon::doTest) + .build() + .awaitAndCheck(); + } + + public static JFrame doTest() { + BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + Graphics g = img.getGraphics(); + g.setColor(Color.red); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + g.dispose(); + + BufferedImage img2 = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + Graphics g2 = img2.getGraphics(); + g2.setColor(Color.red); + g2.fillRect(0, 0, img2.getWidth(), img2.getHeight()); + g2.dispose(); + + JFrame frame = new JFrame("RadioButtonWithImageIcon"); + ImageIcon imageIcon1 = new ImageIcon(img); + ImageIcon imageIcon2 = new ImageIcon(img2); + AbstractButton button1; + JRadioButtonMenuItem m1 = new JRadioButtonMenuItem("JRadioButtonMenuItem 1", + imageIcon1); + m1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, ActionEvent.ALT_MASK|ActionEvent.CTRL_MASK|ActionEvent.SHIFT_MASK)); + button1 = m1; + button1.setSelected(true); + AbstractButton button2 = new JRadioButtonMenuItem("JRadioButtonMenuItem 2", imageIcon2); + AbstractButton button3 = new JRadioButtonMenuItem("JRadioButtonMenuItem 3"); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(button1); + buttonGroup.add(button2); + buttonGroup.add(button3); + + AbstractButton check1 = new JCheckBoxMenuItem("JCheckBoxMenuItem 1", + imageIcon1); + check1.setSelected(true); + AbstractButton check2 = new JCheckBoxMenuItem("JCheckBoxMenuItem 2", imageIcon1); + JCheckBoxMenuItem c3; + AbstractButton check3 = c3 = new JCheckBoxMenuItem("JCheckBoxMenuItem 3"); + c3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, ActionEvent.ALT_MASK|ActionEvent.CTRL_MASK|ActionEvent.SHIFT_MASK)); + + JMenu topLevel = new JMenu("Menu"); + + topLevel.add(button1); + topLevel.add(button2); + topLevel.add(button3); + + topLevel.addSeparator(); + + topLevel.add(check1); + topLevel.add(check2); + topLevel.add(check3); + + AbstractButton menuitem1 = new JMenuItem("MenuItem1"); + AbstractButton menuitem2 = new JMenuItem("MenuItem2", imageIcon1); + topLevel.addSeparator(); + topLevel.add(menuitem1); + topLevel.add(menuitem2); + + JMenuBar menuBar = new JMenuBar(); + menuBar.add(topLevel); + + frame.setJMenuBar(menuBar); + frame.setSize(300, 300); + return frame; + + } +} diff --git a/test/jdk/javax/swing/JPasswordField/PasswordFieldInputMapWordTest.java b/test/jdk/javax/swing/JPasswordField/PasswordFieldInputMapWordTest.java new file mode 100644 index 00000000000..c49e9f7083c --- /dev/null +++ b/test/jdk/javax/swing/JPasswordField/PasswordFieldInputMapWordTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key headful + * @bug 8358813 + * @summary Password fields' InputMap should not include any word-related action. + * + * @run main PasswordFieldInputMapWordTest + */ + +import java.util.Collection; +import java.util.Set; + +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.JPasswordField; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.text.DefaultEditorKit; + +public class PasswordFieldInputMapWordTest { + public static void main(String[] args) throws Exception { + for (UIManager.LookAndFeelInfo laf : + UIManager.getInstalledLookAndFeels()) { + System.out.println("Testing LAF: " + laf.getClassName()); + SwingUtilities.invokeAndWait(() -> { + if (setLookAndFeel(laf)) { + runTest(); + } + }); + } + } + + private static boolean setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + return true; + } catch (UnsupportedLookAndFeelException e) { + System.err.println("Skipping unsupported look and feel:"); + e.printStackTrace(); + return false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static int[] inputMapConditions = new int[] { + JComponent.WHEN_IN_FOCUSED_WINDOW, + JComponent.WHEN_FOCUSED, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT + }; + + /** + * These are all the actions with "word" in their field name. + */ + static Collection wordActions = Set.of( + DefaultEditorKit.deleteNextWordAction, + DefaultEditorKit.deletePrevWordAction, + DefaultEditorKit.beginWordAction, + DefaultEditorKit.endWordAction, + DefaultEditorKit.selectionBeginWordAction, + DefaultEditorKit.selectionEndWordAction, + DefaultEditorKit.previousWordAction, + DefaultEditorKit.nextWordAction, + DefaultEditorKit.selectionPreviousWordAction, + DefaultEditorKit.selectionNextWordAction + ); + + private static void runTest() { + JPasswordField field = new JPasswordField(); + + boolean testPassed = true; + for (int condition : inputMapConditions) { + InputMap inputMap = field.getInputMap(condition); + if (inputMap.allKeys() == null) { + continue; + } + for (KeyStroke keyStroke : inputMap.allKeys()) { + Object actionBinding = inputMap.get(keyStroke); + if (wordActions.contains(actionBinding)) { + if (testPassed) { + System.err.println("The following inputs/actions should not be available in a JPasswordField:"); + } + System.err.println(inputMap.get(keyStroke) + " (try typing " + keyStroke + ")"); + testPassed = false; + } + } + } + + if (!testPassed) { + throw new RuntimeException("One or more input/action binding was observed for a JPasswordField."); + } + } +} diff --git a/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java b/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java new file mode 100644 index 00000000000..c8ddfe67999 --- /dev/null +++ b/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 4938801 + * @key headful + * @summary Verifies popup is removed when the component is removed + * @run main TestPopupInvoker + */ + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Robot; +import java.util.concurrent.CountDownLatch; + +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; + +import static java.util.concurrent.TimeUnit.SECONDS; + +public class TestPopupInvoker { + static JPopupMenu popupMenu; + static JFrame frame; + static JLabel label; + static Container pane; + + private static final CountDownLatch popupShown = new CountDownLatch(1); + private static final CountDownLatch popupHidden = new CountDownLatch(1); + + private static void createUI() { + frame = new JFrame("TestPopupInvoker"); + pane = frame.getContentPane(); + pane.setLayout(new BorderLayout()); + label = new JLabel("Popup Invoker"); + pane.add(label, BorderLayout.CENTER); + + popupMenu = new JPopupMenu("Popup"); + popupMenu.add("One"); + popupMenu.add("Two"); + popupMenu.add("Three"); + + popupMenu.addPopupMenuListener(new PopupMenuListener() { + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + popupShown.countDown(); + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + popupHidden.countDown(); + } + + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + } + }); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public static void main(String[] args) throws Exception { + try { + Robot robot = new Robot(); + SwingUtilities.invokeAndWait(TestPopupInvoker::createUI); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> popupMenu.show(label, 0, 0)); + + if (!popupShown.await(2, SECONDS)) { + throw new RuntimeException("Popup isn't displayed"); + } + robot.waitForIdle(); + + SwingUtilities.invokeAndWait(() -> { + pane.remove(label); + pane.repaint(); + }); + if (!popupHidden.await(1, SECONDS)) { + throw new RuntimeException("Popup is visible after component is removed"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/SwingUtilities/bug4967768.java b/test/jdk/javax/swing/SwingUtilities/bug4967768.java index 43f9f7cabfb..6ce1f5c787b 100644 --- a/test/jdk/javax/swing/SwingUtilities/bug4967768.java +++ b/test/jdk/javax/swing/SwingUtilities/bug4967768.java @@ -37,13 +37,19 @@ import javax.swing.JPanel; public class bug4967768 { private static final String INSTRUCTIONS = """ - When the test starts you'll see a button "Oops" - with the "p" letter underlined at the bottom - of the instruction frame. + When the test starts you'll see a button "Oops". + + For Windows and GTK Look and Feel, you will need to + press the ALT key to make the mnemonic visible. + Once the ALT key is pressed, the letter "p" will be + underlined at the bottom of the instruction frame. Ensure the underline cuts through the descender of letter "p", i.e. the underline is painted not below the letter but below the baseline. + + Press Pass if you see the expected behaviour else + press Fail. """; public static void main(String[] args) throws Exception { diff --git a/test/jdk/javax/swing/text/AbstractDocument/StringContentPositionTest.java b/test/jdk/javax/swing/text/AbstractDocument/StringContentPositionTest.java new file mode 100644 index 00000000000..c535c4819e6 --- /dev/null +++ b/test/jdk/javax/swing/text/AbstractDocument/StringContentPositionTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.text.BadLocationException; +import javax.swing.text.Position; +import javax.swing.text.StringContent; + +/* + * @test + * @summary test that StringContent Position APIs behave as expected. + */ + +public class StringContentPositionTest { + + static final int SIZE = 20; + static final String TEXT = "hello"; + static final int LEN = TEXT.length(); + static final StringContent SC = new StringContent(); + + public static void main(String[] args) throws BadLocationException { + + for (int i = 0; i < 1000; i++) { + test(); + System.gc(); + } + } + + static void test() throws BadLocationException { + + Position[] positions = new Position[SIZE]; + + for (int i = 0; i < SIZE; i++) { + SC.insertString(0, TEXT); + positions[i] = SC.createPosition(LEN); + } + for (int i = 0; i < SIZE; i++) { + int expected = ((SIZE - i) * LEN); + if (positions[i].getOffset() != expected) { + throw new RuntimeException("insert: Bad offset i=" + i + " off=" + positions[i].getOffset()); + } + } + SC.remove(0, SIZE * LEN); + for (int i = 0; i < SIZE; i++) { + if (positions[i].getOffset() != 0) { + throw new RuntimeException("remove: Bad offset i=" + i + " off=" + positions[i].getOffset()); + } + } + } +} diff --git a/test/jdk/javax/xml/crypto/dsig/Properties.java b/test/jdk/javax/xml/crypto/dsig/Properties.java new file mode 100644 index 00000000000..6e257823011 --- /dev/null +++ b/test/jdk/javax/xml/crypto/dsig/Properties.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.Asserts; +import jdk.test.lib.security.SeededSecureRandom; +import jdk.test.lib.security.XMLUtils; +import org.w3c.dom.Document; + +import javax.xml.crypto.dsig.XMLSignatureException; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.Base64; + +/** + * @test + * @bug 8359395 + * @summary ensure properties are used + * @library /test/lib + */ +public class Properties { + + private static final String DOM_SIGNATURE_PROVIDER + = "org.jcp.xml.dsig.internal.dom.SignatureProvider"; + private static final String DOM_SIGNATURE_RANDOM + = "jdk.xmldsig.SecureRandom"; + + public static void main(String[] args) throws Exception { + // Do not test on RSA. It's always deterministic. + test("EC"); + test("RSASSA-PSS"); + } + + static void test(String alg) throws Exception { + var kp = KeyPairGenerator.getInstance(alg).generateKeyPair(); + var signer = XMLUtils.signer(kp.getPrivate(), kp.getPublic()); + + var n1 = getSignature(signer.sign("hello")); // random one + var n2 = getSignature(signer.sign("hello")); // another random one + + signer.prop(DOM_SIGNATURE_RANDOM, new SeededSecureRandom(1L)); + var s1 = getSignature(signer.sign("hello")); // deterministic one + + signer.prop(DOM_SIGNATURE_RANDOM, new SeededSecureRandom(1L)); + var s1again = getSignature(signer.sign("hello")); // deterministic one repeated + + signer.prop(DOM_SIGNATURE_RANDOM, new SeededSecureRandom(2L)); + var s2 = getSignature(signer.sign("hello")); // deterministic two + + Asserts.assertEqualsByteArray(s1, s1again); + assertsAllDifferent(n1, n2, s1, s2); + + signer.prop(DOM_SIGNATURE_PROVIDER, Security.getProvider("SunJCE")); + // Asserts throwing XMLSignatureException with cause NoSuchAlgorithmException + Asserts.assertEquals( + Asserts.assertThrows(XMLSignatureException.class, + () -> signer.sign("hello")).getCause().getClass(), + NoSuchAlgorithmException.class); + } + + private static void assertsAllDifferent(byte[]... inputs) { + for (var a : inputs) { + for (var b : inputs) { + if (a != b) { + Asserts.assertNotEqualsByteArray(a, b); + } + } + } + } + + static byte[] getSignature(Document doc) { + for (var n = doc.getDocumentElement().getFirstChild(); + n != null; n = n.getNextSibling()) { + if ("SignatureValue".equals(n.getLocalName())) { + return Base64.getMimeDecoder().decode(n.getTextContent()); + } + } + throw new IllegalArgumentException("Not found"); + } +} diff --git a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java index fd017ce183b..bea4d541987 100644 --- a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java @@ -1162,6 +1162,22 @@ public class Byte128VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSaturatingUnaryOpProvider() { + return BYTE_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] byteSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4487,6 +4503,94 @@ public class Byte128VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Byte128VectorTests::allTrue); } + static byte SUADDReduce(byte[] a, int idx) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAll(byte[] a) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceByte128VectorTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Byte128VectorTests::SUADDReduce, Byte128VectorTests::SUADDReduceAll); + } + + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "byteSaturatingUnaryOpMaskProvider") + static void SUADDReduceByte128VectorTestsMasked(IntFunction fa, IntFunction fm) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Byte128VectorTests::SUADDReduceMasked, Byte128VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "byteBinaryOpProvider") static void withByte128VectorTests(IntFunction fa, IntFunction fb) { byte[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java index 17ab56f2540..be5b3cf6198 100644 --- a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java @@ -1162,6 +1162,22 @@ public class Byte256VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSaturatingUnaryOpProvider() { + return BYTE_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] byteSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4487,6 +4503,94 @@ public class Byte256VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Byte256VectorTests::allTrue); } + static byte SUADDReduce(byte[] a, int idx) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAll(byte[] a) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceByte256VectorTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Byte256VectorTests::SUADDReduce, Byte256VectorTests::SUADDReduceAll); + } + + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "byteSaturatingUnaryOpMaskProvider") + static void SUADDReduceByte256VectorTestsMasked(IntFunction fa, IntFunction fm) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Byte256VectorTests::SUADDReduceMasked, Byte256VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "byteBinaryOpProvider") static void withByte256VectorTests(IntFunction fa, IntFunction fb) { byte[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java index 4c43b995c2f..0fd68b6f712 100644 --- a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java @@ -1162,6 +1162,22 @@ public class Byte512VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSaturatingUnaryOpProvider() { + return BYTE_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] byteSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4487,6 +4503,94 @@ public class Byte512VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Byte512VectorTests::allTrue); } + static byte SUADDReduce(byte[] a, int idx) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAll(byte[] a) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceByte512VectorTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Byte512VectorTests::SUADDReduce, Byte512VectorTests::SUADDReduceAll); + } + + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "byteSaturatingUnaryOpMaskProvider") + static void SUADDReduceByte512VectorTestsMasked(IntFunction fa, IntFunction fm) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Byte512VectorTests::SUADDReduceMasked, Byte512VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "byteBinaryOpProvider") static void withByte512VectorTests(IntFunction fa, IntFunction fb) { byte[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java index d30afe30fe2..112b2e56b6f 100644 --- a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java @@ -1162,6 +1162,22 @@ public class Byte64VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSaturatingUnaryOpProvider() { + return BYTE_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] byteSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4487,6 +4503,94 @@ public class Byte64VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Byte64VectorTests::allTrue); } + static byte SUADDReduce(byte[] a, int idx) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAll(byte[] a) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceByte64VectorTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Byte64VectorTests::SUADDReduce, Byte64VectorTests::SUADDReduceAll); + } + + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "byteSaturatingUnaryOpMaskProvider") + static void SUADDReduceByte64VectorTestsMasked(IntFunction fa, IntFunction fm) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Byte64VectorTests::SUADDReduceMasked, Byte64VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "byteBinaryOpProvider") static void withByte64VectorTests(IntFunction fa, IntFunction fb) { byte[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java index 8738191f5ca..435cacc013e 100644 --- a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java @@ -1167,6 +1167,22 @@ public class ByteMaxVectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] byteSaturatingUnaryOpProvider() { + return BYTE_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] byteSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> BYTE_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] byteBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4492,6 +4508,94 @@ public class ByteMaxVectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, ByteMaxVectorTests::allTrue); } + static byte SUADDReduce(byte[] a, int idx) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAll(byte[] a) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceByteMaxVectorTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + ByteMaxVectorTests::SUADDReduce, ByteMaxVectorTests::SUADDReduceAll); + } + + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { + byte res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { + byte res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "byteSaturatingUnaryOpMaskProvider") + static void SUADDReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunction fm) { + byte[] a = fa.apply(SPECIES.length()); + byte[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + byte ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ByteVector av = ByteVector.fromArray(SPECIES, a, i); + ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + ByteMaxVectorTests::SUADDReduceMasked, ByteMaxVectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "byteBinaryOpProvider") static void withByteMaxVectorTests(IntFunction fa, IntFunction fb) { byte[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Int128VectorTests.java b/test/jdk/jdk/incubator/vector/Int128VectorTests.java index 0bd77cc1008..d6bca96ea6e 100644 --- a/test/jdk/jdk/incubator/vector/Int128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int128VectorTests.java @@ -1152,6 +1152,22 @@ public class Int128VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSaturatingUnaryOpProvider() { + return INT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] intSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4531,6 +4547,94 @@ public class Int128VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Int128VectorTests::allTrue); } + static int SUADDReduce(int[] a, int idx) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAll(int[] a) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceInt128VectorTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Int128VectorTests::SUADDReduce, Int128VectorTests::SUADDReduceAll); + } + + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAllMasked(int[] a, boolean[] mask) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "intSaturatingUnaryOpMaskProvider") + static void SUADDReduceInt128VectorTestsMasked(IntFunction fa, IntFunction fm) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Int128VectorTests::SUADDReduceMasked, Int128VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "intBinaryOpProvider") static void withInt128VectorTests(IntFunction fa, IntFunction fb) { int[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Int256VectorTests.java b/test/jdk/jdk/incubator/vector/Int256VectorTests.java index a4a0a2bd88e..ac98217b714 100644 --- a/test/jdk/jdk/incubator/vector/Int256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int256VectorTests.java @@ -1152,6 +1152,22 @@ public class Int256VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSaturatingUnaryOpProvider() { + return INT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] intSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4531,6 +4547,94 @@ public class Int256VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Int256VectorTests::allTrue); } + static int SUADDReduce(int[] a, int idx) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAll(int[] a) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceInt256VectorTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Int256VectorTests::SUADDReduce, Int256VectorTests::SUADDReduceAll); + } + + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAllMasked(int[] a, boolean[] mask) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "intSaturatingUnaryOpMaskProvider") + static void SUADDReduceInt256VectorTestsMasked(IntFunction fa, IntFunction fm) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Int256VectorTests::SUADDReduceMasked, Int256VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "intBinaryOpProvider") static void withInt256VectorTests(IntFunction fa, IntFunction fb) { int[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Int512VectorTests.java b/test/jdk/jdk/incubator/vector/Int512VectorTests.java index 0679b410e59..b56236db322 100644 --- a/test/jdk/jdk/incubator/vector/Int512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int512VectorTests.java @@ -1152,6 +1152,22 @@ public class Int512VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSaturatingUnaryOpProvider() { + return INT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] intSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4531,6 +4547,94 @@ public class Int512VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Int512VectorTests::allTrue); } + static int SUADDReduce(int[] a, int idx) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAll(int[] a) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceInt512VectorTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Int512VectorTests::SUADDReduce, Int512VectorTests::SUADDReduceAll); + } + + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAllMasked(int[] a, boolean[] mask) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "intSaturatingUnaryOpMaskProvider") + static void SUADDReduceInt512VectorTestsMasked(IntFunction fa, IntFunction fm) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Int512VectorTests::SUADDReduceMasked, Int512VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "intBinaryOpProvider") static void withInt512VectorTests(IntFunction fa, IntFunction fb) { int[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Int64VectorTests.java b/test/jdk/jdk/incubator/vector/Int64VectorTests.java index c77e246473b..f87a0eb458c 100644 --- a/test/jdk/jdk/incubator/vector/Int64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int64VectorTests.java @@ -1152,6 +1152,22 @@ public class Int64VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSaturatingUnaryOpProvider() { + return INT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] intSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4531,6 +4547,94 @@ public class Int64VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Int64VectorTests::allTrue); } + static int SUADDReduce(int[] a, int idx) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAll(int[] a) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceInt64VectorTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Int64VectorTests::SUADDReduce, Int64VectorTests::SUADDReduceAll); + } + + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAllMasked(int[] a, boolean[] mask) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "intSaturatingUnaryOpMaskProvider") + static void SUADDReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fm) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Int64VectorTests::SUADDReduceMasked, Int64VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "intBinaryOpProvider") static void withInt64VectorTests(IntFunction fa, IntFunction fb) { int[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java index 4f7338c50cb..c61aab0013e 100644 --- a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java @@ -1157,6 +1157,22 @@ public class IntMaxVectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] intSaturatingUnaryOpProvider() { + return INT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] intSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> INT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] intBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4536,6 +4552,94 @@ public class IntMaxVectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, IntMaxVectorTests::allTrue); } + static int SUADDReduce(int[] a, int idx) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAll(int[] a) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIntMaxVectorTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + IntMaxVectorTests::SUADDReduce, IntMaxVectorTests::SUADDReduceAll); + } + + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { + int res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static int SUADDReduceAllMasked(int[] a, boolean[] mask) { + int res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "intSaturatingUnaryOpMaskProvider") + static void SUADDReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction fm) { + int[] a = fa.apply(SPECIES.length()); + int[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + int ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + IntVector av = IntVector.fromArray(SPECIES, a, i); + ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + IntMaxVectorTests::SUADDReduceMasked, IntMaxVectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "intBinaryOpProvider") static void withIntMaxVectorTests(IntFunction fa, IntFunction fb) { int[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Long128VectorTests.java b/test/jdk/jdk/incubator/vector/Long128VectorTests.java index 01cac6a505e..a8cf38c003a 100644 --- a/test/jdk/jdk/incubator/vector/Long128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long128VectorTests.java @@ -1142,6 +1142,22 @@ public class Long128VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSaturatingUnaryOpProvider() { + return LONG_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> LONG_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4553,6 +4569,94 @@ public class Long128VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Long128VectorTests::allTrue); } + static long SUADDReduce(long[] a, int idx) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAll(long[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceLong128VectorTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Long128VectorTests::SUADDReduce, Long128VectorTests::SUADDReduceAll); + } + + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAllMasked(long[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "longSaturatingUnaryOpMaskProvider") + static void SUADDReduceLong128VectorTestsMasked(IntFunction fa, IntFunction fm) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Long128VectorTests::SUADDReduceMasked, Long128VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "longBinaryOpProvider") static void withLong128VectorTests(IntFunction fa, IntFunction fb) { long[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Long256VectorTests.java b/test/jdk/jdk/incubator/vector/Long256VectorTests.java index 9e571c3cbe9..a394a59699f 100644 --- a/test/jdk/jdk/incubator/vector/Long256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long256VectorTests.java @@ -1142,6 +1142,22 @@ public class Long256VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSaturatingUnaryOpProvider() { + return LONG_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> LONG_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4553,6 +4569,94 @@ public class Long256VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Long256VectorTests::allTrue); } + static long SUADDReduce(long[] a, int idx) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAll(long[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceLong256VectorTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Long256VectorTests::SUADDReduce, Long256VectorTests::SUADDReduceAll); + } + + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAllMasked(long[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "longSaturatingUnaryOpMaskProvider") + static void SUADDReduceLong256VectorTestsMasked(IntFunction fa, IntFunction fm) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Long256VectorTests::SUADDReduceMasked, Long256VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "longBinaryOpProvider") static void withLong256VectorTests(IntFunction fa, IntFunction fb) { long[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Long512VectorTests.java b/test/jdk/jdk/incubator/vector/Long512VectorTests.java index 5cb3cc32a68..2f12ea98399 100644 --- a/test/jdk/jdk/incubator/vector/Long512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long512VectorTests.java @@ -1142,6 +1142,22 @@ public class Long512VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSaturatingUnaryOpProvider() { + return LONG_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> LONG_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4553,6 +4569,94 @@ public class Long512VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Long512VectorTests::allTrue); } + static long SUADDReduce(long[] a, int idx) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAll(long[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceLong512VectorTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Long512VectorTests::SUADDReduce, Long512VectorTests::SUADDReduceAll); + } + + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAllMasked(long[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "longSaturatingUnaryOpMaskProvider") + static void SUADDReduceLong512VectorTestsMasked(IntFunction fa, IntFunction fm) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Long512VectorTests::SUADDReduceMasked, Long512VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "longBinaryOpProvider") static void withLong512VectorTests(IntFunction fa, IntFunction fb) { long[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Long64VectorTests.java b/test/jdk/jdk/incubator/vector/Long64VectorTests.java index d5595395b0a..0fda0abed58 100644 --- a/test/jdk/jdk/incubator/vector/Long64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long64VectorTests.java @@ -1142,6 +1142,22 @@ public class Long64VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSaturatingUnaryOpProvider() { + return LONG_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> LONG_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4553,6 +4569,94 @@ public class Long64VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Long64VectorTests::allTrue); } + static long SUADDReduce(long[] a, int idx) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAll(long[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceLong64VectorTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Long64VectorTests::SUADDReduce, Long64VectorTests::SUADDReduceAll); + } + + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAllMasked(long[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "longSaturatingUnaryOpMaskProvider") + static void SUADDReduceLong64VectorTestsMasked(IntFunction fa, IntFunction fm) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Long64VectorTests::SUADDReduceMasked, Long64VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "longBinaryOpProvider") static void withLong64VectorTests(IntFunction fa, IntFunction fb) { long[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java index cec3e0756b2..7aa2bc4c510 100644 --- a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java @@ -1147,6 +1147,22 @@ public class LongMaxVectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] longSaturatingUnaryOpProvider() { + return LONG_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] longSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> LONG_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] longBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4558,6 +4574,94 @@ public class LongMaxVectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, LongMaxVectorTests::allTrue); } + static long SUADDReduce(long[] a, int idx) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAll(long[] a) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceLongMaxVectorTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + LongMaxVectorTests::SUADDReduce, LongMaxVectorTests::SUADDReduceAll); + } + + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { + long res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static long SUADDReduceAllMasked(long[] a, boolean[] mask) { + long res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "longSaturatingUnaryOpMaskProvider") + static void SUADDReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunction fm) { + long[] a = fa.apply(SPECIES.length()); + long[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + long ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + LongVector av = LongVector.fromArray(SPECIES, a, i); + ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + LongMaxVectorTests::SUADDReduceMasked, LongMaxVectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "longBinaryOpProvider") static void withLongMaxVectorTests(IntFunction fa, IntFunction fb) { long[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Short128VectorTests.java b/test/jdk/jdk/incubator/vector/Short128VectorTests.java index 1ce486eb709..5f4c54bb708 100644 --- a/test/jdk/jdk/incubator/vector/Short128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short128VectorTests.java @@ -1152,6 +1152,22 @@ public class Short128VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSaturatingUnaryOpProvider() { + return SHORT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> SHORT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4478,6 +4494,94 @@ public class Short128VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Short128VectorTests::allTrue); } + static short SUADDReduce(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAll(short[] a) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceShort128VectorTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Short128VectorTests::SUADDReduce, Short128VectorTests::SUADDReduceAll); + } + + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAllMasked(short[] a, boolean[] mask) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "shortSaturatingUnaryOpMaskProvider") + static void SUADDReduceShort128VectorTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Short128VectorTests::SUADDReduceMasked, Short128VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "shortBinaryOpProvider") static void withShort128VectorTests(IntFunction fa, IntFunction fb) { short[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Short256VectorTests.java b/test/jdk/jdk/incubator/vector/Short256VectorTests.java index 8361c59923f..88986575f60 100644 --- a/test/jdk/jdk/incubator/vector/Short256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short256VectorTests.java @@ -1152,6 +1152,22 @@ public class Short256VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSaturatingUnaryOpProvider() { + return SHORT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> SHORT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4478,6 +4494,94 @@ public class Short256VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Short256VectorTests::allTrue); } + static short SUADDReduce(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAll(short[] a) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceShort256VectorTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Short256VectorTests::SUADDReduce, Short256VectorTests::SUADDReduceAll); + } + + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAllMasked(short[] a, boolean[] mask) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "shortSaturatingUnaryOpMaskProvider") + static void SUADDReduceShort256VectorTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Short256VectorTests::SUADDReduceMasked, Short256VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "shortBinaryOpProvider") static void withShort256VectorTests(IntFunction fa, IntFunction fb) { short[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Short512VectorTests.java b/test/jdk/jdk/incubator/vector/Short512VectorTests.java index a9c1a5eb7ed..0f474375a47 100644 --- a/test/jdk/jdk/incubator/vector/Short512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short512VectorTests.java @@ -1152,6 +1152,22 @@ public class Short512VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSaturatingUnaryOpProvider() { + return SHORT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> SHORT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4478,6 +4494,94 @@ public class Short512VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Short512VectorTests::allTrue); } + static short SUADDReduce(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAll(short[] a) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceShort512VectorTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Short512VectorTests::SUADDReduce, Short512VectorTests::SUADDReduceAll); + } + + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAllMasked(short[] a, boolean[] mask) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "shortSaturatingUnaryOpMaskProvider") + static void SUADDReduceShort512VectorTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Short512VectorTests::SUADDReduceMasked, Short512VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "shortBinaryOpProvider") static void withShort512VectorTests(IntFunction fa, IntFunction fb) { short[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/Short64VectorTests.java b/test/jdk/jdk/incubator/vector/Short64VectorTests.java index d10b4ca7196..4b99ed6d84c 100644 --- a/test/jdk/jdk/incubator/vector/Short64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short64VectorTests.java @@ -1152,6 +1152,22 @@ public class Short64VectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSaturatingUnaryOpProvider() { + return SHORT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> SHORT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4478,6 +4494,94 @@ public class Short64VectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, Short64VectorTests::allTrue); } + static short SUADDReduce(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAll(short[] a) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceShort64VectorTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + Short64VectorTests::SUADDReduce, Short64VectorTests::SUADDReduceAll); + } + + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAllMasked(short[] a, boolean[] mask) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "shortSaturatingUnaryOpMaskProvider") + static void SUADDReduceShort64VectorTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + Short64VectorTests::SUADDReduceMasked, Short64VectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "shortBinaryOpProvider") static void withShort64VectorTests(IntFunction fa, IntFunction fb) { short[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java index cca15c4e941..2bb3b9c1557 100644 --- a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java @@ -1157,6 +1157,22 @@ public class ShortMaxVectorTests extends AbstractVectorTest { toArray(Object[][]::new); } + @DataProvider + public Object[][] shortSaturatingUnaryOpProvider() { + return SHORT_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] shortSaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> SHORT_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + @DataProvider public Object[][] shortBinaryOpMaskProvider() { return BOOLEAN_MASK_GENERATORS.stream(). @@ -4483,6 +4499,94 @@ public class ShortMaxVectorTests extends AbstractVectorTest { assertReductionBoolArraysEquals(r, mask, ShortMaxVectorTests::allTrue); } + static short SUADDReduce(short[] a, int idx) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAll(short[] a) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); + } + + return res; + } + + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceShortMaxVectorTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + } + } + + assertReductionArraysEquals(r, ra, a, + ShortMaxVectorTests::SUADDReduce, ShortMaxVectorTests::SUADDReduceAll); + } + + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { + short res = 0; + for (int i = idx; i < (idx + SPECIES.length()); i++) { + if (mask[i % SPECIES.length()]) + res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); + } + + return res; + } + + static short SUADDReduceAllMasked(short[] a, boolean[] mask) { + short res = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); + } + + return res; + } + @Test(dataProvider = "shortSaturatingUnaryOpMaskProvider") + static void SUADDReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunction fm) { + short[] a = fa.apply(SPECIES.length()); + short[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); + short ra = 0; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = 0; + for (int i = 0; i < a.length; i += SPECIES.length()) { + ShortVector av = ShortVector.fromArray(SPECIES, a, i); + ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + } + } + + assertReductionArraysEqualsMasked(r, ra, a, mask, + ShortMaxVectorTests::SUADDReduceMasked, ShortMaxVectorTests::SUADDReduceAllMasked); + } + @Test(dataProvider = "shortBinaryOpProvider") static void withShortMaxVectorTests(IntFunction fa, IntFunction fb) { short[] a = fa.apply(SPECIES.length()); diff --git a/test/jdk/jdk/incubator/vector/gen-template.sh b/test/jdk/jdk/incubator/vector/gen-template.sh index 71dc4a1ef0a..a6f794a5559 100644 --- a/test/jdk/jdk/incubator/vector/gen-template.sh +++ b/test/jdk/jdk/incubator/vector/gen-template.sh @@ -66,6 +66,8 @@ reduction_op="Reduction-op" reduction_op_func="Reduction-op-func" reduction_op_masked="Reduction-Masked-op" reduction_op_masked_func="Reduction-Masked-op-func" +reduction_saturating_op="SaturatingReduction-op" +reduction_saturating_op_masked="SaturatingReduction-Masked-op" unary_math_template="Unary-op-math" binary_math_template="Binary-op-math" binary_math_broadcast_template="Binary-Broadcast-op-math" @@ -377,6 +379,13 @@ function gen_bool_reduction_op { gen_op_tmpl $bool_reduction_scalar "$@" gen_op_tmpl $bool_reduction_template "$@" } +function gen_saturating_reduction_op { + echo "Generating saturating reduction op $1 ($2)..." + gen_op_tmpl $reduction_scalar_func "$@" + gen_op_tmpl $reduction_saturating_op "$@" + gen_op_tmpl $reduction_scalar_masked_func "$@" + gen_op_tmpl $reduction_saturating_op_masked "$@" +} function gen_with_op { echo "Generating with op $1 ($2)..." @@ -513,6 +522,9 @@ gen_reduction_op_func "FIRST_NONZERO" "firstNonZero" "" "(\$type\$) 0" gen_bool_reduction_op "anyTrue" "|" "BITWISE" "false" gen_bool_reduction_op "allTrue" "\&" "BITWISE" "true" +# Saturating reductions. +gen_saturating_reduction_op "SUADD" "(\$type\$) VectorMath.addSaturatingUnsigned" "BITWISE" "0" + #Insert gen_with_op "withLane" "" "" "" diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template new file mode 100644 index 00000000000..bf03a4d0430 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template @@ -0,0 +1,21 @@ + $type$[] a = fa.apply(SPECIES.length()); + $type$[] r = fr.apply(SPECIES.length()); + boolean[] mask = fm.apply(SPECIES.length()); + VectorMask<$Wideboxtype$> vmask = VectorMask.fromArray(SPECIES, mask, 0); + $type$ ra = [[TEST_INIT]]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.[[TEST]], vmask); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = [[TEST_INIT]]; + for (int i = 0; i < a.length; i += SPECIES.length()) { + $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); + ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]], vmask)); + } + } + diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template new file mode 100644 index 00000000000..7082ff0795e --- /dev/null +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template @@ -0,0 +1,19 @@ + $type$[] a = fa.apply(SPECIES.length()); + $type$[] r = fr.apply(SPECIES.length()); + $type$ ra = [[TEST_INIT]]; + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + for (int i = 0; i < a.length; i += SPECIES.length()) { + $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); + r[i] = av.reduceLanes(VectorOperators.[[TEST]]); + } + } + + for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = [[TEST_INIT]]; + for (int i = 0; i < a.length; i += SPECIES.length()) { + $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); + ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]])); + } + } + diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-Masked-op.template new file mode 100644 index 00000000000..f9e965128b1 --- /dev/null +++ b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-Masked-op.template @@ -0,0 +1,6 @@ + @Test(dataProvider = "$type$SaturatingUnaryOpMaskProvider") + static void [[TEST]]Reduce$vectorteststype$Masked(IntFunction<$type$[]> fa, IntFunction fm) { +[[KERNEL]] + assertReductionArraysEqualsMasked(r, ra, a, mask, + $vectorteststype$::[[TEST]]ReduceMasked, $vectorteststype$::[[TEST]]ReduceAllMasked); + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template new file mode 100644 index 00000000000..fdd9e47167e --- /dev/null +++ b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template @@ -0,0 +1,7 @@ + + @Test(dataProvider = "$type$SaturatingUnaryOpProvider") + static void [[TEST]]Reduce$vectorteststype$(IntFunction<$type$[]> fa) { +[[KERNEL]] + assertReductionArraysEquals(r, ra, a, + $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index d50267318f5..33c52f18c1c 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -1430,6 +1430,22 @@ relativeError)); toArray(Object[][]::new); } + @DataProvider + public Object[][] $type$SaturatingUnaryOpProvider() { + return $TYPE$_SATURATING_GENERATORS.stream(). + map(f -> new Object[]{f}). + toArray(Object[][]::new); + } + + @DataProvider + public Object[][] $type$SaturatingUnaryOpMaskProvider() { + return BOOLEAN_MASK_GENERATORS.stream(). + flatMap(fm -> $TYPE$_SATURATING_GENERATORS.stream().map(fa -> { + return new Object[] {fa, fm}; + })). + toArray(Object[][]::new); + } + #end[!FP] @DataProvider public Object[][] $type$BinaryOpMaskProvider() { diff --git a/test/jdk/jdk/internal/misc/Unsafe/AddressComputationContractTest.java b/test/jdk/jdk/internal/misc/Unsafe/AddressComputationContractTest.java new file mode 100644 index 00000000000..ebdfa843272 --- /dev/null +++ b/test/jdk/jdk/internal/misc/Unsafe/AddressComputationContractTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 org.junit.jupiter.api.Test; + +import static jdk.internal.misc.Unsafe.getUnsafe; +import static org.junit.jupiter.api.Assertions.*; + +/* + * @test + * @bug 8361300 + * @summary Verify Unsafe memory address computation method contracts, + * exposed via sun.misc.Unsafe + * @modules java.base/jdk.internal.misc + * @run junit AddressComputationContractTest + */ +public class AddressComputationContractTest { + + int instanceField; + static int staticField; + + private static final Field INSTANCE_FIELD; + private static final Field STATIC_FIELD; + + static { + try { + INSTANCE_FIELD = AddressComputationContractTest.class.getDeclaredField("instanceField"); + STATIC_FIELD = AddressComputationContractTest.class.getDeclaredField("staticField"); + } catch (ReflectiveOperationException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + @Test + void objectFieldOffset() { + assertDoesNotThrow(() -> getUnsafe().objectFieldOffset(INSTANCE_FIELD)); + assertThrows(NullPointerException.class, () -> getUnsafe().objectFieldOffset(null)); + assertThrows(IllegalArgumentException.class, () -> getUnsafe().objectFieldOffset(STATIC_FIELD)); + } + + @Test + void knownObjectFieldOffset() { + assertDoesNotThrow(() -> getUnsafe().objectFieldOffset(AddressComputationContractTest.class, "instanceField")); + assertThrows(NullPointerException.class, () -> getUnsafe().objectFieldOffset(null, "instanceField")); + assertThrows(NullPointerException.class, () -> getUnsafe().objectFieldOffset(AddressComputationContractTest.class, null)); + // Two conventional failure cases, not necessarily complete + var dneMsg = assertThrows(InternalError.class, () -> getUnsafe().objectFieldOffset(AddressComputationContractTest.class, "doesNotExist")).getMessage(); + assertTrue(dneMsg.contains("AddressComputationContractTest.doesNotExist") && dneMsg.contains("not found"), dneMsg); + var staticMsg = assertThrows(InternalError.class, () -> getUnsafe().objectFieldOffset(AddressComputationContractTest.class, "staticField")).getMessage(); + assertTrue(staticMsg.contains("AddressComputationContractTest.staticField") && staticMsg.contains("static field"), staticMsg); + } + + @Test + void staticFieldOffset() { + assertDoesNotThrow(() -> getUnsafe().staticFieldOffset(STATIC_FIELD)); + assertThrows(NullPointerException.class, () -> getUnsafe().staticFieldOffset(null)); + assertThrows(IllegalArgumentException.class, () -> getUnsafe().staticFieldOffset(INSTANCE_FIELD)); + } + + @Test + void staticFieldBase() { + assertDoesNotThrow(() -> getUnsafe().staticFieldBase(STATIC_FIELD)); + assertThrows(NullPointerException.class, () -> getUnsafe().staticFieldBase(null)); + assertThrows(IllegalArgumentException.class, () -> getUnsafe().staticFieldBase(INSTANCE_FIELD)); + } + + @Test + void arrayBaseOffset() { + assertDoesNotThrow(() -> getUnsafe().arrayBaseOffset(int[].class)); + assertThrows(NullPointerException.class, () -> getUnsafe().arrayBaseOffset(null)); + // Caused by VM trying to throw java.lang.InvalidClassException (there's one in java.io instead) + assertThrows(NoClassDefFoundError.class, () -> getUnsafe().arrayBaseOffset(AddressComputationContractTest.class)); + } + + @Test + void arrayIndexScale() { + assertDoesNotThrow(() -> getUnsafe().arrayIndexScale(int[].class)); + assertThrows(NullPointerException.class, () -> getUnsafe().arrayIndexScale(null)); + // Caused by VM trying to throw java.lang.InvalidClassException (there's one in java.io instead) + assertThrows(NoClassDefFoundError.class, () -> getUnsafe().arrayIndexScale(AddressComputationContractTest.class)); + } +} diff --git a/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java b/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java index 92b8cf282b7..b7721899ea3 100644 --- a/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java +++ b/test/jdk/jdk/internal/platform/docker/GetFreeSwapSpaceSize.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020, 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2020, 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java index 8e3d0cacd57..b9d031f0309 100644 --- a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java +++ b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020, 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2020, 2022, Tencent. 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. * diff --git a/test/jdk/jdk/jfr/api/flightrecorder/TestSettingsControl.java b/test/jdk/jdk/jfr/api/flightrecorder/TestSettingsControl.java index e186db41bc2..e0e7e8b09c6 100644 --- a/test/jdk/jdk/jfr/api/flightrecorder/TestSettingsControl.java +++ b/test/jdk/jdk/jfr/api/flightrecorder/TestSettingsControl.java @@ -28,6 +28,7 @@ import static jdk.test.lib.Asserts.assertTrue; import java.util.Set; import jdk.jfr.Event; +import jdk.jfr.Name; import jdk.jfr.Recording; import jdk.jfr.SettingControl; import jdk.jfr.SettingDefinition; @@ -42,7 +43,7 @@ import jdk.jfr.SettingDefinition; public class TestSettingsControl { static class MySettingsControl extends SettingControl { - public static boolean setWasCalled; + public static boolean myvalueSet; private String value = "default"; @@ -57,7 +58,9 @@ public class TestSettingsControl { @Override public void setValue(String value) { - setWasCalled = true; + if ("myvalue".equals(value)) { + myvalueSet = true; + } this.value = value; } @@ -67,9 +70,10 @@ public class TestSettingsControl { } } - + @Name("M") static class MyCustomSettingEvent extends Event { @SettingDefinition + @Name("m") boolean mySetting(MySettingsControl msc) { return true; } @@ -77,13 +81,13 @@ public class TestSettingsControl { public static void main(String[] args) throws Throwable { Recording r = new Recording(); - r.enable(MyCustomSettingEvent.class).with("mySetting", "myvalue"); + r.enable("M").with("m", "myvalue"); r.start(); MyCustomSettingEvent e = new MyCustomSettingEvent(); e.commit(); r.stop(); r.close(); - assertTrue(MySettingsControl.setWasCalled, "SettingControl.setValue was not called"); + assertTrue(MySettingsControl.myvalueSet, "SettingControl.setValue(\"myvalue\") was not called"); } } diff --git a/test/jdk/jdk/jfr/api/metadata/annotations/TestThrottle.java b/test/jdk/jdk/jfr/api/metadata/annotations/TestThrottle.java index 89aab24b802..fb1985a03ba 100644 --- a/test/jdk/jdk/jfr/api/metadata/annotations/TestThrottle.java +++ b/test/jdk/jdk/jfr/api/metadata/annotations/TestThrottle.java @@ -29,11 +29,17 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; +import jdk.jfr.AnnotationElement; import jdk.jfr.Enabled; import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.Name; import jdk.jfr.Recording; import jdk.jfr.SettingControl; import jdk.jfr.SettingDefinition; @@ -132,6 +138,7 @@ public class TestThrottle { testThrottleThresholded(); testThrottleNormalRate(); testThrottleUserdefined(); + testThrottleDynamic(); } private static void testUnthrottled() throws Exception { @@ -232,6 +239,40 @@ public class TestThrottle { } } + private static void testThrottleDynamic() throws Exception { + List offAnnotations = new ArrayList<>(); + offAnnotations.add(new AnnotationElement(Name.class, "DynamicZero")); + offAnnotations.add(new AnnotationElement(Throttle.class, "0/s")); + EventFactory offFactory = EventFactory.create(offAnnotations, List.of()); + + List highRateAnnotations = new ArrayList<>(); + highRateAnnotations.add(new AnnotationElement(Name.class, "DynamicHighRate")); + highRateAnnotations.add(new AnnotationElement(Throttle.class, "1000/s")); + EventFactory highRateFactory = EventFactory.create(highRateAnnotations, List.of()); + + List events = new CopyOnWriteArrayList<>(); + try (RecordingStream r = new RecordingStream()) { + r.enable("DynamicZero"); + r.enable("DynamicHighRate"); + r.onEvent(events::add); + r.startAsync(); + Event offEvent = offFactory.newEvent(); + offEvent.commit(); + Event highRateEvent = highRateFactory.newEvent(); + highRateEvent.begin(); + highRateEvent.commit(); + r.stop(); + if (events.size() != 1) { + System.out.println(events); + throw new Exception("Expected one dynamic event"); + } + if (!events.get(0).getEventType().getName().equals("DynamicHighRate")) { + System.out.println(events); + throw new Exception("Expected DynamicHighRate"); + } + } + } + @SuppressWarnings("unchecked") private static void testEvent(Class eventClass, boolean shouldCommit) throws Exception { try (Recording r = new Recording()) { @@ -272,6 +313,8 @@ public class TestThrottle { } if (shouldCommit) { assertEvents(r, eventClass.getName(), 17 + 50 + 11); + } else { + assertEvents(r, eventClass.getName(), 0); } } } diff --git a/test/jdk/jdk/jfr/api/recording/misc/TestGetStreamWithFailure.java b/test/jdk/jdk/jfr/api/recording/misc/TestGetStreamWithFailure.java new file mode 100644 index 00000000000..d294b06270e --- /dev/null +++ b/test/jdk/jdk/jfr/api/recording/misc/TestGetStreamWithFailure.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.api.recording.misc; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.ParseException; +import java.util.List; +import java.util.function.BiConsumer; + +import jdk.jfr.Configuration; +import jdk.jfr.Event; +import jdk.jfr.Recording; + +/** + * @test + * @summary Verify that resources are not leaked in case of failure + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm jdk.jfr.api.recording.misc.TestGetStreamWithFailure + */ +public class TestGetStreamWithFailure { + private interface TestCase { + void execute(Recording recording, List chunks) throws Exception; + } + + private static class FillerEvent extends Event { + } + + public static void main(String[] args) throws Exception { + testMissingChunk(); + testClosedStream(); + testNoChunks(); + } + + // Simulates a user deleting all chunks for a recording + private static void testNoChunks() throws Exception { + testStream((r, chunks) -> { + deleteAllChunks(); + try { + InputStream is = r.getStream(null, null); + throw new Exception("Expected exception when all chunks are missing"); + } catch (IOException ioe) { + if (!ioe.getMessage().equals("Recording data missing on disk.")) { + throw new Exception("Unexpected exception: " + ioe.getMessage()); + } + } + }); + } + + private static void deleteAllChunks() throws Exception { + for (Path chunk : findChunks()) { + Files.delete(chunk); + } + } + + // Simulates a user deleting a single chunk file on disk. + private static void testMissingChunk() throws Exception { + testStream((r, chunks) -> { + Files.delete(chunks.get(1)); + try (InputStream is = r.getStream(null, null)) { + is.readAllBytes(); + } + }); + } + + // Simulates a user closing a stream before all the data has been read, for + // example, if InputStream::read throws an IOException. + private static void testClosedStream() throws Exception { + testStream((r, chunks) -> { + int size = (int) (r.getSize()); + try (InputStream is = r.getStream(null, null)) { + is.readNBytes(size / 2); + } + }); + } + + private static void testStream(TestCase testCase) throws Exception { + try (Recording r = createRecordingData()) { + List chunks = findChunks(); + if (chunks.size() < 3) { + throw new Exception("Expected recording to have at least three chunks"); + } + testCase.execute(r, chunks); + r.close(); + if (!findChunks().isEmpty()) { + throw new Exception("Chunks left behind."); + } + deleteAllChunks(); + } + } + + private static List findChunks() throws Exception { + String repository = System.getProperty("jdk.jfr.repository"); + if (repository == null) { + throw new Exception("No system property for JFR repository"); + } + Path dir = Path.of(repository); + return Files.walk(dir).filter(p -> p.toString().endsWith(".jfr")).toList(); + } + + private static Recording createRecordingData() throws IOException, ParseException, InterruptedException { + Configuration c = Configuration.getConfiguration("default"); + Recording r = new Recording(); + r.start(); + emitEvents(); // Chunk 1 + try (Recording s = new Recording()) { + s.start(); + emitEvents(); // Chunk 2 + } + emitEvents(); // Chunk 3 + r.stop(); + return r; + } + + private static void emitEvents() throws InterruptedException { + for (int i = 0; i < 100_000; i++) { + FillerEvent e = new FillerEvent(); + e.commit(); + } + } +} diff --git a/test/jdk/jdk/jfr/api/settings/RegExpControl.java b/test/jdk/jdk/jfr/api/settings/RegExpControl.java index 051cfb3aa09..182cd3998b9 100644 --- a/test/jdk/jdk/jfr/api/settings/RegExpControl.java +++ b/test/jdk/jdk/jfr/api/settings/RegExpControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,14 @@ import jdk.jfr.SettingControl; public final class RegExpControl extends SettingControl { private Pattern pattern = Pattern.compile(".*"); + // Purpose of this constructor is to ensure that the correct + // constructor is picked when the event class is registered + public RegExpControl(String dummy) { + } + + public RegExpControl() { + } + public void setValue(String value) { this.pattern = Pattern.compile(value); } diff --git a/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java b/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java index 674270b0449..16dd6f44fed 100644 --- a/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java +++ b/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java @@ -161,18 +161,18 @@ public class HeapSummaryEventAllGcs { long toStart = Events.assertField(event, "toSpace.start").getValue(); long toEnd = Events.assertField(event, "toSpace.end").getValue(); Asserts.assertEquals(oldEnd, youngStart, "Young should start where old ends"); - Asserts.assertEquals(youngStart, edenStart, "Eden should be placed first in young"); if (fromStart < toStart) { - // [eden][from][to] - Asserts.assertGreaterThanOrEqual(fromStart, edenEnd, "From should start after eden"); + // [from][to][eden] + Asserts.assertEquals(youngStart, fromStart, "From should be placed first in young"); Asserts.assertLessThanOrEqual(fromEnd, toStart, "To should start after From"); - Asserts.assertLessThanOrEqual(toEnd, youngEnd, "To should start after From"); + Asserts.assertLessThanOrEqual(toEnd, edenStart, "Eden should start after To"); } else { - // [eden][to][from] - Asserts.assertGreaterThanOrEqual(toStart, edenEnd, "From should start after eden"); - Asserts.assertLessThanOrEqual(toEnd, fromStart, "To should start after From"); - Asserts.assertLessThanOrEqual(fromEnd, youngEnd, "To should start after From"); + // [to][from][eden] + Asserts.assertEquals(youngStart, toStart, "To should be placed first in young"); + Asserts.assertLessThanOrEqual(toEnd, fromStart, "From should start after to"); + Asserts.assertLessThanOrEqual(fromEnd, edenStart, "Eden should start after From"); } + Asserts.assertEquals(edenEnd, youngEnd, "Eden should be last of young"); } private static void checkVirtualSpace(RecordedEvent event, String structName) { diff --git a/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java b/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java new file mode 100644 index 00000000000..a86229c65e3 --- /dev/null +++ b/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.event.oldobject; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import jdk.jfr.consumer.EventStream; +import jdk.jfr.consumer.RecordingFile; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/** +* @test +* @bug 8364090 +* @summary Tests Dump reason and OldObjectSample events at OOME. +* @requires vm.flagless +* @requires vm.hasJFR +* @library /test/lib +* @run main/othervm jdk.jfr.event.oldobject.TestEmergencyDumpAtOOM +*/ +public class TestEmergencyDumpAtOOM { + + public static List DEFAULT_LEAKER_ARGS = List.of( + "-Xmx64m", + "-XX:TLABSize=2k", + "-XX:StartFlightRecording:dumponexit=true,filename=oom.jfr", + Leaker.class.getName() + ); + + public static class Leaker { + public static void main(String... args) { + List list = new ArrayList<>(); + while (true) { + list.add(new byte[1024]); + } + } + } + + private static void test(boolean shouldCrash) throws Exception { + List args = new ArrayList<>(DEFAULT_LEAKER_ARGS); + if (shouldCrash) { + args.add(0, "-XX:+CrashOnOutOfMemoryError"); + } + + while (true) { + Process p = ProcessTools.createTestJavaProcessBuilder(args).start(); + p.waitFor(); + OutputAnalyzer output = new OutputAnalyzer(p); + if (!output.contains("java.lang.OutOfMemoryError")) { + throw new RuntimeException("OutOfMemoryError did not happen."); + } + + // Check recording file + String jfrFileName = shouldCrash ? String.format("hs_err_pid%d.jfr", p.pid()) : "oom.jfr"; + Path jfrPath = Path.of(jfrFileName); + Asserts.assertTrue(Files.exists(jfrPath), "No jfr recording file " + jfrFileName + " exists"); + + // Check events + AtomicLong oldObjects = new AtomicLong(); + AtomicReference shutdownReason = new AtomicReference<>(); + AtomicReference dumpReason = new AtomicReference<>(); + try (EventStream stream = EventStream.openFile(jfrPath)) { + stream.onEvent("jdk.OldObjectSample", e -> oldObjects.incrementAndGet()); + stream.onEvent("jdk.Shutdown", e -> shutdownReason.set(e.getString("reason"))); + stream.onEvent("jdk.DumpReason", e -> dumpReason.set(e.getString("reason"))); + stream.start(); + } + + // Check OldObjectSample events + if (oldObjects.get() > 0L) { + if (shouldCrash) { + Asserts.assertEquals("VM Error", shutdownReason.get()); + Asserts.assertEquals("Out of Memory", dumpReason.get()); + } else { + Asserts.assertEquals("No remaining non-daemon Java threads", shutdownReason.get()); + } + // Passed this test + return; + } + + System.out.println("Could not find OldObjectSample events. Retrying."); + } + } + + public static void main(String... args) throws Exception { + test(true); + test(false); + } +} diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java index b0b9d6d2be7..ef2c73514a3 100644 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java +++ b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleThrottling.java @@ -23,6 +23,7 @@ package jdk.jfr.event.profiling; import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; import java.time.Duration; import java.time.Instant; import java.util.List; @@ -71,7 +72,7 @@ public class TestCPUTimeSampleThrottling { } /** - * Counting the events that are emitted for a given throttle in a given time. + * Counting the events that are emitted for a given throttle in a given (CPU) time. *

    * The result is wall-clock independent; it only records the CPU-time and the number of * emitted events. The result, therefore, does not depend on the load of the machine. @@ -83,15 +84,9 @@ public class TestCPUTimeSampleThrottling { recording.enable(EventNames.CPUTimeSample) .with("throttle", throttle); - var bean = ManagementFactory.getThreadMXBean(); - recording.start(); - long startThreadCpuTime = bean.getCurrentThreadCpuTime(); - - wasteCPU(timeMs); - - long spendCPUTime = bean.getCurrentThreadCpuTime() - startThreadCpuTime; + long spendCPUTime = wasteCPU(timeMs); recording.stop(); @@ -99,19 +94,20 @@ public class TestCPUTimeSampleThrottling { .filter(e -> e.getThread().getJavaName() .equals(Thread.currentThread().getName())) .count(); - return new EventCount(eventCount, spendCPUTime / 1_000_000_000f); } } - private static void wasteCPU(int durationMs) { - long start = System.currentTimeMillis(); + private static long wasteCPU(int durationMs) { + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + long start = bean.getCurrentThreadCpuTime(); double i = 0; - while (System.currentTimeMillis() - start < durationMs) { + while (bean.getCurrentThreadCpuTime() - start < durationMs * 1_000_000) { for (int j = 0; j < 100000; j++) { i = Math.sqrt(i * Math.pow(Math.sqrt(Math.random()), Math.random())); } } + return bean.getCurrentThreadCpuTime() - start; } } diff --git a/test/jdk/jdk/jfr/jmx/streaming/TestEnableDisable.java b/test/jdk/jdk/jfr/jmx/streaming/TestEnableDisable.java index 06e9691cfa2..5b1435c9d9d 100644 --- a/test/jdk/jdk/jfr/jmx/streaming/TestEnableDisable.java +++ b/test/jdk/jdk/jfr/jmx/streaming/TestEnableDisable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ import jdk.management.jfr.RemoteRecordingStream; /** * @test * @requires vm.flagless - * @summary Tests that event settings for a RemoteRecordingStream can be changed + * @summary Tests that the enabled setting can be configured for a RemoteRecordingStream * @requires vm.hasJFR * @library /test/lib /test/jdk * @run main/othervm jdk.jfr.jmx.streaming.TestEnableDisable diff --git a/test/jdk/jdk/jfr/jmx/streaming/TestWithers.java b/test/jdk/jdk/jfr/jmx/streaming/TestWithers.java new file mode 100644 index 00000000000..933337ef76b --- /dev/null +++ b/test/jdk/jdk/jfr/jmx/streaming/TestWithers.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.jmx.streaming; + +import java.lang.management.ManagementFactory; +import java.time.Duration; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import javax.management.MBeanServerConnection; + +import jdk.jfr.Event; +import jdk.jfr.EventSettings; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Name; +import jdk.jfr.Period; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.management.jfr.RemoteRecordingStream; + +/** + * @test + * @requires vm.flagless + * @summary Tests that event settings for a RemoteRecordingStream can be changed + * @requires vm.hasJFR + * @library /test/lib /test/jdk + * @run main/othervm jdk.jfr.jmx.streaming.TestWithers + */ +public class TestWithers { + private static final Set RESULT = Collections.synchronizedSet(new HashSet<>()); + + @Name("AA") + @StackTrace(false) + static class A extends Event { + } + + @Name("BB") + @StackTrace(true) + static class B extends Event { + } + + @Name("CC") + @Threshold("10 h") + static class C extends Event { + } + + @Name("DD") + @Threshold("10 h") + static class D extends Event { + } + + @Name("EE") + @StackTrace(false) + static class E extends Event { + } + + @Name("FF") + @Period("10 h") + static class F extends Event { + } + + public static void main(String... args) throws Exception { + MBeanServerConnection conn = ManagementFactory.getPlatformMBeanServer(); + try (RemoteRecordingStream stream = new RemoteRecordingStream(conn)) { + addCheck(stream, es -> es.withStackTrace(), "AA", TestWithers::hasStackTrace); + addCheck(stream, es -> es.withoutStackTrace(), "BB", e -> !hasStackTrace(e)); + addCheck(stream, es -> es.withThreshold(Duration.ofMillis(0)), "CC", e -> true); + addCheck(stream, es -> es.withoutThreshold(), "DD", e -> true); + addCheck(stream, es -> es.with("stackTrace", "true"), "EE", TestWithers::hasStackTrace); + addCheck(stream, es -> es.withPeriod(Duration.ofMillis(700)), "FF", e -> true); + FlightRecorder.addPeriodicEvent(F.class, () -> { + F f = new F(); + f.commit(); + }); + stream.onFlush(() -> { + System.out.println(RESULT); + if (RESULT.size() == 6) { + stream.close(); + } + }); + + stream.startAsync(); + A a = new A(); + a.commit(); + + B b = new B(); + b.commit(); + + C c = new C(); + c.commit(); + + D d = new D(); + d.commit(); + + E e = new E(); + e.commit(); + + stream.awaitTermination(); + } + } + + private static void addCheck(RemoteRecordingStream stream, Consumer es, String eventName, Predicate validator) { + es.accept(stream.enable(eventName)); + stream.onEvent(eventName, e -> { + System.out.println(e); + if (validator.test(e)) { + RESULT.add(eventName); + } + }); + } + + private static boolean hasStackTrace(RecordedEvent e) { + RecordedStackTrace rs = e.getStackTrace(); + return rs != null && !rs.getFrames().isEmpty(); + } +} \ 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 0822205bbaf..8754f2c6e64 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 @@ -416,50 +416,6 @@ * @run main/othervm/manual -Djava.security.debug=certpath CAInterop certignarootca CRL */ -/* - * @test id=affirmtrustcommercialca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Commercial CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustcommercialca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustcommercialca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustcommercialca CRL - */ - -/* - * @test id=affirmtrustnetworkingca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Networking CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustnetworkingca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustnetworkingca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustnetworkingca CRL - */ - -/* - * @test id=affirmtrustpremiumca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Premium CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustpremiumca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustpremiumca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustpremiumca CRL - */ - -/* - * @test id=affirmtrustpremiumeccca - * @bug 8040012 - * @summary Interoperability tests with AffirmTrust Premium ECC CA - * @library /test/lib - * @build jtreg.SkippedException ValidatePathWithURL CAInterop - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp CAInterop affirmtrustpremiumeccca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath,ocsp -Dcom.sun.security.ocsp.useget=false CAInterop affirmtrustpremiumeccca OCSP - * @run main/othervm/manual -Djava.security.debug=certpath CAInterop affirmtrustpremiumeccca CRL - */ - /* * @test id=teliarootcav2 * @bug 8317373 @@ -726,20 +682,6 @@ public class CAInterop { new CATestURLs("https://valid.servicesca.dhimyotis.com", "https://revoked.servicesca.dhimyotis.com"); - // These are listed at https://www.affirmtrust.com/resources/ - case "affirmtrustcommercialca" -> - new CATestURLs("https://validcommercial.affirmtrust.com", - "https://revokedcommercial.affirmtrust.com"); - case "affirmtrustnetworkingca" -> - new CATestURLs("https://validnetworking.affirmtrust.com", - "https://revokednetworking.affirmtrust.com"); - case "affirmtrustpremiumca" -> - new CATestURLs("https://validpremium.affirmtrust.com", - "https://revokedpremium.affirmtrust.com"); - case "affirmtrustpremiumeccca" -> - new CATestURLs("https://validpremiumecc.affirmtrust.com", - "https://revokedpremiumecc.affirmtrust.com"); - case "teliarootcav2" -> new CATestURLs("https://juolukka.cover.telia.fi:10600", "https://juolukka.cover.telia.fi:10601"); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java b/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java index ddbca2c444d..7d1756be062 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/AbstractFilePermissionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -47,7 +46,6 @@ import java.util.Set; * @author Taras Ledkov */ public abstract class AbstractFilePermissionTest { - private final String TEST_CLASS_PATH = System.getProperty("test.class.path"); protected final String TEST_CLASSES = System.getProperty("test.classes"); protected final FileSystem FS = FileSystems.getDefault(); private int MAX_GET_FREE_PORT_TRIES = 10; @@ -169,11 +167,8 @@ public abstract class AbstractFilePermissionTest { final String pp = "-Dcom.sun.management.jmxremote.port=" + jdk.test.lib.Utils.getFreePort(); List command = new ArrayList<>(); - Collections.addAll(command, jdk.test.lib.Utils.getTestJavaOpts()); command.add(mp); command.add(pp); - command.add("-cp"); - command.add(TEST_CLASSES); command.add(className); ProcessBuilder processBuilder = ProcessTools.createTestJavaProcessBuilder(command); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java b/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java index 0a7c735e2e0..b6e478528d9 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/LocalManagementTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,12 +24,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.Utils; /** * @test @@ -48,7 +46,6 @@ import jdk.test.lib.Utils; * @run main/othervm/timeout=300 LocalManagementTest */ public class LocalManagementTest { - private static final String TEST_CLASSPATH = System.getProperty("test.class.path"); public static void main(String[] args) throws Exception { int failures = 0; @@ -99,16 +96,13 @@ public class LocalManagementTest { private static boolean doTest(String testId, String arg) throws Exception { List args = new ArrayList<>(); args.add("-XX:+UsePerfData"); - Collections.addAll(args, Utils.getTestJavaOpts()); - args.add("-cp"); - args.add(TEST_CLASSPATH); if (arg != null) { args.add(arg); } args.add("TestApplication"); ProcessBuilder server = ProcessTools.createTestJavaProcessBuilder( - args.toArray(new String[args.size()]) + args.toArray(new String[0]) ); Process serverPrc = null, clientPrc = null; @@ -134,8 +128,6 @@ public class LocalManagementTest { System.out.println(" shutdown port : " + port.get()); ProcessBuilder client = ProcessTools.createTestJavaProcessBuilder( - "-cp", - TEST_CLASSPATH, "--add-exports", "jdk.management.agent/jdk.internal.agent=ALL-UNNAMED", "TestManager", String.valueOf(serverPrc.pid()), diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java index 6cc5708b385..1aa20937962 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ import jdk.test.lib.Utils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; -import java.net.BindException; import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -180,12 +179,9 @@ public class RmiRegistrySslTest { initTestEnvironment(); List command = new ArrayList<>(); - Collections.addAll(command, Utils.getTestJavaOpts()); command.add("-Dtest.src=" + TEST_SRC); command.add("-Dtest.rmi.port=" + port); command.addAll(Arrays.asList(args)); - command.add("-cp"); - command.add(TEST_CLASS_PATH); command.add(className); ProcessBuilder processBuilder = ProcessTools.createTestJavaProcessBuilder(command); diff --git a/test/jdk/sun/net/www/protocol/file/FileURLTest.java b/test/jdk/sun/net/www/protocol/file/FileURLTest.java index 135274a9504..e111a538aca 100644 --- a/test/jdk/sun/net/www/protocol/file/FileURLTest.java +++ b/test/jdk/sun/net/www/protocol/file/FileURLTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ * @test * @bug 4474391 * @summary url: file:///D|/Projects/tmp/test.html: urlConnection.getInputStream() broken. + * @requires os.family == "windows" */ import java.io.*; import java.net.*; @@ -33,10 +34,7 @@ public class FileURLTest { public static void main(String [] args) { - String name = System.getProperty("os.name"); - if (name.startsWith("Windows")) { String urlStr = "file:///C|/nonexisted.txt"; - try { URL url = new URL(urlStr); URLConnection urlConnection = url.openConnection(); @@ -49,6 +47,5 @@ public class FileURLTest { throw new RuntimeException("Can't handle '|' in place of ':' in file urls"); } } - } } } diff --git a/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java b/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java index 15224e22e6e..1e0afd0e586 100644 --- a/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java +++ b/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,31 +31,58 @@ * @bug 4328195 * @summary Need to include the alternate subject DN for certs, * https should check for this + * @modules java.base/sun.security.x509 + * java.base/sun.security.util * @library /javax/net/ssl/templates - * @run main/othervm ServerIdentityTest dnsstore localhost - * @run main/othervm ServerIdentityTest ipstore 127.0.0.1 + * /test/lib + * @run main/othervm ServerIdentityTest dns localhost + * @run main/othervm ServerIdentityTest ip 127.0.0.1 * * @author Yingxian Wang */ -import java.io.InputStream; +import static jdk.test.lib.Asserts.fail; + import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; import java.io.OutputStreamWriter; +import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.Proxy; import java.net.URL; import java.net.UnknownHostException; - +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.List; import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManagerFactory; +import jdk.test.lib.security.CertificateBuilder; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNames; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.SerialNumber; +import sun.security.x509.X500Name; public final class ServerIdentityTest extends SSLSocketTemplate { - private static String keystore; private static String hostname; - private static SSLContext context; + private static SSLContext serverContext; /* * Run the test case. @@ -64,7 +91,7 @@ public final class ServerIdentityTest extends SSLSocketTemplate { // Get the customized arguments. initialize(args); - (new ServerIdentityTest()).run(); + new ServerIdentityTest().run(); } ServerIdentityTest() throws UnknownHostException { @@ -95,7 +122,7 @@ public final class ServerIdentityTest extends SSLSocketTemplate { HttpURLConnection urlc = null; InputStream is = null; try { - urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY); + urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); is = urlc.getInputStream(); } finally { if (is != null) { @@ -109,31 +136,127 @@ public final class ServerIdentityTest extends SSLSocketTemplate { @Override protected SSLContext createServerSSLContext() throws Exception { - return context; - } - - @Override - protected SSLContext createClientSSLContext() throws Exception { - return context; + return serverContext; } private static void initialize(String[] args) throws Exception { - keystore = args[0]; + String mode = args[0]; hostname = args[1]; - String password = "changeit"; - String keyFilename = - System.getProperty("test.src", ".") + "/" + keystore; - String trustFilename = - System.getProperty("test.src", ".") + "/" + keystore; + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + KeyPair caKeys = kpg.generateKeyPair(); + KeyPair serverKeys = kpg.generateKeyPair(); + KeyPair clientKeys = kpg.generateKeyPair(); - System.setProperty("javax.net.ssl.keyStore", keyFilename); - System.setProperty("javax.net.ssl.keyStorePassword", password); - System.setProperty("javax.net.ssl.trustStore", trustFilename); - System.setProperty("javax.net.ssl.trustStorePassword", password); + CertificateBuilder serverCertificateBuilder = customCertificateBuilder( + "CN=server, O=Some-Org, L=Some-City, ST=Some-State, C=US", + serverKeys.getPublic(), caKeys.getPublic()) + .addBasicConstraintsExt(false, false, -1); + + if (mode.equalsIgnoreCase("dns")) { + serverCertificateBuilder.addSubjectAltNameDNSExt(List.of(hostname)); + } else if (mode.equalsIgnoreCase("ip")) { + serverCertificateBuilder.addSubjectAltNameIPExt(List.of(hostname)); + } else { + fail("Unknown mode: " + mode); + } + + X509Certificate trustedCert = createTrustedCert(caKeys); + + X509Certificate serverCert = serverCertificateBuilder.build( + trustedCert, caKeys.getPrivate(), "SHA256WithRSA"); + + X509Certificate clientCert = customCertificateBuilder( + "CN=localhost, OU=SSL-Client, O=Some-Org, L=Some-City, ST=Some-State, C=US", + clientKeys.getPublic(), caKeys.getPublic()) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), "SHA256WithRSA"); + + serverContext = getSSLContext( + trustedCert, serverCert, serverKeys.getPrivate()); + + SSLContext clientContext = getSSLContext( + trustedCert, clientCert, clientKeys.getPrivate()); - context = SSLContext.getDefault(); HttpsURLConnection.setDefaultSSLSocketFactory( - context.getSocketFactory()); + clientContext.getSocketFactory()); } + + private static SSLContext getSSLContext( + X509Certificate trustedCertificate, X509Certificate keyCertificate, + PrivateKey privateKey) + throws Exception { + + // create a key store + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + // import the trusted cert + ks.setCertificateEntry("TLS Signer", trustedCertificate); + + // generate certificate chain + Certificate[] chain = new Certificate[2]; + chain[0] = keyCertificate; + chain[1] = trustedCertificate; + + // import the key entry. + final char[] passphrase = "passphrase".toCharArray(); + ks.setKeyEntry("Whatever", privateKey, passphrase, chain); + + // Using PKIX TrustManager + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(ks); + + // Using PKIX KeyManager + KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); + + // create SSL context + SSLContext ctx = SSLContext.getInstance("TLS"); + kmf.init(ks, passphrase); + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + return ctx; + } + + private static X509Certificate createTrustedCert(KeyPair caKeys) + throws Exception { + SecureRandom random = new SecureRandom(); + + KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic()); + GeneralNames gns = new GeneralNames(); + GeneralName name = new GeneralName(new X500Name( + "O=Some-Org, L=Some-City, ST=Some-State, C=US")); + gns.add(name); + BigInteger serialNumber = BigInteger.valueOf( + random.nextLong(1000000) + 1); + return customCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + caKeys.getPublic(), caKeys.getPublic()) + .setSerialNumber(serialNumber) + .addExtension(new AuthorityKeyIdentifierExtension(kid, gns, + new SerialNumber(serialNumber))) + .addBasicConstraintsExt(true, true, -1) + .build(null, caKeys.getPrivate(), "SHA256WithRSA"); + } + + private static CertificateBuilder customCertificateBuilder( + String subjectName, PublicKey publicKey, PublicKey caKey) + throws CertificateException, IOException { + SecureRandom random = new SecureRandom(); + + CertificateBuilder builder = new CertificateBuilder() + .setSubjectName(subjectName) + .setPublicKey(publicKey) + .setNotBefore( + Date.from(Instant.now().minus(1, ChronoUnit.HOURS))) + .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS))) + .setSerialNumber( + BigInteger.valueOf(random.nextLong(1000000) + 1)) + .addSubjectKeyIdExt(publicKey) + .addAuthorityKeyIdExt(caKey); + builder.addKeyUsageExt( + new boolean[]{true, true, true, true, true, true}); + + return builder; + } + } diff --git a/test/jdk/sun/net/www/protocol/https/HttpsClient/dnsstore b/test/jdk/sun/net/www/protocol/https/HttpsClient/dnsstore deleted file mode 100644 index 5f4fc81c9b7..00000000000 Binary files a/test/jdk/sun/net/www/protocol/https/HttpsClient/dnsstore and /dev/null differ diff --git a/test/jdk/sun/net/www/protocol/https/HttpsClient/ipstore b/test/jdk/sun/net/www/protocol/https/HttpsClient/ipstore deleted file mode 100644 index 04a9508e0a1..00000000000 Binary files a/test/jdk/sun/net/www/protocol/https/HttpsClient/ipstore and /dev/null differ diff --git a/test/jdk/sun/nio/cs/TestEncoderReplaceLatin1.java b/test/jdk/sun/nio/cs/TestEncoderReplaceLatin1.java new file mode 100644 index 00000000000..401f3650734 --- /dev/null +++ b/test/jdk/sun/nio/cs/TestEncoderReplaceLatin1.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import sun.nio.cs.ArrayEncoder; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @bug 8364365 + * @summary Verifies `CodingErrorAction.REPLACE` behaviour of all available + * character set encoders while encoding a Latin-1 character + * @modules java.base/jdk.internal.access + * java.base/sun.nio.cs + * @run junit TestEncoderReplaceLatin1 + */ + +class TestEncoderReplaceLatin1 { + + static Collection charsets() { + return Charset.availableCharsets().values(); + } + + @ParameterizedTest + @MethodSource("charsets") + void testEncoderReplace(Charset charset) { + + // Create an encoder + CharsetEncoder encoder = createEncoder(charset); + if (encoder == null) { + return; + } + + // Find an unmappable character to test the `REPLACE` action. + char[] unmappable = findUnmappable(encoder); + if (unmappable == null) { + return; + } + + // Configure the `REPLACE` action + byte[] replacement = findCustomReplacement(encoder, new byte[]{(byte) unmappable[0]}); + if (replacement == null) { + return; + } + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE).replaceWith(replacement); + + // Verify the replacement + System.err.println("Verifying replacement... " + Map.of( + "unmappable", TestEncoderReplaceLatin1.prettyPrintChars(unmappable), + "replacement", TestEncoderReplaceLatin1.prettyPrintBytes(replacement))); + testCharsetEncoderReplace(encoder, unmappable, replacement); + testArrayEncoderLatin1Replace(encoder, unmappable[0], replacement); + + } + + private static CharsetEncoder createEncoder(Charset charset) { + try { + return charset.newEncoder(); + } catch (UnsupportedOperationException _) { + System.err.println("Could not create the character encoder!"); + } + return null; + } + + private static char[] findUnmappable(CharsetEncoder encoder) { + char[] unmappable1 = {0}; + for (char c = 0; c < 0xFF; c++) { + unmappable1[0] = c; + boolean unmappable = !encoder.canEncode(c); + if (unmappable) { + return unmappable1; + } + } + System.err.println("Could not find an unmappable character!"); + return null; + } + + /** + * Finds a {@linkplain CharsetEncoder#replacement() replacement} which is + * different from the given unmappable and the default one. + */ + static byte[] findCustomReplacement(CharsetEncoder encoder, byte[] unmappable) { + + // Obtain the default replacement + byte[] replacementD = encoder.replacement(); + + // Try to find a single-byte replacement + byte[] replacement1 = {0}; + for (int i = 0; i < 0xFF; i++) { + // Skip if the replacement is equal to the unmappable. + // They need to be distinct to be able to determine whether the replacement has occurred. + if (unmappable[0] == i) { + continue; + } + replacement1[0] = (byte) i; + // Skip the default value, since we're verifying if a custom one works + if (replacement1[0] == replacementD[0]) { + continue; + } + if (encoder.isLegalReplacement(replacement1)) { + return replacement1; + } + } + + // Try to find a double-byte replacement + byte[] replacement2 = {0, 0}; + for (int i = 0; i < 0xFF; i++) { + // Skip if the replacement is equal to the unmappable. + // They need to be distinct to be able to determine whether the replacement has occurred. + if (unmappable[0] == i) { + continue; + } + replacement2[0] = (byte) i; + for (int j = 0; j < 0xFF; j++) { + // Skip if the replacement is equal to the unmappable. + // They need to be distinct to be able to determine whether the replacement has occurred. + if (unmappable.length > 1 && unmappable[1] == j) { + continue; + } + replacement2[1] = (byte) j; + // Skip the default value, since we're verifying if a custom one works + if (replacementD.length > 1 && replacement2[1] == replacementD[1]) { + continue; + } + if (encoder.isLegalReplacement(replacement2)) { + return replacement2; + } + } + } + + System.err.println("Could not find a replacement!"); + return null; + + } + + /** + * Verifies {@linkplain CoderResult#isUnmappable() unmappable} character + * {@linkplain CodingErrorAction#REPLACE replacement} using {@link + * CharsetEncoder#encode(CharBuffer, ByteBuffer, boolean) + * CharsetEncoder::encode}. + */ + static void testCharsetEncoderReplace(CharsetEncoder encoder, char[] unmappable, byte[] replacement) { + CharBuffer charBuffer = CharBuffer.wrap(unmappable); + ByteBuffer byteBuffer = ByteBuffer.allocate(replacement.length); + CoderResult coderResult = encoder.encode(charBuffer, byteBuffer, true); + assertArrayEquals(replacement, byteBuffer.array(), () -> { + Object context = Map.of( + "coderResult", coderResult, + "byteBuffer.position()", byteBuffer.position(), + "byteBuffer.array()", prettyPrintBytes(byteBuffer.array()), + "unmappable", prettyPrintChars(unmappable), + "replacement", prettyPrintBytes(replacement)); + return "Unexpected `CharsetEncoder::encode` output! " + context; + }); + } + + /** + * Verifies {@linkplain CoderResult#isUnmappable() unmappable} character + * {@linkplain CodingErrorAction#REPLACE replacement} using {@link + * ArrayEncoder#encodeFromLatin1(byte[], int, int, byte[]) + * ArrayEncoder::encodeFromLatin1}. + */ + private static void testArrayEncoderLatin1Replace(CharsetEncoder encoder, char unmappable, byte[] replacement) { + if (!(encoder instanceof ArrayEncoder arrayEncoder)) { + System.err.println("Encoder is not of type `ArrayEncoder`, skipping the `ArrayEncoder::encodeFromLatin1` test."); + return; + } + byte[] sa = {(byte) unmappable}; + byte[] da = new byte[replacement.length]; + int dp = arrayEncoder.encodeFromLatin1(sa, 0, 1, da); + assertTrue(dp == replacement.length && Arrays.equals(da, replacement), () -> { + Object context = Map.of( + "dp", dp, + "da", prettyPrintBytes(da), + "sa", prettyPrintBytes(sa), + "unmappable", prettyPrintChars(new char[]{unmappable}), + "replacement", prettyPrintBytes(replacement)); + return "Unexpected `ArrayEncoder::encodeFromLatin1` output! " + context; + }); + } + + static String prettyPrintChars(char[] cs) { + return IntStream.range(0, cs.length) + .mapToObj(i -> String.format("U+%04X", (int) cs[i])) + .collect(Collectors.joining(", ", "[", "]")); + } + + static String prettyPrintBytes(byte[] bs) { + return IntStream.range(0, bs.length) + .mapToObj(i -> String.format("0x%02X", bs[i] & 0xFF)) + .collect(Collectors.joining(", ", "[", "]")); + } + +} diff --git a/test/jdk/sun/nio/cs/TestEncoderReplaceUTF16.java b/test/jdk/sun/nio/cs/TestEncoderReplaceUTF16.java new file mode 100644 index 00000000000..c61e85d6698 --- /dev/null +++ b/test/jdk/sun/nio/cs/TestEncoderReplaceUTF16.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import sun.nio.cs.ArrayEncoder; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @bug 8364365 + * @summary Verifies `CodingErrorAction.REPLACE` behaviour of all available + * character set encoders while encoding a UTF-16 character + * @modules java.base/jdk.internal.access + * java.base/sun.nio.cs + * @build TestEncoderReplaceLatin1 + * @run junit/timeout=10 TestEncoderReplaceUTF16 + * @run junit/timeout=10/othervm -XX:-CompactStrings TestEncoderReplaceUTF16 + */ + +class TestEncoderReplaceUTF16 { + + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + + /** + * Character sets known to be absent of non-Latin-1 {@linkplain CoderResult#isUnmappable() unmappable} characters. + */ + private static final Set CHARSETS_WITHOUT_UNMAPPABLE = Set.of( + "CESU-8", + "EUC-JP", + "GB18030", + "ISO-2022-JP", + "ISO-2022-JP-2", + "ISO-2022-KR", + "ISO-8859-1", + "US-ASCII", + "UTF-16", + "UTF-16BE", + "UTF-16LE", + "UTF-32", + "UTF-32BE", + "UTF-32LE", + "UTF-8", + "x-euc-jp-linux", + "x-EUC-TW", + "x-eucJP-Open", + "x-IBM29626C", + "x-IBM33722", + "x-IBM964", + "x-ISCII91", + "x-ISO-2022-CN-CNS", + "x-ISO-2022-CN-GB", + "x-MS932_0213", + "x-SJIS_0213", + "x-UTF-16LE-BOM", + "X-UTF-32BE-BOM", + "X-UTF-32LE-BOM", + "x-windows-50220", + "x-windows-50221", + "x-windows-iso2022jp"); + + @ParameterizedTest + @MethodSource("TestEncoderReplaceLatin1#charsets") + void testEncoderReplace(Charset charset) { + + // Create an encoder + CharsetEncoder encoder = createEncoder(charset); + if (encoder == null) { + return; + } + + // Find an unmappable character to test the `REPLACE` action. + char[] unmappable = findUnmappableNonLatin1(encoder); + if (unmappable == null) { + return; + } + + // Configure the `REPLACE` action + byte[] unmappableUTF16Bytes = utf16Bytes(unmappable); + byte[] replacement = TestEncoderReplaceLatin1.findCustomReplacement(encoder, unmappableUTF16Bytes); + if (replacement == null) { + return; + } + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE).replaceWith(replacement); + + // Verify the replacement + System.err.println("Verifying replacement... " + Map.of( + "unmappable", TestEncoderReplaceLatin1.prettyPrintChars(unmappable), + "unmappableUTF16Bytes", TestEncoderReplaceLatin1.prettyPrintBytes(unmappableUTF16Bytes), + "replacement", TestEncoderReplaceLatin1.prettyPrintBytes(replacement))); + TestEncoderReplaceLatin1.testCharsetEncoderReplace(encoder, unmappable, replacement); + testArrayEncoderUTF16Replace(encoder, unmappableUTF16Bytes, replacement); + + } + + private static CharsetEncoder createEncoder(Charset charset) { + try { + return charset.newEncoder(); + } catch (UnsupportedOperationException _) { + System.err.println("Could not create the character encoder!"); + } + return null; + } + + /** + * Finds an {@linkplain CoderResult#isUnmappable() unmappable} non-Latin-1 {@code char[]} for the given encoder. + */ + private static char[] findUnmappableNonLatin1(CharsetEncoder encoder) { + + // Fast-path for characters sets known to be absent of unmappable non-Latin-1 characters + if (CHARSETS_WITHOUT_UNMAPPABLE.contains(encoder.charset().name())) { + System.err.println("Character set is known to be absent of unmappable non-Latin-1 characters!"); + return null; + } + + // Try to find a single-`char` unmappable + for (int i = 0xFF; i <= 0xFFFF; i++) { + char c = (char) i; + // Skip the surrogate, as a single dangling surrogate `char` should + // trigger a "malformed" error, instead of "unmappable" + if (Character.isSurrogate(c)) { + continue; + } + boolean unmappable = !encoder.canEncode(c); + if (unmappable) { + return new char[]{c}; + } + } + + // Try to find a double-`char` (i.e., surrogate pair) unmappable + int[] nonBmpRange = {0x10000, 0x10FFFF}; + for (int i = nonBmpRange[0]; i < nonBmpRange[1]; i++) { + char[] cs = Character.toChars(i); + if (!encoder.canEncode(new String(cs))) + return cs; + } + + System.err.println("Could not find an unmappable character!"); + return null; + } + + private static byte[] utf16Bytes(char[] cs) { + int sl = cs.length; + byte[] sa = new byte[sl << 1]; + for (int i = 0; i < sl; i++) { + JLA.uncheckedPutCharUTF16(sa, i, cs[i]); + } + return sa; + } + + /** + * Verifies {@linkplain CoderResult#isUnmappable() unmappable} character + * {@linkplain CodingErrorAction#REPLACE replacement} using {@link + * ArrayEncoder#encodeFromUTF16(byte[], int, int, byte[]) + * ArrayEncoder::encodeFromUTF16}. + */ + private static void testArrayEncoderUTF16Replace(CharsetEncoder encoder, byte[] unmappableUTF16Bytes, byte[] replacement) { + if (!(encoder instanceof ArrayEncoder arrayEncoder)) { + System.err.println("Encoder is not of type `ArrayEncoder`, skipping the `ArrayEncoder::encodeFromUTF16` test."); + return; + } + byte[] da = new byte[replacement.length]; + int dp = arrayEncoder.encodeFromUTF16(unmappableUTF16Bytes, 0, unmappableUTF16Bytes.length >>> 1, da); + assertTrue(dp == replacement.length && Arrays.equals(da, replacement), () -> { + Object context = Map.of( + "dp", dp, + "da", TestEncoderReplaceLatin1.prettyPrintBytes(da), + "unmappableUTF16Bytes", TestEncoderReplaceLatin1.prettyPrintBytes(unmappableUTF16Bytes), + "replacement", TestEncoderReplaceLatin1.prettyPrintBytes(replacement)); + return "Unexpected `ArrayEncoder::encodeFromUTF16` output! " + context; + }); + } + +} diff --git a/test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java b/test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java index 0864d650162..3a77030f259 100644 --- a/test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java +++ b/test/jdk/sun/security/ec/ECDHKeyAgreementParamValidation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2024, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/sun/security/jgss/GssContextCleanup.java b/test/jdk/sun/security/jgss/GssContextCleanup.java index 00d84e8a70e..1e992df97d5 100644 --- a/test/jdk/sun/security/jgss/GssContextCleanup.java +++ b/test/jdk/sun/security/jgss/GssContextCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,4 +59,3 @@ public final class GssContextCleanup { } } } - diff --git a/test/jdk/sun/security/jgss/GssNameCleanup.java b/test/jdk/sun/security/jgss/GssNameCleanup.java index be68c46087d..ef19479b6a0 100644 --- a/test/jdk/sun/security/jgss/GssNameCleanup.java +++ b/test/jdk/sun/security/jgss/GssNameCleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * 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,4 +66,3 @@ public final class GssNameCleanup { } } } - diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index 86a612ac3ba..1d28c3494e7 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -28,7 +28,7 @@ * 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 8341057 8303770 8350498 8359170 + * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -47,12 +47,12 @@ public class VerifyCACerts { + File.separator + "security" + File.separator + "cacerts"; // The numbers of certs now. - private static final int COUNT = 113; + private static final int COUNT = 109; // 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 - = "18:36:49:15:B6:71:85:FF:F1:8E:C0:10:BE:0A:41:52:5B:DC:F7:B3:1F:51:7A:45:7D:7A:14:10:3A:59:42:4C"; + = "70:73:12:D3:E8:01:89:28:F5:3D:10:8E:45:34:F6:28:CB:BF:AD:18:19:6D:F1:A2:E7:28:84:30:0B:E1:A6:9F"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -193,14 +193,6 @@ public class VerifyCACerts { "43:DF:57:74:B0:3E:7F:EF:5F:E4:0D:93:1A:7B:ED:F1:BB:2E:6B:42:73:8C:4E:6D:38:41:10:3D:3A:A7:F3:39"); put("entrustevca [jdk]", "73:C1:76:43:4F:1B:C6:D5:AD:F4:5B:0E:76:E7:27:28:7C:8D:E5:76:16:C1:E6:E6:14:1A:2B:2C:BC:7D:8E:4C"); - put("affirmtrustnetworkingca [jdk]", - "0A:81:EC:5A:92:97:77:F1:45:90:4A:F3:8D:5D:50:9F:66:B5:E2:C5:8F:CD:B5:31:05:8B:0E:17:F3:F0:B4:1B"); - put("affirmtrustpremiumca [jdk]", - "70:A7:3F:7F:37:6B:60:07:42:48:90:45:34:B1:14:82:D5:BF:0E:69:8E:CC:49:8D:F5:25:77:EB:F2:E9:3B:9A"); - put("affirmtrustcommercialca [jdk]", - "03:76:AB:1D:54:C5:F9:80:3C:E4:B2:E2:01:A0:EE:7E:EF:7B:57:B6:36:E8:A9:3C:9B:8D:48:60:C9:6F:5F:A7"); - put("affirmtrustpremiumeccca [jdk]", - "BD:71:FD:F6:DA:97:E4:CF:62:D1:64:7A:DD:25:81:B0:7D:79:AD:F8:39:7E:B4:EC:BA:9C:5E:84:88:82:14:23"); put("ttelesecglobalrootclass3ca [jdk]", "FD:73:DA:D3:1C:64:4F:F1:B4:3B:EF:0C:CD:DA:96:71:0B:9C:D9:87:5E:CA:7E:31:70:7A:F3:E9:6D:52:2B:BD"); put("ttelesecglobalrootclass2ca [jdk]", diff --git a/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java b/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java index 26180a8fcdc..5ecbb602273 100644 --- a/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java +++ b/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @modules java.base/sun.security.util * java.base/sun.security.tools.keytool * java.base/sun.security.x509 + * @library /test/lib * @run main ShortRSAKeyWithinTLS 1024 * @run main ShortRSAKeyWithinTLS 768 * @run main ShortRSAKeyWithinTLS 512 @@ -42,6 +43,7 @@ import java.security.cert.*; import javax.net.*; import javax.net.ssl.*; +import jdk.test.lib.security.SecurityUtils; import sun.security.tools.keytool.CertAndKeyGen; import sun.security.util.KeyUtil; import sun.security.x509.X500Name; @@ -233,6 +235,10 @@ public class ShortRSAKeyWithinTLS { private static String clientCiperSuite = null; public static void main(String[] args) throws Exception { + // Make sure we don't block the key on algorithm constraints check. + SecurityUtils.removeFromDisabledAlgs("jdk.certpath.disabledAlgorithms", + List.of("RSA keySize < 1024")); + if (debug) { System.setProperty("javax.net.debug", "all"); } diff --git a/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java index 9db4e460a86..49f92928bc8 100644 --- a/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java +++ b/test/jdk/sun/security/ssl/SSLLogger/DebugPropertyValuesTest.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8350582 8044609 + * @bug 8350582 8340312 8044609 * @library /test/lib /javax/net/ssl/templates * @summary Correct the parsing of the ssl value in javax.net.debug * @run junit DebugPropertyValuesTest @@ -54,6 +54,8 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { private static final String DATE_REGEX = "\\d{4}-\\d{2}-\\d{2}"; static { + + debugMessages.put("handshake", List.of("Produced ClientHello handshake message", "supported_versions")); @@ -76,6 +78,8 @@ public class DebugPropertyValuesTest extends SSLSocketTemplate { debugMessages.put("java.security.debug", List.of("properties\\[.*\\|main\\|.*" + DATE_REGEX + ".*\\]:", "certpath\\[.*\\|main\\|.*" + DATE_REGEX + ".*\\]:")); + // "ALL" shouldn't be seen as a valid Level + debugMessages.put("javax.net.debug.logger.ALL", List.of("ALL:")); debugMessages.put("javax.net.debug.logger", List.of("FINE: adding as trusted certificates", "FINE: WRITE: TLSv1.3 application_data")); diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java b/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java index ed61aace3f4..2fa046e1dc2 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java @@ -150,9 +150,11 @@ public class MD5NotAllowedInTLS13CertificateSignature extends // create SSL context SSLContext ctx = SSLContext.getInstance(protocol); - // Using "SunX509" which doesn't check peer supported signature - // algorithms, so we check against local supported signature + // Disable KeyManager's algorithm constraints checking, + // so we check against local supported signature // algorithms which constitutes the fix being tested. + System.setProperty( + "jdk.tls.SunX509KeyManager.certChecking", "false"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passphrase); @@ -166,7 +168,6 @@ public class MD5NotAllowedInTLS13CertificateSignature extends private void setupCertificates() throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); - kpg.initialize(1024); KeyPair caKeys = kpg.generateKeyPair(); this.serverKeys = kpg.generateKeyPair(); this.clientKeys = kpg.generateKeyPair(); @@ -215,7 +216,7 @@ public class MD5NotAllowedInTLS13CertificateSignature extends CertificateBuilder builder = new CertificateBuilder() .setSubjectName(subjectName) .setPublicKey(publicKey) - .setNotAfter( + .setNotBefore( Date.from(Instant.now().minus(1, ChronoUnit.HOURS))) .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS))) .setSerialNumber( diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java index 598c0fe62af..43bc40fabf2 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (C) 2021, 2024 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, 2024, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java index 2ad48f59e83..034af3ac7c5 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS13.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/sun/security/ssl/X509KeyManager/AlgorithmConstraintsCheck.java b/test/jdk/sun/security/ssl/X509KeyManager/AlgorithmConstraintsCheck.java new file mode 100644 index 00000000000..997fde5a07a --- /dev/null +++ b/test/jdk/sun/security/ssl/X509KeyManager/AlgorithmConstraintsCheck.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNull; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509KeyManager; +import jdk.test.lib.security.CertificateBuilder; +import jdk.test.lib.security.SecurityUtils; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNames; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.SerialNumber; +import sun.security.x509.X500Name; + +/* + * @test + * @bug 8359956 + * @summary Support algorithm constraints and certificate checks in SunX509 + * key manager + * @modules java.base/sun.security.x509 + * java.base/sun.security.util + * @library /test/lib + * @run main/othervm AlgorithmConstraintsCheck false SunX509 SHA256withRSA + * @run main/othervm AlgorithmConstraintsCheck true SunX509 SHA256withRSA + * @run main/othervm AlgorithmConstraintsCheck false PKIX SHA256withRSA + * @run main/othervm AlgorithmConstraintsCheck true PKIX SHA256withRSA + */ + +public class AlgorithmConstraintsCheck { + + private static final String CERT_ALIAS = "testalias"; + private static final String KEY_TYPE = "RSA"; + + public static void main(String[] args) throws Exception { + if (args.length != 3) { + throw new RuntimeException("Wrong number of arguments"); + } + + String enabled = args[0]; + String kmAlg = args[1]; + String certSignatureAlg = args[2]; + + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", enabled); + SecurityUtils.addToDisabledTlsAlgs(certSignatureAlg); + + X509ExtendedKeyManager km = (X509ExtendedKeyManager) getKeyManager( + kmAlg, certSignatureAlg); + String serverAlias = km.chooseServerAlias(KEY_TYPE, null, null); + String engineServerAlias = km.chooseEngineServerAlias( + KEY_TYPE, null, null); + String clientAlias = km.chooseClientAlias( + new String[]{KEY_TYPE}, null, null); + String engineClientAlias = km.chooseEngineClientAlias( + new String[]{KEY_TYPE}, null, null); + + // PKIX KeyManager adds a cache prefix to an alias. + String serverAliasPrefix = kmAlg.equalsIgnoreCase("PKIX") ? "1.0." : ""; + String clientAliasPrefix = kmAlg.equalsIgnoreCase("PKIX") ? "2.0." : ""; + + if ("false".equals(enabled) && kmAlg.equals("SunX509")) { + assertEquals(CERT_ALIAS, normalizeAlias(serverAlias)); + assertEquals(CERT_ALIAS, normalizeAlias(engineServerAlias)); + assertEquals(CERT_ALIAS, normalizeAlias(clientAlias)); + assertEquals(CERT_ALIAS, normalizeAlias(engineClientAlias)); + } else { + assertNull(serverAlias); + assertNull(engineServerAlias); + assertNull(clientAlias); + assertNull(engineClientAlias); + } + } + + // PKIX KeyManager adds a cache prefix to an alias. + private static String normalizeAlias(String alias) { + return alias.substring(alias.lastIndexOf(".") + 1); + } + + private static X509KeyManager getKeyManager(String kmAlg, + String certSignatureAlg) throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_TYPE); + KeyPair caKeys = kpg.generateKeyPair(); + KeyPair endpointKeys = kpg.generateKeyPair(); + + X509Certificate trustedCert = createTrustedCert(caKeys, + certSignatureAlg); + + X509Certificate endpointCert = customCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + endpointKeys.getPublic(), caKeys.getPublic()) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), certSignatureAlg); + + // create a key store + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + // import the trusted cert + ks.setCertificateEntry("TLS Signer", trustedCert); + + // generate certificate chain + Certificate[] chain = new Certificate[2]; + chain[0] = endpointCert; + chain[1] = trustedCert; + + // import the key entry. + final char[] passphrase = "passphrase".toCharArray(); + ks.setKeyEntry(CERT_ALIAS, endpointKeys.getPrivate(), passphrase, + chain); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmAlg); + kmf.init(ks, passphrase); + + return (X509KeyManager) kmf.getKeyManagers()[0]; + } + + // Certificate-building helper methods. + + private static X509Certificate createTrustedCert(KeyPair caKeys, + String certSignatureAlg) + throws Exception { + SecureRandom random = new SecureRandom(); + + KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic()); + GeneralNames gns = new GeneralNames(); + GeneralName name = new GeneralName(new X500Name( + "O=Some-Org, L=Some-City, ST=Some-State, C=US")); + gns.add(name); + BigInteger serialNumber = BigInteger.valueOf( + random.nextLong(1000000) + 1); + return customCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + caKeys.getPublic(), caKeys.getPublic()) + .setSerialNumber(serialNumber) + .addExtension(new AuthorityKeyIdentifierExtension(kid, gns, + new SerialNumber(serialNumber))) + .addBasicConstraintsExt(true, true, -1) + .build(null, caKeys.getPrivate(), certSignatureAlg); + } + + private static CertificateBuilder customCertificateBuilder( + String subjectName, PublicKey publicKey, PublicKey caKey) + throws CertificateException, IOException { + SecureRandom random = new SecureRandom(); + + CertificateBuilder builder = new CertificateBuilder() + .setSubjectName(subjectName) + .setPublicKey(publicKey) + .setNotBefore( + Date.from(Instant.now().minus(1, ChronoUnit.HOURS))) + .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS))) + .setSerialNumber( + BigInteger.valueOf(random.nextLong(1000000) + 1)) + .addSubjectKeyIdExt(publicKey) + .addAuthorityKeyIdExt(caKey); + builder.addKeyUsageExt( + new boolean[]{true, true, true, true, true, true}); + + return builder; + } +} diff --git a/test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java b/test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java new file mode 100644 index 00000000000..81e16b925fc --- /dev/null +++ b/test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNull; + +import com.sun.security.auth.UserPrincipal; +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.Principal; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509KeyManager; +import javax.security.auth.x500.X500Principal; +import jdk.test.lib.Asserts; +import jdk.test.lib.security.CertificateBuilder; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNames; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.SerialNumber; +import sun.security.x509.X500Name; + +/* + * @test + * @bug 8359956 + * @summary Support algorithm constraints and certificate checks in SunX509 + * key manager + * @modules java.base/sun.security.x509 + * java.base/sun.security.util + * @library /test/lib + * @run main/othervm CertChecking false SunX509 + * @run main/othervm CertChecking true SunX509 + * @run main/othervm CertChecking false PKIX + * @run main/othervm CertChecking true PKIX + */ + +/* + * This class tests against the certificate's expiration, key usage, key type + * and issuers. + */ + +public class CertChecking { + + private static final String PREFERRED_ALIAS = "preferred-alias"; + private static final String EXPIRED_ALIAS = "expired-alias"; + private static final String USAGE_MISMATCH_ALIAS = "usage-mismatch-alias"; + private static final String CA_KEY_TYPE = "RSA"; + private static final String CERT_SIG_ALG = "SHA256withRSA"; + private static final String CA_ISSUER_STRING = + "O=TrustedCert, L=Some-City, ST=Some-State, C=US"; + private static final String EE_ISSUER_STRING = + "O=EndpointCert, L=Some-City, ST=Some-State, C=US"; + private static final String UNKNOWN_ISSUER_STRING = + "O=UnknownCert, L=Some-City, ST=Some-State, C=US"; + + /* + * Certificate KeyUsage reference: + * + * digitalSignature (0), + * nonRepudiation (1), + * keyEncipherment (2), + * dataEncipherment (3), + * keyAgreement (4), + * keyCertSign (5), + * cRLSign (6), + * encipherOnly (7), + * decipherOnly (8) + */ + + private static final boolean[] DEFAULT_KEY_USAGES = + new boolean[]{true, true, true, true, true, true}; + private static final boolean[] NONE_KEY_USAGES = + new boolean[]{false, false, false, false, false, false}; + private static final boolean[] NO_DS_USAGE = + new boolean[]{false, true, true, true, true, true}; + private static final boolean[] NO_DS_NO_KE_USAGE = + new boolean[]{false, true, false, true, true, true}; + private static final boolean[] NO_KA_USAGE = + new boolean[]{true, true, true, true, false, true}; + + + public static void main(String[] args) throws Exception { + if (args.length != 2) { + throw new RuntimeException("Wrong number of arguments"); + } + + String enabled = args[0]; + String kmAlg = args[1]; + + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", enabled); + + // --- Usage and expired test cases -- + + // Both client and server should be checked with no usages at all + usageTestCase(enabled, kmAlg, "RSA", NONE_KEY_USAGES, true, true); + + // Only client should be checked with RSA algorithm and + // no digital signature bit set + usageTestCase(enabled, kmAlg, "RSA", NO_DS_USAGE, false, true); + + // Only server should be checked with RSA algorithm and + // no digital signature bit set + usageTestCase(enabled, kmAlg, "RSASSA-PSS", NO_DS_USAGE, true, false); + + // Both client and server should be checked with DSA algorithm and no + // digital signature bit set + usageTestCase(enabled, kmAlg, "DSA", NO_DS_USAGE, true, true); + + // Both client and server should be checked with EC algorithm and no + // digital signature bit set + usageTestCase(enabled, kmAlg, "EC", NO_DS_USAGE, true, true); + + // Both client and server should be checked with RSA algorithm and + // missing digital signature and key encipherment bits. + usageTestCase(enabled, kmAlg, "RSA", NO_DS_NO_KE_USAGE, true, true); + + // Both client and server should be checked with DH algorithm and no + // key agreement bit set. + usageTestCase(enabled, kmAlg, "DH", NO_KA_USAGE, true, true); + + // Only server should be checked with EC algorithm and + // no digital signature bit set + usageTestCase(enabled, kmAlg, "EC", NO_KA_USAGE, true, false); + + // --- Issuer match test cases --- + + // Check CA issuer match + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA", + new Principal[]{new X500Principal(CA_ISSUER_STRING)}, true); + + // Check CA issuer match with non-X500 principal + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA", + new Principal[]{new UserPrincipal(CA_ISSUER_STRING)}, true); + + // Non-convertable principal should match all + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA", + new Principal[]{new InvalidPrincipal()}, true); + + // Empty issuer array should match all + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA", + new Principal[]{}, true); + + // Null issuer array should match all + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA", null, true); + + // Issuer that is not in the chain should not match. + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA", + new Principal[]{new X500Principal(UNKNOWN_ISSUER_STRING)}, + false); + + // --- Key Type match test cases --- + + // Exact key type match. + issuerAndKeyTypeTestCase(enabled, kmAlg, "EC", "EC", null, true); + + // Key type with a signature algorithm match. + issuerAndKeyTypeTestCase( + enabled, kmAlg, "EC", "EC_" + CA_KEY_TYPE, null, true); + + // Null KeyType should not match. + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", null, null, false); + + // Wrong KeyType should not match. + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "EC", null, false); + + // Wrong signature algorithm should not match. + issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA_EC", null, false); + + // Correct signature algorithm but incorrect key algorithm + // should not match. + issuerAndKeyTypeTestCase( + enabled, kmAlg, "RSA", "EC_" + CA_KEY_TYPE, null, false); + } + + private static void usageTestCase(String enabled, String kmAlg, + String keyAlg, boolean[] certKeyUsages, boolean checkServer, + boolean checkClient) throws Exception { + + X509ExtendedKeyManager km = (X509ExtendedKeyManager) getKeyManager( + kmAlg, keyAlg, certKeyUsages); + + String chosenServerAlias = km.chooseServerAlias(keyAlg, null, null); + String chosenEngineServerAlias = km.chooseEngineServerAlias( + keyAlg, null, null); + String chosenClientAlias = km.chooseClientAlias( + new String[]{keyAlg}, null, null); + String chosenEngineClientAlias = km.chooseEngineClientAlias( + new String[]{keyAlg}, null, null); + + String[] allServerAliases = km.getServerAliases(keyAlg, null); + String[] allClientAliases = km.getClientAliases(keyAlg, null); + + if ("false".equals(enabled) && kmAlg.equals("SunX509")) { + // Initial order alias returned + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(chosenServerAlias)); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(chosenClientAlias)); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(chosenEngineServerAlias)); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(chosenEngineClientAlias)); + + // Assert the initial order of all aliases. + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(allServerAliases[0])); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(allClientAliases[0])); + assertEquals(PREFERRED_ALIAS, normalizeAlias(allServerAliases[1])); + assertEquals(PREFERRED_ALIAS, normalizeAlias(allClientAliases[1])); + assertEquals(EXPIRED_ALIAS, normalizeAlias(allServerAliases[2])); + assertEquals(EXPIRED_ALIAS, normalizeAlias(allClientAliases[2])); + + } else { + if (checkServer) { + // Preferred alias returned + assertEquals(PREFERRED_ALIAS, + normalizeAlias(chosenServerAlias)); + assertEquals(PREFERRED_ALIAS, + normalizeAlias(chosenEngineServerAlias)); + + // Assert the correct sorted order of all aliases. + assertEquals(PREFERRED_ALIAS, + normalizeAlias(allServerAliases[0])); + assertEquals(EXPIRED_ALIAS, + normalizeAlias(allServerAliases[1])); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(allServerAliases[2])); + } + + if (checkClient) { + // Preferred alias returned + assertEquals(PREFERRED_ALIAS, + normalizeAlias(chosenClientAlias)); + assertEquals(PREFERRED_ALIAS, + normalizeAlias(chosenEngineClientAlias)); + + // Assert the correct sorted order of all aliases. + assertEquals(PREFERRED_ALIAS, + normalizeAlias(allClientAliases[0])); + assertEquals(EXPIRED_ALIAS, + normalizeAlias(allClientAliases[1])); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(allClientAliases[2])); + } + } + } + + private static void issuerAndKeyTypeTestCase(String enabled, String kmAlg, + String keyAlg, String keyType, Principal[] issuers, boolean found) + throws Exception { + + X509ExtendedKeyManager km = (X509ExtendedKeyManager) getKeyManager( + kmAlg, keyAlg, NONE_KEY_USAGES); + + List chosenAliases = new ArrayList<>(4); + + chosenAliases.add(km.chooseServerAlias(keyType, issuers, null)); + chosenAliases.add(km.chooseEngineServerAlias(keyType, issuers, null)); + chosenAliases.add( + km.chooseClientAlias(new String[]{keyType}, issuers, null)); + chosenAliases.add(km.chooseEngineClientAlias( + new String[]{keyType}, issuers, null)); + + String[] allServerAliases = km.getServerAliases(keyType, issuers); + String[] allClientAliases = km.getClientAliases(keyType, issuers); + + if (found) { + if ("false".equals(enabled) && kmAlg.equals("SunX509")) { + chosenAliases.forEach(a -> + assertEquals(USAGE_MISMATCH_ALIAS, normalizeAlias(a))); + + // Assert the initial order of all aliases. + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(allServerAliases[0])); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(allClientAliases[0])); + assertEquals(PREFERRED_ALIAS, + normalizeAlias(allServerAliases[1])); + assertEquals(PREFERRED_ALIAS, + normalizeAlias(allClientAliases[1])); + assertEquals(EXPIRED_ALIAS, + normalizeAlias(allServerAliases[2])); + assertEquals(EXPIRED_ALIAS, + normalizeAlias(allClientAliases[2])); + } else { + chosenAliases.forEach(a -> + assertEquals(PREFERRED_ALIAS, normalizeAlias(a))); + + // Assert the correct sorted order of all aliases. + assertEquals(PREFERRED_ALIAS, + normalizeAlias(allServerAliases[0])); + assertEquals(EXPIRED_ALIAS, + normalizeAlias(allServerAliases[1])); + assertEquals(USAGE_MISMATCH_ALIAS, + normalizeAlias(allServerAliases[2])); + } + } else { + chosenAliases.forEach(Asserts::assertNull); + assertNull(allServerAliases); + assertNull(allClientAliases); + } + } + + // PKIX KeyManager adds a cache prefix to an alias. + private static String normalizeAlias(String alias) { + return alias.substring(alias.lastIndexOf(".") + 1); + + } + + private static class InvalidPrincipal implements Principal { + + @Override + public String getName() { + return null; + } + } + + private static X509KeyManager getKeyManager(String kmAlg, + String keyAlg, boolean[] certKeyUsages) + throws Exception { + + // Create a key store. + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + // Generate and set the trusted cert. + KeyPair caKeys = KeyPairGenerator.getInstance(CA_KEY_TYPE) + .generateKeyPair(); + X509Certificate trustedCert = createTrustedCert(caKeys); + ks.setCertificateEntry("CA entry", trustedCert); + + // Generate valid certificate chain. + KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlg); + KeyPair validEndpointKeys = kpg.generateKeyPair(); + + X509Certificate validEndpointCert = customCertificateBuilder( + EE_ISSUER_STRING, + validEndpointKeys.getPublic(), caKeys.getPublic(), + Instant.now(), DEFAULT_KEY_USAGES) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), CERT_SIG_ALG); + + Certificate[] validChain = new Certificate[2]; + validChain[0] = validEndpointCert; + validChain[1] = trustedCert; + + // Generate expired certificate chain. + KeyPair expiredEndpointKeys = kpg.generateKeyPair(); + + X509Certificate expiredEndpointCert = customCertificateBuilder( + EE_ISSUER_STRING, + expiredEndpointKeys.getPublic(), caKeys.getPublic(), + Instant.now().minus(1, ChronoUnit.DAYS), DEFAULT_KEY_USAGES) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), CERT_SIG_ALG); + + Certificate[] expiredChain = new Certificate[2]; + expiredChain[0] = expiredEndpointCert; + expiredChain[1] = trustedCert; + + // Generate usage mismatch certificate chain. + KeyPair usageMismatchEndpointKeys = kpg.generateKeyPair(); + + X509Certificate usageMismatchEndpointCert = customCertificateBuilder( + EE_ISSUER_STRING, + usageMismatchEndpointKeys.getPublic(), caKeys.getPublic(), + Instant.now(), certKeyUsages) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), CERT_SIG_ALG); + + Certificate[] usageMismatchChain = new Certificate[2]; + usageMismatchChain[0] = usageMismatchEndpointCert; + usageMismatchChain[1] = trustedCert; + + // Import the key entries, order matters. + final char[] passphrase = "passphrase".toCharArray(); + ks.setKeyEntry(USAGE_MISMATCH_ALIAS, + usageMismatchEndpointKeys.getPrivate(), passphrase, + usageMismatchChain); + ks.setKeyEntry(PREFERRED_ALIAS, validEndpointKeys.getPrivate(), + passphrase, + validChain); + ks.setKeyEntry(EXPIRED_ALIAS, expiredEndpointKeys.getPrivate(), + passphrase, + expiredChain); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmAlg); + kmf.init(ks, passphrase); + + return (X509KeyManager) kmf.getKeyManagers()[0]; + } + + // Certificate-building helper methods. + + private static X509Certificate createTrustedCert(KeyPair caKeys) + throws Exception { + SecureRandom random = new SecureRandom(); + + KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic()); + GeneralNames gns = new GeneralNames(); + GeneralName name = new GeneralName(new X500Name( + "O=Some-Org, L=Some-City, ST=Some-State, C=US")); + gns.add(name); + BigInteger serialNumber = BigInteger.valueOf( + random.nextLong(1000000) + 1); + return customCertificateBuilder( + CA_ISSUER_STRING, + caKeys.getPublic(), caKeys.getPublic(), Instant.now(), + DEFAULT_KEY_USAGES) + .setSerialNumber(serialNumber) + .addExtension(new AuthorityKeyIdentifierExtension(kid, gns, + new SerialNumber(serialNumber))) + .addBasicConstraintsExt(true, true, -1) + .build(null, caKeys.getPrivate(), CERT_SIG_ALG); + } + + private static CertificateBuilder customCertificateBuilder( + String subjectName, PublicKey publicKey, PublicKey caKey, + Instant certDate, boolean[] certKeyUsages) + throws CertificateException, IOException { + SecureRandom random = new SecureRandom(); + + CertificateBuilder builder = new CertificateBuilder() + .setSubjectName(subjectName) + .setPublicKey(publicKey) + .setNotBefore( + Date.from(certDate.minus(1, ChronoUnit.HOURS))) + .setNotAfter(Date.from(certDate.plus(1, ChronoUnit.HOURS))) + .setSerialNumber( + BigInteger.valueOf(random.nextLong(1000000) + 1)) + .addSubjectKeyIdExt(publicKey) + .addAuthorityKeyIdExt(caKey); + builder.addKeyUsageExt(certKeyUsages); + + return builder; + } +} diff --git a/test/jdk/sun/security/ssl/X509KeyManager/PeerConstraintsCheck.java b/test/jdk/sun/security/ssl/X509KeyManager/PeerConstraintsCheck.java new file mode 100644 index 00000000000..bbfbfe0b46e --- /dev/null +++ b/test/jdk/sun/security/ssl/X509KeyManager/PeerConstraintsCheck.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; +import static jdk.test.lib.Utils.runAndCheckException; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.Socket; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.X509ExtendedTrustManager; +import jdk.test.lib.security.CertificateBuilder; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNames; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.SerialNumber; +import sun.security.x509.X500Name; + +/* + * @test + * @bug 8359956 + * @summary Support algorithm constraints and certificate checks in SunX509 + * key manager + * @modules java.base/sun.security.x509 + * java.base/sun.security.util + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm PeerConstraintsCheck false SunX509 + * @run main/othervm PeerConstraintsCheck true SunX509 + * @run main/othervm PeerConstraintsCheck false PKIX + * @run main/othervm PeerConstraintsCheck true PKIX + */ + +/* + * This class tests against the peer supported certificate signatures sent in + * "signature_algorithms_cert" extension. + */ + +public class PeerConstraintsCheck extends SSLSocketTemplate { + + private static final String KEY_ALGORITHM = "EC"; + private static final String CLIENT_CERT_SIG_SCHEME = + "ecdsa_secp384r1_sha384"; + private static final String CLIENT_CERT_SIG_ALG = "SHA384withECDSA"; + private static final String SERVER_CERT_SIG_ALG = "SHA256withECDSA"; + private static final String TRUSTED_CERT_SIG_ALG = "SHA512withECDSA"; + + private final String kmAlg; + private X509Certificate trustedCert; + private X509Certificate serverCert; + private X509Certificate clientCert; + private KeyPair serverKeys; + private KeyPair clientKeys; + + protected PeerConstraintsCheck(String kmAlg) throws Exception { + super(); + this.kmAlg = kmAlg; + setupCertificates(); + } + + public static void main(String[] args) throws Exception { + // Make sure both client and server support client's signature scheme, + // so the exception happens later during KeyManager's algorithm check. + System.setProperty( + "jdk.tls.client.SignatureSchemes", CLIENT_CERT_SIG_SCHEME); + System.setProperty( + "jdk.tls.server.SignatureSchemes", CLIENT_CERT_SIG_SCHEME); + + String enabled = args[0]; + String kmAlg = args[1]; + + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", enabled); + + if ("false".equals(enabled) && kmAlg.equals("SunX509")) { + new PeerConstraintsCheck(kmAlg).run(); + } else { + // "jdk.tls.client.SignatureSchemes" and + // "jdk.tls.server.SignatureSchemes" system properties set + // signature schemes for both "signature_algorithms" and + // "signature_algorithms_cert" extensions. Then we fail because + // server's certificate is signed with "SHA256withECDSA" while + // "signature_algorithms_cert" extension only contains an + // "ecdsa_secp384r1_sha384" signature scheme corresponding to + // "SHA384withECDSA" certificate signature. + runAndCheckException( + () -> new PeerConstraintsCheck(kmAlg).run(), + ex -> { + assertTrue(ex instanceof SSLHandshakeException); + assertEquals(ex.getMessage(), "(handshake_failure) " + + "No available authentication scheme"); + } + ); + } + } + + @Override + public SSLContext createServerSSLContext() throws Exception { + return getSSLContext( + trustedCert, serverCert, serverKeys.getPrivate(), kmAlg); + } + + @Override + public SSLContext createClientSSLContext() throws Exception { + return getSSLContext( + trustedCert, clientCert, clientKeys.getPrivate(), kmAlg); + } + + private static SSLContext getSSLContext(X509Certificate trustedCertificate, + X509Certificate keyCertificate, PrivateKey privateKey, String kmAlg) + throws Exception { + + // create a key store + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(null, null); + + // import the trusted cert + ks.setCertificateEntry("TLS Signer", trustedCertificate); + + // generate certificate chain + Certificate[] chain = new Certificate[2]; + chain[0] = keyCertificate; + chain[1] = trustedCertificate; + + // import the key entry. + final char[] passphrase = "passphrase".toCharArray(); + ks.setKeyEntry("Whatever", privateKey, passphrase, chain); + + SSLContext ctx = SSLContext.getInstance("TLS"); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmAlg); + kmf.init(ks, passphrase); + + // Use custom trust-all TrustManager so we perform only KeyManager's + // constraints check. + X509ExtendedTrustManager[] trustAll = new X509ExtendedTrustManager[]{ + new X509ExtendedTrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, + String authType, Socket socket) + throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, + String authType, Socket socket) + throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) + throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) + throws CertificateException { + } + + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } + }; + + ctx.init(kmf.getKeyManagers(), trustAll, null); + return ctx; + } + + // Certificate-building helper methods. + + private void setupCertificates() throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM); + KeyPair caKeys = kpg.generateKeyPair(); + this.serverKeys = kpg.generateKeyPair(); + this.clientKeys = kpg.generateKeyPair(); + + this.trustedCert = createTrustedCert(caKeys); + + this.serverCert = customCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + serverKeys.getPublic(), caKeys.getPublic()) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), SERVER_CERT_SIG_ALG); + + this.clientCert = customCertificateBuilder( + "CN=localhost, OU=SSL-Client, O=Some-Org, L=Some-City," + + " ST=Some-State, C=US", + clientKeys.getPublic(), caKeys.getPublic()) + .addBasicConstraintsExt(false, false, -1) + .build(trustedCert, caKeys.getPrivate(), CLIENT_CERT_SIG_ALG); + } + + private static X509Certificate createTrustedCert(KeyPair caKeys) + throws Exception { + SecureRandom random = new SecureRandom(); + KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic()); + GeneralNames gns = new GeneralNames(); + GeneralName name = new GeneralName(new X500Name( + "O=Some-Org, L=Some-City, ST=Some-State, C=US")); + gns.add(name); + BigInteger serialNumber = BigInteger.valueOf( + random.nextLong(1000000) + 1); + return customCertificateBuilder( + "O=Some-Org, L=Some-City, ST=Some-State, C=US", + caKeys.getPublic(), caKeys.getPublic()) + .setSerialNumber(serialNumber) + .addExtension(new AuthorityKeyIdentifierExtension(kid, gns, + new SerialNumber(serialNumber))) + .addBasicConstraintsExt(true, true, -1) + .build(null, caKeys.getPrivate(), TRUSTED_CERT_SIG_ALG); + } + + private static CertificateBuilder customCertificateBuilder( + String subjectName, PublicKey publicKey, PublicKey caKey) + throws CertificateException, IOException { + SecureRandom random = new SecureRandom(); + + CertificateBuilder builder = new CertificateBuilder() + .setSubjectName(subjectName) + .setPublicKey(publicKey) + .setNotBefore( + Date.from(Instant.now().minus(1, ChronoUnit.HOURS))) + .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS))) + .setSerialNumber( + BigInteger.valueOf(random.nextLong(1000000) + 1)) + .addSubjectKeyIdExt(publicKey) + .addAuthorityKeyIdExt(caKey); + builder.addKeyUsageExt( + new boolean[]{true, true, true, true, true, true}); + + return builder; + } +} diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java index e2e5e066f9c..fcc7cbf73f0 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,8 +36,6 @@ * @run main/othervm PKIXExtendedTM 3 */ -import java.net.*; -import java.util.*; import java.io.*; import javax.net.ssl.*; import java.security.Security; @@ -1114,6 +1112,10 @@ public class PKIXExtendedTM { }; public static void main(String args[]) throws Exception { + // Disable KeyManager's algorithm constraints checking as this test + // is about TrustManager's constraints check. + System.setProperty("jdk.tls.SunX509KeyManager.certChecking", "false"); + if (args.length != 1) { throw new Exception("Incorrect number of arguments"); } diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java index 809674e8f20..0e7617d1b9c 100644 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Entrust.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ import javax.net.ssl.*; /** * @test - * @bug 8337664 8341059 + * @bug 8337664 8341059 8361212 * @summary Check that TLS Server certificates chaining back to distrusted * Entrust roots are invalid * @library /test/lib @@ -41,14 +41,13 @@ import javax.net.ssl.*; public class Entrust { - private static final String certPath = "chains" + File.separator + "entrust"; + private static final String CERT_PATH = "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"}; + private static final String[] ROOTS_TO_TEST = new String[]{ + "entrustevca", "entrustrootcaec1", "entrustrootcag2", + "entrustrootcag4", "entrust2048ca"}; // Date when the restrictions take effect private static final ZonedDateTime DISTRUST_DATE = @@ -63,6 +62,6 @@ public class Entrust { }; Date notBefore = distrust.getNotBefore(DISTRUST_DATE); - distrust.testCertificateChain(certPath, notBefore, tms, rootsToTest); + distrust.testCertificateChain(CERT_PATH, notBefore, tms, ROOTS_TO_TEST); } } diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem deleted file mode 100644 index 76aa6d14338..00000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustcommercialca-chain.pem +++ /dev/null @@ -1,77 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 8608355977964138876 (0x7777062726a9b17c) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Commercial - Validity - Not Before: Jan 29 14:06:06 2010 GMT - Not After : Dec 31 14:06:06 2030 GMT - ------BEGIN CERTIFICATE----- -MIIHHjCCBgagAwIBAgIQAWZjFOyCvT00u/gtkCvS2TANBgkqhkiG9w0BAQsFADCB -gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT -ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp -cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYxMB4XDTI0MDYyODIx -MzgwNVoXDTI1MDcyODIxMzgwNFowgdgxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP -bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW -BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt -aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 -NDA1NDcxKDAmBgNVBAMTH3ZhbGlkY29tbWVyY2lhbC5hZmZpcm10cnVzdC5jb20w -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeIT2XO0hJ5wDSbIiIcMvs -P3NpQc7O7v5DqldpME6+Qn2sF5b9hc6j72hgTXREa77uUcP5u1JcMWCSWwYQHMpJ -kFzmIzijhS60wW1epb5QyTgM3ZYh1WKvttFCbHUcrTtd+LoPFYsjw9ZK//K9tPp+ -ddn06/ivWvUO5y5vn0wrCaB9tuLdDn4RCQzK2XoZdDuqhPlBBogJX0vM6lsXjgLy -EbvE+/sKYps/In6VtRvCoYavg3OqaIMeaA7gTiYTb1ZGFOAiltnq7fcp6SZUohK3 -QNihv1DadVc+n8LnEUKKDkgG2YgWEFczaE3qwG3ef6L3MzLGrkgVY+qGHyyv2IE7 -AgMBAAGjggM1MIIDMTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT4ARNL47hAsOpa -96VMgKEY3sLIAjAfBgNVHSMEGDAWgBTb72U3C+VHyzXRkB8DwbyIx6fqgDBsBggr -BgEFBQcBAQRgMF4wJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLmFmZmlybXRydXN0 -LmNvbTAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5hZmZpcm10cnVzdC5jb20vYWZ0 -ZXYxY2EuY3J0MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuYWZmaXJtdHJ1 -c3QuY29tL2NybC9hZnRldjFjYS5jcmwwKgYDVR0RBCMwIYIfdmFsaWRjb21tZXJj -aWFsLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI -KwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARPME0wBwYFZ4EMAQEwQgYKKwYBBAGC -jwkCATA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5hZmZpcm10cnVzdC5jb20v -cmVwb3NpdG9yeTCCAYAGCisGAQQB1nkCBAIEggFwBIIBbAFqAHcAEvFONL1TckyE -BhnDjz96E/jntWKHiJxtMAWE6+WGJjoAAAGQYMi3wQAABAMASDBGAiEAjvdsU4G2 -o4BZSOOjaH6gOp7zhKtXQByQUvfHfsi2ePcCIQDnnIO2qlHBm+sskUDlXfR0lCUW -yFPVr9nFZ0L9YPpozgB2AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0 -AAABkGDIt9MAAAQDAEcwRQIhANh1zS3Qeo9yKF+j3G52JhmDRYBS+1TM0wykoXCY -llpxAiAG+LAlKSbwwgrboUSTDDXWNeoRYZ7fKbU72kKfHrpZvwB3ABoE/0nQVB1A -r/agw7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDIt9sAAAQDAEgwRgIhAN8OoC4I -zw8bFJy8ACgK40c9ZfsIfFhePTc9CyrL5uDsAiEA4Jn/IqBB9L5DeTgqw9hBaYag -FmY/2gWDip36ga0WUsAwDQYJKoZIhvcNAQELBQADggEBABywPLJP097Emz6LNeFU -/HvfhaUKv2pgIHf/Kvjs5x78RK9G605THPEHr/TeUjNZ4PBd48WBNVWzyd/8FuOt -r+FsYkRJb9CnrOhZHuCwlcdWXvuY8PiuBmT+xB16BWR5yhYbbiGe4hea0Pf6CfHh -jJoGJw4dQKfgneZOV7IcaWnNTKYawlcZOgxvEwFvj+iZM31WphEPKRAV+N+Tp+ZR -nxlEdjmdbOjqBydlYIEzuFIgxgtnPdK5wqCOWb+z2cARUAO/AkiWrOLTPDc7ydQK -GcfDrSqffHOlwaee08C6STFaJWIcpqxZdXE6Jc+8/85bfPEAG1UepgfnBTqW9RGT -Q3s= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEqDCCA5CgAwIBAgIQFylVHtaOf7Ht9XMA811/1TANBgkqhkiG9w0BAQsFADBE -MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHzAdBgNVBAMMFkFm -ZmlybVRydXN0IENvbW1lcmNpYWwwHhcNMTkwMzIxMjAyNzU0WhcNMzAxMjAyMDQw -MDAwWjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYD -VQQLEyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQD -EyhBZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYxMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuPBMIa9VuXJGAw0MHvieGciPFA11 -b9T49YJ7T+zVpoMMQO+ueUKVHb2l26oeCiwIhXMQ5LquOVcx+rofouzcKXY3wKDZ -zHIOnAkU+23Ucn/3dRH7aHJULsBufZq+NvwgYSgJJEDKfqvIV/c5HiRyZ2H+nAI5 -10Q2xC0UxgSBsufccQ+Fwkg6BAGDlTXrvi8wi75UaGue6jv/qcKLybeVUrgqKE64 -d9oa9PG5/g89QwSdsIQEdVSFzFvFpOG9YhJbJ177Zg6DGCxU0lWwFrVpyH/2vnXl -jhMQScn8UxzCJdDg3EDqjgaV0JH2yoLug+QVYgURPu5BEb5ut9vAdP7cLwIDAQAB -o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz -cC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFNvvZTcL5UfLNdGQHwPBvIjHp+qA -MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUnZPGU4teyq8/nx4P5ZmV -vCT2lI8wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v -d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEkGA1UdHwRCMEAwPqA8oDqG -OGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdENvbW1l -cmNpYWwuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI -KwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAATH11fMrINGmQGQqQW0ATteVnUG -LrmRSN2OlmRm+dkUwKXhcQQEfYYlEggPqgvxSUpw13fXSOqVHqAcj3BIqF957kh+ -m3DmC0RX9KaEKD165pf77P5nZcRmZpBl9cctvzIxN19uzcminchusYwLyeWhBtTZ -xpER9LbrfMNaQ7GnrgalMx54QvdjOhw/GJs9/SqEzYmPshL+DzgZX/oAzY63rQIh -rBblf6/2talZqci96oFzNst8rGfPy/xQ7lgkki1hwIYbORMfloBhP+vAZJo0mxdM -ipu3Z0ToK+KU2iqnBxXVr2/kod+CpkHnjUHa1wnQuSaefng3XwZ/vqtSL9c= ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem deleted file mode 100644 index 7384d31152e..00000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustnetworkingca-chain.pem +++ /dev/null @@ -1,76 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 8957382827206547757 (0x7c4f04391cd4992d) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Networking - Validity - Not Before: Jan 29 14:08:24 2010 GMT - Not After : Dec 31 14:08:24 2030 GMT - ------BEGIN CERTIFICATE----- -MIIHGjCCBgKgAwIBAgIQX2vGPaCJ1tS0ncp2OlBMFjANBgkqhkiG9w0BAQsFADCB -gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT -ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp -cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYzMB4XDTI0MDYyODIx -NDU0OVoXDTI1MDcyODIxNDU0OFowgdgxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP -bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW -BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt -aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 -NDA1NDcxKDAmBgNVBAMTH3ZhbGlkbmV0d29ya2luZy5hZmZpcm10cnVzdC5jb20w -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkGknE8kFr+CaIybQrDPRw -z9OKXq77p4CnrkF1/g9w/HiIs6Ps8YqTjsiTKM3wYLbvPA+TbO9DpCSyCP2bVyLf -AjUE617KZSpfy9RqzvGjn/1qH/cBKohhEliMfDj4ZHfY4x+1WYTZPVK/g0Ny5RAP -wz9lJHR2SsVGLvpqXzWaVoxifJ8HZWD7n5z/75WeYko+Hubx3WvzJZcN2Xjn+q6a -7wkDaXPayrvn5uWGPlOLQHqJ5z7wts21jASMTfJAToFyzH6dGwvqxkP3bVJGJ8AF -vtMfqVjcOcjWgmmOEHMPAAqs5QKrYuSLccH6hFTwFEUCdMwVqfloznt2sNUSBoKj -AgMBAAGjggMxMIIDLTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTrE0z4fRyx9P9M -0FfA6VgGkJiYVDAfBgNVHSMEGDAWgBR5HrHJF8cerLHHFNfD6H+8uVCbFTBsBggr -BgEFBQcBAQRgMF4wJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLmFmZmlybXRydXN0 -LmNvbTAzBggrBgEFBQcwAoYnaHR0cDovL2FpYS5hZmZpcm10cnVzdC5jb20vYWZ0 -ZXYzY2EuY3J0MDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuYWZmaXJtdHJ1 -c3QuY29tL2NybC9hZnRldjNjYS5jcmwwKgYDVR0RBCMwIYIfdmFsaWRuZXR3b3Jr -aW5nLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI -KwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARPME0wBwYFZ4EMAQEwQgYKKwYBBAGC -jwkCAjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5hZmZpcm10cnVzdC5jb20v -cmVwb3NpdG9yeTCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHYADeHyMCvTDcFA -YhIJ6lUu/Ed0fLHX6TDvDkIetH5OqjQAAAGQYM/MjQAABAMARzBFAiBjnehs1mvh -5Xm3uXZ7Bq8gijwiXThwnLSYROQxnWrnbAIhALbgJG+PRZQfzTBbgM/zAwNsBjhe -F5iENnaajJCxzOhaAHUAEvFONL1TckyEBhnDjz96E/jntWKHiJxtMAWE6+WGJjoA -AAGQYM/MgQAABAMARjBEAiAsWOm1IIjaxQP9uaPI9tQmkiJPUOTrBTsTDO+jkgiG -+QIgVNhND82rsFGjrtAAHzzgCVzLDUM3zaHxnP/z3BNuO4QAdQAaBP9J0FQdQK/2 -oMO/8djEZy9O7O4jQGiYaxdALtyJfQAAAZBgz8zLAAAEAwBGMEQCIBIGxtjk7Lw8 -i+oggK7VrPMNTB632t321cwhEm517BbZAiBws3+uytwh59N6qGJUuSFQnOZNPOPj -eQnH2fSdT1J2sDANBgkqhkiG9w0BAQsFAAOCAQEAcSzitESRKlbcUvxvUB7FjK0I -CaBU1Nyu0xDFCoG2pmp7GASJz34wtPYfsiX5+j4hDh/noMcgk7WlD8pzgWYw15Rk -+5kTv2v4U85y/JFjzMOHbz64KjQdGebqhjvC/E/EXxK+AZf4H574/w7rbyJ30vFL -gNvPF9AxS1MuYIO55jXrHMByKnFoQZgPsmAY/x+n+OzMxWOdR18PupypCB5TyJZ8 -pQzwoxmX7qeZHiXyJ8jQUwe1qoQc2SbwfQxfwSPUPSJuQo90N+5nyQMe7vvPBM0Y -/CXaFpfPqh71D4C0Ey+0hYxSt99gYs4P9twUByjIlP0wTyhaoEpt3zw9DdZypQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEqDCCA5CgAwIBAgIQNCSh7Pjwo1/nRrcBHEPoRDANBgkqhkiG9w0BAQsFADBE -MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHzAdBgNVBAMMFkFm -ZmlybVRydXN0IE5ldHdvcmtpbmcwHhcNMTkwMzIxMjAzODU5WhcNMzAxMjAyMDQw -MDAwWjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYD -VQQLEyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQD -EyhBZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYzMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHDl/3xr1qiHoe0Rzb3AGLw56e9J -l2a3X59+PAfI5wGBHuK9Dl7XsyoH65X6QIC/rXyVpuNgKbbwIGHB+rCSplyHzGyC -WeM3LXa2q1US7VteeFDS959nxJVRFfwATR9xAK6YTUWQ/yWdw0dZSm0lQNmEMBwS -qi0ufWokiWXZUzWHOu7A6driCohu9sFDwe1INJUPH6uIlovmzGvG3UYbUSymJcjs -Ka0fXXX9zukco8exlOIKWRJSNLxKtSSPDVASrGLQ1xi3qkiLTKci3+jKMNDFf1vw -foZN99HhUcWKXfr2KlWfANdjTMlsTKCfuhfWl1OBVNHGRrACAQCXI/ji0wIDAQAB -o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz -cC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFHkesckXxx6ssccU18Pof7y5UJsV -MBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUBx/S55zawm6iQLSwelAQ -UHTEyL0wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8v -d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEkGA1UdHwRCMEAwPqA8oDqG -OGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdE5ldHdv -cmtpbmcuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI -KwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAAhmE4I56hNpnWXQ2Si8a/TgQUZr -X5Jlv1LDvl3rkDyfEIHNZ8dth17SakJYJBWHExph/iIYjCJ9YmeyhghV5rPqT+wF -4yyE2ngenIusfnWT2bTpT9u2VZbCNeACE5XnN2UHSA0J9idPjfLuthViWEvSZZUh -DJ53bX+exO366nDY4AI7owIyhz8hdsWyhZ/0ST+eD+kbgd8osd+GdxzRmyKcfl84 -D1K1uff01T9w2dyUaZglQsFljkaO6xmeXZJsPnhwCp/HlMHWzhAneUQ7I9FZSOW+ -WiYbt4RitmBpysadBReikWM4knECzJQ/fMT9vC0k9BLlqUYRwCH9vr0UnZo= ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem deleted file mode 100644 index 6f108bc1229..00000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumca-chain.pem +++ /dev/null @@ -1,88 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 7893706540734352110 (0x6d8c1446b1a60aee) - Signature Algorithm: sha384WithRSAEncryption - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Premium - Validity - Not Before: Jan 29 14:10:36 2010 GMT - Not After : Dec 31 14:10:36 2040 GMT - ------BEGIN CERTIFICATE----- -MIIIFjCCBv6gAwIBAgIQQVOTWr7tEAJXmRDkCSxkajANBgkqhkiG9w0BAQsFADCB -gzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJT -ZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhBZmZp -cm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYyMB4XDTI0MDYyODIx -NDgyN1oXDTI1MDcyODIxNDgyNlowgdUxCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdP -bnRhcmlvMQ8wDQYDVQQHEwZPdHRhd2ExEzARBgsrBgEEAYI3PAIBAxMCQ0ExGDAW -BgsrBgEEAYI3PAIBAhMHT250YXJpbzEcMBoGA1UEChMTQWZmaXJtdHJ1c3QgTGlt -aXRlZDEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzI1 -NDA1NDcxJTAjBgNVBAMTHHZhbGlkcHJlbWl1bS5hZmZpcm10cnVzdC5jb20wggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVRMzwbDq47ivHOKqJdiEJNL2+ -g9Snj/BRctqcQTrIV99RP0pmAh5fHg7vnhVsHqc9sRLVcQWTJk9NuRJ2VnDKWsBa -Xrp5UWaNjS0vaFA4jzCi1gWzTTZgPTQn3VRG3JP1F5CZb405/mtWDaw/CfWkcUqQ -VSilqFlJRsjcPCzQh7ZaXAo+FmzJxNSwjxdP6JSYMeTDRCUpSb3T8PypVI1CEmLZ -jsxrg5oIZn25591g/pzgLE56N0stNY4d3q4YD1t5x46RsqYAJYSkk8rcTN+kHzsY -VSqaRDyPkGbmuCeJUvW24wJ30yQtXQWA+U0dMYLe7LyglJ7dkOzvWNbqrIcvM8My -hxH/wwVH7e4dL/1E58yr1BHENUk7Mp9rzIXj496eLkF5G1lMkNnuVRQqCAOW0rPY -V0rI8yrCMTK52s4mNjQo2J7JOYdTUvAWZ92MKvEjjhQlMH8eK72Km/+mkxpsgGmr -3c6u+Gom7oI5VaLZ+3p2uWaOsutk1tkzWjhzY4L27hwmIdWujfrWMRx8uxcfoJxX -gQ40d1QiSN51BtCPE5UnpLU/YUxMdzWmtUoGUfYIGVqDVToBnunIFMdmFjC0IrNl -hquDQi/OGMpzuOvxX1FoXb+rRwOhhdrcR0BQqUVRTV0U5LlcsDeNMqmqPE9mzGtJ -W69Fsh7crntng/L72wIDAQABo4IDMDCCAywwDAYDVR0TAQH/BAIwADAdBgNVHQ4E -FgQU3PWyi/4usZghgahc/Tj+Q60QLOcwHwYDVR0jBBgwFoAUc3yaOGg8UXxBCP6h -HyoetGHbzTwwbAYIKwYBBQUHAQEEYDBeMCcGCCsGAQUFBzABhhtodHRwOi8vb2Nz -cC5hZmZpcm10cnVzdC5jb20wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9haWEuYWZmaXJt -dHJ1c3QuY29tL2FmdGV2MmNhLmNydDA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8v -Y3JsLmFmZmlybXRydXN0LmNvbS9jcmwvYWZ0ZXYyY2EuY3JsMCcGA1UdEQQgMB6C -HHZhbGlkcHJlbWl1bS5hZmZpcm10cnVzdC5jb20wDgYDVR0PAQH/BAQDAgWgMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBWBgNVHSAETzBNMAcGBWeBDAEB -MEIGCisGAQQBgo8JAgMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuYWZmaXJt -dHJ1c3QuY29tL3JlcG9zaXRvcnkwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB2 -ABoE/0nQVB1Ar/agw7/x2MRnL07s7iNAaJhrF0Au3Il9AAABkGDSN7EAAAQDAEcw -RQIgVDWwhv7yG6RNnkMZnVq1YYA7ypn/GSH0ibUKnESHRpYCIQCY8gyCX7VFONUI -QuR8daz7ra2FCUI9TwylrR3eFfIgGgB3AN3cyjSV1+EWBeeVMvrHn/g9HFDf2wA6 -FBJ2Ciysu8gqAAABkGDSN5cAAAQDAEgwRgIhAM1edsSyFUKU0Dj1WxTGwziE6fCW -g2ByfL8kDrP260YXAiEA6YQOpJf04N13Nn263BxAl+laH9Ar0eo03fArlv743TQA -dQAN4fIwK9MNwUBiEgnqVS78R3R8sdfpMO8OQh60fk6qNAAAAZBg0je+AAAEAwBG -MEQCIExqK4katETAQo+H0+ImuNJCSeFEI9C+9wrjhl6ZnWb9AiBwkC1vpLYOIm/1 -YCLCQIOmTdg2wf8LITlrQNJA8vbBljANBgkqhkiG9w0BAQsFAAOCAQEASOmPu7ot -yl6MoMns19uI6H2KSUjMFh3/fKMcY/ettmEYalgrytexFMrLnD2UniBlD+nJEshp -5/z7o0YDiRoiLhMAs7VqIdX3erNu/ghNh7P2bDnoMWShSoAKxez1XOGL3rRE0NAi -DsWCaNRHH9rnC97275sbGnua7ZYg+8BiF62vpJlqjrxDHjGiej8qAWSjztbB43Af -bwRscpXTxNkMvOBuRFMH+rSxB8CrOV68W+yxmzPuPxVjM7oJH8Qk5BC53NRqFsVz -JhbNfot0+/drj7JT3jlacUVQcD/BzDuC3+qczQlLjLdHgQM2/e4fXsD6C5S6B11d -BDx6ipGpaASofA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFojCCA4qgAwIBAgIQU3HI6weE/VEI5dTz4yPsRjANBgkqhkiG9w0BAQsFADBB -MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxHDAaBgNVBAMME0Fm -ZmlybVRydXN0IFByZW1pdW0wHhcNMTkwMzIxMjA0NjM1WhcNMzAxMjAyMDQwMDAw -WjCBgzELMAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQL -EyJTZWUgd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTEwLwYDVQQDEyhB -ZmZpcm1UcnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVYyMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvDDZHfxkB1nAGFKdw0VCgV+B/eBtW1o+ -bXzwRcpeFh5saDI+tv1RAMrYFq+AJkXCCJopgMF2Wqfv5myE3JMgxEHuuKUpJz7H -FprrFckVOGCtJKH8Iy9AWPjBwt8lKmxGJF7EZst+QoVt4hMe0qhL0WEKbATFPe41 -DcM7UsyQv6Bvpn424uePy3/1ATIsVL3YmvAbUNR0aqVxYAJzTefvyIet/761bKGc -NyqdOVWFFeTDtr8iL1TBXToAgl0GJ39bFQZsP19VcCpfk9Zj3YHTPRPq5wZOZuUN -F7jiBUEi6DaVOi3Wy4vdySHtWPeBHRYif1I6fcUfdCNORMc4ee6KewIDAQABo4IB -UTCCAU0wNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5h -ZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFHN8mjhoPFF8QQj+oR8qHrRh2808MBIG -A1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAUncBnpgwi2Sb1RaumZVIRJ9hF -rGMwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3 -LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MEYGA1UdHwQ/MD0wO6A5oDeGNWh0 -dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2NybC9BZmZpcm1UcnVzdFByZW1pdW0u -Y3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH -AwIwDQYJKoZIhvcNAQELBQADggIBABi64UEwl3l0yIiuSACyVQQIBI60BUmhseac -4BzCAsJrR5tE/2U9QAa2y6JpR1nqm76DJvw1QQgvFcNe+fkwpvoViCaSTbZkGGwD -mQe2xRSYJcDSMQUc/GgzLcX2c1CrexQXE1vwV/q33af1en5s1GzLl915aNS/k1ch -G7EMruJ/D4cuH9j4j2i+b+llmVBzavBwelN5rc693o+Ot9id/1sTWNugwAu3uXGb -VlhETMnjXGIciegOLdWYhWBln0izYlt9IwlDEpjMVaZ0HZlj2JBGaSe4PfEFpJPO -beuPcQpLQGw2XpW2ZMG5JcRYaoKWjixXAGktRA3H9nvVW92jvzx/RX484w2ZM5Rt -E+I1ikAuQLAyWG7clht387e2RuC3NZTtefSyjE3L9gQDOPC+Z9ycwr0WJHRsxFvh -FJQi3JnxgFZf5mc5n2mh3qAgALTNOUHuDiHrerjTOWbpF/1/NJmo/c/YZ63vZIhc -EaER4HuhbBqlpf6z3WOIQdZm1ChwXYHrEcLDgfwm9cXoaVK2HZapkMwQbPffPlT1 -E+AxRFB4YmT1y2WzdaHfhFA9nH6ByUdL5+FfrDoIIUO2e8OLOAcrJsf5+unhAhc0 -v7N48JWdmpstjkXCaCIaidrZLJxS+pikNgHB1dXF/TxokLTiPB9jcYKdGaYs3XHb -YKLdwubu ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem deleted file mode 100644 index 37b1b787084..00000000000 --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/entrust/affirmtrustpremiumeccca-chain.pem +++ /dev/null @@ -1,63 +0,0 @@ -Root Certificate: - Version: 3 (0x2) - Serial Number: 8401224907861490260 (0x7497258ac73f7a54) - Signature Algorithm: ecdsa-with-SHA384 - Issuer: C=US, O=AffirmTrust, CN=AffirmTrust Premium ECC - Validity - Not Before: Jan 29 14:20:24 2010 GMT - Not After : Dec 31 14:20:24 2040 GMT - ------BEGIN CERTIFICATE----- -MIIF0zCCBVmgAwIBAgIQFVwk9nYUM5SYOnBd+IoGtzAKBggqhkjOPQQDAzCBhTEL -MAkGA1UEBhMCQ0ExFDASBgNVBAoTC0FmZmlybVRydXN0MSswKQYDVQQLEyJTZWUg -d3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5MTMwMQYDVQQDEypBZmZpcm1U -cnVzdCBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0gRVZFQzEwHhcNMjQwNjI4MjE0 -OTUwWhcNMjUwNzI4MjE0OTQ4WjCB2DELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09u -dGFyaW8xDzANBgNVBAcTBk90dGF3YTETMBEGCysGAQQBgjc8AgEDEwJDQTEYMBYG -CysGAQQBgjc8AgECEwdPbnRhcmlvMRwwGgYDVQQKExNBZmZpcm10cnVzdCBMaW1p -dGVkMR0wGwYDVQQPExRQcml2YXRlIE9yZ2FuaXphdGlvbjEQMA4GA1UEBRMHMjU0 -MDU0NzEoMCYGA1UEAxMfdmFsaWRwcmVtaXVtZWNjLmFmZmlybXRydXN0LmNvbTB2 -MBAGByqGSM49AgEGBSuBBAAiA2IABEkLBzBYSJPRENKDaA1iBPQz+jZUV+OoM9nJ -sr9sMfmHaqr3nlWxAMM99b9/usVfYyUxqyi+YL2Z3ZSxjX2dpyhwMtPpIQkL1pMW -Iv55XBIcYRyl2NjcADS9B06G+nnix6OCAzcwggMzMAwGA1UdEwEB/wQCMAAwHQYD -VR0OBBYEFP+37ywf2YJJ/4CEVy1GY4ioGm1yMB8GA1UdIwQYMBaAFMaQjAKD113j -vjucLtVlfSoQYO7lMG4GCCsGAQUFBwEBBGIwYDAnBggrBgEFBQcwAYYbaHR0cDov -L29jc3AuYWZmaXJtdHJ1c3QuY29tMDUGCCsGAQUFBzAChilodHRwOi8vYWlhLmFm -ZmlybXRydXN0LmNvbS9hZnRldmVjMWNhLmNydDA+BgNVHR8ENzA1MDOgMaAvhi1o -dHRwOi8vY3JsLmFmZmlybXRydXN0LmNvbS9jcmwvYWZ0ZXZlYzFjYS5jcmwwKgYD -VR0RBCMwIYIfdmFsaWRwcmVtaXVtZWNjLmFmZmlybXRydXN0LmNvbTAOBgNVHQ8B -Af8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMFYGA1UdIARP -ME0wBwYFZ4EMAQEwQgYKKwYBBAGCjwkCBDA0MDIGCCsGAQUFBwIBFiZodHRwczov -L3d3dy5hZmZpcm10cnVzdC5jb20vcmVwb3NpdG9yeTCCAX4GCisGAQQB1nkCBAIE -ggFuBIIBagFoAHUA5tIxY0B3jMEQQQbXcbnOwdJA9paEhvu6hzId/R43jlAAAAGQ -YNN5tQAABAMARjBEAiAnainEoBGI9czVh+c9QLPL30S3Rtov8zrnhlXfeKLzZQIg -UGkntBMux0MqHt9Aj60qMsS/C4ZWF7AihVVaUKcrEVgAdgAN4fIwK9MNwUBiEgnq -VS78R3R8sdfpMO8OQh60fk6qNAAAAZBg03m1AAAEAwBHMEUCIGI9kBByoozH4cfS -ECW/O2N/ElkdATkt7EwQ52kcc4ICAiEA9QTh8JlJTb/ytYC1ECX0vQbrYVexg+fu -dw7dfToF9nAAdwAS8U40vVNyTIQGGcOPP3oT+Oe1YoeInG0wBYTr5YYmOgAAAZBg -03ndAAAEAwBIMEYCIQCox5nSCcVB2AfNYXco77zsJnYP7KAU2I4VA2GNL7I4wQIh -AP6WEzyfBoGpYYqFmNnJUavyhKBmeNiR7eNtaFwpSc+UMAoGCCqGSM49BAMDA2gA -MGUCMAGSNMXAAKDRk0ZOtydN95Rkja97+70TatCIIxEAsJD8Hu7lfj2LHCYFQjVY -oaWTrQIxAKUudx7E/JnjsthuL6sNqKVHfD3iLUJyQNK9wE0SVt1xAm7Cu1JXZORE -M64KMKoQFQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDXDCCAuKgAwIBAgIQAgKlhME0Bk3J8y0gfqNymDAKBggqhkjOPQQDAzBFMQsw -CQYDVQQGEwJVUzEUMBIGA1UECgwLQWZmaXJtVHJ1c3QxIDAeBgNVBAMMF0FmZmly -bVRydXN0IFByZW1pdW0gRUNDMB4XDTE5MDMyMTIwNTUwN1oXDTMwMTIwMjA0MDAw -MFowgYUxCzAJBgNVBAYTAkNBMRQwEgYDVQQKEwtBZmZpcm1UcnVzdDErMCkGA1UE -CxMiU2VlIHd3dy5hZmZpcm10cnVzdC5jb20vcmVwb3NpdG9yeTEzMDEGA1UEAxMq -QWZmaXJtVHJ1c3QgRXh0ZW5kZWQgVmFsaWRhdGlvbiBDQSAtIEVWRUMxMHYwEAYH -KoZIzj0CAQYFK4EEACIDYgAEu9f5NkumdaVlmaNaxpDB+rBk/S6lhqcUU1zTLcRz -4G0dr4290hezjrvZJxGJ/X15aexpdD2V9cwaPD/yuEJcaaz+rg/qDoqQF3+AFqVc -41jw1E0S59+57XVKLtXI7Xh6o4IBVDCCAVAwNwYIKwYBBQUHAQEEKzApMCcGCCsG -AQUFBzABhhtodHRwOi8vb2NzcC5hZmZpcm10cnVzdC5jb20wHQYDVR0OBBYEFMaQ -jAKD113jvjucLtVlfSoQYO7lMBIGA1UdEwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgw -FoAUmq8pesARNTUmUTAAw2r+QNWu1jwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYI -KwYBBQUHAgEWJmh0dHBzOi8vd3d3LmFmZmlybXRydXN0LmNvbS9yZXBvc2l0b3J5 -MEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuYWZmaXJtdHJ1c3QuY29tL2Ny -bC9BZmZpcm1UcnVzdFByZW1pdW1FQ0MuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNV -HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwCgYIKoZIzj0EAwMDaAAwZQIwHJ5g -a6sHvQ51DGr0bWq34awuwlWbybC2grHoNp5uYapcXr/qTJusb/6n+dczqFdaAjEA -7VQY06fE9ifMnTgT9824jc3+H6kfhMk4PoIj9ouWdYfc1DyTBS/low9Hb8liQyFr ------END CERTIFICATE----- diff --git a/test/jdk/sun/security/tools/keytool/PrintSSL.java b/test/jdk/sun/security/tools/keytool/PrintSSL.java index 7cdc0a45771..9403ae62d92 100644 --- a/test/jdk/sun/security/tools/keytool/PrintSSL.java +++ b/test/jdk/sun/security/tools/keytool/PrintSSL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,12 @@ import jdk.test.lib.process.OutputAnalyzer; public class PrintSSL { public static void main(String[] args) throws Throwable { + // Disable KeyManager's algorithm constraints checking, + // so we can make keytool print certificate with weak + // MD5withRSA signature algorithm. + System.setProperty( + "jdk.tls.SunX509KeyManager.certChecking", "false"); + Files.deleteIfExists(Paths.get("keystore")); // make sure that "-printcert" works with weak algorithms diff --git a/test/jdk/sun/security/util/Debug/DebugOptions.java b/test/jdk/sun/security/util/Debug/DebugOptions.java index 5fa02af5112..72d8b3606bc 100644 --- a/test/jdk/sun/security/util/Debug/DebugOptions.java +++ b/test/jdk/sun/security/util/Debug/DebugOptions.java @@ -29,16 +29,20 @@ * @run junit DebugOptions */ -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - import java.security.KeyStore; import java.security.Security; -import java.util.stream.Stream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class DebugOptions { @@ -47,54 +51,133 @@ public class DebugOptions { "properties\\[.*\\|main|" + DATE_REGEX + ".*\\]:"; static final String EXPECTED_PROP_KEYSTORE_REGEX = "properties\\[.*\\|main|" + DATE_REGEX + - ".*\\Rkeystore\\[.*\\|main|" + DATE_REGEX + ".*\\]:"; + ".*\\Rkeystore\\[.*\\|main|" + DATE_REGEX + ".*\\]:"; static final String EXPECTED_ALL_REGEX = "properties\\[.*\\|main.*\\|" + DATE_REGEX + - ".*\\]((.*\\R)*)keystore\\[.*\\|main.*\\|" - + DATE_REGEX + ".*\\]:"; + ".*\\]((.*\\R)*)keystore\\[.*\\|main.*\\|" + + DATE_REGEX + ".*\\]:"; - private static Stream patternMatches() { - return Stream.of( - // test for thread and timestamp info - Arguments.of("properties", - EXPECTED_PROP_REGEX, - "properties:"), - // test for thread and timestamp info - Arguments.of("properties+thread", - EXPECTED_PROP_REGEX, - "properties:"), - // flip the arguments of previous test - Arguments.of("properties+thread+timestamp", - EXPECTED_PROP_REGEX, - "properties:"), - // regular keystore,properties component string - Arguments.of("keystore,properties", - EXPECTED_PROP_KEYSTORE_REGEX, - "properties:"), - // turn on all - Arguments.of("all", - EXPECTED_ALL_REGEX, - "properties:"), - // expect thread and timestamp info - Arguments.of("all+thread", - EXPECTED_ALL_REGEX, - "properties:") - ); - } + private static final List patternMatches = List.of( + // test for thread and timestamp info + new String[]{"properties", + EXPECTED_PROP_REGEX, + "properties:"}, + // test for thread and timestamp info + new String[]{"properties+thread", + EXPECTED_PROP_REGEX, + "properties:"}, + // flip the arguments of previous test + new String[]{"properties+thread+timestamp", + EXPECTED_PROP_REGEX, + "properties:"}, + // regular keystore,properties component string + new String[]{"keystore,properties", + EXPECTED_PROP_KEYSTORE_REGEX, + "properties:"}, + // turn on all + new String[]{"all", + EXPECTED_ALL_REGEX, + "properties:"}, + // expect thread and timestamp info + new String[]{"all+thread", + EXPECTED_ALL_REGEX, + "properties:"} + ); - @ParameterizedTest - @MethodSource("patternMatches") - public void shouldContain(String params, String expected, String notExpected) throws Exception { - OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava( - "-Djava.security.debug=" + params, - "DebugOptions" - ); + /** + * This will execute the test logic, but first change the param + * to be mixed case + * + * @param paramName name of the parameter e.g. -Djava.security.debug= + * @param paramVal value of the parameter + * @param expected expected output + * @param notExpected not expected output + */ + public void testMixedCaseParameter(String paramName, + String paramVal, + String expected, + String notExpected) throws Exception { + + final String formattedParam = makeFirstAndLastLetterUppercase(paramVal); + System.out.printf("Executing: {%s%s DebugOptions}%n", + paramName, + formattedParam); + + final OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava( + paramName + formattedParam, + "DebugOptions"); outputAnalyzer.shouldHaveExitValue(0) .shouldMatch(expected) .shouldNotMatch(notExpected); } + /** + * This method will change the input string to have + * first and last letters uppercase + *

    + * e.g.: + * hello -> HellO + * + * @param paramString string to change. Must not be null or empty + * @return resulting string + */ + private String makeFirstAndLastLetterUppercase(final String paramString) { + Assertions.assertTrue(paramString != null && !paramString.isEmpty()); + + final int length = paramString.length(); + final String firstLetter = paramString.substring(0, 1); + final String lastLetter = paramString.substring((length - 1), + length); + + return firstLetter.toUpperCase() + + paramString.substring(1, length - 1) + + lastLetter.toUpperCase(); + } + + /** + * This test will run all options in parallel with all param names + * in mixed case + */ + @Test + public void debugOptionsMixedCaseTest() throws Exception { + + try (final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) { + final List> testsCallables = new ArrayList<>(); + + patternMatches.forEach(params -> { + testsCallables.add(() -> { + testMixedCaseParameter( + "-Djava.security.debug=", + params[0], + params[1], + params[2]); + return null; + }); + testsCallables.add(() -> { + testMixedCaseParameter( + "-Djava.security.auth.debug=", + params[0], + params[1], + params[2]); + return null; + }); + + System.out.println("Option added to all mixed case tests " + Arrays.toString(params)); + }); + + System.out.println("Starting all the threads"); + final List> res = executorService.invokeAll(testsCallables); + for (final Future future : res) { + future.get(); + } + } + } + + /** + * This is used for the test logic itself + */ public static void main(String[] args) throws Exception { + // something to trigger "properties" debug output Security.getProperty("test"); // trigger "keystore" debug output diff --git a/test/jdk/tools/jpackage/apps/PrintEnv.java b/test/jdk/tools/jpackage/apps/PrintEnv.java index bb1cef800f4..64a243a0abc 100644 --- a/test/jdk/tools/jpackage/apps/PrintEnv.java +++ b/test/jdk/tools/jpackage/apps/PrintEnv.java @@ -21,18 +21,38 @@ * questions. */ +import java.io.IOException; +import java.io.UncheckedIOException; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; public class PrintEnv { public static void main(String[] args) { List lines = printArgs(args); - lines.forEach(System.out::println); + Optional.ofNullable(System.getProperty("jpackage.test.appOutput")).map(Path::of).ifPresentOrElse(outputFilePath -> { + Optional.ofNullable(outputFilePath.getParent()).ifPresent(dir -> { + try { + Files.createDirectories(dir); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + try { + Files.write(outputFilePath, lines); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }, () -> { + lines.forEach(System.out::println); + }); } private static List printArgs(String[] args) { @@ -45,11 +65,13 @@ public class PrintEnv { } else if (arg.startsWith(PRINT_SYS_PROP)) { String name = arg.substring(PRINT_SYS_PROP.length()); lines.add(name + "=" + System.getProperty(name)); - } else if (arg.startsWith(PRINT_MODULES)) { + } else if (arg.equals(PRINT_MODULES)) { lines.add(ModuleFinder.ofSystem().findAll().stream() .map(ModuleReference::descriptor) .map(ModuleDescriptor::name) .collect(Collectors.joining(","))); + } else if (arg.equals(PRINT_WORK_DIR)) { + lines.add("$CD=" + Path.of("").toAbsolutePath()); } else { throw new IllegalArgumentException(); } @@ -58,7 +80,8 @@ public class PrintEnv { return lines; } - private final static String PRINT_ENV_VAR = "--print-env-var="; - private final static String PRINT_SYS_PROP = "--print-sys-prop="; - private final static String PRINT_MODULES = "--print-modules"; + private static final String PRINT_ENV_VAR = "--print-env-var="; + private static final String PRINT_SYS_PROP = "--print-sys-prop="; + private static final String PRINT_MODULES = "--print-modules"; + private static final String PRINT_WORK_DIR = "--print-workdir"; } diff --git a/test/jdk/tools/jpackage/clean_test_output.sh b/test/jdk/tools/jpackage/clean_test_output.sh new file mode 100644 index 00000000000..e472d780ded --- /dev/null +++ b/test/jdk/tools/jpackage/clean_test_output.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# +# Filters output produced by running jpackage test(s). +# + +set -eu +set -o pipefail + + +sed_inplace_option=-i +sed_version_string=$(sed --version 2>&1 | head -1 || true) +if [ "${sed_version_string#sed (GNU sed)}" != "$sed_version_string" ]; then + # GNU sed, the default + : +elif [ "${sed_version_string#sed: illegal option}" != "$sed_version_string" ]; then + # Macos sed + sed_inplace_option="-i ''" +else + echo 'WARNING: Unknown sed variant, assume it is GNU compatible' +fi + + +filterFile () { + local expressions=( + # Strip leading log message timestamp `[19:33:44.713] ` + -e 's/^\[[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}\.[0-9]\{3\}\] //' + + # Strip log message timestamps `[19:33:44.713]` + -e 's/\[[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}\.[0-9]\{3\}\]//g' + + # Convert variable part of R/O directory path timestamp `#2025-07-24T16:38:13.3589878Z` + -e 's/#[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}T[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}\.[0-9]\{1,\}Z/#Z/' + + # Strip variable part of temporary directory name `jdk.jpackage5060841750457404688` + -e 's|\([\/]\)jdk\.jpackage[0-9]\{1,\}\b|\1jdk.jpackage|g' + + # Convert PID value `[PID: 131561]` + -e 's/\[PID: [0-9]\{1,\}\]/[PID: ]/' + + # Strip a warning message `Windows Defender may prevent jpackage from functioning` + -e '/Windows Defender may prevent jpackage from functioning/d' + + # Convert variable part of test output directory `out-6268` + -e 's|\bout-[0-9]\{1,\}\b|out-N|g' + + # Convert variable part of test summary `[ OK ] IconTest(AppImage, ResourceDirIcon, DefaultIcon).test; checks=39` + -e 's/^\(.*\bchecks=\)[0-9]\{1,\}\(\r\{0,1\}\)$/\1N\2/' + + # Convert variable part of ldd output `libdl.so.2 => /lib64/libdl.so.2 (0x00007fbf63c81000)` + -e 's/(0x[[:xdigit:]]\{1,\})$/(0xHEX)/' + + # Convert variable part of rpmbuild output `Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.CMO6a9` + -e 's|/rpm-tmp\...*$|/rpm-tmp.V|' + + # Convert variable part of stack trace entry `at jdk.jpackage.test.JPackageCommand.execute(JPackageCommand.java:863)` + -e 's/^\(.*\b\.java:\)[0-9]\{1,\}\()\r\{0,1\}\)$/\1N\2/' + ) + + sed $sed_inplace_option "$1" "${expressions[@]}" +} + + +for f in "$@"; do + filterFile "$f" +done diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java index 801df8624c4..07c8e06856f 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java @@ -22,39 +22,54 @@ */ package jdk.jpackage.test; -import static java.util.stream.Collectors.toMap; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static jdk.jpackage.test.LauncherShortcut.LINUX_SHORTCUT; +import static jdk.jpackage.test.LauncherShortcut.WIN_DESKTOP_SHORTCUT; +import static jdk.jpackage.test.LauncherShortcut.WIN_START_MENU_SHORTCUT; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Properties; +import java.util.Set; import java.util.function.BiConsumer; -import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ThrowingBiConsumer; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.test.LauncherShortcut.StartupDirectory; +import jdk.jpackage.test.LauncherVerifier.Action; -public class AdditionalLauncher { +public final class AdditionalLauncher { public AdditionalLauncher(String name) { - this.name = name; - this.rawProperties = new ArrayList<>(); + this.name = Objects.requireNonNull(name); setPersistenceHandler(null); } - public final AdditionalLauncher setDefaultArguments(String... v) { + public AdditionalLauncher withVerifyActions(Action... actions) { + verifyActions.addAll(List.of(actions)); + return this; + } + + public AdditionalLauncher withoutVerifyActions(Action... actions) { + verifyActions.removeAll(List.of(actions)); + return this; + } + + public AdditionalLauncher setDefaultArguments(String... v) { defaultArguments = new ArrayList<>(List.of(v)); return this; } - public final AdditionalLauncher addDefaultArguments(String... v) { + public AdditionalLauncher addDefaultArguments(String... v) { if (defaultArguments == null) { return setDefaultArguments(v); } @@ -63,12 +78,12 @@ public class AdditionalLauncher { return this; } - public final AdditionalLauncher setJavaOptions(String... v) { + public AdditionalLauncher setJavaOptions(String... v) { javaOptions = new ArrayList<>(List.of(v)); return this; } - public final AdditionalLauncher addJavaOptions(String... v) { + public AdditionalLauncher addJavaOptions(String... v) { if (javaOptions == null) { return setJavaOptions(v); } @@ -77,51 +92,46 @@ public class AdditionalLauncher { return this; } - public final AdditionalLauncher setVerifyUninstalled(boolean value) { - verifyUninstalled = value; + public AdditionalLauncher setProperty(String name, Object value) { + rawProperties.put(Objects.requireNonNull(name), Objects.requireNonNull(value.toString())); return this; } - public final AdditionalLauncher setLauncherAsService() { - return addRawProperties(LAUNCHER_AS_SERVICE); - } - - public final AdditionalLauncher addRawProperties( - Map.Entry v) { - return addRawProperties(List.of(v)); - } - - public final AdditionalLauncher addRawProperties( - Map.Entry v, Map.Entry v2) { - return addRawProperties(List.of(v, v2)); - } - - public final AdditionalLauncher addRawProperties( - Collection> v) { - rawProperties.addAll(v); + public AdditionalLauncher setShortcuts(boolean menu, boolean desktop) { + if (TKit.isLinux()) { + setShortcut(LINUX_SHORTCUT, desktop); + } else if (TKit.isWindows()) { + setShortcut(WIN_DESKTOP_SHORTCUT, desktop); + setShortcut(WIN_START_MENU_SHORTCUT, menu); + } return this; } - public final String getRawPropertyValue( - String key, Supplier getDefault) { - return rawProperties.stream() - .filter(item -> item.getKey().equals(key)) - .map(e -> e.getValue()).findAny().orElseGet(getDefault); - } - - private String getDesciption(JPackageCommand cmd) { - return getRawPropertyValue("description", () -> cmd.getArgumentValue( - "--description", unused -> cmd.name())); - } - - public final AdditionalLauncher setShortcuts(boolean menu, boolean shortcut) { - withMenuShortcut = menu; - withShortcut = shortcut; + public AdditionalLauncher setShortcut(LauncherShortcut shortcut, StartupDirectory value) { + if (value != null) { + setProperty(shortcut.propertyName(), value.asStringValue()); + } else { + setProperty(shortcut.propertyName(), false); + } return this; } - public final AdditionalLauncher setIcon(Path iconPath) { - if (iconPath == NO_ICON) { + public AdditionalLauncher setShortcut(LauncherShortcut shortcut, boolean value) { + if (value) { + setShortcut(shortcut, StartupDirectory.DEFAULT); + } else { + setShortcut(shortcut, null); + } + return this; + } + + public AdditionalLauncher removeShortcut(LauncherShortcut shortcut) { + rawProperties.remove(shortcut.propertyName()); + return this; + } + + public AdditionalLauncher setIcon(Path iconPath) { + if (iconPath.equals(NO_ICON)) { throw new IllegalArgumentException(); } @@ -129,13 +139,13 @@ public class AdditionalLauncher { return this; } - public final AdditionalLauncher setNoIcon() { + public AdditionalLauncher setNoIcon() { icon = NO_ICON; return this; } - public final AdditionalLauncher setPersistenceHandler( - ThrowingBiConsumer>> handler) { + public AdditionalLauncher setPersistenceHandler( + ThrowingBiConsumer>> handler) { if (handler != null) { createFileHandler = ThrowingBiConsumer.toBiConsumer(handler); } else { @@ -144,21 +154,31 @@ public class AdditionalLauncher { return this; } - public final void applyTo(JPackageCommand cmd) { + public void applyTo(JPackageCommand cmd) { cmd.addPrerequisiteAction(this::initialize); - cmd.addVerifyAction(this::verify); + cmd.addVerifyAction(createVerifierAsConsumer()); } - public final void applyTo(PackageTest test) { + public void applyTo(PackageTest test) { test.addInitializer(this::initialize); - test.addInstallVerifier(this::verify); - if (verifyUninstalled) { - test.addUninstallVerifier(this::verifyUninstalled); - } + test.addInstallVerifier(createVerifierAsConsumer()); } public final void verifyRemovedInUpgrade(PackageTest test) { - test.addInstallVerifier(this::verifyUninstalled); + test.addInstallVerifier(cmd -> { + createVerifier().verify(cmd, LauncherVerifier.Action.VERIFY_UNINSTALLED); + }); + } + + private LauncherVerifier createVerifier() { + return new LauncherVerifier(name, Optional.ofNullable(javaOptions), + Optional.ofNullable(defaultArguments), Optional.ofNullable(icon), rawProperties); + } + + private ThrowingConsumer createVerifierAsConsumer() { + return cmd -> { + createVerifier().verify(cmd, verifyActions.stream().sorted(Comparator.comparing(Action::ordinal)).toArray(Action[]::new)); + }; } static void forEachAdditionalLauncher(JPackageCommand cmd, @@ -179,11 +199,12 @@ public class AdditionalLauncher { PropertyFile shell[] = new PropertyFile[1]; forEachAdditionalLauncher(cmd, (name, propertiesFilePath) -> { if (name.equals(launcherName)) { - shell[0] = toFunction(PropertyFile::new).apply( - propertiesFilePath); + shell[0] = toSupplier(() -> { + return new PropertyFile(propertiesFilePath); + }).get(); } }); - return Optional.of(shell[0]).get(); + return Objects.requireNonNull(shell[0]); } private void initialize(JPackageCommand cmd) throws IOException { @@ -191,259 +212,63 @@ public class AdditionalLauncher { cmd.addArguments("--add-launcher", String.format("%s=%s", name, propsFile)); - List> properties = new ArrayList<>(); + Map properties = new HashMap<>(); if (defaultArguments != null) { - properties.add(Map.entry("arguments", - JPackageCommand.escapeAndJoin(defaultArguments))); + properties.put("arguments", JPackageCommand.escapeAndJoin(defaultArguments)); } if (javaOptions != null) { - properties.add(Map.entry("java-options", - JPackageCommand.escapeAndJoin(javaOptions))); + properties.put("java-options", JPackageCommand.escapeAndJoin(javaOptions)); } if (icon != null) { final String iconPath; - if (icon == NO_ICON) { + if (icon.equals(NO_ICON)) { iconPath = ""; } else { iconPath = icon.toAbsolutePath().toString().replace('\\', '/'); } - properties.add(Map.entry("icon", iconPath)); + properties.put("icon", iconPath); } - if (withShortcut != null) { - if (TKit.isLinux()) { - properties.add(Map.entry("linux-shortcut", withShortcut.toString())); - } else if (TKit.isWindows()) { - properties.add(Map.entry("win-shortcut", withShortcut.toString())); - } - } + properties.putAll(rawProperties); - if (TKit.isWindows() && withMenuShortcut != null) { - properties.add(Map.entry("win-menu", withMenuShortcut.toString())); - } - - properties.addAll(rawProperties); - - createFileHandler.accept(propsFile, properties); - } - - private static Path iconInResourceDir(JPackageCommand cmd, - String launcherName) { - Path resourceDir = cmd.getArgumentValue("--resource-dir", () -> null, - Path::of); - if (resourceDir != null) { - Path icon = resourceDir.resolve( - Optional.ofNullable(launcherName).orElseGet(() -> cmd.name()) - + TKit.ICON_SUFFIX); - if (Files.exists(icon)) { - return icon; - } - } - return null; - } - - private void verifyIcon(JPackageCommand cmd) throws IOException { - var verifier = new LauncherIconVerifier().setLauncherName(name); - - if (TKit.isOSX()) { - // On Mac should be no icon files for additional launchers. - verifier.applyTo(cmd); - return; - } - - boolean withLinuxDesktopFile = false; - - final Path effectiveIcon = Optional.ofNullable(icon).orElseGet( - () -> iconInResourceDir(cmd, name)); - while (effectiveIcon != NO_ICON) { - if (effectiveIcon != null) { - withLinuxDesktopFile = Boolean.FALSE != withShortcut; - verifier.setExpectedIcon(effectiveIcon); - break; - } - - Path customMainLauncherIcon = cmd.getArgumentValue("--icon", - () -> iconInResourceDir(cmd, null), Path::of); - if (customMainLauncherIcon != null) { - withLinuxDesktopFile = Boolean.FALSE != withShortcut; - verifier.setExpectedIcon(customMainLauncherIcon); - break; - } - - verifier.setExpectedDefaultIcon(); - break; - } - - if (TKit.isLinux() && !cmd.isImagePackageType()) { - if (effectiveIcon != NO_ICON && !withLinuxDesktopFile) { - withLinuxDesktopFile = (Boolean.FALSE != withShortcut) && - Stream.of("--linux-shortcut").anyMatch(cmd::hasArgument); - verifier.setExpectedDefaultIcon(); - } - Path desktopFile = LinuxHelper.getDesktopFile(cmd, name); - if (withLinuxDesktopFile) { - TKit.assertFileExists(desktopFile); - } else { - TKit.assertPathExists(desktopFile, false); - } - } - - verifier.applyTo(cmd); - } - - private void verifyShortcuts(JPackageCommand cmd) throws IOException { - if (TKit.isLinux() && !cmd.isImagePackageType() - && withShortcut != null) { - Path desktopFile = LinuxHelper.getDesktopFile(cmd, name); - if (withShortcut) { - TKit.assertFileExists(desktopFile); - } else { - TKit.assertPathExists(desktopFile, false); - } - } - } - - private void verifyDescription(JPackageCommand cmd) throws IOException { - if (TKit.isWindows()) { - String expectedDescription = getDesciption(cmd); - Path launcherPath = cmd.appLauncherPath(name); - String actualDescription = - WindowsHelper.getExecutableDesciption(launcherPath); - TKit.assertEquals(expectedDescription, actualDescription, - String.format("Check file description of [%s]", launcherPath)); - } else if (TKit.isLinux() && !cmd.isImagePackageType()) { - String expectedDescription = getDesciption(cmd); - Path desktopFile = LinuxHelper.getDesktopFile(cmd, name); - if (Files.exists(desktopFile)) { - TKit.assertTextStream("Comment=" + expectedDescription) - .label(String.format("[%s] file", desktopFile)) - .predicate(String::equals) - .apply(Files.readAllLines(desktopFile)); - } - } - } - - private void verifyInstalled(JPackageCommand cmd, boolean installed) throws IOException { - if (TKit.isLinux() && !cmd.isImagePackageType() && !cmd. - isPackageUnpacked(String.format( - "Not verifying package and system .desktop files for [%s] launcher", - cmd.appLauncherPath(name)))) { - Path packageDesktopFile = LinuxHelper.getDesktopFile(cmd, name); - Path systemDesktopFile = LinuxHelper.getSystemDesktopFilesFolder(). - resolve(packageDesktopFile.getFileName()); - if (Files.exists(packageDesktopFile) && installed) { - TKit.assertFileExists(systemDesktopFile); - TKit.assertStringListEquals(Files.readAllLines( - packageDesktopFile), - Files.readAllLines(systemDesktopFile), String.format( - "Check [%s] and [%s] files are equal", - packageDesktopFile, - systemDesktopFile)); - } else { - TKit.assertPathExists(packageDesktopFile, false); - TKit.assertPathExists(systemDesktopFile, false); - } - } - } - - protected void verifyUninstalled(JPackageCommand cmd) throws IOException { - verifyInstalled(cmd, false); - Path launcherPath = cmd.appLauncherPath(name); - TKit.assertPathExists(launcherPath, false); - } - - protected void verify(JPackageCommand cmd) throws IOException { - verifyIcon(cmd); - verifyShortcuts(cmd); - verifyDescription(cmd); - verifyInstalled(cmd, true); - - Path launcherPath = cmd.appLauncherPath(name); - - TKit.assertExecutableFileExists(launcherPath); - - if (!cmd.canRunLauncher(String.format( - "Not running %s launcher", launcherPath))) { - return; - } - - var appVerifier = HelloApp.assertApp(launcherPath) - .addDefaultArguments(Optional - .ofNullable(defaultArguments) - .orElseGet(() -> List.of(cmd.getAllArgumentValues("--arguments")))) - .addJavaOptions(Optional - .ofNullable(javaOptions) - .orElseGet(() -> List.of(cmd.getAllArgumentValues( - "--java-options"))).stream().map( - str -> resolveVariables(cmd, str)).toList()); - - if (!rawProperties.contains(LAUNCHER_AS_SERVICE)) { - appVerifier.executeAndVerifyOutput(); - } else if (!cmd.isPackageUnpacked(String.format( - "Not verifying contents of test output file for [%s] launcher", - launcherPath))) { - appVerifier.verifyOutput(); - } + createFileHandler.accept(propsFile, properties.entrySet()); } public static final class PropertyFile { + PropertyFile(Map data) { + this.data = new Properties(); + this.data.putAll(data); + } + PropertyFile(Path path) throws IOException { - data = Files.readAllLines(path).stream().map(str -> { - return str.split("=", 2); - }).collect(toMap(tokens -> tokens[0], tokens -> { - if (tokens.length == 1) { - return ""; - } else { - return tokens[1]; - } - }, (oldValue, newValue) -> { - return newValue; - })); + data = new Properties(); + try (var reader = Files.newBufferedReader(path)) { + data.load(reader); + } } - public boolean isPropertySet(String name) { + public Optional findProperty(String name) { Objects.requireNonNull(name); - return data.containsKey(name); + return Optional.ofNullable(data.getProperty(name)); } - public Optional getPropertyValue(String name) { - Objects.requireNonNull(name); - return Optional.of(data.get(name)); + public Optional findBooleanProperty(String name) { + return findProperty(name).map(Boolean::parseBoolean); } - public Optional getPropertyBooleanValue(String name) { - Objects.requireNonNull(name); - return Optional.ofNullable(data.get(name)).map(Boolean::parseBoolean); - } - - private final Map data; + private final Properties data; } - private static String resolveVariables(JPackageCommand cmd, String str) { - var map = Stream.of(JPackageCommand.Macro.values()).collect(toMap(x -> { - return String.format("$%s", x.name()); - }, cmd::macroValue)); - for (var e : map.entrySet()) { - str = str.replaceAll(Pattern.quote(e.getKey()), - Matcher.quoteReplacement(e.getValue().toString())); - } - return str; - } - - private boolean verifyUninstalled; private List javaOptions; private List defaultArguments; private Path icon; private final String name; - private final List> rawProperties; - private BiConsumer>> createFileHandler; - private Boolean withMenuShortcut; - private Boolean withShortcut; + private final Map rawProperties = new HashMap<>(); + private BiConsumer>> createFileHandler; + private final Set verifyActions = new HashSet<>(Action.VERIFY_DEFAULTS); - private static final Path NO_ICON = Path.of(""); - private static final Map.Entry LAUNCHER_AS_SERVICE = Map.entry( - "launcher-as-service", "true"); + static final Path NO_ICON = Path.of(""); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java index 2381aecec2e..e676e0d1e87 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java @@ -22,20 +22,28 @@ */ package jdk.jpackage.test; +import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Stream; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.XmlUtils; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; public record AppImageFile(String mainLauncherName, String mainLauncherClassName, - String version, boolean macSigned, boolean macAppStore) { + String version, boolean macSigned, boolean macAppStore, Map> launchers) { public static Path getPathInAppImage(Path appImageDir) { return ApplicationLayout.platformAppImage() @@ -44,8 +52,23 @@ public record AppImageFile(String mainLauncherName, String mainLauncherClassName .resolve(FILENAME); } + public AppImageFile { + Objects.requireNonNull(mainLauncherName); + Objects.requireNonNull(mainLauncherClassName); + Objects.requireNonNull(version); + if (!launchers.containsKey(mainLauncherName)) { + throw new IllegalArgumentException(); + } + } + public AppImageFile(String mainLauncherName, String mainLauncherClassName) { - this(mainLauncherName, mainLauncherClassName, "1.0", false, false); + this(mainLauncherName, mainLauncherClassName, "1.0", false, false, Map.of(mainLauncherName, Map.of())); + } + + public Map> addLaunchers() { + return launchers.entrySet().stream().filter(e -> { + return !e.getKey().equals(mainLauncherName); + }).collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); } public void save(Path appImageDir) throws IOException { @@ -73,6 +96,18 @@ public record AppImageFile(String mainLauncherName, String mainLauncherClassName xml.writeStartElement("app-store"); xml.writeCharacters(Boolean.toString(macAppStore)); xml.writeEndElement(); + + for (var al : addLaunchers().keySet().stream().sorted().toList()) { + xml.writeStartElement("add-launcher"); + xml.writeAttribute("name", al); + var props = launchers.get(al); + for (var prop : props.keySet().stream().sorted().toList()) { + xml.writeStartElement(prop); + xml.writeCharacters(props.get(prop)); + xml.writeEndElement(); + } + xml.writeEndElement(); + } }); } @@ -99,8 +134,34 @@ public record AppImageFile(String mainLauncherName, String mainLauncherClassName "/jpackage-state/app-store/text()", doc)).map( Boolean::parseBoolean).orElse(false); + var addLaunchers = XmlUtils.queryNodes(doc, xPath, "/jpackage-state/add-launcher").map(Element.class::cast).map(toFunction(addLauncher -> { + Map launcherProps = new HashMap<>(); + + // @name and @service attributes. + XmlUtils.toStream(addLauncher.getAttributes()).forEach(attr -> { + launcherProps.put(attr.getNodeName(), attr.getNodeValue()); + }); + + // Extra properties. + XmlUtils.queryNodes(addLauncher, xPath, "*[count(*) = 0]").map(Element.class::cast).forEach(e -> { + launcherProps.put(e.getNodeName(), e.getTextContent()); + }); + + return launcherProps; + })); + + var mainLauncherProperties = Map.of("name", mainLauncherName); + + var launchers = Stream.concat(Stream.of(mainLauncherProperties), addLaunchers).collect(toMap(attrs -> { + return Objects.requireNonNull(attrs.get("name")); + }, attrs -> { + Map copy = new HashMap<>(attrs); + copy.remove("name"); + return Map.copyOf(copy); + })); + return new AppImageFile(mainLauncherName, mainLauncherClassName, - version, macSigned, macAppStore); + version, macSigned, macAppStore, launchers); }).get(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java index cb7f0574afd..4a78ad40cd1 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java @@ -35,16 +35,17 @@ public class CommandArguments { } public final T clearArguments() { + verifyMutable(); args.clear(); return thiz(); } public final T addArgument(String v) { - args.add(v); - return thiz(); + return addArguments(v); } public final T addArguments(List v) { + verifyMutable(); args.addAll(v); return thiz(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java index 98c79131045..e630659bdb1 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/ConfigFilesStasher.java @@ -220,7 +220,7 @@ final class ConfigFilesStasher { AdditionalLauncher.forEachAdditionalLauncher(cmd, (launcherName, propertyFilePath) -> { try { final var launcherAsService = new AdditionalLauncher.PropertyFile(propertyFilePath) - .getPropertyBooleanValue("launcher-as-service").orElse(false); + .findBooleanProperty("launcher-as-service").orElse(false); if (launcherAsService) { withServices[0] = true; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 169457d6f58..3a89ba28d26 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -72,7 +72,7 @@ public class JPackageCommand extends CommandArguments { verifyActions = new Actions(); } - public JPackageCommand(JPackageCommand cmd) { + private JPackageCommand(JPackageCommand cmd, boolean immutable) { args.addAll(cmd.args); withToolProvider = cmd.withToolProvider; saveConsoleOutput = cmd.saveConsoleOutput; @@ -81,7 +81,7 @@ public class JPackageCommand extends CommandArguments { suppressOutput = cmd.suppressOutput; ignoreDefaultRuntime = cmd.ignoreDefaultRuntime; ignoreDefaultVerbose = cmd.ignoreDefaultVerbose; - immutable = cmd.immutable; + this.immutable = immutable; dmgInstallDir = cmd.dmgInstallDir; prerequisiteActions = new Actions(cmd.prerequisiteActions); verifyActions = new Actions(cmd.verifyActions); @@ -90,12 +90,15 @@ public class JPackageCommand extends CommandArguments { outputValidators = cmd.outputValidators; executeInDirectory = cmd.executeInDirectory; winMsiLogFile = cmd.winMsiLogFile; + unpackedPackageDirectory = cmd.unpackedPackageDirectory; } JPackageCommand createImmutableCopy() { - JPackageCommand reply = new JPackageCommand(this); - reply.immutable = true; - return reply; + return new JPackageCommand(this, true); + } + + JPackageCommand createMutableCopy() { + return new JPackageCommand(this, false); } public JPackageCommand setArgumentValue(String argName, String newValue) { @@ -316,13 +319,11 @@ public class JPackageCommand extends CommandArguments { } JPackageCommand addPrerequisiteAction(ThrowingConsumer action) { - verifyMutable(); prerequisiteActions.add(action); return this; } JPackageCommand addVerifyAction(ThrowingConsumer action) { - verifyMutable(); verifyActions.add(action); return this; } @@ -484,7 +485,7 @@ public class JPackageCommand extends CommandArguments { Path unpackedPackageDirectory() { verifyIsOfType(PackageType.NATIVE); - return getArgumentValue(UNPACKED_PATH_ARGNAME, () -> null, Path::of); + return unpackedPackageDirectory; } /** @@ -662,7 +663,7 @@ public class JPackageCommand extends CommandArguments { } public boolean isPackageUnpacked() { - return hasArgument(UNPACKED_PATH_ARGNAME); + return unpackedPackageDirectory != null; } public static void useToolProviderByDefault(ToolProvider jpackageToolProvider) { @@ -791,11 +792,6 @@ public class JPackageCommand extends CommandArguments { return this; } - public JPackageCommand executeVerifyActions() { - verifyActions.run(); - return this; - } - private Executor createExecutor() { Executor exec = new Executor() .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput) @@ -820,6 +816,7 @@ public class JPackageCommand extends CommandArguments { } public Executor.Result execute(int expectedExitCode) { + verifyMutable(); executePrerequisiteActions(); if (hasArgument("--dest")) { @@ -859,7 +856,7 @@ public class JPackageCommand extends CommandArguments { ConfigFilesStasher.INSTANCE.accept(this); } - final var copy = new JPackageCommand(this).adjustArgumentsBeforeExecution(); + final var copy = createMutableCopy().adjustArgumentsBeforeExecution(); final var directoriesAssert = new ReadOnlyPathsAssert(copy); @@ -876,7 +873,7 @@ public class JPackageCommand extends CommandArguments { } if (result.exitCode() == 0) { - executeVerifyActions(); + verifyActions.run(); } return result; @@ -884,7 +881,7 @@ public class JPackageCommand extends CommandArguments { public Executor.Result executeAndAssertHelloAppImageCreated() { Executor.Result result = executeAndAssertImageCreated(); - HelloApp.executeLauncherAndVerifyOutput(this); + LauncherVerifier.executeMainLauncherAndVerifyOutput(this); return result; } @@ -1046,6 +1043,7 @@ public class JPackageCommand extends CommandArguments { } public JPackageCommand setReadOnlyPathAsserts(ReadOnlyPathAssert... asserts) { + verifyMutable(); readOnlyPathAsserts = Set.of(asserts); return this; } @@ -1059,18 +1057,19 @@ public class JPackageCommand extends CommandArguments { public static enum AppLayoutAssert { APP_IMAGE_FILE(JPackageCommand::assertAppImageFile), PACKAGE_FILE(JPackageCommand::assertPackageFile), - MAIN_LAUNCHER(cmd -> { + NO_MAIN_LAUNCHER_IN_RUNTIME(cmd -> { if (cmd.isRuntime()) { TKit.assertPathExists(convertFromRuntime(cmd).appLauncherPath(), false); - } else { - TKit.assertExecutableFileExists(cmd.appLauncherPath()); } }), - MAIN_LAUNCHER_CFG_FILE(cmd -> { + NO_MAIN_LAUNCHER_CFG_FILE_IN_RUNTIME(cmd -> { if (cmd.isRuntime()) { TKit.assertPathExists(convertFromRuntime(cmd).appLauncherCfgPath(null), false); - } else { - TKit.assertFileExists(cmd.appLauncherCfgPath(null)); + } + }), + MAIN_LAUNCHER_FILES(cmd -> { + if (!cmd.isRuntime()) { + new LauncherVerifier(cmd).verify(cmd, LauncherVerifier.Action.VERIFY_INSTALLED); } }), MAIN_JAR_FILE(cmd -> { @@ -1097,7 +1096,7 @@ public class JPackageCommand extends CommandArguments { } private static JPackageCommand convertFromRuntime(JPackageCommand cmd) { - var copy = new JPackageCommand(cmd); + var copy = cmd.createMutableCopy(); copy.immutable = false; copy.removeArgumentWithValue("--runtime-image"); copy.dmgInstallDir = cmd.appInstallationDirectory(); @@ -1111,6 +1110,7 @@ public class JPackageCommand extends CommandArguments { } public JPackageCommand setAppLayoutAsserts(AppLayoutAssert ... asserts) { + verifyMutable(); appLayoutAsserts = Set.of(asserts); return this; } @@ -1157,12 +1157,12 @@ public class JPackageCommand extends CommandArguments { } else { assertFileInAppImage(lookupPath); + final Path rootDir = isImagePackageType() ? outputBundle() : + pathToUnpackedPackageFile(appInstallationDirectory()); + + final AppImageFile aif = AppImageFile.load(rootDir); + if (TKit.isOSX()) { - final Path rootDir = isImagePackageType() ? outputBundle() : - pathToUnpackedPackageFile(appInstallationDirectory()); - - AppImageFile aif = AppImageFile.load(rootDir); - boolean expectedValue = MacHelper.appImageSigned(this); boolean actualValue = aif.macSigned(); TKit.assertEquals(expectedValue, actualValue, @@ -1173,6 +1173,11 @@ public class JPackageCommand extends CommandArguments { TKit.assertEquals(expectedValue, actualValue, "Check for unexpected value of property in app image file"); } + + TKit.assertStringListEquals( + addLauncherNames().stream().sorted().toList(), + aif.addLaunchers().keySet().stream().sorted().toList(), + "Check additional launcher names"); } } @@ -1254,16 +1259,14 @@ public class JPackageCommand extends CommandArguments { } JPackageCommand setUnpackedPackageLocation(Path path) { + verifyMutable(); verifyIsOfType(PackageType.NATIVE); - if (path != null) { - setArgumentValue(UNPACKED_PATH_ARGNAME, path); - } else { - removeArgumentWithValue(UNPACKED_PATH_ARGNAME); - } + unpackedPackageDirectory = path; return this; } JPackageCommand winMsiLogFile(Path v) { + verifyMutable(); if (!TKit.isWindows()) { throw new UnsupportedOperationException(); } @@ -1286,6 +1289,7 @@ public class JPackageCommand extends CommandArguments { } private JPackageCommand adjustArgumentsBeforeExecution() { + verifyMutable(); if (!isWithToolProvider()) { // if jpackage is launched as a process then set the jlink.debug system property // to allow the jlink process to print exception stacktraces on any failure @@ -1469,6 +1473,7 @@ public class JPackageCommand extends CommandArguments { private final Actions verifyActions; private Path executeInDirectory; private Path winMsiLogFile; + private Path unpackedPackageDirectory; private Set readOnlyPathAsserts = Set.of(ReadOnlyPathAssert.values()); private Set appLayoutAsserts = Set.of(AppLayoutAssert.values()); private List>> outputValidators = new ArrayList<>(); @@ -1496,8 +1501,6 @@ public class JPackageCommand extends CommandArguments { return null; }).get(); - private static final String UNPACKED_PATH_ARGNAME = "jpt-unpacked-folder"; - // [HH:mm:ss.SSS] private static final Pattern TIMESTAMP_REGEXP = Pattern.compile( "^\\[\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d\\] "); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java index fd8b4011341..42821822894 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java @@ -22,11 +22,19 @@ */ package jdk.jpackage.test; +import static jdk.jpackage.internal.util.function.ThrowingBiConsumer.toBiConsumer; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.test.AdditionalLauncher.forEachAdditionalLauncher; +import static jdk.jpackage.test.PackageType.LINUX; +import static jdk.jpackage.test.PackageType.MAC_PKG; +import static jdk.jpackage.test.PackageType.WINDOWS; + import java.io.IOException; import java.nio.file.FileSystemException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -36,12 +44,9 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.internal.util.PathUtils; -import jdk.jpackage.internal.util.function.ThrowingBiConsumer; -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; import jdk.jpackage.internal.util.function.ThrowingRunnable; -import static jdk.jpackage.test.PackageType.LINUX; -import static jdk.jpackage.test.PackageType.MAC_PKG; -import static jdk.jpackage.test.PackageType.WINDOWS; +import jdk.jpackage.test.AdditionalLauncher.PropertyFile; +import jdk.jpackage.test.LauncherVerifier.Action; public final class LauncherAsServiceVerifier { @@ -111,6 +116,7 @@ public final class LauncherAsServiceVerifier { } else { applyToAdditionalLauncher(pkg); } + pkg.addInstallVerifier(this::verifyLauncherExecuted); } static void verify(JPackageCommand cmd) { @@ -127,7 +133,6 @@ public final class LauncherAsServiceVerifier { "service-installer.exe"); if (launcherNames.isEmpty()) { TKit.assertPathExists(serviceInstallerPath, false); - } else { TKit.assertFileExists(serviceInstallerPath); } @@ -188,23 +193,11 @@ public final class LauncherAsServiceVerifier { launcherNames.add(null); } - AdditionalLauncher.forEachAdditionalLauncher(cmd, - ThrowingBiConsumer.toBiConsumer( - (launcherName, propFilePath) -> { - if (Files.readAllLines(propFilePath).stream().anyMatch( - line -> { - if (line.startsWith( - "launcher-as-service=")) { - return Boolean.parseBoolean( - line.substring( - "launcher-as-service=".length())); - } else { - return false; - } - })) { - launcherNames.add(launcherName); - } - })); + forEachAdditionalLauncher(cmd, toBiConsumer((launcherName, propFilePath) -> { + if (new PropertyFile(propFilePath).findBooleanProperty("launcher-as-service").orElse(false)) { + launcherNames.add(launcherName); + } + })); return launcherNames; } @@ -237,45 +230,33 @@ public final class LauncherAsServiceVerifier { + appOutputFilePathInitialize().toString()); cmd.addArguments("--java-options", "-Djpackage.test.noexit=true"); }); - pkg.addInstallVerifier(cmd -> { - if (canVerifyInstall(cmd)) { - delayInstallVerify(); - Path outputFilePath = appOutputFilePathVerify(cmd); - HelloApp.assertApp(cmd.appLauncherPath()) - .addParam("jpackage.test.appOutput", - outputFilePath.toString()) - .addDefaultArguments(expectedValue) - .verifyOutput(); - deleteOutputFile(outputFilePath); - } - }); - pkg.addInstallVerifier(cmd -> { - verify(cmd, launcherName); - }); } private void applyToAdditionalLauncher(PackageTest pkg) { - AdditionalLauncher al = new AdditionalLauncher(launcherName) { - @Override - protected void verify(JPackageCommand cmd) throws IOException { - if (canVerifyInstall(cmd)) { - delayInstallVerify(); - super.verify(cmd); - deleteOutputFile(appOutputFilePathVerify(cmd)); - } - LauncherAsServiceVerifier.verify(cmd, launcherName); - } - }.setLauncherAsService() - .addJavaOptions("-Djpackage.test.appOutput=" - + appOutputFilePathInitialize().toString()) + var al = new AdditionalLauncher(launcherName) + .setProperty("launcher-as-service", true) + .addJavaOptions("-Djpackage.test.appOutput=" + appOutputFilePathInitialize().toString()) .addJavaOptions("-Djpackage.test.noexit=true") - .addDefaultArguments(expectedValue); + .addDefaultArguments(expectedValue) + .withoutVerifyActions(Action.EXECUTE_LAUNCHER); Optional.ofNullable(additionalLauncherCallback).ifPresent(v -> v.accept(al)); al.applyTo(pkg); } + private void verifyLauncherExecuted(JPackageCommand cmd) throws IOException { + if (canVerifyInstall(cmd)) { + delayInstallVerify(); + Path outputFilePath = appOutputFilePathVerify(cmd); + HelloApp.assertApp(cmd.appLauncherPath()) + .addParam("jpackage.test.appOutput", outputFilePath.toString()) + .addDefaultArguments(expectedValue) + .verifyOutput(); + deleteOutputFile(outputFilePath); + } + } + private static void deleteOutputFile(Path file) throws IOException { try { TKit.deleteIfExists(file); @@ -291,8 +272,7 @@ public final class LauncherAsServiceVerifier { } } - private static void verify(JPackageCommand cmd, String launcherName) throws - IOException { + private static void verify(JPackageCommand cmd, String launcherName) throws IOException { if (LINUX.contains(cmd.packageType())) { verifyLinuxUnitFile(cmd, launcherName); } else if (MAC_PKG.equals(cmd.packageType())) { @@ -370,6 +350,9 @@ public final class LauncherAsServiceVerifier { private final Path appOutputFileName; private final Consumer additionalLauncherCallback; - static final Set SUPPORTED_PACKAGES = Stream.of(LINUX, WINDOWS, - Set.of(MAC_PKG)).flatMap(x -> x.stream()).collect(Collectors.toSet()); + static final Set SUPPORTED_PACKAGES = Stream.of( + LINUX, + WINDOWS, + Set.of(MAC_PKG) + ).flatMap(Collection::stream).collect(Collectors.toSet()); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java index a1971ee0835..6285d9d93a0 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherIconVerifier.java @@ -46,6 +46,11 @@ public final class LauncherIconVerifier { return this; } + public LauncherIconVerifier verifyFileInAppImageOnly(boolean v) { + verifyFileInAppImageOnly = true; + return this; + } + public void applyTo(JPackageCommand cmd) throws IOException { final String curLauncherName; final String label; @@ -62,22 +67,26 @@ public final class LauncherIconVerifier { if (TKit.isWindows()) { TKit.assertPathExists(iconPath, false); - WinExecutableIconVerifier.verifyLauncherIcon(cmd, launcherName, - expectedIcon, expectedDefault); + if (!verifyFileInAppImageOnly) { + WinExecutableIconVerifier.verifyLauncherIcon(cmd, launcherName, expectedIcon, expectedDefault); + } } else if (expectedDefault) { TKit.assertPathExists(iconPath, true); } else if (expectedIcon == null) { TKit.assertPathExists(iconPath, false); } else { TKit.assertFileExists(iconPath); - TKit.assertTrue(-1 == Files.mismatch(expectedIcon, iconPath), - String.format( - "Check icon file [%s] of %s launcher is a copy of source icon file [%s]", - iconPath, label, expectedIcon)); + if (!verifyFileInAppImageOnly) { + TKit.assertTrue(-1 == Files.mismatch(expectedIcon, iconPath), + String.format( + "Check icon file [%s] of %s launcher is a copy of source icon file [%s]", + iconPath, label, expectedIcon)); + } } } private String launcherName; private Path expectedIcon; private boolean expectedDefault; + private boolean verifyFileInAppImageOnly; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java new file mode 100644 index 00000000000..15bb3ea0333 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.jpackage.test; + +import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.test.AdditionalLauncher.getAdditionalLauncherProperties; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; +import jdk.jpackage.test.AdditionalLauncher.PropertyFile; + +public enum LauncherShortcut { + + LINUX_SHORTCUT("linux-shortcut"), + + WIN_DESKTOP_SHORTCUT("win-shortcut"), + + WIN_START_MENU_SHORTCUT("win-menu"); + + public enum StartupDirectory { + DEFAULT("true"), + ; + + StartupDirectory(String stringValue) { + this.stringValue = Objects.requireNonNull(stringValue); + } + + public String asStringValue() { + return stringValue; + } + + /** + * Returns shortcut startup directory or an empty {@link Optional} instance if + * the value of the {@code str} parameter evaluates to {@code false}. + * + * @param str the value of a shortcut startup directory + * @return shortcut startup directory or an empty {@link Optional} instance + * @throws IllegalArgumentException if the value of the {@code str} parameter is + * unrecognized + */ + static Optional parse(String str) { + Objects.requireNonNull(str); + return Optional.ofNullable(VALUE_MAP.get(str)).or(() -> { + if (Boolean.TRUE.toString().equals(str)) { + return Optional.of(StartupDirectory.DEFAULT); + } else if (Boolean.FALSE.toString().equals(str)) { + return Optional.empty(); + } else { + throw new IllegalArgumentException(String.format( + "Unrecognized launcher shortcut startup directory: [%s]", str)); + } + }); + } + + private final String stringValue; + + private final static Map VALUE_MAP = + Stream.of(values()).collect(toMap(StartupDirectory::asStringValue, x -> x)); + } + + LauncherShortcut(String propertyName) { + this.propertyName = Objects.requireNonNull(propertyName); + } + + public String propertyName() { + return propertyName; + } + + public String appImageFilePropertyName() { + return propertyName; + } + + public String optionName() { + return "--" + propertyName; + } + + Optional expectShortcut(JPackageCommand cmd, Optional predefinedAppImage, String launcherName) { + Objects.requireNonNull(predefinedAppImage); + + final var name = Optional.ofNullable(launcherName).orElseGet(cmd::name); + + if (name.equals(cmd.name())) { + return findMainLauncherShortcut(cmd); + } else { + String[] propertyName = new String[1]; + return findAddLauncherShortcut(cmd, predefinedAppImage.map(appImage -> { + propertyName[0] = appImageFilePropertyName(); + return new PropertyFile(appImage.addLaunchers().get(launcherName)); + }).orElseGet(() -> { + propertyName[0] = this.propertyName; + return getAdditionalLauncherProperties(cmd, launcherName); + })::findProperty, propertyName[0]); + } + } + + + public interface InvokeShortcutSpec { + String launcherName(); + LauncherShortcut shortcut(); + Optional expectedWorkDirectory(); + List commandLine(); + + default Executor.Result execute() { + return HelloApp.configureAndExecute(0, Executor.of(commandLine()).dumpOutput()); + } + + record Stub( + String launcherName, + LauncherShortcut shortcut, + Optional expectedWorkDirectory, + List commandLine) implements InvokeShortcutSpec { + + public Stub { + Objects.requireNonNull(launcherName); + Objects.requireNonNull(shortcut); + Objects.requireNonNull(expectedWorkDirectory); + Objects.requireNonNull(commandLine); + } + } + } + + + private Optional findMainLauncherShortcut(JPackageCommand cmd) { + if (cmd.hasArgument(optionName())) { + return Optional.of(StartupDirectory.DEFAULT); + } else { + return Optional.empty(); + } + } + + private Optional findAddLauncherShortcut(JPackageCommand cmd, + Function> addlauncherProperties, String propertyName) { + var explicit = addlauncherProperties.apply(propertyName); + if (explicit.isPresent()) { + return explicit.flatMap(StartupDirectory::parse); + } else { + return findMainLauncherShortcut(cmd); + } + } + + private final String propertyName; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java new file mode 100644 index 00000000000..b3ed030c69d --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.jpackage.test; + +import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.test.AdditionalLauncher.NO_ICON; +import static jdk.jpackage.test.LauncherShortcut.LINUX_SHORTCUT; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ThrowingBiConsumer; +import jdk.jpackage.test.AdditionalLauncher.PropertyFile; +import jdk.jpackage.test.LauncherShortcut.StartupDirectory; + +public final class LauncherVerifier { + + LauncherVerifier(JPackageCommand cmd) { + name = cmd.name(); + javaOptions = Optional.empty(); + arguments = Optional.empty(); + icon = Optional.empty(); + properties = Optional.empty(); + } + + LauncherVerifier(String name, + Optional> javaOptions, + Optional> arguments, + Optional icon, + Map properties) { + this.name = Objects.requireNonNull(name); + this.javaOptions = javaOptions.map(List::copyOf); + this.arguments = arguments.map(List::copyOf); + this.icon = icon; + this.properties = Optional.of(new PropertyFile(properties)); + } + + static void executeMainLauncherAndVerifyOutput(JPackageCommand cmd) { + new LauncherVerifier(cmd).verify(cmd, Action.EXECUTE_LAUNCHER); + } + + + public enum Action { + VERIFY_ICON(LauncherVerifier::verifyIcon), + VERIFY_DESCRIPTION(LauncherVerifier::verifyDescription), + VERIFY_INSTALLED((verifier, cmd) -> { + verifier.verifyInstalled(cmd, true); + }), + VERIFY_UNINSTALLED((verifier, cmd) -> { + verifier.verifyInstalled(cmd, false); + }), + VERIFY_APP_IMAGE_FILE((verifier, cmd) -> { + if (cmd.isImagePackageType()) { + verifier.verifyInAppImageFile(cmd); + } + }), + EXECUTE_LAUNCHER(LauncherVerifier::executeLauncher), + ; + + Action(ThrowingBiConsumer action) { + this.action = ThrowingBiConsumer.toBiConsumer(action); + } + + private void apply(LauncherVerifier verifier, JPackageCommand cmd) { + action.accept(verifier, cmd); + } + + private final BiConsumer action; + + static final List VERIFY_APP_IMAGE = List.of( + VERIFY_ICON, VERIFY_DESCRIPTION, VERIFY_INSTALLED, VERIFY_APP_IMAGE_FILE + ); + + static final List VERIFY_DEFAULTS = Stream.concat( + VERIFY_APP_IMAGE.stream(), Stream.of(EXECUTE_LAUNCHER) + ).toList(); + } + + + void verify(JPackageCommand cmd, Action... actions) { + verify(cmd, List.of(actions)); + } + + void verify(JPackageCommand cmd, Iterable actions) { + Objects.requireNonNull(cmd); + for (var a : actions) { + a.apply(this, cmd); + } + } + + private boolean isMainLauncher() { + return properties.isEmpty(); + } + + private Optional findProperty(String key) { + return properties.flatMap(v -> { + return v.findProperty(key); + }); + } + + private String getDescription(JPackageCommand cmd) { + return findProperty("description").orElseGet(() -> { + return cmd.getArgumentValue("--description", cmd::name); + }); + } + + private List getArguments(JPackageCommand cmd) { + return getStringArrayProperty(cmd, "--arguments", arguments); + } + + private List getJavaOptions(JPackageCommand cmd) { + return getStringArrayProperty(cmd, "--java-options", javaOptions); + } + + private List getStringArrayProperty(JPackageCommand cmd, String optionName, Optional> items) { + Objects.requireNonNull(cmd); + Objects.requireNonNull(optionName); + Objects.requireNonNull(items); + if (isMainLauncher()) { + return List.of(cmd.getAllArgumentValues(optionName)); + } else { + return items.orElseGet(() -> { + return List.of(cmd.getAllArgumentValues(optionName)); + }); + } + } + + private boolean explicitlyNoShortcut(LauncherShortcut shortcut) { + var explicit = findProperty(shortcut.propertyName()); + if (explicit.isPresent()) { + return explicit.flatMap(StartupDirectory::parse).isEmpty(); + } else { + return false; + } + } + + private static boolean explicitShortcutForMainLauncher(JPackageCommand cmd, LauncherShortcut shortcut) { + return cmd.hasArgument(shortcut.optionName()); + } + + private void verifyIcon(JPackageCommand cmd) throws IOException { + initIconVerifier(cmd).applyTo(cmd); + } + + private LauncherIconVerifier initIconVerifier(JPackageCommand cmd) { + var verifier = new LauncherIconVerifier().setLauncherName(name); + + var mainLauncherIcon = Optional.ofNullable(cmd.getArgumentValue("--icon")).map(Path::of).or(() -> { + return iconInResourceDir(cmd, cmd.name()); + }); + + if (TKit.isOSX()) { + // There should be no icon files on Mac for additional launchers, + // and always an icon file for the main launcher. + if (isMainLauncher()) { + mainLauncherIcon.ifPresentOrElse(verifier::setExpectedIcon, verifier::setExpectedDefaultIcon); + } + return verifier; + } + + if (isMainLauncher()) { + mainLauncherIcon.ifPresentOrElse(verifier::setExpectedIcon, verifier::setExpectedDefaultIcon); + } else { + icon.ifPresentOrElse(icon -> { + if (!NO_ICON.equals(icon)) { + verifier.setExpectedIcon(icon); + } + }, () -> { + // No "icon" property in the property file + iconInResourceDir(cmd, name).ifPresentOrElse(verifier::setExpectedIcon, () -> { + // No icon for this additional launcher in the resource directory. + mainLauncherIcon.ifPresentOrElse(verifier::setExpectedIcon, verifier::setExpectedDefaultIcon); + }); + }); + } + + return verifier; + } + + private static boolean withLinuxMainLauncherDesktopFile(JPackageCommand cmd) { + if (!TKit.isLinux() || cmd.isImagePackageType()) { + return false; + } + + return explicitShortcutForMainLauncher(cmd, LINUX_SHORTCUT) + || cmd.hasArgument("--icon") + || cmd.hasArgument("--file-associations") + || iconInResourceDir(cmd, cmd.name()).isPresent(); + } + + private boolean withLinuxDesktopFile(JPackageCommand cmd) { + if (!TKit.isLinux() || cmd.isImagePackageType()) { + return false; + } + + if (isMainLauncher()) { + return withLinuxMainLauncherDesktopFile(cmd); + } else if (explicitlyNoShortcut(LINUX_SHORTCUT) || icon.map(icon -> { + return icon.equals(NO_ICON); + }).orElse(false)) { + return false; + } else if (iconInResourceDir(cmd, name).isPresent() || icon.map(icon -> { + return !icon.equals(NO_ICON); + }).orElse(false)) { + return true; + } else if (findProperty(LINUX_SHORTCUT.propertyName()).flatMap(StartupDirectory::parse).isPresent()) { + return true; + } else { + return withLinuxMainLauncherDesktopFile(cmd.createMutableCopy().removeArgument("--file-associations")); + } + } + + private void verifyDescription(JPackageCommand cmd) throws IOException { + if (TKit.isWindows()) { + String expectedDescription = getDescription(cmd); + Path launcherPath = cmd.appLauncherPath(name); + String actualDescription = + WindowsHelper.getExecutableDescription(launcherPath); + TKit.assertEquals(expectedDescription, actualDescription, + String.format("Check file description of [%s]", launcherPath)); + } else if (TKit.isLinux() && !cmd.isImagePackageType()) { + String expectedDescription = getDescription(cmd); + Path desktopFile = LinuxHelper.getDesktopFile(cmd, name); + if (Files.exists(desktopFile)) { + TKit.assertTextStream("Comment=" + expectedDescription) + .label(String.format("[%s] file", desktopFile)) + .predicate(String::equals) + .apply(Files.readAllLines(desktopFile)); + } + } + } + + private void verifyInstalled(JPackageCommand cmd, boolean installed) throws IOException { + var launcherPath = cmd.appLauncherPath(name); + var launcherCfgFilePath = cmd.appLauncherCfgPath(name); + if (installed) { + TKit.assertExecutableFileExists(launcherPath); + TKit.assertFileExists(launcherCfgFilePath); + } else { + TKit.assertPathExists(launcherPath, false); + TKit.assertPathExists(launcherCfgFilePath, false); + } + + if (TKit.isLinux() && !cmd.isImagePackageType()) { + final var packageDesktopFile = LinuxHelper.getDesktopFile(cmd, name); + final var withLinuxDesktopFile = withLinuxDesktopFile(cmd) && installed; + if (withLinuxDesktopFile) { + TKit.assertFileExists(packageDesktopFile); + } else { + TKit.assertPathExists(packageDesktopFile, false); + } + } + + if (installed) { + initIconVerifier(cmd).verifyFileInAppImageOnly(true).applyTo(cmd); + } + } + + private void verifyInAppImageFile(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.IMAGE); + if (!isMainLauncher()) { + Stream shortcuts; + if (TKit.isWindows()) { + shortcuts = Stream.of(LauncherShortcut.WIN_DESKTOP_SHORTCUT, LauncherShortcut.WIN_START_MENU_SHORTCUT); + } else if (TKit.isLinux()) { + shortcuts = Stream.of(LauncherShortcut.LINUX_SHORTCUT); + } else { + shortcuts = Stream.of(); + } + + var aif = AppImageFile.load(cmd.outputBundle()); + var aifFileName = AppImageFile.getPathInAppImage(Path.of("")).getFileName(); + + var aifProps = Objects.requireNonNull(aif.addLaunchers().get(name)); + + shortcuts.forEach(shortcut -> { + var recordedShortcut = aifProps.get(shortcut.appImageFilePropertyName()); + properties.flatMap(props -> { + return props.findProperty(shortcut.propertyName()); + }).ifPresentOrElse(expectedShortcut -> { + TKit.assertNotNull(recordedShortcut, String.format( + "Check shortcut [%s] of launcher [%s] is recorded in %s file", + shortcut, name, aifFileName)); + TKit.assertEquals( + StartupDirectory.parse(expectedShortcut), + StartupDirectory.parse(recordedShortcut), + String.format("Check the value of shortcut [%s] of launcher [%s] recorded in %s file", + shortcut, name, aifFileName)); + }, () -> { + TKit.assertNull(recordedShortcut, String.format( + "Check shortcut [%s] of launcher [%s] is NOT recorded in %s file", + shortcut, name, aifFileName)); + }); + }); + } + } + + private void executeLauncher(JPackageCommand cmd) throws IOException { + Path launcherPath = cmd.appLauncherPath(name); + + if (!cmd.canRunLauncher(String.format("Not running [%s] launcher", launcherPath))) { + return; + } + + var appVerifier = HelloApp.assertApp(launcherPath) + .addDefaultArguments(getArguments(cmd)) + .addJavaOptions(getJavaOptions(cmd).stream().map(str -> { + return resolveVariables(cmd, str); + }).toList()); + + appVerifier.executeAndVerifyOutput(); + } + + private static String resolveVariables(JPackageCommand cmd, String str) { + var map = Stream.of(JPackageCommand.Macro.values()).collect(toMap(x -> { + return String.format("$%s", x.name()); + }, cmd::macroValue)); + for (var e : map.entrySet()) { + str = str.replaceAll(Pattern.quote(e.getKey()), + Matcher.quoteReplacement(e.getValue().toString())); + } + return str; + } + + private static Optional iconInResourceDir(JPackageCommand cmd, String launcherName) { + Objects.requireNonNull(launcherName); + return Optional.ofNullable(cmd.getArgumentValue("--resource-dir")).map(Path::of).map(resourceDir -> { + Path icon = resourceDir.resolve(launcherName + TKit.ICON_SUFFIX); + if (Files.exists(icon)) { + return icon; + } else { + return null; + } + }); + } + + private final String name; + private final Optional> javaOptions; + private final Optional> arguments; + private final Optional icon; + private final Optional properties; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index 1669d1f8233..e8f6273b18b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -23,25 +23,30 @@ package jdk.jpackage.test; import java.io.IOException; +import java.io.UncheckedIOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.test.LauncherShortcut.InvokeShortcutSpec; import jdk.jpackage.test.PackageTest.PackageHandlers; @@ -308,8 +313,8 @@ public final class LinuxHelper { } static void verifyPackageBundleEssential(JPackageCommand cmd) { - String packageName = LinuxHelper.getPackageName(cmd); - long packageSize = LinuxHelper.getInstalledPackageSizeKB(cmd); + String packageName = getPackageName(cmd); + long packageSize = getInstalledPackageSizeKB(cmd); TKit.trace("InstalledPackageSize: " + packageSize); TKit.assertNotEquals(0, packageSize, String.format( "Check installed size of [%s] package in not zero", packageName)); @@ -330,7 +335,7 @@ public final class LinuxHelper { checkPrerequisites = packageSize > 5; } - List prerequisites = LinuxHelper.getPrerequisitePackages(cmd); + List prerequisites = getPrerequisitePackages(cmd); if (checkPrerequisites) { final String vitalPackage = "libc"; TKit.assertTrue(prerequisites.stream().filter( @@ -340,13 +345,28 @@ public final class LinuxHelper { vitalPackage, prerequisites, packageName)); } else { TKit.trace(String.format( - "Not cheking %s required packages of [%s] package", + "Not checking %s required packages of [%s] package", prerequisites, packageName)); } } - static void addBundleDesktopIntegrationVerifier(PackageTest test, - boolean integrated) { + public static Collection getInvokeShortcutSpecs(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.LINUX); + + final var desktopFiles = getDesktopFiles(cmd); + final var predefinedAppImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of).map(AppImageFile::load); + + return desktopFiles.stream().map(desktopFile -> { + var systemDesktopFile = getSystemDesktopFilesFolder().resolve(desktopFile.getFileName()); + return new InvokeShortcutSpec.Stub( + launcherNameFromDesktopFile(cmd, predefinedAppImage, desktopFile), + LauncherShortcut.LINUX_SHORTCUT, + new DesktopFile(systemDesktopFile, false).findQuotedValue("Path").map(Path::of), + List.of("gtk-launch", PathUtils.replaceSuffix(systemDesktopFile.getFileName(), "").toString())); + }).toList(); + } + + static void addBundleDesktopIntegrationVerifier(PackageTest test, boolean integrated) { final String xdgUtils = "xdg-utils"; Function, String> verifier = (lines) -> { @@ -392,52 +412,81 @@ public final class LinuxHelper { }); test.addInstallVerifier(cmd -> { - // Verify .desktop files. - try (var files = Files.list(cmd.appLayout().desktopIntegrationDirectory())) { - List desktopFiles = files - .filter(path -> path.getFileName().toString().endsWith(".desktop")) - .toList(); - if (!integrated) { - TKit.assertStringListEquals(List.of(), - desktopFiles.stream().map(Path::toString).collect( - Collectors.toList()), - "Check there are no .desktop files in the package"); - } - for (var desktopFile : desktopFiles) { - verifyDesktopFile(cmd, desktopFile); - } + if (!integrated) { + TKit.assertStringListEquals( + List.of(), + getDesktopFiles(cmd).stream().map(Path::toString).toList(), + "Check there are no .desktop files in the package"); } }); } - private static void verifyDesktopFile(JPackageCommand cmd, Path desktopFile) - throws IOException { + static void verifyDesktopFiles(JPackageCommand cmd, boolean installed) { + final var desktopFiles = getDesktopFiles(cmd); + try { + if (installed) { + var predefinedAppImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of).map(AppImageFile::load); + for (var desktopFile : desktopFiles) { + verifyDesktopFile(cmd, predefinedAppImage, desktopFile); + } + + if (!cmd.isPackageUnpacked("Not verifying system .desktop files")) { + for (var desktopFile : desktopFiles) { + Path systemDesktopFile = getSystemDesktopFilesFolder().resolve(desktopFile.getFileName()); + TKit.assertFileExists(systemDesktopFile); + TKit.assertStringListEquals( + Files.readAllLines(desktopFile), + Files.readAllLines(systemDesktopFile), + String.format("Check [%s] and [%s] files are equal", desktopFile, systemDesktopFile)); + } + } + } else { + for (var desktopFile : getDesktopFiles(cmd)) { + Path systemDesktopFile = getSystemDesktopFilesFolder().resolve(desktopFile.getFileName()); + TKit.assertPathExists(systemDesktopFile, false); + } + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static Collection getDesktopFiles(JPackageCommand cmd) { + var unpackedDir = cmd.appLayout().desktopIntegrationDirectory(); + var packageDir = cmd.pathToPackageFile(unpackedDir); + return getPackageFiles(cmd).filter(path -> { + return packageDir.equals(path.getParent()) && path.getFileName().toString().endsWith(".desktop"); + }).map(Path::getFileName).map(unpackedDir::resolve).toList(); + } + + private static String launcherNameFromDesktopFile(JPackageCommand cmd, Optional predefinedAppImage, Path desktopFile) { + Objects.requireNonNull(cmd); + Objects.requireNonNull(predefinedAppImage); + Objects.requireNonNull(desktopFile); + + return predefinedAppImage.map(v -> { + return v.launchers().keySet().stream(); + }).orElseGet(() -> { + return Stream.concat(Stream.of(cmd.name()), cmd.addLauncherNames().stream()); + }).filter(name-> { + return getDesktopFile(cmd, name).equals(desktopFile); + }).findAny().orElseThrow(() -> { + TKit.assertUnexpected(String.format("Failed to find launcher corresponding to [%s] file", desktopFile)); + // Unreachable + return null; + }); + } + + private static void verifyDesktopFile(JPackageCommand cmd, Optional predefinedAppImage, Path desktopFile) throws IOException { + Objects.requireNonNull(cmd); + Objects.requireNonNull(predefinedAppImage); + Objects.requireNonNull(desktopFile); + TKit.trace(String.format("Check [%s] file BEGIN", desktopFile)); - var launcherName = Stream.of(List.of(cmd.name()), cmd.addLauncherNames()).flatMap(List::stream).filter(name -> { - return getDesktopFile(cmd, name).equals(desktopFile); - }).findAny(); - if (!cmd.hasArgument("--app-image")) { - TKit.assertTrue(launcherName.isPresent(), - "Check the desktop file corresponds to one of app launchers"); - } + var launcherName = launcherNameFromDesktopFile(cmd, predefinedAppImage, desktopFile); - List lines = Files.readAllLines(desktopFile); - TKit.assertEquals("[Desktop Entry]", lines.get(0), "Check file header"); - - Map data = lines.stream() - .skip(1) - .peek(str -> TKit.assertTextStream("=").predicate(String::contains).apply(List.of(str))) - .map(str -> { - String components[] = str.split("=(?=.+)"); - if (components.length == 1) { - return Map.entry(str.substring(0, str.length() - 1), ""); - } - return Map.entry(components[0], components[1]); - }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> { - TKit.assertUnexpected("Multiple values of the same key"); - return null; - })); + var data = new DesktopFile(desktopFile, true); final Set mandatoryKeys = new HashSet<>(Set.of("Name", "Comment", "Exec", "Icon", "Terminal", "Type", "Categories")); @@ -447,34 +496,44 @@ public final class LinuxHelper { for (var e : Map.of("Type", "Application", "Terminal", "false").entrySet()) { String key = e.getKey(); - TKit.assertEquals(e.getValue(), data.get(key), String.format( + TKit.assertEquals(e.getValue(), data.find(key).orElseThrow(), String.format( "Check value of [%s] key", key)); } - // Verify the value of `Exec` key is escaped if required - String launcherPath = data.get("Exec"); - if (Pattern.compile("\\s").matcher(launcherPath).find()) { - TKit.assertTrue(launcherPath.startsWith("\"") - && launcherPath.endsWith("\""), - "Check path to the launcher is enclosed in double quotes"); - launcherPath = launcherPath.substring(1, launcherPath.length() - 1); - } + String launcherPath = data.findQuotedValue("Exec").orElseThrow(); - if (launcherName.isPresent()) { - TKit.assertEquals(launcherPath, cmd.pathToPackageFile( - cmd.appLauncherPath(launcherName.get())).toString(), - String.format( - "Check the value of [Exec] key references [%s] app launcher", - launcherName.get())); - } + TKit.assertEquals( + launcherPath, + cmd.pathToPackageFile(cmd.appLauncherPath(launcherName)).toString(), + String.format("Check the value of [Exec] key references [%s] app launcher", launcherName)); + + var appLayout = cmd.appLayout(); + + LauncherShortcut.LINUX_SHORTCUT.expectShortcut(cmd, predefinedAppImage, launcherName).map(shortcutWorkDirType -> { + switch (shortcutWorkDirType) { + case DEFAULT -> { + return (Path)null; + } + default -> { + throw new AssertionError(); + } + } + }).map(Path::toString).ifPresentOrElse(shortcutWorkDir -> { + var actualShortcutWorkDir = data.find("Path"); + TKit.assertTrue(actualShortcutWorkDir.isPresent(), "Check [Path] key exists"); + TKit.assertEquals(actualShortcutWorkDir.get(), shortcutWorkDir, "Check the value of [Path] key"); + }, () -> { + TKit.assertTrue(data.find("Path").isEmpty(), "Check there is no [Path] key"); + }); for (var e : List.>, Function>>of( Map.entry(Map.entry("Exec", Optional.of(launcherPath)), ApplicationLayout::launchersDirectory), Map.entry(Map.entry("Icon", Optional.empty()), ApplicationLayout::desktopIntegrationDirectory))) { - var path = e.getKey().getValue().or(() -> Optional.of(data.get( - e.getKey().getKey()))).map(Path::of).get(); + var path = e.getKey().getValue().or(() -> { + return data.findQuotedValue(e.getKey().getKey()); + }).map(Path::of).get(); TKit.assertFileExists(cmd.pathToUnpackedPackageFile(path)); - Path expectedDir = cmd.pathToPackageFile(e.getValue().apply(cmd.appLayout())); + Path expectedDir = cmd.pathToPackageFile(e.getValue().apply(appLayout)); TKit.assertTrue(path.getParent().equals(expectedDir), String.format( "Check the value of [%s] key references a file in [%s] folder", e.getKey().getKey(), expectedDir)); @@ -761,6 +820,62 @@ public final class LinuxHelper { } } + + private static final class DesktopFile { + DesktopFile(Path path, boolean verify) { + try { + List lines = Files.readAllLines(path); + if (verify) { + TKit.assertEquals("[Desktop Entry]", lines.getFirst(), "Check file header"); + } + + var stream = lines.stream().skip(1).filter(Predicate.not(String::isEmpty)); + if (verify) { + stream = stream.peek(str -> { + TKit.assertTextStream("=").predicate(String::contains).apply(List.of(str)); + }); + } + + data = stream.map(str -> { + String components[] = str.split("=(?=.+)"); + if (components.length == 1) { + return Map.entry(str.substring(0, str.length() - 1), ""); + } else { + return Map.entry(components[0], components[1]); + } + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + Set keySet() { + return data.keySet(); + } + + Optional find(String property) { + return Optional.ofNullable(data.get(Objects.requireNonNull(property))); + } + + Optional findQuotedValue(String property) { + return find(property).map(value -> { + if (Pattern.compile("\\s").matcher(value).find()) { + boolean quotesMatched = value.startsWith("\"") && value.endsWith("\""); + if (!quotesMatched) { + TKit.assertTrue(quotesMatched, + String.format("Check the value of key [%s] is enclosed in double quotes", property)); + } + return value.substring(1, value.length() - 1); + } else { + return value; + } + }); + } + + private final Map data; + } + + static final Set CRITICAL_RUNTIME_FILES = Set.of(Path.of( "lib/server/libjvm.so")); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java new file mode 100644 index 00000000000..87236575e2e --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.jpackage.test; + + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + + +final class MsiDatabase { + + static MsiDatabase load(Path msiFile, Path idtFileOutputDir, Set tableNames) { + try { + Files.createDirectories(idtFileOutputDir); + + var orderedTableNames = tableNames.stream().sorted().toList(); + + Executor.of("cscript.exe", "//Nologo") + .addArgument(TKit.TEST_SRC_ROOT.resolve("resources/msi-export.js")) + .addArgument(msiFile) + .addArgument(idtFileOutputDir) + .addArguments(orderedTableNames.stream().map(Table::tableName).toList()) + .dumpOutput() + .execute(0); + + var tables = orderedTableNames.stream().map(tableName -> { + return Map.entry(tableName, idtFileOutputDir.resolve(tableName + ".idt")); + }).filter(e -> { + return Files.exists(e.getValue()); + }).collect(Collectors.toMap(Map.Entry::getKey, e -> { + return MsiTable.loadFromTextArchiveFile(e.getValue()); + })); + + return new MsiDatabase(tables); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + + enum Table { + COMPONENT("Component"), + DIRECTORY("Directory"), + FILE("File"), + PROPERTY("Property"), + SHORTCUT("Shortcut"), + ; + + Table(String name) { + this.tableName = Objects.requireNonNull(name); + } + + String tableName() { + return tableName; + } + + private final String tableName; + + static final Set
    FIND_PROPERTY_REQUIRED_TABLES = Set.of(PROPERTY); + static final Set
    LIST_SHORTCUTS_REQUIRED_TABLES = Set.of(COMPONENT, DIRECTORY, FILE, SHORTCUT); + } + + + private MsiDatabase(Map tables) { + this.tables = Map.copyOf(tables); + } + + Set
    tableNames() { + return tables.keySet(); + } + + MsiDatabase append(MsiDatabase other) { + Map newTables = new HashMap<>(tables); + newTables.putAll(other.tables); + return new MsiDatabase(newTables); + } + + Optional findProperty(String propertyName) { + Objects.requireNonNull(propertyName); + return tables.get(Table.PROPERTY).findRow("Property", propertyName).map(row -> { + return row.apply("Value"); + }); + } + + Collection listShortcuts() { + var shortcuts = tables.get(Table.SHORTCUT); + if (shortcuts == null) { + return List.of(); + } + return IntStream.range(0, shortcuts.rowCount()).mapToObj(i -> { + var row = shortcuts.row(i); + var shortcutPath = directoryPath(row.apply("Directory_")).resolve(fileNameFromFieldValue(row.apply("Name"))); + var workDir = directoryPath(row.apply("WkDir")); + var shortcutTarget = Path.of(expandFormattedString(row.apply("Target"))); + return new Shortcut(shortcutPath, shortcutTarget, workDir); + }).toList(); + } + + record Shortcut(Path path, Path target, Path workDir) { + + Shortcut { + Objects.requireNonNull(path); + Objects.requireNonNull(target); + Objects.requireNonNull(workDir); + } + + void assertEquals(Shortcut expected) { + TKit.assertEquals(expected.path, path, "Check the shortcut path"); + TKit.assertEquals(expected.target, target, "Check the shortcut target"); + TKit.assertEquals(expected.workDir, workDir, "Check the shortcut work directory"); + } + } + + private Path directoryPath(String directoryId) { + var table = tables.get(Table.DIRECTORY); + Path result = null; + for (var row = table.findRow("Directory", directoryId); + row.isPresent(); + directoryId = row.get().apply("Directory_Parent"), row = table.findRow("Directory", directoryId)) { + + Path pathComponent; + if (DIRECTORY_PROPERTIES.contains(directoryId)) { + pathComponent = Path.of(directoryId); + directoryId = null; + } else { + pathComponent = fileNameFromFieldValue(row.get().apply("DefaultDir")); + } + + if (result != null) { + result = pathComponent.resolve(result); + } else { + result = pathComponent; + } + + if (directoryId == null) { + break; + } + } + + return Objects.requireNonNull(result); + } + + private String expandFormattedString(String str) { + return expandFormattedString(str, token -> { + if (token.charAt(0) == '#') { + var filekey = token.substring(1); + var fileRow = tables.get(Table.FILE).findRow("File", filekey).orElseThrow(); + + var component = fileRow.apply("Component_"); + var componentRow = tables.get(Table.COMPONENT).findRow("Component", component).orElseThrow(); + + var fileName = fileNameFromFieldValue(fileRow.apply("FileName")); + var filePath = directoryPath(componentRow.apply("Directory_")); + + return filePath.resolve(fileName).toString(); + } else { + throw new UnsupportedOperationException(String.format( + "Unrecognized token [%s] in formatted string [%s]", token, str)); + } + }); + } + + private static Path fileNameFromFieldValue(String fieldValue) { + var pipeIdx = fieldValue.indexOf('|'); + if (pipeIdx < 0) { + return Path.of(fieldValue); + } else { + return Path.of(fieldValue.substring(pipeIdx + 1)); + } + } + + private static String expandFormattedString(String str, Function callback) { + // Naive implementation of https://learn.microsoft.com/en-us/windows/win32/msi/formatted + // - No recursive property expansion. + // - No curly brakes ({}) handling. + + Objects.requireNonNull(str); + Objects.requireNonNull(callback); + var sb = new StringBuffer(); + var m = FORMATTED_STRING_TOKEN.matcher(str); + while (m.find()) { + var token = m.group(); + token = token.substring(1, token.length() - 1); + if (token.equals("~")) { + m.appendReplacement(sb, "\0"); + } else { + var replacement = Matcher.quoteReplacement(callback.apply(token)); + m.appendReplacement(sb, replacement); + } + } + m.appendTail(sb); + return sb.toString(); + } + + + private record MsiTable(Map> columns) { + + MsiTable { + Objects.requireNonNull(columns); + if (columns.isEmpty()) { + throw new IllegalArgumentException("Table should have columns"); + } + } + + Optional> findRow(String columnName, String fieldValue) { + Objects.requireNonNull(columnName); + Objects.requireNonNull(fieldValue); + var column = columns.get(columnName); + for (int i = 0; i != column.size(); i++) { + if (fieldValue.equals(column.get(i))) { + return Optional.of(row(i)); + } + } + return Optional.empty(); + } + + /** + * Loads a table from a text archive file. + * @param idtFile path to the input text archive file + * @return the table + */ + static MsiTable loadFromTextArchiveFile(Path idtFile) { + + var header = IdtFileHeader.loadFromTextArchiveFile(idtFile); + + Map> columns = new HashMap<>(); + header.columns.forEach(column -> { + columns.put(column, new ArrayList<>()); + }); + + try { + var lines = Files.readAllLines(idtFile, header.charset()).toArray(String[]::new); + for (int i = 3; i != lines.length; i++) { + var line = lines[i]; + var row = line.split("\t", -1); + if (row.length != header.columns().size()) { + throw new IllegalArgumentException(String.format( + "Expected %d columns. Actual is %d in line %d in [%s] file", + header.columns().size(), row.length, i, idtFile)); + } + for (int j = 0; j != row.length; j++) { + var field = row[j]; + // https://learn.microsoft.com/en-us/windows/win32/msi/archive-file-format + field = field.replace((char)21, (char)0); + field = field.replace((char)27, '\b'); + field = field.replace((char)16, '\t'); + field = field.replace((char)25, '\n'); + field = field.replace((char)24, '\f'); + field = field.replace((char)17, '\r'); + columns.get(header.columns.get(j)).add(field); + } + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + return new MsiTable(columns); + } + + int columnCount() { + return columns.size(); + } + + int rowCount() { + return columns.values().stream().findAny().orElseThrow().size(); + } + + Function row(int rowIndex) { + return columnName -> { + var column = Objects.requireNonNull(columns.get(Objects.requireNonNull(columnName))); + return column.get(rowIndex); + }; + } + } + + + private record IdtFileHeader(Charset charset, List columns) { + + IdtFileHeader { + Objects.requireNonNull(charset); + columns.forEach(Objects::requireNonNull); + if (columns.isEmpty()) { + throw new IllegalArgumentException("Table should have columns"); + } + } + + /** + * Loads a table header from a text archive (.idt) file. + * @see https://learn.microsoft.com/en-us/windows/win32/msi/archive-file-format + * @see https://learn.microsoft.com/en-us/windows/win32/msi/ascii-data-in-text-archive-files + * @param path path to the input text archive file + * @return the table header + */ + static IdtFileHeader loadFromTextArchiveFile(Path idtFile) { + var charset = StandardCharsets.US_ASCII; + try (var stream = Files.lines(idtFile, charset)) { + var headerLines = stream.limit(3).toList(); + if (headerLines.size() != 3) { + throw new IllegalArgumentException(String.format( + "[%s] file should have at least three text lines", idtFile)); + } + + var columns = headerLines.get(0).split("\t"); + + var header = headerLines.get(2).split("\t", 4); + if (header.length == 3) { + if (Pattern.matches("^[1-9]\\d+$", header[0])) { + charset = Charset.forName(header[0]); + } else { + throw new IllegalArgumentException(String.format( + "Unexpected charset name [%s] in [%s] file", header[0], idtFile)); + } + } else if (header.length != 2) { + throw new IllegalArgumentException(String.format( + "Unexpected number of fields (%d) in the 3rd line of [%s] file", + header.length, idtFile)); + } + + return new IdtFileHeader(charset, List.of(columns)); + + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + + private final Map tables; + + // https://learn.microsoft.com/en-us/windows/win32/msi/formatted + private static final Pattern FORMATTED_STRING_TOKEN = Pattern.compile("\\[[^\\]]+\\]"); + + // https://learn.microsoft.com/en-us/windows/win32/msi/property-reference#system-folder-properties + private final Set DIRECTORY_PROPERTIES = Set.of( + "DesktopFolder", + "LocalAppDataFolder", + "ProgramFiles64Folder", + "ProgramMenuFolder" + ); +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index 61e4ccdb4a2..84453038cd2 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java @@ -30,11 +30,13 @@ import static jdk.jpackage.test.PackageType.LINUX; import static jdk.jpackage.test.PackageType.MAC_PKG; import static jdk.jpackage.test.PackageType.NATIVE; import static jdk.jpackage.test.PackageType.WINDOWS; +import static jdk.jpackage.test.PackageType.WIN_MSI; import java.awt.GraphicsEnvironment; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -270,8 +272,7 @@ public final class PackageTest extends RunnablePackageTest { PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa) { Objects.requireNonNull(fa); - // Setup test app to have valid jpackage command line before - // running check of type of environment. + // Setup test app to have valid jpackage command line before running the check. addHelloAppInitializer(null); forTypes(LINUX, () -> { @@ -296,13 +297,9 @@ public final class PackageTest extends RunnablePackageTest { Files.deleteIfExists(appOutput); List expectedArgs = testRun.openFiles(testFiles); - TKit.waitForFileCreated(appOutput, 7); + TKit.waitForFileCreated(appOutput, Duration.ofSeconds(7), Duration.ofSeconds(3)); - // Wait a little bit after file has been created to - // make sure there are no pending writes into it. - Thread.sleep(3000); - HelloApp.verifyOutputFile(appOutput, expectedArgs, - Collections.emptyMap()); + HelloApp.verifyOutputFile(appOutput, expectedArgs, Map.of()); }); if (isOfType(cmd, WINDOWS)) { @@ -336,6 +333,10 @@ public final class PackageTest extends RunnablePackageTest { return forTypes(List.of(type), action); } + public PackageTest forTypes(PackageType type, Consumer action) { + return forTypes(List.of(type), () -> action.accept(this)); + } + public PackageTest notForTypes(Collection types, Runnable action) { Set workset = new HashSet<>(currentTypes); workset.removeAll(types); @@ -346,21 +347,24 @@ public final class PackageTest extends RunnablePackageTest { return notForTypes(List.of(type), action); } + public PackageTest notForTypes(PackageType type, Consumer action) { + return notForTypes(List.of(type), () -> action.accept(this)); + } + public PackageTest configureHelloApp() { return configureHelloApp(null); } public PackageTest configureHelloApp(String javaAppDesc) { addHelloAppInitializer(javaAppDesc); - addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput); + addInstallVerifier(LauncherVerifier::executeMainLauncherAndVerifyOutput); return this; } public PackageTest addHelloAppInitializer(String javaAppDesc) { - addInitializer( - cmd -> new HelloApp(JavaAppDesc.parse(javaAppDesc)).addTo(cmd), - "HelloApp"); - return this; + return addInitializer(cmd -> { + new HelloApp(JavaAppDesc.parse(javaAppDesc)).addTo(cmd); + }, "HelloApp"); } public static class Group extends RunnablePackageTest { @@ -603,11 +607,7 @@ public final class PackageTest extends RunnablePackageTest { } } case VERIFY_INSTALL -> { - if (unpackNotSupported()) { - return ActionAction.SKIP; - } - - if (installFailed()) { + if (unpackNotSupported() || installFailed()) { return ActionAction.SKIP; } } @@ -745,6 +745,8 @@ public final class PackageTest extends RunnablePackageTest { if (expectedJPackageExitCode == 0) { if (isOfType(cmd, LINUX)) { LinuxHelper.verifyPackageBundleEssential(cmd); + } else if (isOfType(cmd, WIN_MSI)) { + WinShortcutVerifier.verifyBundleShortcuts(cmd); } } bundleVerifiers.forEach(v -> v.accept(cmd, result)); @@ -766,12 +768,11 @@ public final class PackageTest extends RunnablePackageTest { if (!cmd.isRuntime()) { if (isOfType(cmd, WINDOWS) && !cmd.isPackageUnpacked("Not verifying desktop integration")) { - // Check main launcher - WindowsHelper.verifyDesktopIntegration(cmd, null); - // Check additional launchers - cmd.addLauncherNames().forEach(name -> { - WindowsHelper.verifyDesktopIntegration(cmd, name); - }); + WindowsHelper.verifyDeployedDesktopIntegration(cmd, true); + } + + if (isOfType(cmd, LINUX)) { + LinuxHelper.verifyDesktopFiles(cmd, true); } } @@ -848,12 +849,11 @@ public final class PackageTest extends RunnablePackageTest { TKit.assertPathExists(cmd.appLauncherPath(), false); if (isOfType(cmd, WINDOWS)) { - // Check main launcher - WindowsHelper.verifyDesktopIntegration(cmd, null); - // Check additional launchers - cmd.addLauncherNames().forEach(name -> { - WindowsHelper.verifyDesktopIntegration(cmd, name); - }); + WindowsHelper.verifyDeployedDesktopIntegration(cmd, false); + } + + if (isOfType(cmd, LINUX)) { + LinuxHelper.verifyDesktopFiles(cmd, false); } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index 2508db00295..a94dfa135c1 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -43,6 +43,8 @@ import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -597,8 +599,14 @@ public final class TKit { return file; } - static void waitForFileCreated(Path fileToWaitFor, - long timeoutSeconds) throws IOException { + public static void waitForFileCreated(Path fileToWaitFor, + Duration timeout, Duration afterCreatedTimeout) throws IOException { + waitForFileCreated(fileToWaitFor, timeout); + // Wait after the file has been created to ensure it is fully written. + ThrowingConsumer.toConsumer(Thread::sleep).accept(afterCreatedTimeout); + } + + private static void waitForFileCreated(Path fileToWaitFor, Duration timeout) throws IOException { trace(String.format("Wait for file [%s] to be available", fileToWaitFor.toAbsolutePath())); @@ -608,22 +616,23 @@ public final class TKit { Path watchDirectory = fileToWaitFor.toAbsolutePath().getParent(); watchDirectory.register(ws, ENTRY_CREATE, ENTRY_MODIFY); - long waitUntil = System.currentTimeMillis() + timeoutSeconds * 1000; + var waitUntil = Instant.now().plus(timeout); for (;;) { - long timeout = waitUntil - System.currentTimeMillis(); - assertTrue(timeout > 0, String.format( - "Check timeout value %d is positive", timeout)); + var remainderTimeout = Instant.now().until(waitUntil); + assertTrue(remainderTimeout.isPositive(), String.format( + "Check timeout value %dms is positive", remainderTimeout.toMillis())); - WatchKey key = ThrowingSupplier.toSupplier(() -> ws.poll(timeout, - TimeUnit.MILLISECONDS)).get(); + WatchKey key = ThrowingSupplier.toSupplier(() -> { + return ws.poll(remainderTimeout.toMillis(), TimeUnit.MILLISECONDS); + }).get(); if (key == null) { - if (fileToWaitFor.toFile().exists()) { + if (Files.exists(fileToWaitFor)) { trace(String.format( "File [%s] is available after poll timeout expired", fileToWaitFor)); return; } - assertUnexpected(String.format("Timeout expired", timeout)); + assertUnexpected(String.format("Timeout %dms expired", remainderTimeout.toMillis())); } for (WatchEvent event : key.pollEvents()) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java new file mode 100644 index 00000000000..cca904e017e --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.jpackage.test; + +import static java.util.stream.Collectors.groupingBy; +import static jdk.jpackage.test.LauncherShortcut.WIN_DESKTOP_SHORTCUT; +import static jdk.jpackage.test.LauncherShortcut.WIN_START_MENU_SHORTCUT; +import static jdk.jpackage.test.WindowsHelper.getInstallationSubDirectory; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.PathUtils; +import jdk.jpackage.test.LauncherShortcut.InvokeShortcutSpec; +import jdk.jpackage.test.LauncherShortcut.StartupDirectory; +import jdk.jpackage.test.MsiDatabase.Shortcut; +import jdk.jpackage.test.WindowsHelper.SpecialFolder; + + +public final class WinShortcutVerifier { + + static void verifyBundleShortcuts(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WIN_MSI); + + if (Stream.of("--win-menu", "--win-shortcut").noneMatch(cmd::hasArgument) && cmd.addLauncherNames().isEmpty()) { + return; + } + + var actualShortcuts = WindowsHelper.getMsiShortcuts(cmd).stream().collect(groupingBy(shortcut -> { + return PathUtils.replaceSuffix(shortcut.target().getFileName(), "").toString(); + })); + + var expectedShortcuts = expectShortcuts(cmd); + + var launcherNames = expectedShortcuts.keySet().stream().sorted().toList(); + + TKit.assertStringListEquals( + launcherNames, + actualShortcuts.keySet().stream().sorted().toList(), + "Check the list of launchers with shortcuts"); + + Function, List> sorter = shortcuts -> { + return shortcuts.stream().sorted(SHORTCUT_COMPARATOR).toList(); + }; + + for (var name : launcherNames) { + var actualLauncherShortcuts = sorter.apply(actualShortcuts.get(name)); + var expectedLauncherShortcuts = sorter.apply(expectedShortcuts.get(name)); + + TKit.assertEquals(expectedLauncherShortcuts.size(), actualLauncherShortcuts.size(), + String.format("Check the number of shortcuts of launcher [%s]", name)); + + for (int i = 0; i != expectedLauncherShortcuts.size(); i++) { + TKit.trace(String.format("Verify shortcut #%d of launcher [%s]", i + 1, name)); + actualLauncherShortcuts.get(i).assertEquals(expectedLauncherShortcuts.get(i)); + TKit.trace("Done"); + } + } + } + + static void verifyDeployedShortcuts(JPackageCommand cmd, boolean installed) { + cmd.verifyIsOfType(PackageType.WINDOWS); + + verifyDeployedShortcutsInternal(cmd, installed); + var copyCmd = cmd.createMutableCopy(); + if (copyCmd.hasArgument("--win-per-user-install")) { + copyCmd.removeArgument("--win-per-user-install"); + } else { + copyCmd.addArgument("--win-per-user-install"); + } + verifyDeployedShortcutsInternal(copyCmd, false); + } + + public static Collection getInvokeShortcutSpecs(JPackageCommand cmd) { + return expectShortcuts(cmd).entrySet().stream().map(e -> { + return e.getValue().stream().map(shortcut -> { + return convert(cmd, e.getKey(), shortcut); + }); + }).flatMap(x -> x).toList(); + } + + private static void verifyDeployedShortcutsInternal(JPackageCommand cmd, boolean installed) { + + var expectedShortcuts = expectShortcuts(cmd).values().stream().flatMap(Collection::stream).toList(); + + var isUserLocalInstall = WindowsHelper.isUserLocalInstall(cmd); + + expectedShortcuts.stream().map(Shortcut::path).sorted().map(path -> { + return resolvePath(path, !isUserLocalInstall); + }).map(path -> { + return PathUtils.addSuffix(path, ".lnk"); + }).forEach(path -> { + if (installed) { + TKit.assertFileExists(path); + } else { + TKit.assertPathExists(path, false); + } + }); + + if (!installed) { + expectedShortcuts.stream().map(Shortcut::path).filter(path -> { + return Stream.of(ShortcutType.COMMON_START_MENU, ShortcutType.USER_START_MENU).anyMatch(type -> { + return path.startsWith(Path.of(type.rootFolder().getMsiPropertyName())); + }); + }).map(Path::getParent).distinct().map(unresolvedShortcutDir -> { + return resolvePath(unresolvedShortcutDir, !isUserLocalInstall); + }).forEach(shortcutDir -> { + if (Files.isDirectory(shortcutDir)) { + TKit.assertDirectoryNotEmpty(shortcutDir); + } else { + TKit.assertPathExists(shortcutDir, false); + } + }); + } + } + + private enum ShortcutType { + COMMON_START_MENU(SpecialFolder.COMMON_START_MENU_PROGRAMS), + USER_START_MENU(SpecialFolder.USER_START_MENU_PROGRAMS), + COMMON_DESKTOP(SpecialFolder.COMMON_DESKTOP), + USER_DESKTOP(SpecialFolder.USER_DESKTOP), + ; + + ShortcutType(SpecialFolder rootFolder) { + this.rootFolder = Objects.requireNonNull(rootFolder); + } + + SpecialFolder rootFolder() { + return rootFolder; + } + + private final SpecialFolder rootFolder; + } + + private static Path resolvePath(Path path, boolean allUsers) { + var root = path.getName(0); + var resolvedRoot = SpecialFolder.findMsiProperty(root.toString(), allUsers).orElseThrow().getPath(); + return resolvedRoot.resolve(root.relativize(path)); + } + + private static Shortcut createLauncherShortcutSpec(JPackageCommand cmd, String launcherName, + SpecialFolder installRoot, Path workDir, ShortcutType type) { + + var name = Optional.ofNullable(launcherName).orElseGet(cmd::name); + + var appLayout = ApplicationLayout.windowsAppImage().resolveAt( + Path.of(installRoot.getMsiPropertyName()).resolve(getInstallationSubDirectory(cmd))); + + Path path; + switch (type) { + case COMMON_START_MENU, USER_START_MENU -> { + path = Path.of(cmd.getArgumentValue("--win-menu-group", () -> "Unknown"), name); + } + default -> { + path = Path.of(name); + } + } + + return new Shortcut( + Path.of(type.rootFolder().getMsiPropertyName()).resolve(path), + appLayout.launchersDirectory().resolve(name + ".exe"), + workDir); + } + + private static Collection expectLauncherShortcuts(JPackageCommand cmd, + Optional predefinedAppImage, String launcherName) { + Objects.requireNonNull(cmd); + Objects.requireNonNull(predefinedAppImage); + + final List shortcuts = new ArrayList<>(); + + final var winMenu = WIN_START_MENU_SHORTCUT.expectShortcut(cmd, predefinedAppImage, launcherName); + final var desktop = WIN_DESKTOP_SHORTCUT.expectShortcut(cmd, predefinedAppImage, launcherName); + + final var isUserLocalInstall = WindowsHelper.isUserLocalInstall(cmd); + + final SpecialFolder installRoot; + if (isUserLocalInstall) { + installRoot = SpecialFolder.LOCAL_APPLICATION_DATA; + } else { + installRoot = SpecialFolder.PROGRAM_FILES; + } + + final var installDir = Path.of(installRoot.getMsiPropertyName()).resolve(getInstallationSubDirectory(cmd)); + + final Function workDir = startupDirectory -> { + return installDir; + }; + + if (winMenu.isPresent()) { + ShortcutType type; + if (isUserLocalInstall) { + type = ShortcutType.USER_START_MENU; + } else { + type = ShortcutType.COMMON_START_MENU; + } + shortcuts.add(createLauncherShortcutSpec(cmd, launcherName, installRoot, winMenu.map(workDir).orElseThrow(), type)); + } + + if (desktop.isPresent()) { + ShortcutType type; + if (isUserLocalInstall) { + type = ShortcutType.USER_DESKTOP; + } else { + type = ShortcutType.COMMON_DESKTOP; + } + shortcuts.add(createLauncherShortcutSpec(cmd, launcherName, installRoot, desktop.map(workDir).orElseThrow(), type)); + } + + return shortcuts; + } + + private static Map> expectShortcuts(JPackageCommand cmd) { + Map> expectedShortcuts = new HashMap<>(); + + var predefinedAppImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of).map(AppImageFile::load); + + predefinedAppImage.map(v -> { + return v.launchers().keySet().stream(); + }).orElseGet(() -> { + return Stream.concat(Stream.of(cmd.name()), cmd.addLauncherNames().stream()); + }).forEach(launcherName -> { + var shortcuts = expectLauncherShortcuts(cmd, predefinedAppImage, launcherName); + if (!shortcuts.isEmpty()) { + expectedShortcuts.put(launcherName, shortcuts); + } + }); + + return expectedShortcuts; + } + + private static InvokeShortcutSpec convert(JPackageCommand cmd, String launcherName, Shortcut shortcut) { + LauncherShortcut launcherShortcut; + if (Stream.of(ShortcutType.COMMON_START_MENU, ShortcutType.USER_START_MENU).anyMatch(type -> { + return shortcut.path().startsWith(Path.of(type.rootFolder().getMsiPropertyName())); + })) { + launcherShortcut = WIN_START_MENU_SHORTCUT; + } else { + launcherShortcut = WIN_DESKTOP_SHORTCUT; + } + + var isUserLocalInstall = WindowsHelper.isUserLocalInstall(cmd); + return new InvokeShortcutSpec.Stub( + launcherName, + launcherShortcut, + Optional.of(resolvePath(shortcut.workDir(), !isUserLocalInstall)), + List.of("cmd", "/c", "start", "/wait", PathUtils.addSuffix(resolvePath(shortcut.path(), !isUserLocalInstall), ".lnk").toString())); + } + + + private static final Comparator SHORTCUT_COMPARATOR = Comparator.comparing(Shortcut::target) + .thenComparing(Comparator.comparing(Shortcut::path)) + .thenComparing(Comparator.comparing(Shortcut::workDir)); +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index 3ac302ce0fe..5e97b0d2dde 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -26,19 +26,24 @@ import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.ref.SoftReference; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Instant; +import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; +import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ThrowingRunnable; import jdk.jpackage.test.PackageTest.PackageHandlers; @@ -63,7 +68,7 @@ public class WindowsHelper { return PROGRAM_FILES; } - private static Path getInstallationSubDirectory(JPackageCommand cmd) { + static Path getInstallationSubDirectory(JPackageCommand cmd) { cmd.verifyIsOfType(PackageType.WINDOWS); return Path.of(cmd.getArgumentValue("--install-dir", cmd::name)); } @@ -263,22 +268,22 @@ public class WindowsHelper { } } - static void verifyDesktopIntegration(JPackageCommand cmd, - String launcherName) { - new DesktopIntegrationVerifier(cmd, launcherName); + static void verifyDeployedDesktopIntegration(JPackageCommand cmd, boolean installed) { + WinShortcutVerifier.verifyDeployedShortcuts(cmd, installed); + DesktopIntegrationVerifier.verify(cmd, installed); } public static String getMsiProperty(JPackageCommand cmd, String propertyName) { cmd.verifyIsOfType(PackageType.WIN_MSI); - return Executor.of("cscript.exe", "//Nologo") - .addArgument(TKit.TEST_SRC_ROOT.resolve("resources/query-msi-property.js")) - .addArgument(cmd.outputBundle()) - .addArgument(propertyName) - .dumpOutput() - .executeAndGetOutput().stream().collect(Collectors.joining("\n")); + return MsiDatabaseCache.INSTANCE.findProperty(cmd.outputBundle(), propertyName).orElseThrow(); } - public static String getExecutableDesciption(Path pathToExeFile) { + static Collection getMsiShortcuts(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WIN_MSI); + return MsiDatabaseCache.INSTANCE.listShortcuts(cmd.outputBundle()); + } + + public static String getExecutableDescription(Path pathToExeFile) { Executor exec = Executor.of("powershell", "-NoLogo", "-NoProfile", @@ -386,7 +391,7 @@ public class WindowsHelper { } } - private static boolean isUserLocalInstall(JPackageCommand cmd) { + static boolean isUserLocalInstall(JPackageCommand cmd) { return cmd.hasArgument("--win-per-user-install"); } @@ -394,141 +399,42 @@ public class WindowsHelper { return path.toString().length() > WIN_MAX_PATH; } + private static class DesktopIntegrationVerifier { - DesktopIntegrationVerifier(JPackageCommand cmd, String launcherName) { + static void verify(JPackageCommand cmd, boolean installed) { cmd.verifyIsOfType(PackageType.WINDOWS); - - name = Optional.ofNullable(launcherName).orElseGet(cmd::name); - - isUserLocalInstall = isUserLocalInstall(cmd); - - appInstalled = cmd.appLauncherPath(launcherName).toFile().exists(); - - desktopShortcutPath = Path.of(name + ".lnk"); - - startMenuShortcutPath = Path.of(cmd.getArgumentValue( - "--win-menu-group", () -> "Unknown"), name + ".lnk"); - - if (name.equals(cmd.name())) { - isWinMenu = cmd.hasArgument("--win-menu"); - isDesktop = cmd.hasArgument("--win-shortcut"); - } else { - var props = AdditionalLauncher.getAdditionalLauncherProperties(cmd, - launcherName); - isWinMenu = props.getPropertyBooleanValue("win-menu").orElseGet( - () -> cmd.hasArgument("--win-menu")); - isDesktop = props.getPropertyBooleanValue("win-shortcut").orElseGet( - () -> cmd.hasArgument("--win-shortcut")); - } - - verifyStartMenuShortcut(); - - verifyDesktopShortcut(); - - Stream.of(cmd.getAllArgumentValues("--file-associations")).map( - Path::of).forEach(this::verifyFileAssociationsRegistry); - } - - private void verifyDesktopShortcut() { - if (isDesktop) { - if (isUserLocalInstall) { - verifyUserLocalDesktopShortcut(appInstalled); - verifySystemDesktopShortcut(false); - } else { - verifySystemDesktopShortcut(appInstalled); - verifyUserLocalDesktopShortcut(false); - } - } else { - verifySystemDesktopShortcut(false); - verifyUserLocalDesktopShortcut(false); + for (var faFile : cmd.getAllArgumentValues("--file-associations")) { + verifyFileAssociationsRegistry(Path.of(faFile), installed); } } - private void verifyShortcut(Path path, boolean exists) { - if (exists) { - TKit.assertFileExists(path); - } else { - TKit.assertPathExists(path, false); - } - } + private static void verifyFileAssociationsRegistry(Path faFile, boolean installed) { - private void verifySystemDesktopShortcut(boolean exists) { - Path dir = SpecialFolder.COMMON_DESKTOP.getPath(); - verifyShortcut(dir.resolve(desktopShortcutPath), exists); - } + TKit.trace(String.format( + "Get file association properties from [%s] file", + faFile)); - private void verifyUserLocalDesktopShortcut(boolean exists) { - Path dir = SpecialFolder.USER_DESKTOP.getPath(); - verifyShortcut(dir.resolve(desktopShortcutPath), exists); - } + var faProps = new Properties(); - private void verifyStartMenuShortcut() { - if (isWinMenu) { - if (isUserLocalInstall) { - verifyUserLocalStartMenuShortcut(appInstalled); - verifySystemStartMenuShortcut(false); - } else { - verifySystemStartMenuShortcut(appInstalled); - verifyUserLocalStartMenuShortcut(false); - } - } else { - verifySystemStartMenuShortcut(false); - verifyUserLocalStartMenuShortcut(false); - } - } - - private void verifyStartMenuShortcut(Path shortcutsRoot, boolean exists) { - Path shortcutPath = shortcutsRoot.resolve(startMenuShortcutPath); - verifyShortcut(shortcutPath, exists); - if (!exists) { - final var parentDir = shortcutPath.getParent(); - if (Files.isDirectory(parentDir)) { - TKit.assertDirectoryNotEmpty(parentDir); - } else { - TKit.assertPathExists(parentDir, false); - } - } - } - - private void verifySystemStartMenuShortcut(boolean exists) { - verifyStartMenuShortcut(SpecialFolder.COMMON_START_MENU_PROGRAMS.getPath(), exists); - - } - - private void verifyUserLocalStartMenuShortcut(boolean exists) { - verifyStartMenuShortcut(SpecialFolder.USER_START_MENU_PROGRAMS.getPath(), exists); - } - - private void verifyFileAssociationsRegistry(Path faFile) { - try { - TKit.trace(String.format( - "Get file association properties from [%s] file", - faFile)); - Map faProps = Files.readAllLines(faFile).stream().filter( - line -> line.trim().startsWith("extension=") || line.trim().startsWith( - "mime-type=")).map( - line -> { - String[] keyValue = line.trim().split("=", 2); - return Map.entry(keyValue[0], keyValue[1]); - }).collect(Collectors.toMap( - entry -> entry.getKey(), - entry -> entry.getValue())); - String suffix = faProps.get("extension"); - String contentType = faProps.get("mime-type"); + try (var reader = Files.newBufferedReader(faFile)) { + faProps.load(reader); + String suffix = faProps.getProperty("extension"); + String contentType = faProps.getProperty("mime-type"); TKit.assertNotNull(suffix, String.format( "Check file association suffix [%s] is found in [%s] property file", suffix, faFile)); TKit.assertNotNull(contentType, String.format( "Check file association content type [%s] is found in [%s] property file", contentType, faFile)); - verifyFileAssociations(appInstalled, "." + suffix, contentType); + verifyFileAssociations(installed, "." + suffix, contentType); + } catch (IOException ex) { - throw new RuntimeException(ex); + throw new UncheckedIOException(ex); } } - private void verifyFileAssociations(boolean exists, String suffix, + private static void verifyFileAssociations(boolean exists, String suffix, String contentType) { String contentTypeFromRegistry = queryRegistryValue(Path.of( "HKLM\\Software\\Classes", suffix).toString(), @@ -549,16 +455,9 @@ public class WindowsHelper { "Check content type in registry not found"); } } - - private final Path desktopShortcutPath; - private final Path startMenuShortcutPath; - private final boolean isUserLocalInstall; - private final boolean appInstalled; - private final boolean isWinMenu; - private final boolean isDesktop; - private final String name; } + static String queryRegistryValue(String keyPath, String valueName) { var status = Executor.of("reg", "query", keyPath, "/v", valueName) .saveOutput() @@ -611,7 +510,12 @@ public class WindowsHelper { CommonDesktop, Programs, - CommonPrograms; + CommonPrograms, + + ProgramFiles, + + LocalApplicationData, + ; Path getPath() { final var str = Executor.of("powershell", "-NoLogo", "-NoProfile", @@ -636,33 +540,84 @@ public class WindowsHelper { } } - private enum SpecialFolder { - COMMON_START_MENU_PROGRAMS(SYSTEM_SHELL_FOLDERS_REGKEY, "Common Programs", SpecialFolderDotNet.CommonPrograms), - USER_START_MENU_PROGRAMS(USER_SHELL_FOLDERS_REGKEY, "Programs", SpecialFolderDotNet.Programs), + enum SpecialFolder { + COMMON_START_MENU_PROGRAMS( + SYSTEM_SHELL_FOLDERS_REGKEY, + "Common Programs", + "ProgramMenuFolder", + SpecialFolderDotNet.CommonPrograms), + USER_START_MENU_PROGRAMS( + USER_SHELL_FOLDERS_REGKEY, + "Programs", + "ProgramMenuFolder", + SpecialFolderDotNet.Programs), - COMMON_DESKTOP(SYSTEM_SHELL_FOLDERS_REGKEY, "Common Desktop", SpecialFolderDotNet.CommonDesktop), - USER_DESKTOP(USER_SHELL_FOLDERS_REGKEY, "Desktop", SpecialFolderDotNet.Desktop); + COMMON_DESKTOP( + SYSTEM_SHELL_FOLDERS_REGKEY, + "Common Desktop", + "DesktopFolder", + SpecialFolderDotNet.CommonDesktop), + USER_DESKTOP( + USER_SHELL_FOLDERS_REGKEY, + "Desktop", + "DesktopFolder", + SpecialFolderDotNet.Desktop), - SpecialFolder(String keyPath, String valueName) { - reg = new RegValuePath(keyPath, valueName); + PROGRAM_FILES("ProgramFiles64Folder", SpecialFolderDotNet.ProgramFiles), + + LOCAL_APPLICATION_DATA("LocalAppDataFolder", SpecialFolderDotNet.LocalApplicationData), + ; + + SpecialFolder(String keyPath, String valueName, String msiPropertyName) { + reg = Optional.of(new RegValuePath(keyPath, valueName)); alt = Optional.empty(); + this.msiPropertyName = Objects.requireNonNull(msiPropertyName); } - SpecialFolder(String keyPath, String valueName, SpecialFolderDotNet alt) { - reg = new RegValuePath(keyPath, valueName); + SpecialFolder(String keyPath, String valueName, String msiPropertyName, SpecialFolderDotNet alt) { + reg = Optional.of(new RegValuePath(keyPath, valueName)); this.alt = Optional.of(alt); + this.msiPropertyName = Objects.requireNonNull(msiPropertyName); + } + + SpecialFolder(String msiPropertyName, SpecialFolderDotNet alt) { + reg = Optional.empty(); + this.alt = Optional.of(alt); + this.msiPropertyName = Objects.requireNonNull(msiPropertyName); + } + + static Optional findMsiProperty(String pathComponent, boolean allUsers) { + Objects.requireNonNull(pathComponent); + String regPath; + if (allUsers) { + regPath = SYSTEM_SHELL_FOLDERS_REGKEY; + } else { + regPath = USER_SHELL_FOLDERS_REGKEY; + } + return Stream.of(values()) + .filter(v -> v.msiPropertyName.equals(pathComponent)) + .filter(v -> { + return v.reg.map(r -> r.keyPath().equals(regPath)).orElse(true); + }) + .findFirst(); + } + + String getMsiPropertyName() { + return msiPropertyName; } Path getPath() { - return CACHE.computeIfAbsent(this, k -> reg.findValue().map(Path::of).orElseGet(() -> { + return CACHE.computeIfAbsent(this, k -> reg.flatMap(RegValuePath::findValue).map(Path::of).orElseGet(() -> { return alt.map(SpecialFolderDotNet::getPath).orElseThrow(() -> { return new NoSuchElementException(String.format("Failed to find path to %s folder", name())); }); })); } - private final RegValuePath reg; + private final Optional reg; private final Optional alt; + // One of "System Folder Properties" from https://learn.microsoft.com/en-us/windows/win32/msi/property-reference + private final String msiPropertyName; private static final Map CACHE = new ConcurrentHashMap<>(); } @@ -693,6 +648,63 @@ public class WindowsHelper { private static final ShortPathUtils INSTANCE = new ShortPathUtils(); } + + private static final class MsiDatabaseCache { + + Optional findProperty(Path msiPath, String propertyName) { + return ensureTables(msiPath, MsiDatabase.Table.FIND_PROPERTY_REQUIRED_TABLES).findProperty(propertyName); + } + + Collection listShortcuts(Path msiPath) { + return ensureTables(msiPath, MsiDatabase.Table.LIST_SHORTCUTS_REQUIRED_TABLES).listShortcuts(); + } + + MsiDatabase ensureTables(Path msiPath, Set tableNames) { + Objects.requireNonNull(msiPath); + try { + synchronized (items) { + var value = Optional.ofNullable(items.get(msiPath)).map(SoftReference::get).orElse(null); + if (value != null) { + var lastModifiedTime = Files.getLastModifiedTime(msiPath).toInstant(); + if (lastModifiedTime.isAfter(value.timestamp())) { + value = null; + } else { + tableNames = Comm.compare(value.db().tableNames(), tableNames).unique2(); + } + } + + if (!tableNames.isEmpty()) { + var idtOutputDir = TKit.createTempDirectory("msi-db"); + var db = MsiDatabase.load(msiPath, idtOutputDir, tableNames); + if (value != null) { + value = new MsiDatabaseWithTimestamp(db.append(value.db()), value.timestamp()); + } else { + value = new MsiDatabaseWithTimestamp(db, Files.getLastModifiedTime(msiPath).toInstant()); + } + items.put(msiPath, new SoftReference<>(value)); + } + + return value.db(); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private record MsiDatabaseWithTimestamp(MsiDatabase db, Instant timestamp) { + + MsiDatabaseWithTimestamp { + Objects.requireNonNull(db); + Objects.requireNonNull(timestamp); + } + } + + private final Map> items = new HashMap<>(); + + static final MsiDatabaseCache INSTANCE = new MsiDatabaseCache(); + } + + static final Set CRITICAL_RUNTIME_FILES = Set.of(Path.of( "bin\\server\\jvm.dll")); diff --git a/test/jdk/tools/jpackage/linux/UpgradeTest.java b/test/jdk/tools/jpackage/linux/UpgradeTest.java index fb399cec12b..bfbdcf3aa0c 100644 --- a/test/jdk/tools/jpackage/linux/UpgradeTest.java +++ b/test/jdk/tools/jpackage/linux/UpgradeTest.java @@ -60,16 +60,16 @@ public class UpgradeTest { var alA = createAdditionalLauncher("launcherA"); alA.applyTo(pkg); - createAdditionalLauncher("launcherB").addRawProperties(Map.entry( - "description", "Foo")).applyTo(pkg); + createAdditionalLauncher("launcherB").setProperty( + "description", "Foo").applyTo(pkg); var pkg2 = createPackageTest().addInitializer(cmd -> { cmd.addArguments("--app-version", "2.0"); }); alA.verifyRemovedInUpgrade(pkg2); - createAdditionalLauncher("launcherB").addRawProperties(Map.entry( - "description", "Bar")).applyTo(pkg2); + createAdditionalLauncher("launcherB").setProperty( + "description", "Bar").applyTo(pkg2); createAdditionalLauncher("launcherC").applyTo(pkg2); new PackageTest.Group(pkg, pkg2).run(); @@ -88,6 +88,6 @@ public class UpgradeTest { return new AdditionalLauncher(name).setIcon(GOLDEN_ICON); } - private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( + private static final Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( "resources", "icon" + TKit.ICON_SUFFIX)); } diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index f0aae601877..2c2f5c3bb0f 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -55,6 +55,25 @@ import jdk.jpackage.test.Annotations.Parameter; * @build jdk.jpackage.test.* * @build SigningPackageTest * @requires (jpackage.test.MacSignTests == "run") + * @requires (jpackage.test.SQETest != null) + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SigningPackageTest + * --jpt-space-subst=* + * --jpt-include=SigningPackageTest.test(true,*true,*true,*ASCII_INDEX) + * --jpt-before-run=SigningBase.verifySignTestEnvReady + */ + +/* + * @test + * @summary jpackage with --type pkg,dmg --mac-sign + * @library /test/jdk/tools/jpackage/helpers + * @library base + * @key jpackagePlatformPackage + * @build SigningBase + * @build jdk.jpackage.test.* + * @build SigningPackageTest + * @requires (jpackage.test.MacSignTests == "run") + * @requires (jpackage.test.SQETest == null) * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningPackageTest * --jpt-before-run=SigningBase.verifySignTestEnvReady diff --git a/test/jdk/tools/jpackage/resources/msi-export.js b/test/jdk/tools/jpackage/resources/msi-export.js new file mode 100644 index 00000000000..d639f19ca44 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/msi-export.js @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 readMsi(msiPath, callback) { + var installer = new ActiveXObject('WindowsInstaller.Installer') + var database = installer.OpenDatabase(msiPath, 0 /* msiOpenDatabaseModeReadOnly */) + + return callback(database) +} + + +function exportTables(db, outputDir, requestedTableNames) { + var tables = {} + + var view = db.OpenView("SELECT `Name` FROM _Tables") + view.Execute() + + try { + while (true) { + var record = view.Fetch() + if (!record) { + break + } + + var name = record.StringData(1) + + if (requestedTableNames.hasOwnProperty(name)) { + tables[name] = name + } + } + } finally { + view.Close() + } + + var fso = new ActiveXObject("Scripting.FileSystemObject") + for (var table in tables) { + var idtFileName = table + ".idt" + var idtFile = outputDir + "/" + idtFileName + if (fso.FileExists(idtFile)) { + WScript.Echo("Delete [" + idtFile + "]") + fso.DeleteFile(idtFile) + } + WScript.Echo("Export table [" + table + "] in [" + idtFile + "] file") + db.Export(table, fso.GetFolder(outputDir).Path, idtFileName) + } +} + + +(function () { + var msi = WScript.arguments(0) + var outputDir = WScript.arguments(1) + var tables = {} + for (var i = 0; i !== WScript.arguments.Count(); i++) { + tables[WScript.arguments(i)] = true + } + + readMsi(msi, function (db) { + exportTables(db, outputDir, tables) + }) +})() diff --git a/test/jdk/tools/jpackage/resources/query-msi-property.js b/test/jdk/tools/jpackage/resources/query-msi-property.js deleted file mode 100644 index d821f5a8a54..00000000000 --- a/test/jdk/tools/jpackage/resources/query-msi-property.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -function readMsi(msiPath, callback) { - var installer = new ActiveXObject('WindowsInstaller.Installer') - var database = installer.OpenDatabase(msiPath, 0 /* msiOpenDatabaseModeReadOnly */) - - return callback(database) -} - - -function queryAllProperties(db) { - var reply = {} - - var view = db.OpenView("SELECT `Property`, `Value` FROM Property") - view.Execute() - - try { - while(true) { - var record = view.Fetch() - if (!record) { - break - } - - var name = record.StringData(1) - var value = record.StringData(2) - - reply[name] = value - } - } finally { - view.Close() - } - - return reply -} - - -(function () { - var msi = WScript.arguments(0) - var propName = WScript.arguments(1) - - var props = readMsi(msi, queryAllProperties) - WScript.Echo(props[propName]) -})() diff --git a/test/jdk/tools/jpackage/share/AddLShortcutTest.java b/test/jdk/tools/jpackage/share/AddLShortcutTest.java index 6430a55d784..7d7d8b50c1d 100644 --- a/test/jdk/tools/jpackage/share/AddLShortcutTest.java +++ b/test/jdk/tools/jpackage/share/AddLShortcutTest.java @@ -21,13 +21,32 @@ * questions. */ -import java.nio.file.Path; +import java.io.IOException; import java.lang.invoke.MethodHandles; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.FileAssociations; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import jdk.internal.util.OperatingSystem; import jdk.jpackage.test.AdditionalLauncher; -import jdk.jpackage.test.TKit; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.FileAssociations; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.LauncherShortcut; +import jdk.jpackage.test.LauncherShortcut.InvokeShortcutSpec; +import jdk.jpackage.test.LauncherShortcut.StartupDirectory; +import jdk.jpackage.test.LauncherVerifier.Action; +import jdk.jpackage.test.LinuxHelper; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.RunnablePackageTest; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.WinShortcutVerifier; /** * Test --add-launcher parameter with shortcuts (platform permitting). @@ -44,9 +63,23 @@ import jdk.jpackage.test.Annotations.Test; * @key jpackagePlatformPackage * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* + * @requires (jpackage.test.SQETest != null) * @compile -Xlint:all -Werror AddLShortcutTest.java * @run main/othervm/timeout=540 -Xmx512m * jdk.jpackage.test.Main + * --jpt-run=AddLShortcutTest.test + */ + +/* + * @test + * @summary jpackage with --add-launcher + * @key jpackagePlatformPackage + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @requires (jpackage.test.SQETest == null) + * @compile -Xlint:all -Werror AddLShortcutTest.java + * @run main/othervm/timeout=1080 -Xmx512m + * jdk.jpackage.test.Main * --jpt-run=AddLShortcutTest */ @@ -107,6 +140,287 @@ public class AddLShortcutTest { packageTest.run(); } + @Test(ifNotOS = OperatingSystem.MACOS) + @ParameterSupplier(ifOS = OperatingSystem.LINUX, value = "testShortcutStartupDirectoryLinux") + @ParameterSupplier(ifOS = OperatingSystem.WINDOWS, value = "testShortcutStartupDirectoryWindows") + public void testStartupDirectory(LauncherShortcutStartupDirectoryConfig... cfgs) { + + var test = new PackageTest().addInitializer(cmd -> { + cmd.setArgumentValue("--name", "AddLShortcutDirTest"); + }).addInitializer(JPackageCommand::setFakeRuntime).addHelloAppInitializer(null); + + test.addInitializer(cfgs[0]::applyToMainLauncher); + for (var i = 1; i != cfgs.length; ++i) { + var al = new AdditionalLauncher("launcher-" + i); + cfgs[i].applyToAdditionalLauncher(al); + al.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test); + } + + test.run(RunnablePackageTest.Action.CREATE_AND_UNPACK); + } + + @Test(ifNotOS = OperatingSystem.MACOS) + @ParameterSupplier(ifOS = OperatingSystem.LINUX, value = "testShortcutStartupDirectoryLinux") + @ParameterSupplier(ifOS = OperatingSystem.WINDOWS, value = "testShortcutStartupDirectoryWindows") + public void testStartupDirectory2(LauncherShortcutStartupDirectoryConfig... cfgs) { + + // + // Launcher shortcuts in the predefined app image. + // + // Shortcut configuration for the main launcher is not supported when building an app image. + // However, shortcut configuration for additional launchers is supported. + // The test configures shortcuts for additional launchers in the app image building jpackage command + // and applies shortcut configuration to the main launcher in the native packaging jpackage command. + // + + Path[] predefinedAppImage = new Path[1]; + + new PackageTest().addRunOnceInitializer(() -> { + var cmd = JPackageCommand.helloAppImage() + .setArgumentValue("--name", "foo") + .setFakeRuntime(); + + for (var i = 1; i != cfgs.length; ++i) { + var al = new AdditionalLauncher("launcher-" + i); + cfgs[i].applyToAdditionalLauncher(al); + al.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(cmd); + } + + cmd.execute(); + + predefinedAppImage[0] = cmd.outputBundle(); + }).addInitializer(cmd -> { + cmd.removeArgumentWithValue("--input"); + cmd.setArgumentValue("--name", "AddLShortcutDir2Test"); + cmd.addArguments("--app-image", predefinedAppImage[0]); + cfgs[0].applyToMainLauncher(cmd); + }).run(RunnablePackageTest.Action.CREATE_AND_UNPACK); + } + + public static Collection testShortcutStartupDirectoryLinux() { + return testShortcutStartupDirectory(LauncherShortcut.LINUX_SHORTCUT); + } + + public static Collection testShortcutStartupDirectoryWindows() { + return testShortcutStartupDirectory(LauncherShortcut.WIN_DESKTOP_SHORTCUT, LauncherShortcut.WIN_START_MENU_SHORTCUT); + } + + @Test(ifNotOS = OperatingSystem.MACOS) + @Parameter(value = "DEFAULT") + public void testInvokeShortcuts(StartupDirectory startupDirectory) { + + var testApp = TKit.TEST_SRC_ROOT.resolve("apps/PrintEnv.java"); + + var name = "AddLShortcutRunTest"; + + var test = new PackageTest().addInitializer(cmd -> { + cmd.setArgumentValue("--name", name); + }).addInitializer(cmd -> { + cmd.addArguments("--arguments", "--print-workdir"); + }).addInitializer(JPackageCommand::ignoreFakeRuntime).addHelloAppInitializer(testApp + "*Hello"); + + var shortcutStartupDirectoryVerifier = new ShortcutStartupDirectoryVerifier(name, "a"); + + shortcutStartupDirectoryVerifier.applyTo(test, startupDirectory); + + test.addInstallVerifier(cmd -> { + if (!cmd.isPackageUnpacked("Not invoking launcher shortcuts")) { + Collection invokeShortcutSpecs; + if (TKit.isLinux()) { + invokeShortcutSpecs = LinuxHelper.getInvokeShortcutSpecs(cmd); + } else if (TKit.isWindows()) { + invokeShortcutSpecs = WinShortcutVerifier.getInvokeShortcutSpecs(cmd); + } else { + throw new UnsupportedOperationException(); + } + shortcutStartupDirectoryVerifier.verify(invokeShortcutSpecs); + } + }); + + test.run(); + } + + + private record ShortcutStartupDirectoryVerifier(String packageName, String launcherName) { + ShortcutStartupDirectoryVerifier { + Objects.requireNonNull(packageName); + Objects.requireNonNull(launcherName); + } + + void applyTo(PackageTest test, StartupDirectory startupDirectory) { + var al = new AdditionalLauncher(launcherName); + al.setShortcut(shortcut(), Objects.requireNonNull(startupDirectory)); + al.addJavaOptions(String.format("-Djpackage.test.appOutput=${%s}/%s", + outputDirVarName(), expectedOutputFilename())); + al.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test); + } + + void verify(Collection invokeShortcutSpecs) throws IOException { + + TKit.trace(String.format("Verify shortcut [%s]", launcherName)); + + var expectedOutputFile = Path.of(System.getenv(outputDirVarName())).resolve(expectedOutputFilename()); + + TKit.deleteIfExists(expectedOutputFile); + + var invokeShortcutSpec = invokeShortcutSpecs.stream().filter(v -> { + return launcherName.equals(v.launcherName()); + }).findAny().orElseThrow(); + + invokeShortcutSpec.execute(); + + // On Linux, "gtk-launch" is used to launch a .desktop file. It is async and there is no + // way to make it wait for exit of a process it triggers. + TKit.waitForFileCreated(expectedOutputFile, Duration.ofSeconds(10), Duration.ofSeconds(3)); + + TKit.assertFileExists(expectedOutputFile); + var actualStr = Files.readAllLines(expectedOutputFile).getFirst(); + + var outputPrefix = "$CD="; + + TKit.assertTrue(actualStr.startsWith(outputPrefix), "Check output starts with '" + outputPrefix+ "' string"); + + invokeShortcutSpec.expectedWorkDirectory().ifPresent(expectedWorkDirectory -> { + TKit.assertEquals( + expectedWorkDirectory, + Path.of(actualStr.substring(outputPrefix.length())), + String.format("Check work directory of %s of launcher [%s]", + invokeShortcutSpec.shortcut().propertyName(), + invokeShortcutSpec.launcherName())); + }); + } + + private String expectedOutputFilename() { + return String.format("%s-%s.out", packageName, launcherName); + } + + private String outputDirVarName() { + if (TKit.isLinux()) { + return "HOME"; + } else if (TKit.isWindows()) { + return "LOCALAPPDATA"; + } else { + throw new UnsupportedOperationException(); + } + } + + private LauncherShortcut shortcut() { + if (TKit.isLinux()) { + return LauncherShortcut.LINUX_SHORTCUT; + } else if (TKit.isWindows()) { + return LauncherShortcut.WIN_DESKTOP_SHORTCUT; + } else { + throw new UnsupportedOperationException(); + } + } + } + + + private static Collection testShortcutStartupDirectory(LauncherShortcut... shortcuts) { + List> items = new ArrayList<>(); + + for (var shortcut : shortcuts) { + List mainLauncherVariants = new ArrayList<>(); + for (var valueSetter : StartupDirectoryValueSetter.MAIN_LAUNCHER_VALUES) { + mainLauncherVariants.add(new LauncherShortcutStartupDirectoryConfig(shortcut, valueSetter)); + } + mainLauncherVariants.stream().map(List::of).forEach(items::add); + mainLauncherVariants.add(new LauncherShortcutStartupDirectoryConfig(shortcut)); + + List addLauncherVariants = new ArrayList<>(); + addLauncherVariants.add(new LauncherShortcutStartupDirectoryConfig(shortcut)); + for (var valueSetter : StartupDirectoryValueSetter.ADD_LAUNCHER_VALUES) { + addLauncherVariants.add(new LauncherShortcutStartupDirectoryConfig(shortcut, valueSetter)); + } + + for (var mainLauncherVariant : mainLauncherVariants) { + for (var addLauncherVariant : addLauncherVariants) { + if (mainLauncherVariant.valueSetter().isPresent() || addLauncherVariant.valueSetter().isPresent()) { + items.add(List.of(mainLauncherVariant, addLauncherVariant)); + } + } + } + } + + return items.stream().map(List::toArray).toList(); + } + + + private enum StartupDirectoryValueSetter { + DEFAULT(""), + TRUE("true"), + FALSE("false"), + ; + + StartupDirectoryValueSetter(String value) { + this.value = Objects.requireNonNull(value); + } + + void applyToMainLauncher(LauncherShortcut shortcut, JPackageCommand cmd) { + switch (this) { + case TRUE, FALSE -> { + throw new UnsupportedOperationException(); + } + case DEFAULT -> { + cmd.addArgument(shortcut.optionName()); + } + default -> { + cmd.addArguments(shortcut.optionName(), value); + } + } + } + + void applyToAdditionalLauncher(LauncherShortcut shortcut, AdditionalLauncher addLauncher) { + addLauncher.setProperty(shortcut.propertyName(), value); + } + + private final String value; + + static final List MAIN_LAUNCHER_VALUES = List.of( + StartupDirectoryValueSetter.DEFAULT + ); + + static final List ADD_LAUNCHER_VALUES = List.of( + StartupDirectoryValueSetter.TRUE, + StartupDirectoryValueSetter.FALSE + ); + } + + + record LauncherShortcutStartupDirectoryConfig(LauncherShortcut shortcut, Optional valueSetter) { + + LauncherShortcutStartupDirectoryConfig { + Objects.requireNonNull(shortcut); + Objects.requireNonNull(valueSetter); + } + + LauncherShortcutStartupDirectoryConfig(LauncherShortcut shortcut, StartupDirectoryValueSetter valueSetter) { + this(shortcut, Optional.of(valueSetter)); + } + + LauncherShortcutStartupDirectoryConfig(LauncherShortcut shortcut) { + this(shortcut, Optional.empty()); + } + + void applyToMainLauncher(JPackageCommand target) { + valueSetter.ifPresent(valueSetter -> { + valueSetter.applyToMainLauncher(shortcut, target); + }); + } + + void applyToAdditionalLauncher(AdditionalLauncher target) { + valueSetter.ifPresent(valueSetter -> { + valueSetter.applyToAdditionalLauncher(shortcut, target); + }); + } + + @Override + public String toString() { + return shortcut + "=" + valueSetter.map(Object::toString).orElse(""); + } + } + + private static final Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( "resources", "icon" + TKit.ICON_SUFFIX)); } diff --git a/test/jdk/tools/jpackage/share/AddLauncherTest.java b/test/jdk/tools/jpackage/share/AddLauncherTest.java index 5c21be71258..8d5f0de28f2 100644 --- a/test/jdk/tools/jpackage/share/AddLauncherTest.java +++ b/test/jdk/tools/jpackage/share/AddLauncherTest.java @@ -89,17 +89,17 @@ public class AddLauncherTest { new AdditionalLauncher("Baz2") .setDefaultArguments() - .addRawProperties(Map.entry("description", "Baz2 Description")) + .setProperty("description", "Baz2 Description") .applyTo(packageTest); new AdditionalLauncher("foo") .setDefaultArguments("yep!") - .addRawProperties(Map.entry("description", "foo Description")) + .setProperty("description", "foo Description") .applyTo(packageTest); new AdditionalLauncher("Bar") .setDefaultArguments("one", "two", "three") - .addRawProperties(Map.entry("description", "Bar Description")) + .setProperty("description", "Bar Description") .setIcon(GOLDEN_ICON) .applyTo(packageTest); @@ -194,8 +194,8 @@ public class AddLauncherTest { .toString(); new AdditionalLauncher("ModularAppLauncher") - .addRawProperties(Map.entry("module", expectedMod)) - .addRawProperties(Map.entry("main-jar", "")) + .setProperty("module", expectedMod) + .setProperty("main-jar", "") .applyTo(cmd); new AdditionalLauncher("NonModularAppLauncher") @@ -204,8 +204,8 @@ public class AddLauncherTest { .setPersistenceHandler((path, properties) -> TKit.createTextFile(path, properties.stream().map(entry -> String.join(" ", entry.getKey(), entry.getValue())))) - .addRawProperties(Map.entry("main-class", nonModularAppDesc.className())) - .addRawProperties(Map.entry("main-jar", nonModularAppDesc.jarFileName())) + .setProperty("main-class", nonModularAppDesc.className()) + .setProperty("main-jar", nonModularAppDesc.jarFileName()) .applyTo(cmd); cmd.executeAndAssertHelloAppImageCreated(); diff --git a/test/jdk/tools/jpackage/share/PerUserCfgTest.java b/test/jdk/tools/jpackage/share/PerUserCfgTest.java index 080df1f959d..d2f368cd824 100644 --- a/test/jdk/tools/jpackage/share/PerUserCfgTest.java +++ b/test/jdk/tools/jpackage/share/PerUserCfgTest.java @@ -27,13 +27,14 @@ import java.nio.file.Path; import java.util.List; import java.util.Objects; import java.util.Optional; -import jdk.jpackage.test.AdditionalLauncher; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.LauncherVerifier.Action; import jdk.jpackage.test.LinuxHelper; +import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; @@ -65,7 +66,7 @@ public class PerUserCfgTest { cfgCmd.execute(); - new PackageTest().configureHelloApp().addInstallVerifier(cmd -> { + new PackageTest().addHelloAppInitializer(null).addInstallVerifier(cmd -> { if (cmd.isPackageUnpacked("Not running per-user configuration tests")) { return; } @@ -144,10 +145,7 @@ public class PerUserCfgTest { } private static void addLauncher(JPackageCommand cmd, String name) { - new AdditionalLauncher(name) { - @Override - protected void verify(JPackageCommand cmd) {} - }.setDefaultArguments(name).applyTo(cmd); + new AdditionalLauncher(name).setDefaultArguments(name).withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(cmd); } private static Path getUserHomeDir() { diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index 2af6543c2db..f66f774b227 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -21,26 +21,28 @@ * questions. */ +import static jdk.internal.util.OperatingSystem.LINUX; +import static jdk.internal.util.OperatingSystem.MACOS; +import static jdk.jpackage.test.TKit.assertFalse; +import static jdk.jpackage.test.TKit.assertTrue; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashSet; -import java.util.Set; +import java.util.Objects; import java.util.function.Predicate; -import java.util.stream.Collectors; -import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.RunnablePackageTest.Action; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.internal.util.function.ThrowingSupplier; import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; +import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.LinuxHelper; -import static jdk.jpackage.test.TKit.assertTrue; -import static jdk.jpackage.test.TKit.assertFalse; -import static jdk.internal.util.OperatingSystem.LINUX; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; +import jdk.jpackage.test.TKit; /** * Test --runtime-image parameter. @@ -85,6 +87,11 @@ public class RuntimePackageTest { init().run(); } + @Test(ifOS = MACOS) + public static void testFromBundle() { + init(RuntimePackageTest::createInputRuntimeBundle).run(); + } + @Test(ifOS = LINUX) @Parameter("/usr") @Parameter("/usr/lib/Java") @@ -107,88 +114,116 @@ public class RuntimePackageTest { } private static PackageTest init() { + return init(RuntimePackageTest::createInputRuntimeImage); + } + + private static PackageTest init(ThrowingSupplier createRuntime) { + Objects.requireNonNull(createRuntime); + + final Path[] runtimeImageDir = new Path[1]; + return new PackageTest() + .addRunOnceInitializer(() -> { + runtimeImageDir[0] = createRuntime.get(); + }) .addInitializer(cmd -> { - final Path runtimeImageDir; - - if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { - runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; - } else { - runtimeImageDir = TKit.createTempDirectory("runtime").resolve("data"); - - new Executor() - .setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments( - "--output", runtimeImageDir.toString(), - "--add-modules", "java.desktop", - "--strip-debug", - "--no-header-files", - "--no-man-pages") - .execute(); - } - cmd.addArguments("--runtime-image", runtimeImageDir); + cmd.addArguments("--runtime-image", runtimeImageDir[0]); // Remove --input parameter from jpackage command line as we don't // create input directory in the test and jpackage fails // if --input references non existant directory. cmd.removeArgumentWithValue("--input"); }) .addInstallVerifier(cmd -> { - Set srcRuntime = listFiles(Path.of(cmd.getArgumentValue("--runtime-image"))); + var src = TKit.assertDirectoryContentRecursive(inputRuntimeDir(cmd)).items(); Path dest = cmd.appRuntimeDirectory(); if (TKit.isOSX()) { dest = dest.resolve("Contents/Home"); } - Set dstRuntime = listFiles(dest); - Set intersection = new HashSet<>(srcRuntime); - intersection.retainAll(dstRuntime); - - srcRuntime.removeAll(intersection); - dstRuntime.removeAll(intersection); - - assertFileListEmpty(srcRuntime, "Missing"); - assertFileListEmpty(dstRuntime, "Unexpected"); + TKit.assertDirectoryContentRecursive(dest).match(src); }) - .forTypes(PackageType.LINUX_DEB) - .addInstallVerifier(cmd -> { - String installDir = cmd.getArgumentValue("--install-dir", () -> "/opt"); - Path copyright = Path.of("/usr/share/doc", - LinuxHelper.getPackageName(cmd), "copyright"); - boolean withCopyright = LinuxHelper.getPackageFiles(cmd).anyMatch( - Predicate.isEqual(copyright)); - if (installDir.startsWith("/usr/") || installDir.equals("/usr")) { - assertTrue(withCopyright, String.format( - "Check the package delivers [%s] copyright file", - copyright)); - } else { - assertFalse(withCopyright, String.format( - "Check the package doesn't deliver [%s] copyright file", - copyright)); + .forTypes(PackageType.LINUX_DEB, test -> { + test.addInstallVerifier(cmd -> { + String installDir = cmd.getArgumentValue("--install-dir", () -> "/opt"); + Path copyright = Path.of("/usr/share/doc", + LinuxHelper.getPackageName(cmd), "copyright"); + boolean withCopyright = LinuxHelper.getPackageFiles(cmd).anyMatch( + Predicate.isEqual(copyright)); + if (installDir.startsWith("/usr/") || installDir.equals("/usr")) { + assertTrue(withCopyright, String.format( + "Check the package delivers [%s] copyright file", + copyright)); + } else { + assertFalse(withCopyright, String.format( + "Check the package doesn't deliver [%s] copyright file", + copyright)); + } + }); + }); + } + + private static Path inputRuntimeDir(JPackageCommand cmd) { + var path = Path.of(cmd.getArgumentValue("--runtime-image")); + if (TKit.isOSX()) { + var bundleHome = path.resolve("Contents/Home"); + if (Files.isDirectory(bundleHome)) { + return bundleHome; + } + } + return path; + } + + private static Path createInputRuntimeImage() throws IOException { + + final Path runtimeImageDir; + + if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { + runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; + } else { + runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); + + new Executor().setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", runtimeImageDir.toString(), + "--add-modules", "java.desktop", + "--strip-debug", + "--no-header-files", + "--no-man-pages") + .execute(); + } + + return runtimeImageDir; + } + + private static Path createInputRuntimeBundle() throws IOException { + + final var runtimeImage = createInputRuntimeImage(); + + final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); + + final var unpackadeRuntimeBundleDir = runtimeBundleWorkDir.resolve("unpacked"); + + var cmd = new JPackageCommand() + .useToolProvider(true) + .ignoreDefaultRuntime(true) + .dumpOutput(true) + .setPackageType(PackageType.MAC_DMG) + .setArgumentValue("--name", "foo") + .addArguments("--runtime-image", runtimeImage) + .addArguments("--dest", runtimeBundleWorkDir); + + cmd.execute(); + + MacHelper.withExplodedDmg(cmd, dmgImage -> { + if (dmgImage.endsWith(cmd.appInstallationDirectory().getFileName())) { + Executor.of("cp", "-R") + .addArgument(dmgImage) + .addArgument(unpackadeRuntimeBundleDir) + .execute(0); } }); - } - private static Set listFiles(Path root) throws IOException { - try (var files = Files.walk(root)) { - // Ignore files created by system prefs if any. - final Path prefsDir = Path.of(".systemPrefs"); - return files.map(root::relativize) - .filter(x -> !x.startsWith(prefsDir)) - .filter(x -> !x.endsWith(".DS_Store")) - .collect(Collectors.toSet()); - } - } - - private static void assertFileListEmpty(Set paths, String msg) { - TKit.assertTrue(paths.isEmpty(), String.format( - "Check there are no %s files in installed image", - msg.toLowerCase()), () -> { - String msg2 = String.format("%s %d files", msg, paths.size()); - TKit.trace(msg2 + ":"); - paths.stream().map(Path::toString).sorted().forEachOrdered( - TKit::trace); - TKit.trace("Done"); - }); + return unpackadeRuntimeBundleDir; } } diff --git a/test/jdk/tools/jpackage/windows/WinLongVersionTest.java b/test/jdk/tools/jpackage/windows/WinLongVersionTest.java index d70cda7b264..586b69c1913 100644 --- a/test/jdk/tools/jpackage/windows/WinLongVersionTest.java +++ b/test/jdk/tools/jpackage/windows/WinLongVersionTest.java @@ -126,12 +126,12 @@ public class WinLongVersionTest { Action ended 12:08:38: FindRelatedProducts. Return value 1. ... Action start 12:08:38: JpFindRelatedProducts. - Java [12:08:38.180 libwixhelper.cpp:120 (FindRelatedProductsEx)] TRACE: Entering FindRelatedProductsEx - Java [12:08:38.185 libwixhelper.cpp:85 (`anonymous-namespace'::findInstalledPackages)] TRACE: Found {D88EEA02-56CC-34AD-8216-C2CC244FA898} product + Java [12:08:38.180 libmsica.cpp:120 (FindRelatedProductsEx)] TRACE: Entering FindRelatedProductsEx + Java [12:08:38.185 libmsica.cpp:85 (`anonymous-namespace'::findInstalledPackages)] TRACE: Found {D88EEA02-56CC-34AD-8216-C2CC244FA898} product Java [12:08:38.187 MsiCA.cpp:61 (msi::CAImpl::removeProperty)] TRACE: Removing MSI property 'JP_UPGRADABLE_FOUND' Java [12:08:38.187 MsiCA.cpp:61 (msi::CAImpl::removeProperty)] TRACE: Removing MSI property 'MIGRATE' Java [12:08:38.189 MsiCA.cpp:61 (msi::CAImpl::removeProperty)] TRACE: Removing MSI property 'JP_DOWNGRADABLE_FOUND' - Java [12:08:38.190 libwixhelper.cpp:0 (FindRelatedProductsEx)] TRACE: Exiting FindRelatedProductsEx (entered at libwixhelper.cpp:120) + Java [12:08:38.190 libmsica.cpp:0 (FindRelatedProductsEx)] TRACE: Exiting FindRelatedProductsEx (entered at libmsica.cpp:120) Action ended 12:08:38: JpFindRelatedProducts. Return value 1. */ PackageTest test2 = init.get().addInstallVerifier(cmd -> { diff --git a/test/langtools/jdk/javadoc/doclet/testInheritDocWithinInappropriateTag/TestInheritDocWithinInappropriateTag.java b/test/langtools/jdk/javadoc/doclet/testInheritDocWithinInappropriateTag/TestInheritDocWithinInappropriateTag.java index 253d0b6b232..394b565d245 100644 --- a/test/langtools/jdk/javadoc/doclet/testInheritDocWithinInappropriateTag/TestInheritDocWithinInappropriateTag.java +++ b/test/langtools/jdk/javadoc/doclet/testInheritDocWithinInappropriateTag/TestInheritDocWithinInappropriateTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8284299 8287379 8298525 6934301 + * @bug 8284299 8287379 8298525 6934301 8361316 * @library /tools/lib ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build toolbox.ToolBox javadoc.tester.* @@ -198,22 +198,22 @@ public class TestInheritDocWithinInappropriateTag extends JavadocTester { new OutputChecker(Output.OUT).setExpectOrdered(false).check( """ B.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. /** {@inheritDoc} */ ^ - """, + """, """ D.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. /** {@inheritDoc} */ ^ - """, + """, """ F.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. /** {@inheritDoc} */ ^ - """); + """); } @Test @@ -256,22 +256,22 @@ public class TestInheritDocWithinInappropriateTag extends JavadocTester { new OutputChecker(Output.OUT).setExpectOrdered(false).check( """ B.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. /** @param {@inheritDoc} */ ^ - """, + """, """ D.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. /** @param {@inheritDoc} */ ^ - """, + """, """ F.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. /** @param {@inheritDoc} */ ^ - """); + """); } @Test @@ -313,15 +313,84 @@ public class TestInheritDocWithinInappropriateTag extends JavadocTester { new OutputChecker(Output.OUT).setExpectOrdered(false).check( """ overview.html:6: warning: Tag @inheritDoc cannot be used in overview documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. {@inheritDoc} ^ """, """ example.html:6: warning: Tag @inheritDoc cannot be used in overview documentation.\ - It can only be used in the following types of documentation: method. + It can only be used in the following types of documentation: method. {@inheritDoc} ^ """); } + + @Test + public void testUnsupportedElement(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + """ + /** + * A simple class + */ + public class A { + /** + * A constant {@inheritDoc} ... + */ + public final static int C = 10; + + /** + * A constructor {@inheritDoc} ... + * @param p a parameter {@inheritDoc} ... + * @throws Exception an exception {@inheritDoc} ... + */ + public A(int p) throws Exception { + } + } + """); + javadoc("-Xdoclint:none", + "--no-platform-links", + "-d", base.resolve("out").toString(), + src.resolve("A.java").toString()); + checkExit(Exit.OK); + checkOutput(Output.OUT, true, + """ + A.java:6: warning: Tag @inheritDoc cannot be used in field documentation. \ + It can only be used in the following types of documentation: method. + * A constant {@inheritDoc} ... + ^ + """, + """ + A.java:11: warning: Tag @inheritDoc cannot be used in constructor documentation. \ + It can only be used in the following types of documentation: method. + * A constructor {@inheritDoc} ... + ^ + """, + """ + A.java:12: warning: Tag @inheritDoc cannot be used in constructor documentation. \ + It can only be used in the following types of documentation: method. + * @param p a parameter {@inheritDoc} ... + ^ + """, + """ + A.java:13: warning: Tag @inheritDoc cannot be used in constructor documentation. \ + It can only be used in the following types of documentation: method. + * @throws Exception an exception {@inheritDoc} ... + ^ + """); + + checkOutput("A.html", true, + """ +
    public static final \ + int C
    +
    A constant ...
    """, + """ +
    A constructor ...
    +
    +
    Parameters:
    +
    p - a parameter ...
    +
    Throws:
    +
    java.lang.Exception - an exception ...
    +
    """); + } } diff --git a/test/langtools/tools/javac/analyzer/Diamond.java b/test/langtools/tools/javac/analyzer/Diamond.java index cee8d096cbd..5f77745beaf 100644 --- a/test/langtools/tools/javac/analyzer/Diamond.java +++ b/test/langtools/tools/javac/analyzer/Diamond.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8349132 + * @bug 8349132 8364987 * @summary Check behavior of the diamond analyzer * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -139,4 +139,43 @@ public class Diamond extends TestRunner { } } + @Test //JDK-8364987: + public void testNoCrashErroneousTypes(Path base) throws Exception { + Path current = base.resolve("."); + Path src = current.resolve("src"); + Path classes = current.resolve("classes"); + tb.writeJavaFiles(src, + """ + public class Test { + void t() { + L l = new L(); + } + static class L { } + } + """); + + Files.createDirectories(classes); + + var out = new JavacTask(tb) + .options("-XDfind=diamond", + "-XDshould-stop.at=FLOW", + "-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + var expectedOut = List.of( + "Test.java:3:23: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: Test.L, Test.L)", + "1 error" + ); + + if (!Objects.equals(expectedOut, out)) { + throw new AssertionError("Incorrect Output, expected: " + expectedOut + + ", actual: " + out); + + } + } + } diff --git a/test/langtools/tools/javac/api/file/SJFM_TestBase.java b/test/langtools/tools/javac/api/file/SJFM_TestBase.java index fb3860edb75..52fd36afc5d 100644 --- a/test/langtools/tools/javac/api/file/SJFM_TestBase.java +++ b/test/langtools/tools/javac/api/file/SJFM_TestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -160,7 +161,7 @@ class SJFM_TestBase { List getTestZipPaths() throws IOException { if (zipfs == null) { Path testZip = createSourceZip(); - zipfs = FileSystems.newFileSystem(testZip); + zipfs = FileSystems.newFileSystem(testZip, Map.of("accessMode", "readOnly")); closeables.add(zipfs); zipPaths = Files.list(zipfs.getRootDirectories().iterator().next()) .filter(p -> p.getFileName().toString().endsWith(".java")) diff --git a/test/langtools/tools/javac/launcher/SourceLauncherStackTraceTest.java b/test/langtools/tools/javac/launcher/SourceLauncherStackTraceTest.java new file mode 100644 index 00000000000..7bcb86bde46 --- /dev/null +++ b/test/langtools/tools/javac/launcher/SourceLauncherStackTraceTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8362237 + * @summary Test source launcher with specific VM behaviors + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.launcher + * jdk.compiler/com.sun.tools.javac.main + * java.base/jdk.internal.module + * @build toolbox.JavaTask toolbox.JavacTask toolbox.TestRunner toolbox.ToolBox + * @run main/othervm -XX:-StackTraceInThrowable SourceLauncherStackTraceTest + */ + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import toolbox.TestRunner; + +/// SourceLauncherTest runs the source launcher in the same VM, so we must +/// use another test to run specific tests with specific VM flags +public class SourceLauncherStackTraceTest extends TestRunner { + + // Inheritance will shadow all parent tests + SourceLauncherTest parent = new SourceLauncherTest(); + + SourceLauncherStackTraceTest() { + super(System.err); + } + + public static void main(String... args) throws Exception { + SourceLauncherStackTraceTest t = new SourceLauncherStackTraceTest(); + t.runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + /* + * Tests in which main throws an exception without a stacktrace. + */ + @Test + public void testTargetException2(Path base) throws IOException { + parent.tb.writeJavaFiles(base, """ + public class TestLauncher { + public static TestLauncher test() { + throw new RuntimeException("No trace"); + } + + public static void main(String[] args) { + // This will throw a RuntimeException without + // a stack trace due to VM options + test(); + } + } + """); + Path file = base.resolve("TestLauncher.java"); + SourceLauncherTest.Result r = parent.run(file, List.of(), List.of("3")); + parent.checkEmpty("stdout", r.stdOut()); + parent.checkEmpty("stderr", r.stdErr()); + parent.checkTrace("exception", r.exception(), "java.lang.RuntimeException: No trace"); + } +} diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index 017abb74456..bb5ba7e133f 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -76,7 +76,7 @@ public class SourceLauncherTest extends TestRunner { System.err.println("version: " + thisVersion); } - private final ToolBox tb; + final ToolBox tb; private static final String thisVersion = System.getProperty("java.specification.version"); /* diff --git a/test/langtools/tools/javac/platform/VerifyCTSymClassFiles.java b/test/langtools/tools/javac/platform/VerifyCTSymClassFiles.java index 3c563904b16..aeafb1ee08b 100644 --- a/test/langtools/tools/javac/platform/VerifyCTSymClassFiles.java +++ b/test/langtools/tools/javac/platform/VerifyCTSymClassFiles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,10 +23,11 @@ /** * @test - * @bug 8331027 + * @bug 8331027 8356645 * @summary Verify classfile inside ct.sym * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.file * jdk.compiler/com.sun.tools.javac.main * jdk.compiler/com.sun.tools.javac.platform * jdk.compiler/com.sun.tools.javac.util:+open @@ -44,6 +45,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map; public class VerifyCTSymClassFiles { @@ -60,7 +62,13 @@ public class VerifyCTSymClassFiles { //no ct.sym, nothing to check: return ; } - try (FileSystem fs = FileSystems.newFileSystem(ctSym)) { + // Expected to always be a ZIP filesystem. + Map env = Map.of("accessMode", "readOnly"); + try (FileSystem fs = FileSystems.newFileSystem(ctSym, env)) { + // Check that the file system is read only (not true if not a zip file system). + if (!fs.isReadOnly()) { + throw new AssertionError("Expected read-only file system"); + } Files.walk(fs.getRootDirectories().iterator().next()) .filter(p -> Files.isRegularFile(p)) .forEach(p -> checkClassFile(p)); diff --git a/test/lib/jdk/test/lib/os/linux/HugePageConfiguration.java b/test/lib/jdk/test/lib/os/linux/HugePageConfiguration.java index 0873cb2b5d0..d95f20d27fd 100644 --- a/test/lib/jdk/test/lib/os/linux/HugePageConfiguration.java +++ b/test/lib/jdk/test/lib/os/linux/HugePageConfiguration.java @@ -64,6 +64,7 @@ public class HugePageConfiguration { Set _explicitHugePageConfigurations; long _explicitDefaultHugePageSize = -1; + long _explicitAvailableHugePageNumber = -1; public enum THPMode {always, never, madvise} THPMode _thpMode; @@ -80,6 +81,10 @@ public class HugePageConfiguration { return _explicitDefaultHugePageSize; } + public long getExplicitAvailableHugePageNumber() { + return _explicitAvailableHugePageNumber; + } + public THPMode getThpMode() { return _thpMode; } @@ -116,9 +121,10 @@ public class HugePageConfiguration { return _explicitDefaultHugePageSize > 0 && _explicitHugePageConfigurations.size() > 0; } - public HugePageConfiguration(Set explicitHugePageConfigurations, long explicitDefaultHugePageSize, THPMode _thpMode, long _thpPageSize, ShmemTHPMode _shmemThpMode) { + public HugePageConfiguration(Set explicitHugePageConfigurations, long explicitDefaultHugePageSize, long explicitAvailableHugePageNumber, THPMode _thpMode, long _thpPageSize, ShmemTHPMode _shmemThpMode) { this._explicitHugePageConfigurations = explicitHugePageConfigurations; this._explicitDefaultHugePageSize = explicitDefaultHugePageSize; + this._explicitAvailableHugePageNumber = explicitAvailableHugePageNumber; this._thpMode = _thpMode; this._thpPageSize = _thpPageSize; this._shmemThpMode = _shmemThpMode; @@ -129,6 +135,7 @@ public class HugePageConfiguration { return "Configuration{" + "_explicitHugePageConfigurations=" + _explicitHugePageConfigurations + ", _explicitDefaultHugePageSize=" + _explicitDefaultHugePageSize + + ", _explicitAvailableHugePageNumber=" + _explicitAvailableHugePageNumber + ", _thpMode=" + _thpMode + ", _thpPageSize=" + _thpPageSize + ", _shmemThpMode=" + _shmemThpMode + @@ -138,6 +145,7 @@ public class HugePageConfiguration { @Override public int hashCode() { return Objects.hash(_explicitDefaultHugePageSize, + _explicitAvailableHugePageNumber, _thpPageSize, _explicitHugePageConfigurations, _thpMode, @@ -149,6 +157,7 @@ public class HugePageConfiguration { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HugePageConfiguration that = (HugePageConfiguration) o; + // _explicitAvailableHugePageNumber is not compared here, because there is no direct counterpart on the JVM-side log. return _explicitDefaultHugePageSize == that._explicitDefaultHugePageSize && _thpPageSize == that._thpPageSize && Objects.equals(_explicitHugePageConfigurations, that._explicitHugePageConfigurations) && _thpMode == that._thpMode && _shmemThpMode == that._shmemThpMode; @@ -169,6 +178,21 @@ public class HugePageConfiguration { return 0; } + private static long readAvailableHugePageNumberFromOS() { + Pattern pat = Pattern.compile("HugePages_Free: *(\\d+)$"); + try (Scanner scanner = new Scanner(new File("/proc/meminfo"))) { + while (scanner.hasNextLine()) { + Matcher mat = pat.matcher(scanner.nextLine()); + if (mat.matches()) { + return Long.parseLong(mat.group(1)); + } + } + } catch (FileNotFoundException e) { + System.out.println("Could not open /proc/meminfo"); + } + return 0; + } + private static Set readSupportedHugePagesFromOS() throws IOException { TreeSet hugePageConfigs = new TreeSet<>(); Pattern pat = Pattern.compile("hugepages-(\\d+)kB"); @@ -263,6 +287,7 @@ public class HugePageConfiguration { public static HugePageConfiguration readFromOS() throws IOException { return new HugePageConfiguration(readSupportedHugePagesFromOS(), readDefaultHugePageSizeFromOS(), + readAvailableHugePageNumberFromOS(), readTHPModeFromOS(), readTHPPageSizeFromOS(), readShmemTHPModeFromOS()); @@ -333,7 +358,7 @@ public class HugePageConfiguration { } } - return new HugePageConfiguration(explicitHugePageConfigs, defaultHugepageSize, thpMode, thpPageSize, shmemThpMode); + return new HugePageConfiguration(explicitHugePageConfigs, defaultHugepageSize, -1, thpMode, thpPageSize, shmemThpMode); } } diff --git a/test/lib/jdk/test/lib/security/CertificateBuilder.java b/test/lib/jdk/test/lib/security/CertificateBuilder.java index 60358c9a4ea..857d585f029 100644 --- a/test/lib/jdk/test/lib/security/CertificateBuilder.java +++ b/test/lib/jdk/test/lib/security/CertificateBuilder.java @@ -43,6 +43,7 @@ import sun.security.x509.AccessDescription; import sun.security.x509.AlgorithmId; import sun.security.x509.AuthorityInfoAccessExtension; import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.IPAddressName; import sun.security.x509.SubjectKeyIdentifierExtension; import sun.security.x509.BasicConstraintsExtension; import sun.security.x509.CertificateSerialNumber; @@ -233,6 +234,26 @@ public class CertificateBuilder { return this; } + /** + * Helper method to add IPAddress types for the SAN extension + * + * @param ipAddresses A {@code List} of names to add as IPAddress + * types + * @throws IOException if an encoding error occurs. + */ + public CertificateBuilder addSubjectAltNameIPExt(List ipAddresses) + throws IOException { + if (!ipAddresses.isEmpty()) { + GeneralNames gNames = new GeneralNames(); + for (String name : ipAddresses) { + gNames.add(new GeneralName(new IPAddressName(name))); + } + addExtension(new SubjectAlternativeNameExtension(false, + gNames)); + } + return this; + } + /** * Helper method to add one or more OCSP URIs to the Authority Info Access * certificate extension. Location strings can be in two forms: diff --git a/test/lib/jdk/test/lib/util/FileUtils.java b/test/lib/jdk/test/lib/util/FileUtils.java index 8b99d1e9d54..3e537835c0b 100644 --- a/test/lib/jdk/test/lib/util/FileUtils.java +++ b/test/lib/jdk/test/lib/util/FileUtils.java @@ -24,6 +24,7 @@ package jdk.test.lib.util; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; @@ -33,6 +34,7 @@ import java.lang.management.ManagementFactory; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.FileVisitResult; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; @@ -64,6 +66,14 @@ public final class FileUtils { private static final int MAX_RETRY_DELETE_TIMES = IS_WINDOWS ? 15 : 0; private static volatile boolean nativeLibLoaded; + @SuppressWarnings("restricted") + private static void loadNativeLib() { + if (!nativeLibLoaded) { + System.loadLibrary("FileUtils"); + nativeLibLoaded = true; + } + } + /** * Deletes a file, retrying if necessary. * @@ -392,14 +402,10 @@ public final class FileUtils { } // Return the current process handle count - @SuppressWarnings("restricted") public static long getProcessHandleCount() { if (IS_WINDOWS) { - if (!nativeLibLoaded) { - System.loadLibrary("FileUtils"); - nativeLibLoaded = true; - } - return getWinProcessHandleCount(); + loadNativeLib(); + return getWinProcessHandleCount0(); } else { return ((UnixOperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean()).getOpenFileDescriptorCount(); } @@ -443,7 +449,23 @@ public final class FileUtils { Files.write(path, lines); } - private static native long getWinProcessHandleCount(); + // Create a directory junction with the specified target + public static boolean createWinDirectoryJunction(Path junction, Path target) + throws IOException + { + assert IS_WINDOWS; + + // Convert "target" to its real path + target = target.toRealPath(); + + // Create a directory junction + loadNativeLib(); + return createWinDirectoryJunction0(junction.toString(), target.toString()); + } + + private static native long getWinProcessHandleCount0(); + private static native boolean createWinDirectoryJunction0(String junction, + String target) throws IOException; // Possible command locations and arguments static String[][] lsCommands = new String[][] { diff --git a/test/lib/jdk/test/lib/util/libFileUtils.c b/test/lib/jdk/test/lib/util/libFileUtils.c index 1af90afff49..d0da04f9fb1 100644 --- a/test/lib/jdk/test/lib/util/libFileUtils.c +++ b/test/lib/jdk/test/lib/util/libFileUtils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,9 +27,49 @@ #ifdef _WIN32 #include "jni.h" +#include "jni_util.h" +#include #include +#include +#include +#include +#include +#include -JNIEXPORT jlong JNICALL Java_jdk_test_lib_util_FileUtils_getWinProcessHandleCount(JNIEnv *env) +// Based on Microsoft documentation +#define MAX_REPARSE_BUFFER_SIZE 16384 + +// Unavailable in standard header files: +// copied from Microsoft documentation +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, * PREPARSE_DATA_BUFFER; + +JNIEXPORT jlong JNICALL +Java_jdk_test_lib_util_FileUtils_getWinProcessHandleCount0 + (JNIEnv* env) { DWORD handleCount; HANDLE handle = GetCurrentProcess(); @@ -40,4 +80,118 @@ JNIEXPORT jlong JNICALL Java_jdk_test_lib_util_FileUtils_getWinProcessHandleCoun } } +void throwIOExceptionWithLastError(JNIEnv* env) { +#define BUFSIZE 256 + DWORD errval; + WCHAR buf[BUFSIZE]; + + if ((errval = GetLastError()) != 0) { + jsize n = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errval, 0, buf, BUFSIZE, NULL); + + jclass ioExceptionClass = (*env)->FindClass(env, "java/io/IOException"); + (*env)->ThrowNew(env, ioExceptionClass, (const char*) buf); + } +} + +JNIEXPORT jboolean JNICALL +Java_jdk_test_lib_util_FileUtils_createWinDirectoryJunction0 + (JNIEnv* env, jclass unused, jstring sjunction, jstring starget) +{ + BOOL error = FALSE; + + const jshort bpc = sizeof(wchar_t); // bytes per character + HANDLE hJunction = INVALID_HANDLE_VALUE; + + const jchar* junction = (*env)->GetStringChars(env, sjunction, NULL); + const jchar* target = (*env)->GetStringChars(env, starget, NULL); + if (junction == NULL || target == NULL) { + jclass npeClass = (*env)->FindClass(env, "java/lang/NullPointerException"); + (*env)->ThrowNew(env, npeClass, NULL); + error = TRUE; + } + + USHORT wlen = (USHORT)0; + USHORT blen = (USHORT)0; + void* lpInBuffer = NULL; + if (!error) { + wlen = (USHORT)wcslen(target); + blen = (USHORT)(wlen * sizeof(wchar_t)); + lpInBuffer = calloc(MAX_REPARSE_BUFFER_SIZE, sizeof(char)); + if (lpInBuffer == NULL) { + jclass oomeClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + (*env)->ThrowNew(env, oomeClass, NULL); + error = TRUE; + } + } + + if (!error) { + if (CreateDirectoryW(junction, NULL) == 0) { + throwIOExceptionWithLastError(env); + error = TRUE; + } + } + + if (!error) { + hJunction = CreateFileW(junction, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT + | FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hJunction == INVALID_HANDLE_VALUE) { + throwIOExceptionWithLastError(env); + error = TRUE; + } + } + + if (!error) { + PREPARSE_DATA_BUFFER reparseBuffer = (PREPARSE_DATA_BUFFER)lpInBuffer; + reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + reparseBuffer->Reserved = 0; + WCHAR* prefix = L"\\??\\"; + USHORT prefixLength = (USHORT)(bpc * wcslen(prefix)); + reparseBuffer->MountPointReparseBuffer.SubstituteNameOffset = 0; + reparseBuffer->MountPointReparseBuffer.SubstituteNameLength = + prefixLength + blen; + reparseBuffer->MountPointReparseBuffer.PrintNameOffset = + prefixLength + blen + sizeof(WCHAR); + reparseBuffer->MountPointReparseBuffer.PrintNameLength = blen; + memcpy(&reparseBuffer->MountPointReparseBuffer.PathBuffer, + prefix, prefixLength); + memcpy(&reparseBuffer->MountPointReparseBuffer.PathBuffer[prefixLength/bpc], + target, blen); + memcpy(&reparseBuffer->MountPointReparseBuffer.PathBuffer[prefixLength/bpc + blen/bpc + 1], + target, blen); + reparseBuffer->ReparseDataLength = + (USHORT)(sizeof(reparseBuffer->MountPointReparseBuffer) + + prefixLength + bpc*blen + bpc); + DWORD nInBufferSize = FIELD_OFFSET(REPARSE_DATA_BUFFER, + MountPointReparseBuffer) + reparseBuffer->ReparseDataLength; + BOOL result = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, + lpInBuffer, nInBufferSize, + NULL, 0, NULL, NULL); + if (result == 0) { + throwIOExceptionWithLastError(env); + error = TRUE; + } + } + + if (junction != NULL) { + (*env)->ReleaseStringChars(env, sjunction, junction); + if (target != NULL) { + (*env)->ReleaseStringChars(env, starget, target); + if (lpInBuffer != NULL) { + free(lpInBuffer); + if (hJunction != INVALID_HANDLE_VALUE) { + // Ignore any error in CloseHandle + CloseHandle(hJunction); + } + } + } + } + + return error ? JNI_FALSE : JNI_TRUE; +} + #endif /* _WIN32 */ diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index f3d9ba7b6e9..ce8b61b6393 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -851,4 +851,7 @@ public class WhiteBox { public native long rss(); public native boolean isStatic(); + + // Force a controlled crash (debug builds only) + public native void controlledCrash(int how); } diff --git a/test/micro/org/openjdk/bench/java/lang/SinhPerf.java b/test/micro/org/openjdk/bench/java/lang/SinhPerf.java new file mode 100644 index 00000000000..d98830b7ea5 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/SinhPerf.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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; + +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Random; + +public class SinhPerf { + + @Warmup(iterations = 3, time = 5, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.MILLISECONDS) + @Fork(2) + @BenchmarkMode(Mode.Throughput) + @State(Scope.Thread) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public static class SinhPerfRanges { + public static int sinhInputCount = 2048; + + @Param({"0", "1", "2", "3", "4"}) + public int sinhRangeIndex; + + public double [] sinhPosRandInputs; + public double [] sinhNegRandInputs; + public int sinhInputIndex = 0; + public double sinhRangeInputs[][] = { {0.0, 0x1.0P-28}, + {0x1.0P-28, 22.0}, + {22.0, Math.log(Double.MAX_VALUE)}, + {Math.log(Double.MAX_VALUE), Double.longBitsToDouble(0x408633CE8FB9F87DL)}, + {Double.longBitsToDouble(0x408633CE8FB9F87DL), Double.MAX_VALUE} }; + + @Setup + public void setupValues() { + Random random = new Random(1023); + + // Fill the positive and negative sinh vectors with random values + sinhPosRandInputs = new double[sinhInputCount]; + sinhNegRandInputs = new double[sinhInputCount]; + + for (int i = 0; i < sinhInputCount; i++) { + double sinhLowerBound = sinhRangeInputs[sinhRangeIndex][0]; + double sinhUpperBound = sinhRangeInputs[sinhRangeIndex][1]; + sinhPosRandInputs[i] = random.nextDouble(sinhLowerBound, sinhUpperBound); + sinhNegRandInputs[i] = random.nextDouble(-sinhUpperBound, -sinhLowerBound); + } + } + + @Benchmark + @OperationsPerInvocation(2048) + public double sinhPosRangeDouble() { + double res = 0.0; + for (int i = 0; i < sinhInputCount; i++) { + res += Math.sinh(sinhPosRandInputs[i]); + } + return res; + } + + @Benchmark + @OperationsPerInvocation(2048) + public double sinhNegRangeDouble() { + double res = 0.0; + for (int i = 0; i < sinhInputCount; i++) { + res += Math.sinh(sinhNegRandInputs[i]); + } + return res; + } + } + + @Warmup(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS) + @Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS) + @Fork(2) + @BenchmarkMode(Mode.Throughput) + @State(Scope.Thread) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public static class SinhPerfConstant { + public static final double constDoubleTiny = 0x1.0P-30; + public static final double constDoubleSmall = 0x1.0P-27; + public static final double constDouble21 = 21.0; + public static final double constDouble23 = 23.0; + public static final double constDoubleLarge = Math.log(Double.MAX_VALUE) + 0.5; + public static final double constDoubleOverflow = 0x1.0P10; + + @Benchmark + public double sinhConstDoubleTiny() { + return Math.sinh(constDoubleTiny); + } + + @Benchmark + public double sinhConstDoubleSmall() { + return Math.sinh(constDoubleSmall); + } + + @Benchmark + public double sinhConstDouble21() { + return Math.sinh(constDouble21); + } + + @Benchmark + public double sinhConstDouble23() { + return Math.sinh(constDouble23); + } + + @Benchmark + public double sinhConstDoubleLarge() { + return Math.sinh(constDoubleLarge); + } + + @Benchmark + public double sinhConstDoubleOverflow() { + return Math.sinh(constDoubleOverflow); + } + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(SinhPerfRanges.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + + opt = new OptionsBuilder() + .include(SinhPerfConstant.class.getSimpleName()) + .build(); + + new Runner(opt).run(); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java index 4ef60d88fbf..bf3e5f7a06b 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java +++ b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -50,6 +51,7 @@ public class StringBuilders { private String[] str16p8p7; private String[] str3p9p8; private String[] str22p40p31; + private char[][] charArray22p40p31; private StringBuilder sbLatin1; private StringBuilder sbLatin2; private StringBuilder sbUtf16; @@ -63,10 +65,15 @@ public class StringBuilders { "advise", "you", "to", "drive", "at", "top", "speed", "it'll", "be", "a", "god", "damn", "miracle", "if", "we", "can", "get", "there", "before", "you", "turn", "into", "a", "wild", "animal."}; + str3p4p2 = new String[]{"123", "1234", "12"}; str16p8p7 = new String[]{"1234567890123456", "12345678", "1234567"}; str3p9p8 = new String[]{"123", "123456789", "12345678"}; str22p40p31 = new String[]{"1234567890123456789012", "1234567890123456789012345678901234567890", "1234567890123456789012345678901"}; + charArray22p40p31 = new char[str22p40p31.length][]; + for (int i = 0; i < str22p40p31.length; i++) { + charArray22p40p31[i] = str22p40p31[i].toCharArray(); + } sbLatin1 = new StringBuilder("Latin1 string"); sbLatin2 = new StringBuilder("Latin1 string"); sbUtf16 = new StringBuilder("UTF-\uFF11\uFF16 string"); @@ -273,6 +280,24 @@ public class StringBuilders { return buf.length(); } + @Benchmark + public int appendWithCharArrayLatin1() { + StringBuilder buf = new StringBuilder(); + for (char[] charArray : charArray22p40p31) { + buf.append(charArray); + } + return buf.length(); + } + + @Benchmark + public int appendWithCharArrayUTF16() { + StringBuilder buf = new StringBuilder("\uFF11"); + for (char[] charArray : charArray22p40p31) { + buf.append(charArray); + } + return buf.length(); + } + @Benchmark public String toStringCharWithBool8() { StringBuilder result = new StringBuilder(); diff --git a/test/micro/org/openjdk/bench/java/security/Signatures.java b/test/micro/org/openjdk/bench/java/security/Signatures.java index 1bd72334343..1216e253663 100644 --- a/test/micro/org/openjdk/bench/java/security/Signatures.java +++ b/test/micro/org/openjdk/bench/java/security/Signatures.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * 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,4 +194,3 @@ public class Signatures { } } } - diff --git a/test/micro/org/openjdk/bench/java/text/DecimalFormatParseBench.java b/test/micro/org/openjdk/bench/java/text/DecimalFormatParseBench.java new file mode 100644 index 00000000000..f51591ee73f --- /dev/null +++ b/test/micro/org/openjdk/bench/java/text/DecimalFormatParseBench.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.text; + +import java.text.DecimalFormat; +import java.text.ParseException; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +@State(Scope.Benchmark) +public class DecimalFormatParseBench { + + public String[] valuesLong; + public String[] valuesDouble; + + @Setup + public void setup() { + valuesLong = new String[]{ + "123", "149", "180", "170000000000000000", "0", "-149", "-15000", "99999123", "1494", "1495", "1030", "25996", "-25996" + }; + + valuesDouble = new String[]{ + "1.23", "1.49", "1.80", "17000000000000000.1", "0.01", "-1.49", "-1.50", "9999.9123", "1.494", "1.495", "1.03", "25.996", "-25.996" + }; + } + + private DecimalFormat dnf = new DecimalFormat(); + + @Benchmark + @OperationsPerInvocation(13) + public void testParseLongs(final Blackhole blackhole) throws ParseException { + for (String value : valuesLong) { + blackhole.consume(this.dnf.parse(value)); + } + } + + @Benchmark + @OperationsPerInvocation(13) + public void testParseDoubles(final Blackhole blackhole) throws ParseException { + for (String value : valuesDouble) { + blackhole.consume(this.dnf.parse(value)); + } + } + public static void main(String... args) throws Exception { + Options opts = new OptionsBuilder().include(DefFormatterBench.class.getSimpleName()).shouldDoGC(true).build(); + new Runner(opts).run(); + } + +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskCastOperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskCastOperationsBenchmark.java index fdef723e067..fae27daed9f 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskCastOperationsBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskCastOperationsBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,9 @@ import org.openjdk.jmh.annotations.*; @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Thread) -@Fork(jvmArgs = {"--add-modules=jdk.incubator.vector"}) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 1, jvmArgs = {"--add-modules=jdk.incubator.vector"}) public class MaskCastOperationsBenchmark { VectorMask bmask64; VectorMask bmask128; @@ -50,6 +52,15 @@ public class MaskCastOperationsBenchmark { VectorMask lmask256; VectorMask lmask512; + VectorMask fmask64; + VectorMask fmask128; + VectorMask fmask256; + VectorMask fmask512; + + VectorMask dmask128; + VectorMask dmask256; + VectorMask dmask512; + static final boolean [] mask_arr = { false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, @@ -80,6 +91,15 @@ public class MaskCastOperationsBenchmark { lmask128 = VectorMask.fromArray(LongVector.SPECIES_128, mask_arr, 0); lmask256 = VectorMask.fromArray(LongVector.SPECIES_256, mask_arr, 0); lmask512 = VectorMask.fromArray(LongVector.SPECIES_512, mask_arr, 0); + + fmask64 = VectorMask.fromArray(FloatVector.SPECIES_64, mask_arr, 0); + fmask128 = VectorMask.fromArray(FloatVector.SPECIES_128, mask_arr, 0); + fmask256 = VectorMask.fromArray(FloatVector.SPECIES_256, mask_arr, 0); + fmask512 = VectorMask.fromArray(FloatVector.SPECIES_512, mask_arr, 0); + + dmask128 = VectorMask.fromArray(DoubleVector.SPECIES_128, mask_arr, 0); + dmask256 = VectorMask.fromArray(DoubleVector.SPECIES_256, mask_arr, 0); + dmask512 = VectorMask.fromArray(DoubleVector.SPECIES_512, mask_arr, 0); } @Benchmark @@ -221,4 +241,112 @@ public class MaskCastOperationsBenchmark { public VectorMask microMaskCastLong512ToInteger256() { return lmask512.cast(IntVector.SPECIES_256); } + + // Benchmarks for optimization "VectorMaskCast (VectorMaskCast x) => x" + + @Benchmark + public int microMaskCastCastByte64() { + return bmask64.cast(ShortVector.SPECIES_128).cast(ByteVector.SPECIES_64).trueCount(); + } + + @Benchmark + public int microMaskCastCastByte128() { + return bmask128.cast(ShortVector.SPECIES_256).cast(ByteVector.SPECIES_128).trueCount(); + } + + @Benchmark + public int microMaskCastCastByte256() { + return bmask256.cast(ShortVector.SPECIES_512).cast(ByteVector.SPECIES_256).trueCount(); + } + + @Benchmark + public int microMaskCastCastShort64() { + return smask64.cast(IntVector.SPECIES_128).cast(ShortVector.SPECIES_64).trueCount(); + } + + @Benchmark + public int microMaskCastCastShort128() { + return smask128.cast(ByteVector.SPECIES_64).cast(ShortVector.SPECIES_128).trueCount(); + } + + @Benchmark + public int microMaskCastCastShort256() { + return smask256.cast(IntVector.SPECIES_512).cast(ShortVector.SPECIES_256).trueCount(); + } + + @Benchmark + public int microMaskCastCastShort512() { + return smask512.cast(ByteVector.SPECIES_256).cast(ShortVector.SPECIES_512).trueCount(); + } + + @Benchmark + public int microMaskCastCastInt64() { + return imask64.cast(FloatVector.SPECIES_64).cast(IntVector.SPECIES_64).trueCount(); + } + + @Benchmark + public int microMaskCastCastInt128() { + return imask128.cast(ShortVector.SPECIES_64).cast(IntVector.SPECIES_128).trueCount(); + } + + @Benchmark + public int microMaskCastCastInt256() { + return imask256.cast(LongVector.SPECIES_512).cast(IntVector.SPECIES_256).trueCount(); + } + + @Benchmark + public int microMaskCastCastInt512() { + return imask512.cast(ShortVector.SPECIES_256).cast(IntVector.SPECIES_512).trueCount(); + } + + @Benchmark + public int microMaskCastCastLong128() { + return lmask128.cast(IntVector.SPECIES_64).cast(LongVector.SPECIES_128).trueCount(); + } + + @Benchmark + public int microMaskCastCastLong256() { + return lmask256.cast(DoubleVector.SPECIES_256).cast(LongVector.SPECIES_256).trueCount(); + } + + @Benchmark + public int microMaskCastCastLong512() { + return lmask512.cast(IntVector.SPECIES_256).cast(LongVector.SPECIES_512).trueCount(); + } + + @Benchmark + public int microMaskCastCastFloat64() { + return fmask64.cast(DoubleVector.SPECIES_128).cast(FloatVector.SPECIES_64).trueCount(); + } + + @Benchmark + public int microMaskCastCastFloat128() { + return fmask128.cast(DoubleVector.SPECIES_256).cast(FloatVector.SPECIES_128).trueCount(); + } + + @Benchmark + public int microMaskCastCastFloat256() { + return fmask256.cast(IntVector.SPECIES_256).cast(FloatVector.SPECIES_256).trueCount(); + } + + @Benchmark + public int microMaskCastCastFloat512() { + return fmask512.cast(ShortVector.SPECIES_256).cast(FloatVector.SPECIES_512).trueCount(); + } + + @Benchmark + public int microMaskCastCastDouble128() { + return dmask128.cast(FloatVector.SPECIES_64).cast(DoubleVector.SPECIES_128).trueCount(); + } + + @Benchmark + public int microMaskCastCastDouble256() { + return dmask256.cast(FloatVector.SPECIES_128).cast(DoubleVector.SPECIES_256).trueCount(); + } + + @Benchmark + public int microMaskCastCastDouble512() { + return dmask512.cast(IntVector.SPECIES_256).cast(DoubleVector.SPECIES_512).trueCount(); + } + } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskFromLongToLongBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskFromLongToLongBenchmark.java new file mode 100644 index 00000000000..02aa1aced2b --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/MaskFromLongToLongBenchmark.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.incubator.vector; + +import jdk.incubator.vector.*; +import org.openjdk.jmh.annotations.*; + +@State(Scope.Thread) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 1, jvmArgs = {"--add-modules=jdk.incubator.vector"}) +public class MaskFromLongToLongBenchmark { + private static final int ITERATION = 10000; + + @CompilerControl(CompilerControl.Mode.INLINE) + public long microMaskFromLongToLong(VectorSpecies species) { + long result = 0; + for (int i = 0; i < ITERATION; i++) { + long mask = Math.min(-1, Math.max(-1, result)); + result += VectorMask.fromLong(species, mask).toLong(); + } + return result; + } + + @Benchmark + public long microMaskFromLongToLong_Byte64() { + return microMaskFromLongToLong(ByteVector.SPECIES_64); + } + + @Benchmark + public long microMaskFromLongToLong_Byte128() { + return microMaskFromLongToLong(ByteVector.SPECIES_128); + } + + @Benchmark + public long microMaskFromLongToLong_Byte256() { + return microMaskFromLongToLong(ByteVector.SPECIES_256); + } + + @Benchmark + public long microMaskFromLongToLong_Byte512() { + return microMaskFromLongToLong(ByteVector.SPECIES_512); + } + + @Benchmark + public long microMaskFromLongToLong_Short64() { + return microMaskFromLongToLong(ShortVector.SPECIES_64); + } + + @Benchmark + public long microMaskFromLongToLong_Short128() { + return microMaskFromLongToLong(ShortVector.SPECIES_128); + } + + @Benchmark + public long microMaskFromLongToLong_Short256() { + return microMaskFromLongToLong(ShortVector.SPECIES_256); + } + + @Benchmark + public long microMaskFromLongToLong_Short512() { + return microMaskFromLongToLong(ShortVector.SPECIES_512); + } + + @Benchmark + public long microMaskFromLongToLong_Integer64() { + return microMaskFromLongToLong(IntVector.SPECIES_64); + } + + @Benchmark + public long microMaskFromLongToLong_Integer128() { + return microMaskFromLongToLong(IntVector.SPECIES_128); + } + + @Benchmark + public long microMaskFromLongToLong_Integer256() { + return microMaskFromLongToLong(IntVector.SPECIES_256); + } + + @Benchmark + public long microMaskFromLongToLong_Integer512() { + return microMaskFromLongToLong(IntVector.SPECIES_512); + } + + @Benchmark + public long microMaskFromLongToLong_Long64() { + return microMaskFromLongToLong(LongVector.SPECIES_64); + } + + @Benchmark + public long microMaskFromLongToLong_Long128() { + return microMaskFromLongToLong(LongVector.SPECIES_128); + } + + @Benchmark + public long microMaskFromLongToLong_Long256() { + return microMaskFromLongToLong(LongVector.SPECIES_256); + } + + @Benchmark + public long microMaskFromLongToLong_Long512() { + return microMaskFromLongToLong(LongVector.SPECIES_512); + } + + @Benchmark + public long microMaskFromLongToLong_Float64() { + return microMaskFromLongToLong(FloatVector.SPECIES_64); + } + + @Benchmark + public long microMaskFromLongToLong_Float128() { + return microMaskFromLongToLong(FloatVector.SPECIES_128); + } + + @Benchmark + public long microMaskFromLongToLong_Float256() { + return microMaskFromLongToLong(FloatVector.SPECIES_256); + } + + @Benchmark + public long microMaskFromLongToLong_Float512() { + return microMaskFromLongToLong(FloatVector.SPECIES_512); + } + + @Benchmark + public long microMaskFromLongToLong_Double64() { + return microMaskFromLongToLong(DoubleVector.SPECIES_64); + } + + @Benchmark + public long microMaskFromLongToLong_Double128() { + return microMaskFromLongToLong(DoubleVector.SPECIES_128); + } + + @Benchmark + public long microMaskFromLongToLong_Double256() { + return microMaskFromLongToLong(DoubleVector.SPECIES_256); + } + + @Benchmark + public long microMaskFromLongToLong_Double512() { + return microMaskFromLongToLong(DoubleVector.SPECIES_512); + } +} diff --git a/test/micro/org/openjdk/bench/vm/compiler/AutoVectorization2DArray.java b/test/micro/org/openjdk/bench/vm/compiler/AutoVectorization2DArray.java index a2a4654b40a..e41b1e7a4d0 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/AutoVectorization2DArray.java +++ b/test/micro/org/openjdk/bench/vm/compiler/AutoVectorization2DArray.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java b/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java index cbb216bbaa6..2e7e1da4ba9 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java +++ b/test/micro/org/openjdk/bench/vm/compiler/LoopUnroll.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2021, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it