diff --git a/.github/actions/get-msys2/action.yml b/.github/actions/get-msys2/action.yml index 7dac1538536c6..d36957e3b37e6 100644 --- a/.github/actions/get-msys2/action.yml +++ b/.github/actions/get-msys2/action.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index 5db69f07d98c5..77620640f13bc 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 43a41b939b666..ebc0f3d961d36 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,6 +30,7 @@ on: branches-ignore: - master - pr/* + - jdk* workflow_dispatch: inputs: platforms: diff --git a/configure b/configure index f3b1c536875da..78d30564267df 100644 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -26,10 +26,14 @@ # make sure that is called using bash. # Get an absolute path to this script, since that determines the top-level directory. -this_script_dir=`dirname $0` -this_script_dir=`cd $this_script_dir > /dev/null && pwd` +source_path="$(dirname ${0})" +this_script_dir="$(cd -- "${source_path}" > /dev/null && pwd)" +if test -z "${this_script_dir}"; then + echo "Error: Could not determine location of configure script" + exit 1 +fi # Delegate to wrapper, forcing wrapper to believe $0 is this script by using -c. # This trick is needed to get autoconf to co-operate properly. # The ${-:+-$-} construction passes on bash options. -bash ${-:+-$-} -c ". $this_script_dir/make/autoconf/configure" $this_script_dir/configure CHECKME $this_script_dir "$@" +bash ${-:+-$-} -c ". \"${this_script_dir}/make/autoconf/configure\"" "${this_script_dir}/configure" CHECKME "${this_script_dir}" "$@" diff --git a/doc/testing.html b/doc/testing.html index 00bb3c678a513..251475808498b 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -586,12 +586,15 @@

PKCS11 Tests

are hard to diagnose. For example, sun/security/pkcs11/Secmod/AddTrustedCert.java may fail on Ubuntu 18.04 with the default NSS version in the system. To run these tests -correctly, the system property test.nss.lib.paths is -required on Ubuntu 18.04 to specify the alternative NSS lib -directories.

+correctly, the system property +jdk.test.lib.artifacts.<NAME> is required on Ubuntu +18.04 to specify the alternative NSS lib directory. The +<NAME> component should be replaced with the name +element of the appropriate @Artifact class. (See +test/jdk/sun/security/pkcs11/PKCS11Test.java)

For example:

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

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

Client UI Tests

diff --git a/doc/testing.md b/doc/testing.md index c43ebc23c3f96..63a869bfc1c0d 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -604,14 +604,16 @@ It is highly recommended to use the latest NSS version when running PKCS11 tests. Improper NSS version may lead to unexpected failures which are hard to diagnose. For example, sun/security/pkcs11/Secmod/AddTrustedCert.java may fail on Ubuntu 18.04 with the default NSS version in the system. To run these tests -correctly, the system property `test.nss.lib.paths` is required on Ubuntu 18.04 -to specify the alternative NSS lib directories. +correctly, the system property `jdk.test.lib.artifacts.` is required on +Ubuntu 18.04 to specify the alternative NSS lib directory. The `` +component should be replaced with the name element of the appropriate +`@Artifact` class. (See `test/jdk/sun/security/pkcs11/PKCS11Test.java`) For example: ``` $ make test TEST="jtreg:sun/security/pkcs11/Secmod/AddTrustedCert.java" \ - JTREG="JAVA_OPTIONS=-Dtest.nss.lib.paths=/path/to/your/latest/NSS-libs" + JTREG="JAVA_OPTIONS=-Djdk.test.lib.artifacts.nsslib-linux_aarch64=/path/to/NSS-libs" ``` For more notes about the PKCS11 tests, please refer to diff --git a/make/CompileDemos.gmk b/make/CompileDemos.gmk index c85bc102acbe7..4d8a9598da792 100644 --- a/make/CompileDemos.gmk +++ b/make/CompileDemos.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/CompileModuleTools.gmk b/make/CompileModuleTools.gmk index 59d651d2d9625..bb3948f377ede 100644 --- a/make/CompileModuleTools.gmk +++ b/make/CompileModuleTools.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/Global.gmk b/make/Global.gmk index b41c4051cfafd..e5e76b475b941 100644 --- a/make/Global.gmk +++ b/make/Global.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -52,7 +52,6 @@ help: $(info $(_) make docs # Create all docs) $(info $(_) make docs-jdk-api # Create just JDK javadocs) $(info $(_) make bootcycle-images # Build images twice, second time with newly built JDK) - $(info $(_) make install # Install the generated images locally) $(info $(_) make check # Run basic testing (currently tier1)) $(info $(_) make test- # Run test, e.g. test-tier1) $(info $(_) make test TEST= # Run test(s) given by TEST specification) diff --git a/make/Hsdis.gmk b/make/Hsdis.gmk index ec06a89aaab1f..7496a3a2cf1b4 100644 --- a/make/Hsdis.gmk +++ b/make/Hsdis.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,7 @@ ifeq ($(HSDIS_BACKEND), capstone) CAPSTONE_ARCH := CS_ARCH_X86 CAPSTONE_MODE := CS_MODE_$(OPENJDK_TARGET_CPU_BITS) else ifeq ($(call isTargetCpuArch, aarch64), true) - CAPSTONE_ARCH := CS_ARCH_ARM64 + CAPSTONE_ARCH := CS_ARCH_$(CAPSTONE_ARCH_AARCH64_NAME) CAPSTONE_MODE := CS_MODE_ARM else $(error No support for Capstone on this platform) diff --git a/make/Install.gmk b/make/Install.gmk deleted file mode 100644 index bfe989ffe3100..0000000000000 --- a/make/Install.gmk +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. 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. -# - -default: install - -include $(SPEC) - -BINARIES := $(notdir $(wildcard $(JDK_IMAGE_DIR)/bin/*)) -INSTALLDIR := openjdk-$(VERSION_SHORT) - -# Install the jdk image, in a very crude way. Not taking into -# account, how to install properly on macosx or windows etc. -install: - echo Installing jdk image into $(INSTALL_PREFIX)/jvm/$(INSTALLDIR) - echo and creating $(words $(BINARIES)) links from $(INSTALL_PREFIX)/bin into the jdk. - $(MKDIR) -p $(INSTALL_PREFIX)/jvm/$(INSTALLDIR) - $(RM) -r $(INSTALL_PREFIX)/jvm/$(INSTALLDIR)/* - $(CP) -rp $(JDK_IMAGE_DIR)/* $(INSTALL_PREFIX)/jvm/$(INSTALLDIR) - $(MKDIR) -p $(INSTALL_PREFIX)/bin - $(RM) $(addprefix $(INSTALL_PREFIX)/bin/, $(BINARIES)) - $(foreach b, $(BINARIES), $(LN) -s $(INSTALL_PREFIX)/jvm/$(INSTALLDIR)/bin/$b $(INSTALL_PREFIX)/bin/$b &&) true diff --git a/make/Main.gmk b/make/Main.gmk index 8fdaf788c4dfe..e904235ff3f80 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -608,11 +608,11 @@ endif ifeq ($(CREATING_BUILDJDK), true) # This target is only called by the recursive call below. create-buildjdk-interim-image-helper: interim-image jdk.jlink-launchers \ - java.base-copy jdk.jdeps-launchers + java.base-copy jdk.jdeps-launchers jdk.compiler-launchers endif -BUILDJDK_MODULES := $(sort $(foreach m, jdk.jlink $(INTERIM_IMAGE_MODULES), \ - $(call FindTransitiveDepsForModule, $m) $m)) +BUILDJDK_MODULES := $(sort $(foreach m, jdk.jlink jdk.compiler \ + $(INTERIM_IMAGE_MODULES), $(call FindTransitiveDepsForModule, $m) $m)) $(eval $(call SetupTarget, create-buildjdk-interim-image, \ MAKEFILE := Main, \ @@ -866,14 +866,6 @@ ifeq ($(JCOV_ENABLED), true) )) endif -################################################################################ -# Install targets - -$(eval $(call SetupTarget, install, \ - MAKEFILE := Install, \ - DEPS := product-images, \ -)) - ################################################################################ # # Dependency declarations between targets. @@ -971,7 +963,20 @@ else jdk.jdeps-gendata: java # The ct.sym generation uses all the moduleinfos as input - jdk.compiler-gendata: $(GENSRC_MODULEINFO_TARGETS) + jdk.compiler-gendata: $(GENSRC_MODULEINFO_TARGETS) $(JAVA_TARGETS) + # jdk.compiler-gendata needs the BUILD_JDK. If the BUILD_JDK was supplied + # externally, no extra prerequisites are needed. + ifeq ($(CREATE_BUILDJDK), true) + ifneq ($(CREATING_BUILDJDK), true) + # When cross compiling and an external BUILD_JDK wasn't supplied, it's + # produced by the create-buildjdk target. + jdk.compiler-gendata: create-buildjdk + endif + else ifeq ($(EXTERNAL_BUILDJDK), false) + # When not cross compiling, the BUILD_JDK is the interim jdk image, and + # the javac launcher is needed. + jdk.compiler-gendata: jdk.compiler-launchers + endif # Declare dependencies between jmod targets. # java.base jmod needs jrt-fs.jar and access to the jmods for all non diff --git a/make/ReleaseFile.gmk b/make/ReleaseFile.gmk index 5e8d123fac9c8..dc6f66a69a2af 100644 --- a/make/ReleaseFile.gmk +++ b/make/ReleaseFile.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/RunTestsPrebuilt.gmk b/make/RunTestsPrebuilt.gmk index 93febe5ed31d9..eea325184f3d7 100644 --- a/make/RunTestsPrebuilt.gmk +++ b/make/RunTestsPrebuilt.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/RunTestsPrebuiltSpec.gmk b/make/RunTestsPrebuiltSpec.gmk index 7fcaf56ff527e..16a14fbd2a072 100644 --- a/make/RunTestsPrebuiltSpec.gmk +++ b/make/RunTestsPrebuiltSpec.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/ToolsJdk.gmk b/make/ToolsJdk.gmk index b7310811f0299..e7cc3fc871128 100644 --- a/make/ToolsJdk.gmk +++ b/make/ToolsJdk.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/Makefile.in b/make/autoconf/Makefile.in index 8badd0f43ac84..4cb5057c05c7e 100644 --- a/make/autoconf/Makefile.in +++ b/make/autoconf/Makefile.in @@ -24,4 +24,4 @@ # This Makefile was generated by configure @DATE_WHEN_CONFIGURED@ # GENERATED FILE, DO NOT EDIT SPEC:=@OUTPUTDIR@/spec.gmk -include @TOPDIR@/Makefile +include @WORKSPACE_ROOT@/Makefile diff --git a/make/autoconf/boot-jdk.m4 b/make/autoconf/boot-jdk.m4 index 960a8ffce3a88..8d272c28ad54f 100644 --- a/make/autoconf/boot-jdk.m4 +++ b/make/autoconf/boot-jdk.m4 @@ -520,8 +520,8 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS], # Don't presuppose SerialGC is present in the buildjdk. Also, we cannot test # the buildjdk, but on the other hand we know what it will support. - BUILDJDK_JAVA_FLAGS_SMALL="-Xms32M -Xmx512M -XX:TieredStopAtLevel=1" - AC_SUBST(BUILDJDK_JAVA_FLAGS_SMALL) + BUILD_JAVA_FLAGS_SMALL="-Xms32M -Xmx512M -XX:TieredStopAtLevel=1" + AC_SUBST(BUILD_JAVA_FLAGS_SMALL) JAVA_TOOL_FLAGS_SMALL="" for f in $JAVA_FLAGS_SMALL; do diff --git a/make/autoconf/buildjdk-spec.gmk.in b/make/autoconf/buildjdk-spec.gmk.in index 3e7c4a39f60a6..993ed50390210 100644 --- a/make/autoconf/buildjdk-spec.gmk.in +++ b/make/autoconf/buildjdk-spec.gmk.in @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/configure b/make/autoconf/configure index 4b26e3d706147..6fa0aacfbc907 100644 --- a/make/autoconf/configure +++ b/make/autoconf/configure @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,12 @@ if test "x$BASH" = x; then echo "Error: This script must be run using bash." 1>&2 exit 1 fi + +if [[ "$TOPDIR" =~ .*[[:space:]]+.* ]]; then + echo "Error: Build path containing space character is not supported" 1>&2 + exit 1 +fi + # Force autoconf to use bash. This also means we must disable autoconf re-exec. export CONFIG_SHELL=$BASH export _as_can_reexec=no diff --git a/make/autoconf/flags-other.m4 b/make/autoconf/flags-other.m4 index 0af7c02cff6c2..7e2521ffef3b0 100644 --- a/make/autoconf/flags-other.m4 +++ b/make/autoconf/flags-other.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/lib-ffi.m4 b/make/autoconf/lib-ffi.m4 index 3a11bec4a97d6..101065a1ecec1 100644 --- a/make/autoconf/lib-ffi.m4 +++ b/make/autoconf/lib-ffi.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/autoconf/lib-hsdis.m4 b/make/autoconf/lib-hsdis.m4 index 4414e54886cf9..33d8a35f8fdf4 100644 --- a/make/autoconf/lib-hsdis.m4 +++ b/make/autoconf/lib-hsdis.m4 @@ -63,6 +63,19 @@ AC_DEFUN([LIB_SETUP_HSDIS_CAPSTONE], AC_MSG_ERROR([Cannot continue]) fi fi + + capstone_header="\"$CAPSTONE/include/capstone/capstone.h\"" + AC_MSG_CHECKING([capstone aarch64 arch name]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include $capstone_header],[[cs_arch test = CS_ARCH_AARCH64]])], + [ + AC_MSG_RESULT([AARCH64]) + CAPSTONE_ARCH_AARCH64_NAME="AARCH64" + ], + [ + AC_MSG_RESULT([ARM64]) + CAPSTONE_ARCH_AARCH64_NAME="ARM64" + ] + ) ]) ################################################################################ @@ -278,7 +291,7 @@ AC_DEFUN([LIB_SETUP_HSDIS_BINUTILS], fi AC_MSG_CHECKING([Checking binutils API]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include $disasm_header],[[void foo() {init_disassemble_info(0, 0, 0, 0);}]])], + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include $disasm_header],[[init_disassemble_info(0, 0, 0, 0);]])], [ AC_MSG_RESULT([New API]) HSDIS_CFLAGS="$HSDIS_CFLAGS -DBINUTILS_NEW_API" @@ -365,6 +378,7 @@ AC_DEFUN_ONCE([LIB_SETUP_HSDIS], AC_SUBST(HSDIS_CFLAGS) AC_SUBST(HSDIS_LDFLAGS) AC_SUBST(HSDIS_LIBS) + AC_SUBST(CAPSTONE_ARCH_AARCH64_NAME) AC_MSG_CHECKING([if hsdis should be bundled]) if test "x$ENABLE_HSDIS_BUNDLING" = "xtrue"; then diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 2b7a7b8d1afde..8a1eb8ed27f54 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -31,9 +31,9 @@ # using 'configure @CONFIGURE_COMMAND_LINE@' # The command line given to configure. -CONFIGURE_COMMAND_LINE:=@CONFIGURE_COMMAND_LINE@ +CONFIGURE_COMMAND_LINE := @CONFIGURE_COMMAND_LINE@ # The current directory when configure was run -CONFIGURE_START_DIR:=@CONFIGURE_START_DIR@ +CONFIGURE_START_DIR := @CONFIGURE_START_DIR@ # How configure was originally called, if not called directly REAL_CONFIGURE_COMMAND_EXEC_SHORT := @REAL_CONFIGURE_COMMAND_EXEC_SHORT@ @@ -41,7 +41,7 @@ REAL_CONFIGURE_COMMAND_EXEC_FULL := @REAL_CONFIGURE_COMMAND_EXEC_FULL@ REAL_CONFIGURE_COMMAND_LINE := @REAL_CONFIGURE_COMMAND_LINE@ # A self-referential reference to this file. -SPEC:=@SPEC@ +SPEC := @SPEC@ # Path to autoconf if overridden by the user, to be used by "make reconfigure" AUTOCONF := @AUTOCONF@ @@ -49,9 +49,9 @@ AUTOCONF := @AUTOCONF@ # SPACE and COMMA are defined in MakeBase.gmk, but they are also used in # some definitions here, and are needed if MakeBase.gmk is not included before # this file. -X:= -SPACE:=$(X) $(X) -COMMA:=, +X := +SPACE := $(X) $(X) +COMMA := , # What make to use for main processing, after bootstrapping top-level Makefile. MAKE := @MAKE@ @@ -66,37 +66,37 @@ export CLASSPATH := @CLASSPATH@ MAKE_ARGS = $(MAKE_LOG_FLAGS) -r -R -I $(TOPDIR)/make/common SPEC=$(SPEC) \ MAKE_LOG_FLAGS="$(MAKE_LOG_FLAGS)" $(MAKE_LOG_VARS) -OUTPUT_SYNC_SUPPORTED:=@OUTPUT_SYNC_SUPPORTED@ -OUTPUT_SYNC:=@OUTPUT_SYNC@ +OUTPUT_SYNC_SUPPORTED := @OUTPUT_SYNC_SUPPORTED@ +OUTPUT_SYNC := @OUTPUT_SYNC@ # Override the shell with bash -BASH:=@BASH@ -BASH_ARGS:=@BASH_ARGS@ -SHELL:=$(BASH) $(BASH_ARGS) +BASH := @BASH@ +BASH_ARGS := @BASH_ARGS@ +SHELL := $(BASH) $(BASH_ARGS) # The "human readable" name of this configuration -CONF_NAME:=@CONF_NAME@ +CONF_NAME := @CONF_NAME@ # The built jdk will run in this target system. -OPENJDK_TARGET_OS:=@OPENJDK_TARGET_OS@ -OPENJDK_TARGET_OS_TYPE:=@OPENJDK_TARGET_OS_TYPE@ -OPENJDK_TARGET_OS_ENV:=@OPENJDK_TARGET_OS_ENV@ -OPENJDK_TARGET_OS_UPPERCASE:=@OPENJDK_TARGET_OS_UPPERCASE@ +OPENJDK_TARGET_OS := @OPENJDK_TARGET_OS@ +OPENJDK_TARGET_OS_TYPE := @OPENJDK_TARGET_OS_TYPE@ +OPENJDK_TARGET_OS_ENV := @OPENJDK_TARGET_OS_ENV@ +OPENJDK_TARGET_OS_UPPERCASE := @OPENJDK_TARGET_OS_UPPERCASE@ -OPENJDK_TARGET_CPU:=@OPENJDK_TARGET_CPU@ -OPENJDK_TARGET_CPU_ARCH:=@OPENJDK_TARGET_CPU_ARCH@ -OPENJDK_TARGET_CPU_BITS:=@OPENJDK_TARGET_CPU_BITS@ -OPENJDK_TARGET_CPU_ENDIAN:=@OPENJDK_TARGET_CPU_ENDIAN@ +OPENJDK_TARGET_CPU := @OPENJDK_TARGET_CPU@ +OPENJDK_TARGET_CPU_ARCH := @OPENJDK_TARGET_CPU_ARCH@ +OPENJDK_TARGET_CPU_BITS := @OPENJDK_TARGET_CPU_BITS@ +OPENJDK_TARGET_CPU_ENDIAN := @OPENJDK_TARGET_CPU_ENDIAN@ -OPENJDK_TARGET_LIBC:=@OPENJDK_TARGET_LIBC@ +OPENJDK_TARGET_LIBC := @OPENJDK_TARGET_LIBC@ -COMPILE_TYPE:=@COMPILE_TYPE@ +COMPILE_TYPE := @COMPILE_TYPE@ # Legacy support -OPENJDK_TARGET_CPU_LEGACY:=@OPENJDK_TARGET_CPU_LEGACY@ -OPENJDK_TARGET_CPU_LEGACY_LIB:=@OPENJDK_TARGET_CPU_LEGACY_LIB@ -OPENJDK_TARGET_CPU_OSARCH:=@OPENJDK_TARGET_CPU_OSARCH@ -OPENJDK_TARGET_OS_INCLUDE_SUBDIR:=@OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ +OPENJDK_TARGET_CPU_LEGACY := @OPENJDK_TARGET_CPU_LEGACY@ +OPENJDK_TARGET_CPU_LEGACY_LIB := @OPENJDK_TARGET_CPU_LEGACY_LIB@ +OPENJDK_TARGET_CPU_OSARCH := @OPENJDK_TARGET_CPU_OSARCH@ +OPENJDK_TARGET_OS_INCLUDE_SUBDIR := @OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ HOTSPOT_TARGET_OS := @HOTSPOT_TARGET_OS@ HOTSPOT_TARGET_OS_TYPE := @HOTSPOT_TARGET_OS_TYPE@ @@ -107,31 +107,31 @@ HOTSPOT_TARGET_CPU_DEFINE := @HOTSPOT_TARGET_CPU_DEFINE@ HOTSPOT_TARGET_LIBC := @HOTSPOT_TARGET_LIBC@ -OPENJDK_TARGET_BUNDLE_PLATFORM:=@OPENJDK_TARGET_BUNDLE_PLATFORM@ +OPENJDK_TARGET_BUNDLE_PLATFORM := @OPENJDK_TARGET_BUNDLE_PLATFORM@ JDK_ARCH_ABI_PROP_NAME := @JDK_ARCH_ABI_PROP_NAME@ # We are building on this build system. # When not cross-compiling, it is the same as the target. -OPENJDK_BUILD_OS:=@OPENJDK_BUILD_OS@ -OPENJDK_BUILD_OS_TYPE:=@OPENJDK_BUILD_OS_TYPE@ -OPENJDK_BUILD_OS_ENV:=@OPENJDK_BUILD_OS_ENV@ +OPENJDK_BUILD_OS := @OPENJDK_BUILD_OS@ +OPENJDK_BUILD_OS_TYPE := @OPENJDK_BUILD_OS_TYPE@ +OPENJDK_BUILD_OS_ENV := @OPENJDK_BUILD_OS_ENV@ -OPENJDK_BUILD_CPU:=@OPENJDK_BUILD_CPU@ -OPENJDK_BUILD_CPU_ARCH:=@OPENJDK_BUILD_CPU_ARCH@ -OPENJDK_BUILD_CPU_BITS:=@OPENJDK_BUILD_CPU_BITS@ -OPENJDK_BUILD_CPU_ENDIAN:=@OPENJDK_BUILD_CPU_ENDIAN@ +OPENJDK_BUILD_CPU := @OPENJDK_BUILD_CPU@ +OPENJDK_BUILD_CPU_ARCH := @OPENJDK_BUILD_CPU_ARCH@ +OPENJDK_BUILD_CPU_BITS := @OPENJDK_BUILD_CPU_BITS@ +OPENJDK_BUILD_CPU_ENDIAN := @OPENJDK_BUILD_CPU_ENDIAN@ -OPENJDK_BUILD_LIBC:=@OPENJDK_BUILD_LIBC@ +OPENJDK_BUILD_LIBC := @OPENJDK_BUILD_LIBC@ -OPENJDK_BUILD_OS_INCLUDE_SUBDIR:=@OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ +OPENJDK_BUILD_OS_INCLUDE_SUBDIR := @OPENJDK_TARGET_OS_INCLUDE_SUBDIR@ # Target platform value in ModuleTarget class file attribute. -OPENJDK_MODULE_TARGET_PLATFORM:=@OPENJDK_MODULE_TARGET_PLATFORM@ +OPENJDK_MODULE_TARGET_PLATFORM := @OPENJDK_MODULE_TARGET_PLATFORM@ # OS_* properties in release file -RELEASE_FILE_OS_NAME:=@RELEASE_FILE_OS_NAME@ -RELEASE_FILE_OS_ARCH:=@RELEASE_FILE_OS_ARCH@ -RELEASE_FILE_LIBC:=@RELEASE_FILE_LIBC@ +RELEASE_FILE_OS_NAME := @RELEASE_FILE_OS_NAME@ +RELEASE_FILE_OS_ARCH := @RELEASE_FILE_OS_ARCH@ +RELEASE_FILE_LIBC := @RELEASE_FILE_LIBC@ SOURCE_DATE := @SOURCE_DATE@ ISO_8601_FORMAT_STRING := @ISO_8601_FORMAT_STRING@ @@ -142,8 +142,8 @@ ifneq ($(SOURCE_DATE), updated) SOURCE_DATE_ISO_8601 := @SOURCE_DATE_ISO_8601@ endif -LIBM:=@LIBM@ -LIBDL:=@LIBDL@ +LIBM := @LIBM@ +LIBDL := @LIBDL@ WINENV_ROOT := @WINENV_ROOT@ WINENV_PREFIX := @WINENV_PREFIX@ @@ -169,37 +169,37 @@ SYSROOT_CFLAGS := @SYSROOT_CFLAGS@ SYSROOT_LDFLAGS := @SYSROOT_LDFLAGS@ # The top-level directory of the source repository -TOPDIR:=@TOPDIR@ +TOPDIR := @TOPDIR@ # Usually the top level directory, but could be something else if a custom # root is defined. -WORKSPACE_ROOT:=@WORKSPACE_ROOT@ -IMPORT_MODULES_CLASSES:=@IMPORT_MODULES_CLASSES@ -IMPORT_MODULES_CMDS:=@IMPORT_MODULES_CMDS@ -IMPORT_MODULES_LIBS:=@IMPORT_MODULES_LIBS@ -IMPORT_MODULES_CONF:=@IMPORT_MODULES_CONF@ -IMPORT_MODULES_LEGAL:=@IMPORT_MODULES_LEGAL@ -IMPORT_MODULES_MAN:=@IMPORT_MODULES_MAN@ -IMPORT_MODULES_SRC:=@IMPORT_MODULES_SRC@ -IMPORT_MODULES_MAKE:=@IMPORT_MODULES_MAKE@ - -COPYRIGHT_YEAR:=@COPYRIGHT_YEAR@ -HOTSPOT_BUILD_TIME:=@HOTSPOT_BUILD_TIME@ +WORKSPACE_ROOT := @WORKSPACE_ROOT@ +IMPORT_MODULES_CLASSES := @IMPORT_MODULES_CLASSES@ +IMPORT_MODULES_CMDS := @IMPORT_MODULES_CMDS@ +IMPORT_MODULES_LIBS := @IMPORT_MODULES_LIBS@ +IMPORT_MODULES_CONF := @IMPORT_MODULES_CONF@ +IMPORT_MODULES_LEGAL := @IMPORT_MODULES_LEGAL@ +IMPORT_MODULES_MAN := @IMPORT_MODULES_MAN@ +IMPORT_MODULES_SRC := @IMPORT_MODULES_SRC@ +IMPORT_MODULES_MAKE := @IMPORT_MODULES_MAKE@ + +COPYRIGHT_YEAR := @COPYRIGHT_YEAR@ +HOTSPOT_BUILD_TIME := @HOTSPOT_BUILD_TIME@ # Platform naming variables -LAUNCHER_NAME:=@LAUNCHER_NAME@ -PRODUCT_NAME:=@PRODUCT_NAME@ -PRODUCT_SUFFIX:=@PRODUCT_SUFFIX@ -JDK_RC_PLATFORM_NAME:=@JDK_RC_PLATFORM_NAME@ -JDK_RC_NAME:=@JDK_RC_NAME@ -COMPANY_NAME:=@COMPANY_NAME@ -HOTSPOT_VM_DISTRO:=@HOTSPOT_VM_DISTRO@ -MACOSX_BUNDLE_NAME_BASE=@MACOSX_BUNDLE_NAME_BASE@ -MACOSX_BUNDLE_ID_BASE=@MACOSX_BUNDLE_ID_BASE@ -MACOSX_BUNDLE_BUILD_VERSION=@MACOSX_BUNDLE_BUILD_VERSION@ -USERNAME:=@USERNAME@ -VENDOR_URL:=@VENDOR_URL@ -VENDOR_URL_BUG:=@VENDOR_URL_BUG@ -VENDOR_URL_VM_BUG:=@VENDOR_URL_VM_BUG@ +LAUNCHER_NAME := @LAUNCHER_NAME@ +PRODUCT_NAME := @PRODUCT_NAME@ +PRODUCT_SUFFIX := @PRODUCT_SUFFIX@ +JDK_RC_PLATFORM_NAME := @JDK_RC_PLATFORM_NAME@ +JDK_RC_NAME := @JDK_RC_NAME@ +COMPANY_NAME := @COMPANY_NAME@ +HOTSPOT_VM_DISTRO := @HOTSPOT_VM_DISTRO@ +MACOSX_BUNDLE_NAME_BASE := @MACOSX_BUNDLE_NAME_BASE@ +MACOSX_BUNDLE_ID_BASE := @MACOSX_BUNDLE_ID_BASE@ +MACOSX_BUNDLE_BUILD_VERSION := @MACOSX_BUNDLE_BUILD_VERSION@ +USERNAME := @USERNAME@ +VENDOR_URL := @VENDOR_URL@ +VENDOR_URL_BUG := @VENDOR_URL_BUG@ +VENDOR_URL_VM_BUG := @VENDOR_URL_VM_BUG@ # New (JEP-223) version information @@ -298,11 +298,11 @@ ifneq ($(VENDOR_URL_VM_BUG),) endif # Different naming strings generated from the above information. -RUNTIME_NAME=$(PRODUCT_NAME) $(PRODUCT_SUFFIX) +RUNTIME_NAME = $(PRODUCT_NAME) $(PRODUCT_SUFFIX) # How to compile the code: release, fastdebug or slowdebug -DEBUG_LEVEL:=@DEBUG_LEVEL@ -HOTSPOT_DEBUG_LEVEL:=@HOTSPOT_DEBUG_LEVEL@ +DEBUG_LEVEL := @DEBUG_LEVEL@ +HOTSPOT_DEBUG_LEVEL := @HOTSPOT_DEBUG_LEVEL@ # Which JVM variants to build (space-separated list) JVM_VARIANTS := @JVM_VARIANTS@ @@ -342,19 +342,19 @@ ENABLE_FULL_DOCS := @ENABLE_FULL_DOCS@ OUTPUTDIR := @OUTPUTDIR@ # Colon left out to be able to override IMAGES_OUTPUTDIR for bootcycle-images -SUPPORT_OUTPUTDIR=$(OUTPUTDIR)/support -BUILDTOOLS_OUTPUTDIR=$(OUTPUTDIR)/buildtools +SUPPORT_OUTPUTDIR = $(OUTPUTDIR)/support +BUILDTOOLS_OUTPUTDIR = $(OUTPUTDIR)/buildtools -HOTSPOT_OUTPUTDIR=$(OUTPUTDIR)/hotspot -JDK_OUTPUTDIR=$(OUTPUTDIR)/jdk -IMAGES_OUTPUTDIR=$(OUTPUTDIR)/images -BUNDLES_OUTPUTDIR=$(OUTPUTDIR)/bundles -TESTMAKE_OUTPUTDIR=$(OUTPUTDIR)/test-make -MAKESUPPORT_OUTPUTDIR=$(OUTPUTDIR)/make-support +HOTSPOT_OUTPUTDIR = $(OUTPUTDIR)/hotspot +JDK_OUTPUTDIR = $(OUTPUTDIR)/jdk +IMAGES_OUTPUTDIR = $(OUTPUTDIR)/images +BUNDLES_OUTPUTDIR = $(OUTPUTDIR)/bundles +TESTMAKE_OUTPUTDIR = $(OUTPUTDIR)/test-make +MAKESUPPORT_OUTPUTDIR = $(OUTPUTDIR)/make-support # This does not get overridden in a bootcycle build -CONFIGURESUPPORT_OUTPUTDIR:=@CONFIGURESUPPORT_OUTPUTDIR@ -BUILDJDK_OUTPUTDIR=$(OUTPUTDIR)/buildjdk +CONFIGURESUPPORT_OUTPUTDIR := @CONFIGURESUPPORT_OUTPUTDIR@ +BUILDJDK_OUTPUTDIR = $(OUTPUTDIR)/buildjdk BUILD_FAILURE_HANDLER := @BUILD_FAILURE_HANDLER@ @@ -377,52 +377,53 @@ ENABLE_HSDIS_BUNDLING := @ENABLE_HSDIS_BUNDLING@ HSDIS_CFLAGS := @HSDIS_CFLAGS@ HSDIS_LDFLAGS := @HSDIS_LDFLAGS@ HSDIS_LIBS := @HSDIS_LIBS@ +CAPSTONE_ARCH_AARCH64_NAME := @CAPSTONE_ARCH_AARCH64_NAME@ # The boot jdk to use. This is overridden in bootcycle-spec.gmk. Make sure to keep # it in sync. -BOOT_JDK:=@BOOT_JDK@ +BOOT_JDK := @BOOT_JDK@ -BUILD_JDK:=@BUILD_JDK@ -CREATE_BUILDJDK:=@CREATE_BUILDJDK@ -EXTERNAL_BUILDJDK:=@EXTERNAL_BUILDJDK@ +BUILD_JDK := @BUILD_JDK@ +CREATE_BUILDJDK := @CREATE_BUILDJDK@ +EXTERNAL_BUILDJDK := @EXTERNAL_BUILDJDK@ # Whether the boot jdk jar supports --date=TIMESTAMP -BOOT_JDK_JAR_SUPPORTS_DATE:=@BOOT_JDK_JAR_SUPPORTS_DATE@ +BOOT_JDK_JAR_SUPPORTS_DATE := @BOOT_JDK_JAR_SUPPORTS_DATE@ # When compiling Java source to be run by the boot jdk # use these extra flags, eg -source 6 -target 6 -BOOT_JDK_SOURCETARGET:=@BOOT_JDK_SOURCETARGET@ +BOOT_JDK_SOURCETARGET := @BOOT_JDK_SOURCETARGET@ # Information about the build system -NUM_CORES:=@NUM_CORES@ -MEMORY_SIZE:=@MEMORY_SIZE@ -ENABLE_JAVAC_SERVER:=@ENABLE_JAVAC_SERVER@ +NUM_CORES := @NUM_CORES@ +MEMORY_SIZE := @MEMORY_SIZE@ +ENABLE_JAVAC_SERVER := @ENABLE_JAVAC_SERVER@ # Store javac server synchronization files here, and # the javac server log files. -JAVAC_SERVER_DIR=$(MAKESUPPORT_OUTPUTDIR)/javacservers +JAVAC_SERVER_DIR = $(MAKESUPPORT_OUTPUTDIR)/javacservers # Number of parallel jobs to use for compilation -JOBS?=@JOBS@ -TEST_JOBS?=@TEST_JOBS@ +JOBS ?= @JOBS@ +TEST_JOBS ?= @TEST_JOBS@ # Default make target -DEFAULT_MAKE_TARGET:=@DEFAULT_MAKE_TARGET@ -DEFAULT_LOG:=@DEFAULT_LOG@ +DEFAULT_MAKE_TARGET := @DEFAULT_MAKE_TARGET@ +DEFAULT_LOG := @DEFAULT_LOG@ # Fallback linker -ENABLE_FALLBACK_LINKER:=@ENABLE_FALLBACK_LINKER@ - -FREETYPE_TO_USE:=@FREETYPE_TO_USE@ -FREETYPE_LIBS:=@FREETYPE_LIBS@ -FREETYPE_CFLAGS:=@FREETYPE_CFLAGS@ -FONTCONFIG_CFLAGS:=@FONTCONFIG_CFLAGS@ -CUPS_CFLAGS:=@CUPS_CFLAGS@ -ALSA_LIBS:=@ALSA_LIBS@ -ALSA_CFLAGS:=@ALSA_CFLAGS@ -LIBFFI_LIBS:=@LIBFFI_LIBS@ -LIBFFI_CFLAGS:=@LIBFFI_CFLAGS@ -ENABLE_LIBFFI_BUNDLING:=@ENABLE_LIBFFI_BUNDLING@ -LIBFFI_LIB_FILE:=@LIBFFI_LIB_FILE@ +ENABLE_FALLBACK_LINKER := @ENABLE_FALLBACK_LINKER@ + +FREETYPE_TO_USE := @FREETYPE_TO_USE@ +FREETYPE_LIBS := @FREETYPE_LIBS@ +FREETYPE_CFLAGS := @FREETYPE_CFLAGS@ +FONTCONFIG_CFLAGS := @FONTCONFIG_CFLAGS@ +CUPS_CFLAGS := @CUPS_CFLAGS@ +ALSA_LIBS := @ALSA_LIBS@ +ALSA_CFLAGS := @ALSA_CFLAGS@ +LIBFFI_LIBS := @LIBFFI_LIBS@ +LIBFFI_CFLAGS := @LIBFFI_CFLAGS@ +ENABLE_LIBFFI_BUNDLING := @ENABLE_LIBFFI_BUNDLING@ +LIBFFI_LIB_FILE := @LIBFFI_LIB_FILE@ FILE_MACRO_CFLAGS := @FILE_MACRO_CFLAGS@ REPRODUCIBLE_CFLAGS := @REPRODUCIBLE_CFLAGS@ BRANCH_PROTECTION_CFLAGS := @BRANCH_PROTECTION_CFLAGS@ @@ -438,45 +439,45 @@ JMH_VERSION := @JMH_VERSION@ GTEST_FRAMEWORK_SRC := @GTEST_FRAMEWORK_SRC@ # Source file for cacerts -CACERTS_FILE=@CACERTS_FILE@ +CACERTS_FILE := @CACERTS_FILE@ # Source folder for user provided cacerts PEM files -CACERTS_SRC=@CACERTS_SRC@ +CACERTS_SRC := @CACERTS_SRC@ # Enable unlimited crypto policy -UNLIMITED_CRYPTO=@UNLIMITED_CRYPTO@ +UNLIMITED_CRYPTO := @UNLIMITED_CRYPTO@ -GCOV_ENABLED=@GCOV_ENABLED@ -JCOV_ENABLED=@JCOV_ENABLED@ -JCOV_HOME=@JCOV_HOME@ -JCOV_INPUT_JDK=@JCOV_INPUT_JDK@ -JCOV_FILTERS=@JCOV_FILTERS@ +GCOV_ENABLED := @GCOV_ENABLED@ +JCOV_ENABLED := @JCOV_ENABLED@ +JCOV_HOME := @JCOV_HOME@ +JCOV_INPUT_JDK := @JCOV_INPUT_JDK@ +JCOV_FILTERS := @JCOV_FILTERS@ # AddressSanitizer -ASAN_ENABLED:=@ASAN_ENABLED@ +ASAN_ENABLED := @ASAN_ENABLED@ # LeakSanitizer -LSAN_ENABLED:=@LSAN_ENABLED@ +LSAN_ENABLED := @LSAN_ENABLED@ # UndefinedBehaviorSanitizer -UBSAN_ENABLED:=@UBSAN_ENABLED@ -UBSAN_CFLAGS:=@UBSAN_CFLAGS@ -UBSAN_LDFLAGS:=@UBSAN_LDFLAGS@ +UBSAN_ENABLED := @UBSAN_ENABLED@ +UBSAN_CFLAGS := @UBSAN_CFLAGS@ +UBSAN_LDFLAGS := @UBSAN_LDFLAGS@ # Necessary additional compiler flags to compile X11 -X_CFLAGS:=@X_CFLAGS@ -X_LIBS:=@X_LIBS@ +X_CFLAGS := @X_CFLAGS@ +X_LIBS := @X_LIBS@ # The lowest required version of macosx -MACOSX_VERSION_MIN=@MACOSX_VERSION_MIN@ +MACOSX_VERSION_MIN := @MACOSX_VERSION_MIN@ # The highest allowed version of macosx -MACOSX_VERSION_MAX=@MACOSX_VERSION_MAX@ +MACOSX_VERSION_MAX := @MACOSX_VERSION_MAX@ # The macosx code signing configuration -MACOSX_CODESIGN_MODE:=@MACOSX_CODESIGN_MODE@ -MACOSX_CODESIGN_IDENTITY=@MACOSX_CODESIGN_IDENTITY@ +MACOSX_CODESIGN_MODE := @MACOSX_CODESIGN_MODE@ +MACOSX_CODESIGN_IDENTITY := @MACOSX_CODESIGN_IDENTITY@ # Toolchain type: gcc, clang, xlc, microsoft... -TOOLCHAIN_TYPE:=@TOOLCHAIN_TYPE@ +TOOLCHAIN_TYPE := @TOOLCHAIN_TYPE@ TOOLCHAIN_VERSION := @TOOLCHAIN_VERSION@ CC_VERSION_NUMBER := @CC_VERSION_NUMBER@ CXX_VERSION_NUMBER := @CXX_VERSION_NUMBER@ @@ -485,38 +486,38 @@ CXX_VERSION_NUMBER := @CXX_VERSION_NUMBER@ HOTSPOT_TOOLCHAIN_TYPE := @HOTSPOT_TOOLCHAIN_TYPE@ # Option used to tell the compiler whether to create 32- or 64-bit executables -COMPILER_TARGET_BITS_FLAG:=@COMPILER_TARGET_BITS_FLAG@ -COMPILER_SUPPORTS_TARGET_BITS_FLAG=@COMPILER_SUPPORTS_TARGET_BITS_FLAG@ +COMPILER_TARGET_BITS_FLAG := @COMPILER_TARGET_BITS_FLAG@ +COMPILER_SUPPORTS_TARGET_BITS_FLAG := @COMPILER_SUPPORTS_TARGET_BITS_FLAG@ # Option used to pass a command file to the compiler -COMPILER_COMMAND_FILE_FLAG:=@COMPILER_COMMAND_FILE_FLAG@ +COMPILER_COMMAND_FILE_FLAG := @COMPILER_COMMAND_FILE_FLAG@ # Option for specifying a file which saves the binder commands # produced by the link step (for debugging, currently AIX only) -COMPILER_BINDCMD_FILE_FLAG:=@COMPILER_BINDCMD_FILE_FLAG@ +COMPILER_BINDCMD_FILE_FLAG := @COMPILER_BINDCMD_FILE_FLAG@ -CC_OUT_OPTION:=@CC_OUT_OPTION@ -LD_OUT_OPTION:=@LD_OUT_OPTION@ -AR_OUT_OPTION:=@AR_OUT_OPTION@ +CC_OUT_OPTION := @CC_OUT_OPTION@ +LD_OUT_OPTION := @LD_OUT_OPTION@ +AR_OUT_OPTION := @AR_OUT_OPTION@ # Flags used for overriding the default opt setting for a C/C++ source file. -C_O_FLAG_HIGHEST_JVM:=@C_O_FLAG_HIGHEST_JVM@ -C_O_FLAG_HIGHEST:=@C_O_FLAG_HIGHEST@ -C_O_FLAG_HI:=@C_O_FLAG_HI@ -C_O_FLAG_NORM:=@C_O_FLAG_NORM@ -C_O_FLAG_NONE:=@C_O_FLAG_NONE@ -C_O_FLAG_SIZE:=@C_O_FLAG_SIZE@ -CXX_O_FLAG_HIGHEST_JVM:=@CXX_O_FLAG_HIGHEST_JVM@ -CXX_O_FLAG_HIGHEST:=@CXX_O_FLAG_HIGHEST@ -CXX_O_FLAG_HI:=@CXX_O_FLAG_HI@ -CXX_O_FLAG_NORM:=@CXX_O_FLAG_NORM@ -CXX_O_FLAG_NONE:=@CXX_O_FLAG_NONE@ -CXX_O_FLAG_SIZE:=@CXX_O_FLAG_SIZE@ +C_O_FLAG_HIGHEST_JVM := @C_O_FLAG_HIGHEST_JVM@ +C_O_FLAG_HIGHEST := @C_O_FLAG_HIGHEST@ +C_O_FLAG_HI := @C_O_FLAG_HI@ +C_O_FLAG_NORM := @C_O_FLAG_NORM@ +C_O_FLAG_NONE := @C_O_FLAG_NONE@ +C_O_FLAG_SIZE := @C_O_FLAG_SIZE@ +CXX_O_FLAG_HIGHEST_JVM := @CXX_O_FLAG_HIGHEST_JVM@ +CXX_O_FLAG_HIGHEST := @CXX_O_FLAG_HIGHEST@ +CXX_O_FLAG_HI := @CXX_O_FLAG_HI@ +CXX_O_FLAG_NORM := @CXX_O_FLAG_NORM@ +CXX_O_FLAG_NONE := @CXX_O_FLAG_NONE@ +CXX_O_FLAG_SIZE := @CXX_O_FLAG_SIZE@ GENDEPS_FLAGS := @GENDEPS_FLAGS@ DISABLE_WARNING_PREFIX := @DISABLE_WARNING_PREFIX@ -CFLAGS_WARNINGS_ARE_ERRORS:=@CFLAGS_WARNINGS_ARE_ERRORS@ +CFLAGS_WARNINGS_ARE_ERRORS := @CFLAGS_WARNINGS_ARE_ERRORS@ DISABLED_WARNINGS := @DISABLED_WARNINGS@ DISABLED_WARNINGS_C := @DISABLED_WARNINGS_C@ DISABLED_WARNINGS_CXX := @DISABLED_WARNINGS_CXX@ @@ -524,20 +525,20 @@ DISABLED_WARNINGS_CXX := @DISABLED_WARNINGS_CXX@ # A global flag (true or false) determining if native warnings are considered errors. WARNINGS_AS_ERRORS := @WARNINGS_AS_ERRORS@ -CFLAGS_CCACHE:=@CFLAGS_CCACHE@ -ADLC_LANGSTD_CXXFLAGS=@ADLC_LANGSTD_CXXFLAGS@ -ADLC_LDFLAGS=@ADLC_LDFLAGS@ +CFLAGS_CCACHE := @CFLAGS_CCACHE@ +ADLC_LANGSTD_CXXFLAGS := @ADLC_LANGSTD_CXXFLAGS@ +ADLC_LDFLAGS := @ADLC_LDFLAGS@ # Tools that potentially need to be cross compilation aware. CC := @CCACHE@ @ICECC@ @CC@ # CFLAGS used to compile the jdk native libraries (C-code) -CFLAGS_JDKLIB:=@CFLAGS_JDKLIB@ -CXXFLAGS_JDKLIB:=@CXXFLAGS_JDKLIB@ +CFLAGS_JDKLIB := @CFLAGS_JDKLIB@ +CXXFLAGS_JDKLIB := @CXXFLAGS_JDKLIB@ # CFLAGS used to compile the jdk native launchers (C-code) -CFLAGS_JDKEXE:=@CFLAGS_JDKEXE@ -CXXFLAGS_JDKEXE:=@CXXFLAGS_JDKEXE@ +CFLAGS_JDKEXE := @CFLAGS_JDKEXE@ +CXXFLAGS_JDKEXE := @CXXFLAGS_JDKEXE@ FDLIBM_CFLAGS := @FDLIBM_CFLAGS@ JVM_CFLAGS := @JVM_CFLAGS@ @@ -550,10 +551,10 @@ BASIC_ASFLAGS := @BASIC_ASFLAGS@ MACHINE_FLAG := @MACHINE_FLAG@ # These flags might contain variables set by a custom extension that is included later. -EXTRA_CFLAGS = @EXTRA_CFLAGS@ -EXTRA_CXXFLAGS = @EXTRA_CXXFLAGS@ -EXTRA_LDFLAGS = @EXTRA_LDFLAGS@ -EXTRA_ASFLAGS = @EXTRA_ASFLAGS@ +EXTRA_CFLAGS := @EXTRA_CFLAGS@ +EXTRA_CXXFLAGS := @EXTRA_CXXFLAGS@ +EXTRA_LDFLAGS := @EXTRA_LDFLAGS@ +EXTRA_ASFLAGS := @EXTRA_ASFLAGS@ CXX := @CCACHE@ @ICECC@ @CXX@ @@ -565,26 +566,26 @@ LD := @LD@ SYSROOT := @SYSROOT@ # LDFLAGS used to link the jdk native libraries (C-code) -LDFLAGS_JDKLIB:=@LDFLAGS_JDKLIB@ -JDKLIB_LIBS:=@JDKLIB_LIBS@ +LDFLAGS_JDKLIB := @LDFLAGS_JDKLIB@ +JDKLIB_LIBS := @JDKLIB_LIBS@ # LDFLAGS used to link the jdk native launchers (C-code) -LDFLAGS_JDKEXE:=@LDFLAGS_JDKEXE@ -JDKEXE_LIBS:=@JDKEXE_LIBS@ +LDFLAGS_JDKEXE := @LDFLAGS_JDKEXE@ +JDKEXE_LIBS := @JDKEXE_LIBS@ # LDFLAGS specific to C++ linking. -LDFLAGS_CXX_JDK:=@LDFLAGS_CXX_JDK@ +LDFLAGS_CXX_JDK := @LDFLAGS_CXX_JDK@ # LDFLAGS specific to partial linking. -LDFLAGS_CXX_PARTIAL_LINKING:=@LDFLAGS_CXX_PARTIAL_LINKING@ +LDFLAGS_CXX_PARTIAL_LINKING := @LDFLAGS_CXX_PARTIAL_LINKING@ # Sometimes a different linker is needed for c++ libs LDCXX := @LDCXX@ # The flags for linking libstdc++ linker. -LIBCXX:=@LIBCXX@ +LIBCXX := @LIBCXX@ # Compiler and linker flags used when building native tests -LDFLAGS_TESTEXE:=@LDFLAGS_TESTEXE@ +LDFLAGS_TESTEXE := @LDFLAGS_TESTEXE@ # BUILD_CC/BUILD_LD is a compiler/linker that generates code that is runnable on the # build platform. @@ -595,31 +596,31 @@ BUILD_LDCXX := @BUILD_LDCXX@ BUILD_AS := @BUILD_AS@ BUILD_AR := @BUILD_AR@ BUILD_NM := @BUILD_NM@ -BUILD_OBJCOPY:=@BUILD_OBJCOPY@ -BUILD_STRIP:=@BUILD_STRIP@ -BUILD_SYSROOT_CFLAGS:=@BUILD_SYSROOT_CFLAGS@ -BUILD_SYSROOT_LDFLAGS:=@BUILD_SYSROOT_LDFLAGS@ +BUILD_OBJCOPY := @BUILD_OBJCOPY@ +BUILD_STRIP := @BUILD_STRIP@ +BUILD_SYSROOT_CFLAGS := @BUILD_SYSROOT_CFLAGS@ +BUILD_SYSROOT_LDFLAGS := @BUILD_SYSROOT_LDFLAGS@ AS := @AS@ # AR is used to create a static library (is ar in unix, lib.exe in windows) AR := @AR@ -ARFLAGS:=@ARFLAGS@ +ARFLAGS := @ARFLAGS@ -NM:=@NM@ -NMFLAGS:=@NMFLAGS@ -STRIP:=@STRIP@ -OBJDUMP:=@OBJDUMP@ -CXXFILT:=@CXXFILT@ +NM := @NM@ +NMFLAGS := @NMFLAGS@ +STRIP := @STRIP@ +OBJDUMP := @OBJDUMP@ +CXXFILT := @CXXFILT@ -LIPO:=@LIPO@ -INSTALL_NAME_TOOL:=@INSTALL_NAME_TOOL@ +LIPO := @LIPO@ +INSTALL_NAME_TOOL := @INSTALL_NAME_TOOL@ METAL := @METAL@ METALLIB := @METALLIB@ # Options to linker to specify a mapfile. # (Note absence of := assignment, because we do not want to evaluate the macro body here) -SET_SHARED_LIBRARY_MAPFILE=@SET_SHARED_LIBRARY_MAPFILE@ +SET_SHARED_LIBRARY_MAPFILE = @SET_SHARED_LIBRARY_MAPFILE@ # # Options for generating debug symbols @@ -627,52 +628,53 @@ COMPILE_WITH_DEBUG_SYMBOLS := @COMPILE_WITH_DEBUG_SYMBOLS@ COPY_DEBUG_SYMBOLS := @COPY_DEBUG_SYMBOLS@ ZIP_EXTERNAL_DEBUG_SYMBOLS := @ZIP_EXTERNAL_DEBUG_SYMBOLS@ -CFLAGS_DEBUG_SYMBOLS:=@CFLAGS_DEBUG_SYMBOLS@ -ASFLAGS_DEBUG_SYMBOLS:=@ASFLAGS_DEBUG_SYMBOLS@ +CFLAGS_DEBUG_SYMBOLS := @CFLAGS_DEBUG_SYMBOLS@ +ASFLAGS_DEBUG_SYMBOLS := @ASFLAGS_DEBUG_SYMBOLS@ # # Compress (or not) jars -COMPRESS_JARS=@COMPRESS_JARS@ +COMPRESS_JARS := @COMPRESS_JARS@ # Options to linker to specify the library name. # (Note absence of := assignment, because we do not want to evaluate the macro body here) -SET_SHARED_LIBRARY_NAME=@SET_SHARED_LIBRARY_NAME@ +SET_SHARED_LIBRARY_NAME = @SET_SHARED_LIBRARY_NAME@ -SHARED_LIBRARY_FLAGS=@SHARED_LIBRARY_FLAGS@ +SHARED_LIBRARY_FLAGS := @SHARED_LIBRARY_FLAGS@ # Set origin using the linker, ie use the relative path to the dependent library to find the dependencies. # (Note absence of := assignment, because we do not want to evaluate the macro body here) -SET_SHARED_LIBRARY_ORIGIN=@SET_SHARED_LIBRARY_ORIGIN@ -SET_EXECUTABLE_ORIGIN=@SET_EXECUTABLE_ORIGIN@ +SET_SHARED_LIBRARY_ORIGIN = @SET_SHARED_LIBRARY_ORIGIN@ +SET_EXECUTABLE_ORIGIN = @SET_EXECUTABLE_ORIGIN@ # Different OS:es have different ways of naming shared libraries. -# The SHARED_LIBRARY macro takes "verify" as and argument and returns: +# The SHARED_LIBRARY and STATIC_LIBRARY macros takes "verify" as and argument and returns: # "libverify.so" or "libverify.dylib" or "verify.dll" depending on platform. # (Note absence of := assignment, because we do not want to evaluate the macro body here) -SHARED_LIBRARY=@SHARED_LIBRARY@ -STATIC_LIBRARY=@STATIC_LIBRARY@ -LIBRARY_PREFIX:=@LIBRARY_PREFIX@ -SHARED_LIBRARY_SUFFIX:=@SHARED_LIBRARY_SUFFIX@ -STATIC_LIBRARY_SUFFIX:=@STATIC_LIBRARY_SUFFIX@ -EXECUTABLE_SUFFIX:=@EXECUTABLE_SUFFIX@ -OBJ_SUFFIX:=@OBJ_SUFFIX@ -STATIC_BUILD:=@STATIC_BUILD@ - -STRIPFLAGS:=@STRIPFLAGS@ - -JAVA_FLAGS:=@JAVA_FLAGS@ -JAVA_FLAGS_BIG:=@JAVA_FLAGS_BIG@ -JAVA_FLAGS_SMALL:=@JAVA_FLAGS_SMALL@ -BUILDJDK_JAVA_FLAGS_SMALL:=@BUILDJDK_JAVA_FLAGS_SMALL@ -JAVA_TOOL_FLAGS_SMALL:=@JAVA_TOOL_FLAGS_SMALL@ +SHARED_LIBRARY = @SHARED_LIBRARY@ +STATIC_LIBRARY = @STATIC_LIBRARY@ + +LIBRARY_PREFIX := @LIBRARY_PREFIX@ +SHARED_LIBRARY_SUFFIX := @SHARED_LIBRARY_SUFFIX@ +STATIC_LIBRARY_SUFFIX := @STATIC_LIBRARY_SUFFIX@ +EXECUTABLE_SUFFIX := @EXECUTABLE_SUFFIX@ +OBJ_SUFFIX := @OBJ_SUFFIX@ +STATIC_BUILD := @STATIC_BUILD@ + +STRIPFLAGS := @STRIPFLAGS@ + +JAVA_FLAGS := @JAVA_FLAGS@ +JAVA_FLAGS_BIG := @JAVA_FLAGS_BIG@ +JAVA_FLAGS_SMALL := @JAVA_FLAGS_SMALL@ +BUILD_JAVA_FLAGS_SMALL := @BUILD_JAVA_FLAGS_SMALL@ +JAVA_TOOL_FLAGS_SMALL := @JAVA_TOOL_FLAGS_SMALL@ # The *_CMD variables are defined separately to be easily overridden in bootcycle-spec.gmk # for bootcycle-images build. Make sure to keep them in sync. Do not use the *_CMD # versions of the variables directly. -JAVA_CMD:=@JAVA@ -JAVAC_CMD:=@JAVAC@ -JAVADOC_CMD:=@JAVADOC@ -JAR_CMD:=@JAR@ +JAVA_CMD := @JAVA@ +JAVAC_CMD := @JAVAC@ +JAVADOC_CMD := @JAVADOC@ +JAR_CMD := @JAR@ JLINK_CMD := @JLINK@ JMOD_CMD := @JMOD@ # These variables are meant to be used. They are defined with = instead of := to make @@ -689,9 +691,10 @@ JTREG_JDK := @JTREG_JDK@ JTREG_JAVA = @FIXPATH@ $(JTREG_JDK)/bin/java $(JAVA_FLAGS_BIG) $(JAVA_FLAGS) BUILD_JAVA_FLAGS := @BOOTCYCLE_JVM_ARGS_BIG@ -BUILD_JAVA=@FIXPATH@ $(BUILD_JDK)/bin/java $(BUILD_JAVA_FLAGS) -BUILD_JAVAC=@FIXPATH@ $(BUILD_JDK)/bin/javac -BUILD_JAR=@FIXPATH@ $(BUILD_JDK)/bin/jar +BUILD_JAVA = @FIXPATH@ $(BUILD_JDK)/bin/java $(BUILD_JAVA_FLAGS) +BUILD_JAVA_SMALL = @FIXPATH@ $(BUILD_JDK)/bin/java $(BUILD_JAVA_FLAGS_SMALL) +BUILD_JAVAC = @FIXPATH@ $(BUILD_JDK)/bin/javac +BUILD_JAR = @FIXPATH@ $(BUILD_JDK)/bin/jar DOCS_REFERENCE_JAVADOC := @DOCS_REFERENCE_JAVADOC@ @@ -717,208 +720,157 @@ INTERIM_LANGTOOLS_ARGS := \ --patch-module java.base=$(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim \ $(INTERIM_LANGTOOLS_ADD_EXPORTS) \ # -JAVAC_MAIN_CLASS = -m jdk.compiler.interim/com.sun.tools.javac.Main -JAVADOC_MAIN_CLASS = -m jdk.javadoc.interim/jdk.javadoc.internal.tool.Main +JAVAC_MAIN_CLASS := -m jdk.compiler.interim/com.sun.tools.javac.Main +JAVADOC_MAIN_CLASS := -m jdk.javadoc.interim/jdk.javadoc.internal.tool.Main # You run the new javac using the boot jdk with $(BOOT_JDK)/bin/java $(NEW_JAVAC) ... # Use = assignment to be able to override in bootcycle-spec.gmk -NEW_JAVAC = $(INTERIM_LANGTOOLS_ARGS) $(JAVAC_MAIN_CLASS) +NEW_JAVAC = $(INTERIM_LANGTOOLS_ARGS) $(JAVAC_MAIN_CLASS) NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) $(JAVADOC_MAIN_CLASS) -JMOD_COMPRESS:=@JMOD_COMPRESS@ -JLINK_KEEP_PACKAGED_MODULES:=@JLINK_KEEP_PACKAGED_MODULES@ +JMOD_COMPRESS := @JMOD_COMPRESS@ +JLINK_KEEP_PACKAGED_MODULES := @JLINK_KEEP_PACKAGED_MODULES@ RCFLAGS := @RCFLAGS@ # Tools adhering to a minimal and common standard of posix compliance. -AWK:=@AWK@ -BASENAME:=@BASENAME@ -CAT:=@CAT@ -CCACHE:=@CCACHE@ +AWK := @AWK@ +BASENAME := @BASENAME@ +CAT := @CAT@ +CCACHE := @CCACHE@ # CD is going away, but remains to cater for legacy makefiles. -CD:=cd -CHMOD:=@CHMOD@ -CODESIGN:=@CODESIGN@ -CP:=@CP@ -CUT:=@CUT@ -DATE:=@DATE@ -IS_GNU_DATE:=@IS_GNU_DATE@ -DIFF:=@DIFF@ -DIRNAME:=@DIRNAME@ -DSYMUTIL:=@DSYMUTIL@ -FIND:=@FIND@ -FIND_DELETE:=@FIND_DELETE@ -FLOCK:=@FLOCK@ -ECHO:=@ECHO@ -EGREP:=@EGREP@ -FGREP:=@FGREP@ -GREP:=@GREP@ -GZIP:=@GZIP@ -HEAD:=@HEAD@ -LS:=@LS@ -LN:=@LN@ -MIG:=@MIG@ -MKDIR:=@MKDIR@ -MV:=@MV@ -NICE:=@NICE@ -PANDOC:=@PANDOC@ -PATCH:=@PATCH@ -PRINTF:=@PRINTF@ -READLINK:=@READLINK@ -RM:=@RM@ -RMDIR:=@RMDIR@ -SED:=@SED@ -SH:=@SH@ -SORT:=@SORT@ -TAR:=@TAR@ -TAIL:=@TAIL@ -TEE:=@TEE@ -TIME:=@TIME@ -IS_GNU_TIME:=@IS_GNU_TIME@ -TR:=@TR@ -TOUCH:=@TOUCH@ -WC:=@WC@ -XARGS:=@XARGS@ -ZIPEXE:=@ZIPEXE@ -UNZIP:=@UNZIP@ -MT:=@MT@ -RC:=@RC@ -DUMPBIN:=@DUMPBIN@ -PATHTOOL:=@PATHTOOL@ -WSLPATH:=@WSLPATH@ -LDD:=@LDD@ -OTOOL:=@OTOOL@ -READELF:=@READELF@ -EXPR:=@EXPR@ -FILE:=@FILE@ -DOT:=@DOT@ -GIT:=@GIT@ -OBJCOPY:=@OBJCOPY@ -SETFILE:=@SETFILE@ -XATTR:=@XATTR@ -JT_HOME:=@JT_HOME@ -JIB_HOME:=@JIB_HOME@ +CD := cd +CHMOD := @CHMOD@ +CODESIGN := @CODESIGN@ +CP := @CP@ +CUT := @CUT@ +DATE := @DATE@ +IS_GNU_DATE := @IS_GNU_DATE@ +DIFF := @DIFF@ +DIRNAME := @DIRNAME@ +DSYMUTIL := @DSYMUTIL@ +FIND := @FIND@ +FIND_DELETE := @FIND_DELETE@ +FLOCK := @FLOCK@ +ECHO := @ECHO@ +EGREP := @EGREP@ +FGREP := @FGREP@ +GREP := @GREP@ +GZIP := @GZIP@ +HEAD := @HEAD@ +LS := @LS@ +LN := @LN@ +MIG := @MIG@ +MKDIR := @MKDIR@ +MV := @MV@ +NICE := @NICE@ +PANDOC := @PANDOC@ +PATCH := @PATCH@ +PRINTF := @PRINTF@ +READLINK := @READLINK@ +RM := @RM@ +RMDIR := @RMDIR@ +SED := @SED@ +SH := @SH@ +SORT := @SORT@ +TAR := @TAR@ +TAIL := @TAIL@ +TEE := @TEE@ +TIME := @TIME@ +IS_GNU_TIME := @IS_GNU_TIME@ +TR := @TR@ +TOUCH := @TOUCH@ +WC := @WC@ +XARGS := @XARGS@ +ZIPEXE := @ZIPEXE@ +UNZIP := @UNZIP@ +MT := @MT@ +RC := @RC@ +DUMPBIN := @DUMPBIN@ +PATHTOOL := @PATHTOOL@ +WSLPATH := @WSLPATH@ +LDD := @LDD@ +OTOOL := @OTOOL@ +READELF := @READELF@ +EXPR := @EXPR@ +FILE := @FILE@ +DOT := @DOT@ +GIT := @GIT@ +OBJCOPY := @OBJCOPY@ +SETFILE := @SETFILE@ +XATTR := @XATTR@ +JT_HOME := @JT_HOME@ +JIB_HOME := @JIB_HOME@ DTRACE := @DTRACE@ FIXPATH := @FIXPATH@ FIXPATH_BASE := @FIXPATH_BASE@ -ULIMIT:=@ULIMIT@ +ULIMIT := @ULIMIT@ -TAR_TYPE:=@TAR_TYPE@ -TAR_INCLUDE_PARAM:=@TAR_INCLUDE_PARAM@ -TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TRANSFORM@ +TAR_TYPE := @TAR_TYPE@ +TAR_INCLUDE_PARAM := @TAR_INCLUDE_PARAM@ +TAR_SUPPORTS_TRANSFORM := @TAR_SUPPORTS_TRANSFORM@ # Build setup -USE_EXTERNAL_LIBJPEG:=@USE_EXTERNAL_LIBJPEG@ -USE_EXTERNAL_LIBGIF:=@USE_EXTERNAL_LIBGIF@ -USE_EXTERNAL_LIBZ:=@USE_EXTERNAL_LIBZ@ -LIBZ_CFLAGS:=@LIBZ_CFLAGS@ -LIBZ_LIBS:=@LIBZ_LIBS@ -LIBZIP_CAN_USE_MMAP:=@LIBZIP_CAN_USE_MMAP@ -MSVCR_DLL:=@MSVCR_DLL@ -VCRUNTIME_1_DLL:=@VCRUNTIME_1_DLL@ -MSVCP_DLL:=@MSVCP_DLL@ -UCRT_DLL_DIR:=@UCRT_DLL_DIR@ -ENABLE_PANDOC:=@ENABLE_PANDOC@ -PANDOC_MARKDOWN_FLAG:=@PANDOC_MARKDOWN_FLAG@ - -#################################################### -# -# INSTALLATION -# - -# Common prefix for all installed files. Defaults to /usr/local, -# but /opt/myjdk is another common version. -INSTALL_PREFIX=@prefix@ - -# Directories containing architecture-dependent files should be relative to exec_prefix -INSTALL_EXECPREFIX=@exec_prefix@ - -# java,javac,javap etc are installed here. -INSTALL_BINDIR=@bindir@ - -# Read only architecture-independent data -INSTALL_DATADIR=@datadir@ - -# Root of above. -INSTALL_DATAROOTDIR=@datarootdir@ - -# Doc files, other than info and man. -INSTALL_DOCDIR=@docdir@ - -# Html documentation -INSTALL_HTMLDIR=@htmldir@ - -# Installing C header files, JNI headers for example. -INSTALL_INCLUDEDIR=@includedir@ - -# Installing library files.... -INSTALL_INCLUDEDIR=@libdir@ - -# Executables that other programs run. -INSTALL_LIBEXECDIR=@libexecdir@ - -# Locale-dependent but architecture-independent data, such as message catalogs. -INSTALL_LOCALEDIR=@localedir@ - -# Modifiable single-machine data -INSTALL_LOCALSTATEDIR=@localstatedir@ - -# Man pages -INSTALL_MANDIR=@mandir@ - -# Modifiable architecture-independent data. -INSTALL_SHAREDSTATEDIR=@sharedstatedir@ - -# Read-only single-machine data -INSTALL_SYSCONFDIR=@sysconfdir@ +USE_EXTERNAL_LIBJPEG := @USE_EXTERNAL_LIBJPEG@ +USE_EXTERNAL_LIBGIF := @USE_EXTERNAL_LIBGIF@ +USE_EXTERNAL_LIBZ := @USE_EXTERNAL_LIBZ@ +LIBZ_CFLAGS := @LIBZ_CFLAGS@ +LIBZ_LIBS := @LIBZ_LIBS@ +LIBZIP_CAN_USE_MMAP := @LIBZIP_CAN_USE_MMAP@ +MSVCR_DLL := @MSVCR_DLL@ +VCRUNTIME_1_DLL := @VCRUNTIME_1_DLL@ +MSVCP_DLL := @MSVCP_DLL@ +UCRT_DLL_DIR := @UCRT_DLL_DIR@ +ENABLE_PANDOC := @ENABLE_PANDOC@ +PANDOC_MARKDOWN_FLAG := @PANDOC_MARKDOWN_FLAG@ #################################################### # # Libraries # -USE_EXTERNAL_LCMS:=@USE_EXTERNAL_LCMS@ -LCMS_CFLAGS:=@LCMS_CFLAGS@ -LCMS_LIBS:=@LCMS_LIBS@ +USE_EXTERNAL_LCMS := @USE_EXTERNAL_LCMS@ +LCMS_CFLAGS := @LCMS_CFLAGS@ +LCMS_LIBS := @LCMS_LIBS@ -USE_EXTERNAL_HARFBUZZ:=@USE_EXTERNAL_HARFBUZZ@ -HARFBUZZ_CFLAGS:=@HARFBUZZ_CFLAGS@ -HARFBUZZ_LIBS:=@HARFBUZZ_LIBS@ +USE_EXTERNAL_HARFBUZZ := @USE_EXTERNAL_HARFBUZZ@ +HARFBUZZ_CFLAGS := @HARFBUZZ_CFLAGS@ +HARFBUZZ_LIBS := @HARFBUZZ_LIBS@ -USE_EXTERNAL_LIBPNG:=@USE_EXTERNAL_LIBPNG@ -PNG_LIBS:=@PNG_LIBS@ -PNG_CFLAGS:=@PNG_CFLAGS@ +USE_EXTERNAL_LIBPNG := @USE_EXTERNAL_LIBPNG@ +PNG_LIBS := @PNG_LIBS@ +PNG_CFLAGS := @PNG_CFLAGS@ #################################################### # # Misc # -INCLUDE_SA=@INCLUDE_SA@ -INCLUDE_JVMCI=@INCLUDE_JVMCI@ -INCLUDE_COMPILER2=@INCLUDE_COMPILER2@ +INCLUDE_SA := @INCLUDE_SA@ +INCLUDE_JVMCI := @INCLUDE_JVMCI@ +INCLUDE_COMPILER2 := @INCLUDE_COMPILER2@ -OS_VERSION_MAJOR:=@OS_VERSION_MAJOR@ -OS_VERSION_MINOR:=@OS_VERSION_MINOR@ -OS_VERSION_MICRO:=@OS_VERSION_MICRO@ +OS_VERSION_MAJOR := @OS_VERSION_MAJOR@ +OS_VERSION_MINOR := @OS_VERSION_MINOR@ +OS_VERSION_MICRO := @OS_VERSION_MICRO@ # Images directory definitions -JDK_IMAGE_SUBDIR:=jdk -JRE_IMAGE_SUBDIR:=jre +JDK_IMAGE_SUBDIR := jdk +JRE_IMAGE_SUBDIR := jre JCOV_IMAGE_SUBDIR := jdk-jcov # Colon left out to be able to override output dir for bootcycle-images -JDK_IMAGE_DIR=$(IMAGES_OUTPUTDIR)/$(JDK_IMAGE_SUBDIR) -JRE_IMAGE_DIR=$(IMAGES_OUTPUTDIR)/$(JRE_IMAGE_SUBDIR) +JDK_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(JDK_IMAGE_SUBDIR) +JRE_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(JRE_IMAGE_SUBDIR) JCOV_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(JCOV_IMAGE_SUBDIR) # Test image, as above -TEST_IMAGE_SUBDIR:=test -TEST_IMAGE_DIR=$(IMAGES_OUTPUTDIR)/$(TEST_IMAGE_SUBDIR) +TEST_IMAGE_SUBDIR := test +TEST_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(TEST_IMAGE_SUBDIR) # Symbols image -SYMBOLS_IMAGE_SUBDIR:=symbols -SYMBOLS_IMAGE_DIR=$(IMAGES_OUTPUTDIR)/$(SYMBOLS_IMAGE_SUBDIR) +SYMBOLS_IMAGE_SUBDIR := symbols +SYMBOLS_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(SYMBOLS_IMAGE_SUBDIR) # Interim image INTERIM_JMODS_DIR := $(SUPPORT_OUTPUTDIR)/interim-jmods @@ -947,20 +899,20 @@ GRAAL_BUILDER_IMAGE_SUBDIR := graal-builder-jdk GRAAL_BUILDER_IMAGE_DIR := $(IMAGES_OUTPUTDIR)/$(GRAAL_BUILDER_IMAGE_SUBDIR) # Macosx bundles directory definitions -JDK_MACOSX_BUNDLE_SUBDIR=jdk-bundle -JRE_MACOSX_BUNDLE_SUBDIR=jre-bundle -JDK_MACOSX_BUNDLE_SUBDIR_SIGNED=jdk-bundle-signed -JRE_MACOSX_BUNDLE_SUBDIR_SIGNED=jre-bundle-signed -JDK_MACOSX_BUNDLE_DIR=$(IMAGES_OUTPUTDIR)/$(JDK_MACOSX_BUNDLE_SUBDIR) -JRE_MACOSX_BUNDLE_DIR=$(IMAGES_OUTPUTDIR)/$(JRE_MACOSX_BUNDLE_SUBDIR) -JDK_MACOSX_BUNDLE_DIR_SIGNED=$(IMAGES_OUTPUTDIR)/$(JDK_MACOSX_BUNDLE_SUBDIR_SIGNED) -JRE_MACOSX_BUNDLE_DIR_SIGNED=$(IMAGES_OUTPUTDIR)/$(JRE_MACOSX_BUNDLE_SUBDIR_SIGNED) -JDK_MACOSX_BUNDLE_TOP_DIR=jdk-$(VERSION_NUMBER).jdk -JRE_MACOSX_BUNDLE_TOP_DIR=jre-$(VERSION_NUMBER).jre -JDK_MACOSX_CONTENTS_SUBDIR=$(JDK_MACOSX_BUNDLE_TOP_DIR)/Contents -JRE_MACOSX_CONTENTS_SUBDIR=$(JRE_MACOSX_BUNDLE_TOP_DIR)/Contents -JDK_MACOSX_CONTENTS_DIR=$(JDK_MACOSX_BUNDLE_DIR)/$(JDK_MACOSX_CONTENTS_SUBDIR) -JRE_MACOSX_CONTENTS_DIR=$(JRE_MACOSX_BUNDLE_DIR)/$(JRE_MACOSX_CONTENTS_SUBDIR) +JDK_MACOSX_BUNDLE_SUBDIR := jdk-bundle +JRE_MACOSX_BUNDLE_SUBDIR := jre-bundle +JDK_MACOSX_BUNDLE_SUBDIR_SIGNED := jdk-bundle-signed +JRE_MACOSX_BUNDLE_SUBDIR_SIGNED := jre-bundle-signed +JDK_MACOSX_BUNDLE_DIR = $(IMAGES_OUTPUTDIR)/$(JDK_MACOSX_BUNDLE_SUBDIR) +JRE_MACOSX_BUNDLE_DIR = $(IMAGES_OUTPUTDIR)/$(JRE_MACOSX_BUNDLE_SUBDIR) +JDK_MACOSX_BUNDLE_DIR_SIGNED = $(IMAGES_OUTPUTDIR)/$(JDK_MACOSX_BUNDLE_SUBDIR_SIGNED) +JRE_MACOSX_BUNDLE_DIR_SIGNED = $(IMAGES_OUTPUTDIR)/$(JRE_MACOSX_BUNDLE_SUBDIR_SIGNED) +JDK_MACOSX_BUNDLE_TOP_DIR = jdk-$(VERSION_NUMBER).jdk +JRE_MACOSX_BUNDLE_TOP_DIR = jre-$(VERSION_NUMBER).jre +JDK_MACOSX_CONTENTS_SUBDIR = $(JDK_MACOSX_BUNDLE_TOP_DIR)/Contents +JRE_MACOSX_CONTENTS_SUBDIR = $(JRE_MACOSX_BUNDLE_TOP_DIR)/Contents +JDK_MACOSX_CONTENTS_DIR = $(JDK_MACOSX_BUNDLE_DIR)/$(JDK_MACOSX_CONTENTS_SUBDIR) +JRE_MACOSX_CONTENTS_DIR = $(JRE_MACOSX_BUNDLE_DIR)/$(JRE_MACOSX_CONTENTS_SUBDIR) # Bundle names ifneq ($(VERSION_BUILD), ) @@ -992,13 +944,13 @@ STATIC_LIBS_GRAAL_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-static-libs-graal$(DEBUG_P JCOV_BUNDLE_NAME := jdk-jcov-$(BASE_NAME)_bin$(DEBUG_PART).$(JDK_BUNDLE_EXTENSION) JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JDK_BUNDLE_NAME) -JRE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JRE_BUNDLE_NAME) -JDK_SYMBOLS_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JDK_SYMBOLS_BUNDLE_NAME) +JRE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JRE_BUNDLE_NAME) +JDK_SYMBOLS_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JDK_SYMBOLS_BUNDLE_NAME) TEST_DEMOS_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(TEST_DEMOS_BUNDLE_NAME) -TEST_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(TEST_BUNDLE_NAME) -DOCS_JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JDK_BUNDLE_NAME) -DOCS_JAVASE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JAVASE_BUNDLE_NAME) -DOCS_REFERENCE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_REFERENCE_BUNDLE_NAME) +TEST_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(TEST_BUNDLE_NAME) +DOCS_JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JDK_BUNDLE_NAME) +DOCS_JAVASE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JAVASE_BUNDLE_NAME) +DOCS_REFERENCE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_REFERENCE_BUNDLE_NAME) JCOV_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JCOV_BUNDLE_NAME) # This macro is called to allow inclusion of closed source counterparts. diff --git a/make/common/MakeIO.gmk b/make/common/MakeIO.gmk index 6075e034a19a5..75a387c34d947 100644 --- a/make/common/MakeIO.gmk +++ b/make/common/MakeIO.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk index bcbb6d1bab150..fa885dacb6da6 100644 --- a/make/common/Modules.gmk +++ b/make/common/Modules.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/ProcessMarkdown.gmk b/make/common/ProcessMarkdown.gmk index b5dc020be2c94..e78c026a89c37 100644 --- a/make/common/ProcessMarkdown.gmk +++ b/make/common/ProcessMarkdown.gmk @@ -1,4 +1,4 @@ -# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index c26c648ba3808..f8af10393624a 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -407,6 +407,8 @@ var getJibProfilesCommon = function (input, data) { * @returns {{}} Profiles part of the configuration */ var getJibProfilesProfiles = function (input, common, data) { + var cross_compiling = input.build_platform != input.target_platform; + // Main SE profiles var profiles = { @@ -484,14 +486,12 @@ var getJibProfilesProfiles = function (input, common, data) { "linux-aarch64": { target_os: "linux", target_cpu: "aarch64", - build_cpu: "x64", dependencies: ["devkit", "gtest", "build_devkit", "pandoc"], configure_args: [ - "--openjdk-target=aarch64-linux-gnu", "--with-zlib=system", "--disable-dtrace", "--enable-compatible-cds-alignment", - ], + ].concat(cross_compiling ? ["--openjdk-target=aarch64-linux-gnu"] : []), }, "linux-arm32": { diff --git a/make/data/hotspot-symbols/symbols-unix b/make/data/hotspot-symbols/symbols-unix index d2be2cca7e829..9ca040794f36e 100644 --- a/make/data/hotspot-symbols/symbols-unix +++ b/make/data/hotspot-symbols/symbols-unix @@ -203,7 +203,6 @@ JVM_SetStackWalkContinuation JVM_SetThreadPriority JVM_SleepNanos JVM_StartThread -JVM_SupportsCX8 JVM_TotalMemory JVM_UnloadLibrary JVM_WaitForReferencePendingList diff --git a/make/data/ubsan/ubsan_default_options.c b/make/data/ubsan/ubsan_default_options.c index ee426ebbc39fe..011d1a675a90f 100644 --- a/make/data/ubsan/ubsan_default_options.c +++ b/make/data/ubsan/ubsan_default_options.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/data/ubsan/ubsan_default_options.cpp b/make/data/ubsan/ubsan_default_options.cpp index dbebb5b2977e0..b0eb8eea0e2a5 100644 --- a/make/data/ubsan/ubsan_default_options.cpp +++ b/make/data/ubsan/ubsan_default_options.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/devkit/createMacosxDevkit.sh b/make/devkit/createMacosxDevkit.sh index 0c15ebfaa1fc1..414773194d9ca 100644 --- a/make/devkit/createMacosxDevkit.sh +++ b/make/devkit/createMacosxDevkit.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index bba3ce15bb152..0d17f7a3be562 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index cbe60fde20522..8eb57d76536a8 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/hotspot/lib/JvmFlags.gmk b/make/hotspot/lib/JvmFlags.gmk index 1fadd5331aa38..e5929ab994ba0 100644 --- a/make/hotspot/lib/JvmFlags.gmk +++ b/make/hotspot/lib/JvmFlags.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/ide/eclipse/CreateWorkspace.gmk b/make/ide/eclipse/CreateWorkspace.gmk index 51257d65ae298..a7937a33d9e7e 100644 --- a/make/ide/eclipse/CreateWorkspace.gmk +++ b/make/ide/eclipse/CreateWorkspace.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/ide/idea/jdk/template/src/idea/JdkIdeaAntLogger.java b/make/ide/idea/jdk/template/src/idea/JdkIdeaAntLogger.java index 9634fb9d388d4..dd640bd95a09a 100644 --- a/make/ide/idea/jdk/template/src/idea/JdkIdeaAntLogger.java +++ b/make/ide/idea/jdk/template/src/idea/JdkIdeaAntLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/ide/idea/langtools/template/src/idea/LangtoolsIdeaAntLogger.java b/make/ide/idea/langtools/template/src/idea/LangtoolsIdeaAntLogger.java index 8eb5313fb8407..5ff0f116ce0f5 100644 --- a/make/ide/idea/langtools/template/src/idea/LangtoolsIdeaAntLogger.java +++ b/make/ide/idea/langtools/template/src/idea/LangtoolsIdeaAntLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/ide/visualstudio/hotspot/CreateVSProject.gmk b/make/ide/visualstudio/hotspot/CreateVSProject.gmk index 17051fd03d0bb..cd093d3c6de85 100644 --- a/make/ide/visualstudio/hotspot/CreateVSProject.gmk +++ b/make/ide/visualstudio/hotspot/CreateVSProject.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/ide/visualstudio/hotspot/src/classes/build/tools/projectcreator/WinGammaPlatformVC10.java b/make/ide/visualstudio/hotspot/src/classes/build/tools/projectcreator/WinGammaPlatformVC10.java index ed085dae09562..3a2a0242e5a1c 100644 --- a/make/ide/visualstudio/hotspot/src/classes/build/tools/projectcreator/WinGammaPlatformVC10.java +++ b/make/ide/visualstudio/hotspot/src/classes/build/tools/projectcreator/WinGammaPlatformVC10.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java index 90c70f75cb9c1..49c74544be66e 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/jdk/src/classes/build/tools/depend/Depend.java b/make/jdk/src/classes/build/tools/depend/Depend.java index 4dcf130411f3d..def3265b213b7 100644 --- a/make/jdk/src/classes/build/tools/depend/Depend.java +++ b/make/jdk/src/classes/build/tools/depend/Depend.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/jdk/src/classes/build/tools/depend/DependTest.java b/make/jdk/src/classes/build/tools/depend/DependTest.java index 810397b62d88e..614b91bc0379d 100644 --- a/make/jdk/src/classes/build/tools/depend/DependTest.java +++ b/make/jdk/src/classes/build/tools/depend/DependTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/jdk/src/classes/build/tools/generatecharacter/PropList.java b/make/jdk/src/classes/build/tools/generatecharacter/PropList.java index 8d354ab4d5fd6..7af57da4b9e46 100644 --- a/make/jdk/src/classes/build/tools/generatecharacter/PropList.java +++ b/make/jdk/src/classes/build/tools/generatecharacter/PropList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index 76dcdb6273a2b..860a96f2b677d 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -150,7 +150,9 @@ import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Pair; +import java.nio.file.DirectoryStream; import java.util.Optional; +import java.util.function.Consumer; /** * A tool for processing the .sym.txt files. @@ -228,11 +230,25 @@ public class CreateSymbols { */ @SuppressWarnings("unchecked") public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFile, String ctSymLocation, - long timestamp, String currentVersion, String systemModules) throws IOException { + long timestamp, String currentVersion, String preReleaseTag, String moduleClasses) throws IOException { LoadDescriptions data = load(ctDescriptionFileExtra != null ? Paths.get(ctDescriptionFileExtra) : null, Paths.get(ctDescriptionFile)); + int currentVersionParsed = Integer.parseInt(currentVersion); + + currentVersion = Integer.toString(currentVersionParsed, Character.MAX_RADIX); + currentVersion = currentVersion.toUpperCase(Locale.ROOT); + + String previousVersion = Integer.toString(currentVersionParsed - 1, Character.MAX_RADIX); + + previousVersion = previousVersion.toUpperCase(Locale.ROOT); + + //load current version classes: + Path moduleClassPath = Paths.get(moduleClasses); + + loadVersionClassesFromDirectory(data.classes, data.modules, moduleClassPath, currentVersion, previousVersion); + stripNonExistentAnnotations(data); splitHeaders(data.classes); @@ -241,12 +257,19 @@ public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFil for (ModuleDescription md : data.modules.values()) { for (ModuleHeaderDescription mhd : md.header) { - List versionsList = - Collections.singletonList(mhd.versions); writeModulesForVersions(directory2FileData, md, mhd, - versionsList); + mhd.versions, + version -> { + String versionString = Character.toString(version); + int versionNumber = Integer.parseInt(versionString, Character.MAX_RADIX); + versionString = Integer.toString(versionNumber); + if (versionNumber == currentVersionParsed && !preReleaseTag.isEmpty()) { + versionString = versionString + "-" + preReleaseTag; + } + return versionString; + }); List packages = new ArrayList<>(); mhd.exports.stream() .map(ExportsDescription::packageName) @@ -293,13 +316,6 @@ public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFil } } - currentVersion = Integer.toString(Integer.parseInt(currentVersion), Character.MAX_RADIX); - currentVersion = currentVersion.toUpperCase(Locale.ROOT); - - openDirectory(directory2FileData, currentVersion + "/") - .add(new FileData(currentVersion + "/system-modules", - Files.readAllBytes(Paths.get(systemModules)))); - try (OutputStream fos = new FileOutputStream(ctSymLocation); OutputStream bos = new BufferedOutputStream(fos); ZipOutputStream jos = new ZipOutputStream(bos)) { @@ -788,10 +804,14 @@ void writeClassesForVersions(Map> directory2FileData, void writeModulesForVersions(Map> directory2FileData, ModuleDescription moduleDescription, ModuleHeaderDescription header, - Iterable versions) + String versions, + Function version2ModuleVersion) throws IOException { - for (String ver : versions) { - writeModule(directory2FileData, moduleDescription, header, ver); + //ensure every module-info.class is written separatelly, + //so that the correct version is used for it: + for (char ver : versions.toCharArray()) { + writeModule(directory2FileData, moduleDescription, header, ver, + version2ModuleVersion); } } @@ -799,7 +819,8 @@ void writeModulesForVersions(Map> directory2FileData, void writeModule(Map> directory2FileData, ModuleDescription moduleDescription, ModuleHeaderDescription header, - String version) throws IOException { + char version, + Function version2ModuleVersion) throws IOException { List constantPool = new ArrayList<>(); constantPool.add(null); int currentClass = addClass(constantPool, "module-info"); @@ -807,7 +828,9 @@ void writeModule(Map> directory2FileData, int[] interfaces = new int[0]; AccessFlags flags = new AccessFlags(header.flags); Map attributesMap = new HashMap<>(); - addAttributes(moduleDescription, header, constantPool, attributesMap); + String versionString = Character.toString(version); + addAttributes(moduleDescription, header, constantPool, attributesMap, + version2ModuleVersion.apply(version)); Attributes attributes = new Attributes(attributesMap); CPInfo[] cpData = constantPool.toArray(new CPInfo[constantPool.size()]); ConstantPool cp = new ConstantPool(cpData); @@ -823,7 +846,7 @@ void writeModule(Map> directory2FileData, new Method[0], attributes); - doWrite(directory2FileData, version, moduleDescription.name, "module-info" + EXTENSION, classFile); + doWrite(directory2FileData, versionString, moduleDescription.name, "module-info" + EXTENSION, classFile); } void writeClass(Map> directory2FileData, @@ -924,7 +947,8 @@ public FileData(String fileName, byte[] fileData) { private void addAttributes(ModuleDescription md, ModuleHeaderDescription header, List cp, - Map attributes) { + Map attributes, + String moduleVersion) { addGenericAttributes(header, cp, attributes); if (header.moduleResolution != null) { int attrIdx = addString(cp, Attribute.ModuleResolution); @@ -945,12 +969,13 @@ private void addAttributes(ModuleDescription md, attributes.put(Attribute.ModuleMainClass, new ModuleMainClass_attribute(attrIdx, targetIdx)); } + int versionIdx = addString(cp, moduleVersion); int attrIdx = addString(cp, Attribute.Module); attributes.put(Attribute.Module, new Module_attribute(attrIdx, addModuleName(cp, md.name), 0, - 0, + versionIdx, header.requires .stream() .map(r -> createRequiresEntry(cp, r)) @@ -1123,8 +1148,9 @@ private void addAttributes(MethodDescription desc, List constantPool, Ma MethodParameters_attribute.Entry[] entries = desc.methodParameters .stream() - .map(p -> new MethodParameters_attribute.Entry(addString(constantPool, p.name), - p.flags)) + .map(p -> new MethodParameters_attribute.Entry(p.name == null || p.name.isEmpty() ? 0 + : addString(constantPool, p.name), + p.flags)) .toArray(s -> new MethodParameters_attribute.Entry[s]); attributes.put(Attribute.MethodParameters, new MethodParameters_attribute(attributeString, entries)); @@ -1492,7 +1518,7 @@ private void loadVersionClasses(ClassList classes, } } - ExcludeIncludeList currentEIList = excludesIncludes; + ExcludeIncludeList currentEIList; if (!currentVersionModules.isEmpty()) { Set privateIncludes = @@ -1509,19 +1535,182 @@ private void loadVersionClasses(ClassList classes, currentEIList = new ExcludeIncludeList(includes, privateIncludes, Collections.emptySet()); + } else { + currentEIList = excludesIncludes; } ClassList currentVersionClasses = new ClassList(); + Map extraModulesPackagesToDerive = new HashMap<>(); for (byte[] classFileData : classData) { try (InputStream in = new ByteArrayInputStream(classFileData)) { inspectClassFile(in, currentVersionClasses, - currentEIList, version); + currentEIList, version, + cf -> { + PermittedSubclasses_attribute permitted = (PermittedSubclasses_attribute) cf.getAttribute(Attribute.PermittedSubclasses); + if (permitted != null) { + try { + String currentPack = cf.getName().substring(0, cf.getName().lastIndexOf('/')); + + for (int i = 0; i < permitted.subtypes.length; i++) { + String permittedClassName = cf.constant_pool.getClassInfo(permitted.subtypes[i]).getName(); + if (!currentEIList.accepts(permittedClassName, false)) { + String permittedPack = permittedClassName.substring(0, permittedClassName.lastIndexOf('/')); + + extraModulesPackagesToDerive.put(permittedPack, currentPack); + } + } + } catch (ConstantPoolException ex) { + throw new IllegalStateException(ex); + } + } + }); } catch (IOException | ConstantPoolException ex) { throw new IllegalStateException(ex); } } + //derive extra module packages for permitted types based on on their supertypes: + boolean modified; + + do { + modified = false; + + for (Iterator> it = extraModulesPackagesToDerive.entrySet().iterator(); it.hasNext();) { + Entry e = it.next(); + Optional module = currentVersionModules.values().stream().map(md -> md.header.get(0)).filter(d -> containsPackage(d, e.getValue())).findAny(); + if (module.isPresent()) { + if (!module.get().extraModulePackages.contains(e.getKey())) { + module.get().extraModulePackages.add(e.getKey()); + } + it.remove(); + modified = true; + } + } + } while (modified); + + if (!extraModulesPackagesToDerive.isEmpty()) { + throw new AssertionError("Cannot derive some owning modules: " + extraModulesPackagesToDerive); + } + + finishClassLoading(classes, modules, currentVersionModules, currentVersionClasses, currentEIList, version, baseline); + } + + private boolean containsPackage(ModuleHeaderDescription module, String pack) { + return module.exports.stream().filter(ed -> ed.packageName().equals(pack)).findAny().isPresent() || + module.extraModulePackages.contains(pack); + } + + private void loadVersionClassesFromDirectory(ClassList classes, + Map modules, + Path modulesDirectory, + String version, + String baseline) { + Map currentVersionModules = + new HashMap<>(); + ClassList currentVersionClasses = new ClassList(); + Set privateIncludes = new HashSet<>(); + Set includes = new HashSet<>(); + ExcludeIncludeList currentEIList = new ExcludeIncludeList(includes, + privateIncludes, + Collections.emptySet()); + + try { + Map modulePath2Header = new HashMap<>(); + List pendingExportedDirectories = new ArrayList<>(); + + try (DirectoryStream ds = Files.newDirectoryStream(modulesDirectory)) { + for (Path p : ds) { + Path moduleInfo = p.resolve("module-info.class"); + + if (Files.isReadable(moduleInfo)) { + ModuleDescription md; + + try (InputStream in = Files.newInputStream(moduleInfo)) { + md = inspectModuleInfoClassFile(in, + currentVersionModules, version); + } + if (md == null) { + continue; + } + + modulePath2Header.put(p, md.header.get(0)); + + Set currentModuleExports = + md.header.get(0).exports.stream() + .filter(e -> !e.isQualified()) + .map(e -> e.packageName + '/') + .collect(Collectors.toSet()); + + for (String dir : currentModuleExports) { + includes.add(dir); + pendingExportedDirectories.add(p.resolve(dir)); + } + } + } + } + + List pendingExtraClasses = new ArrayList<>(); + + for (Path exported : pendingExportedDirectories) { + try (DirectoryStream ds = Files.newDirectoryStream(exported)) { + for (Path p2 : ds) { + if (!Files.isRegularFile(p2) || !p2.getFileName().toString().endsWith(".class")) { + continue; + } + + loadFromDirectoryHandleClassFile(p2, currentVersionClasses, + currentEIList, version, + pendingExtraClasses); + } + } + } + + while (!pendingExtraClasses.isEmpty()) { + String current = pendingExtraClasses.remove(pendingExtraClasses.size() - 1); + + if (currentVersionClasses.find(current, true) != null) { + continue; + } + + for (Entry e : modulePath2Header.entrySet()) { + Path currentPath = e.getKey().resolve(current + ".class"); + + if (Files.isReadable(currentPath)) { + String pack = current.substring(0, current.lastIndexOf('/')); + + e.getValue().extraModulePackages.add(pack); + + loadFromDirectoryHandleClassFile(currentPath, currentVersionClasses, + currentEIList, version, + pendingExtraClasses); + } + } + } + } catch (IOException | ConstantPoolException ex) { + throw new IllegalStateException(ex); + } + + finishClassLoading(classes, modules, currentVersionModules, currentVersionClasses, currentEIList, version, baseline); + } + + private void loadFromDirectoryHandleClassFile(Path path, ClassList currentVersionClasses, + ExcludeIncludeList currentEIList, String version, + List todo) throws IOException, ConstantPoolException { + try (InputStream in = Files.newInputStream(path)) { + inspectClassFile(in, currentVersionClasses, + currentEIList, version, + cf -> { + Set superTypes = otherRelevantTypesWithOwners(cf); + + currentEIList.privateIncludeList.addAll(superTypes); + todo.addAll(superTypes); + }); + } + } + + private void finishClassLoading(ClassList classes, Map modules, Map currentVersionModules, ClassList currentVersionClasses, ExcludeIncludeList currentEIList, String version, + String baseline) { ModuleDescription unsupported = currentVersionModules.get("jdk.unsupported"); @@ -1563,6 +1752,11 @@ private void loadVersionClasses(ClassList classes, for (String i : header.implementsAttr) { modified |= include(includedClasses, currentVersionClasses, i); } + if (header.permittedSubclasses != null) { + for (String i : header.permittedSubclasses) { + modified |= include(includedClasses, currentVersionClasses, i); + } + } modified |= includeOutputType(Collections.singleton(header), h -> "", @@ -1962,6 +2156,11 @@ private List dumpCurrentClasses() throws IOException { public static boolean ALLOW_NON_EXISTING_CLASSES = false; private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeList excludesIncludes, String version) throws IOException, ConstantPoolException { + inspectClassFile(in, classes, excludesIncludes, version, cf -> {}); + } + + private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeList excludesIncludes, String version, + Consumer extraTask) throws IOException, ConstantPoolException { ClassFile cf = ClassFile.read(in); if (cf.access_flags.is(AccessFlags.ACC_MODULE)) { @@ -1972,6 +2171,8 @@ private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeL return ; } + extraTask.accept(cf); + ClassHeaderDescription headerDesc = new ClassHeaderDescription(); headerDesc.flags = cf.access_flags.flags; @@ -2032,13 +2233,13 @@ private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeL } } - private void inspectModuleInfoClassFile(InputStream in, + private ModuleDescription inspectModuleInfoClassFile(InputStream in, Map modules, String version) throws IOException, ConstantPoolException { ClassFile cf = ClassFile.read(in); if (!cf.access_flags.is(AccessFlags.ACC_MODULE)) { - return ; + return null; } ModuleHeaderDescription headerDesc = new ModuleHeaderDescription(); @@ -2048,7 +2249,7 @@ private void inspectModuleInfoClassFile(InputStream in, for (Attribute attr : cf.attributes) { if (!readAttribute(cf, headerDesc, attr)) - return ; + return null; } String name = headerDesc.name; @@ -2062,6 +2263,8 @@ private void inspectModuleInfoClassFile(InputStream in, } addModuleHeader(moduleDesc, headerDesc, version); + + return moduleDesc; } private Set enhancedIncludesListBasedOnClassHeaders(ClassList classes, @@ -2072,35 +2275,53 @@ private Set enhancedIncludesListBasedOnClassHeaders(ClassList classes, try (InputStream in = new ByteArrayInputStream(classFileData)) { ClassFile cf = ClassFile.read(in); - if (cf.access_flags.is(AccessFlags.ACC_MODULE)) { - continue; - } + additionalIncludes.addAll(otherRelevantTypesWithOwners(cf)); + } catch (IOException | ConstantPoolException ex) { + throw new IllegalStateException(ex); + } + } - Set additionalClasses = new HashSet<>(); + return additionalIncludes; + } - if (cf.super_class != 0) { - additionalClasses.add(cf.getSuperclassName()); - } - for (int i = 0; i < cf.interfaces.length; i++) { - additionalClasses.add(cf.getInterfaceName(i)); + private Set otherRelevantTypesWithOwners(ClassFile cf) { + Set supertypes = new HashSet<>(); + + try { + if (cf.access_flags.is(AccessFlags.ACC_MODULE)) { + return supertypes; + } + + Set additionalClasses = new HashSet<>(); + + if (cf.super_class != 0) { + additionalClasses.add(cf.getSuperclassName()); + } + for (int i = 0; i < cf.interfaces.length; i++) { + additionalClasses.add(cf.getInterfaceName(i)); + } + PermittedSubclasses_attribute permitted = (PermittedSubclasses_attribute) cf.getAttribute(Attribute.PermittedSubclasses); + if (permitted != null) { + for (int i = 0; i < permitted.subtypes.length; i++) { + additionalClasses.add(cf.constant_pool.getClassInfo(permitted.subtypes[i]).getName()); } + } - for (String additional : additionalClasses) { - int dollar; + for (String additional : additionalClasses) { + int dollar; - additionalIncludes.add(additional); + supertypes.add(additional); - while ((dollar = additional.lastIndexOf('$')) != (-1)) { - additional = additional.substring(0, dollar); - additionalIncludes.add(additional); - } + while ((dollar = additional.lastIndexOf('$')) != (-1)) { + additional = additional.substring(0, dollar); + supertypes.add(additional); } - } catch (IOException | ConstantPoolException ex) { - throw new IllegalStateException(ex); } - } - return additionalIncludes; + return supertypes; + } catch (ConstantPoolException ex) { + throw new IllegalStateException(ex); + } } private void addModuleHeader(ModuleDescription moduleDesc, @@ -2115,7 +2336,9 @@ private void addModuleHeader(ModuleDescription moduleDesc, } } - headerDesc.versions += version; + if (!headerDesc.versions.contains(version)) { + headerDesc.versions += version; + } if (!existed) { moduleDesc.header.add(headerDesc); @@ -2159,7 +2382,9 @@ private void addClassHeader(ClassDescription clazzDesc, ClassHeaderDescription h } } - headerDesc.versions += version; + if (!headerDesc.versions.contains(version)) { + headerDesc.versions += version; + } if (!existed) { clazzDesc.header.add(headerDesc); @@ -4415,22 +4640,25 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOExce String ctSymLocation; String timestampSpec; String currentVersion; - String systemModules; + String preReleaseTag; + String moduleClasses; - if (args.length == 6) { + if (args.length == 7) { ctDescriptionFileExtra = null; ctDescriptionFile = args[1]; ctSymLocation = args[2]; timestampSpec = args[3]; currentVersion = args[4]; - systemModules = args[5]; - } else if (args.length == 7) { + preReleaseTag = args[5]; + moduleClasses = args[6]; + } else if (args.length == 8) { ctDescriptionFileExtra = args[1]; ctDescriptionFile = args[2]; ctSymLocation = args[3]; timestampSpec = args[4]; currentVersion = args[5]; - systemModules = args[6]; + preReleaseTag = args[6]; + moduleClasses = args[7]; } else { help(); return ; @@ -4446,7 +4674,8 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOExce ctSymLocation, timestamp, currentVersion, - systemModules); + preReleaseTag, + moduleClasses); break; } case "build-javadoc-data": { diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/TransitiveDependencies.java b/make/langtools/src/classes/build/tools/symbolgenerator/TransitiveDependencies.java deleted file mode 100644 index a26f74b9e236e..0000000000000 --- a/make/langtools/src/classes/build/tools/symbolgenerator/TransitiveDependencies.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package build.tools.symbolgenerator; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Deque; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.lang.model.element.ModuleElement.RequiresDirective; -import javax.lang.model.util.Elements; -import javax.tools.JavaCompiler; - -import com.sun.tools.javac.api.JavacTaskImpl; -import com.sun.tools.javac.api.JavacTool; -import com.sun.tools.javac.code.Source; -import com.sun.tools.javac.code.Symbol.ModuleSymbol; -import com.sun.tools.javac.jvm.Target; - -/** - * Write reflexive transitive closure of the given modules along their requires transitive edges into - * file /system-modules in the specified directory. - */ -public class TransitiveDependencies { - - private static void help() { - System.err.println("java TransitiveDependencies "); - } - - public static void main(String... args) throws IOException { - if (args.length < 2) { - help(); - return ; - } - - JavaCompiler compiler = JavacTool.create(); - List options = List.of("-source", Source.DEFAULT.name, - "-target", Target.DEFAULT.name, - "-proc:only", - "--system", "none", - "--module-source-path", args[1], - "--add-modules", Arrays.stream(args) - .skip(2) - .collect(Collectors.joining(","))); - List jlObjectList = List.of("java.lang.Object"); - JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, options, jlObjectList, null); - task.enter(); - Elements elements = task.getElements(); - Deque todo = new ArrayDeque<>(); - Arrays.stream(args).skip(2).forEach(todo::add); - Set allModules = new HashSet<>(); - - while (!todo.isEmpty()) { - String current = todo.removeFirst(); - - if (!allModules.add(current)) - continue; - - ModuleSymbol mod = (ModuleSymbol) elements.getModuleElement(current); - - if (mod == null) { - throw new IllegalStateException("Missing: " + current); - } - - //use the internal structure to avoid unnecessarily completing the symbol using the UsesProvidesVisitor: - for (RequiresDirective rd : mod.requires) { - if (rd.isTransitive()) { - todo.offerLast(rd.getDependency().getQualifiedName().toString()); - } - } - } - - allModules.add("java.base"); - allModules.add("jdk.unsupported"); - - Path targetFile = Paths.get(args[0]); - - Files.createDirectories(targetFile.getParent()); - - try (Writer w = Files.newBufferedWriter(targetFile); - PrintWriter out = new PrintWriter(w)) { - allModules.stream() - .sorted() - .forEach(out::println); - } - } - -} diff --git a/make/langtools/tools/javacserver/client/Client.java b/make/langtools/tools/javacserver/client/Client.java index 9576a9923a4e5..cc4ecca941ef4 100644 --- a/make/langtools/tools/javacserver/client/Client.java +++ b/make/langtools/tools/javacserver/client/Client.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.base/Java.gmk b/make/modules/java.base/Java.gmk index 729f7b19f6d07..9d960402f76c5 100644 --- a/make/modules/java.base/Java.gmk +++ b/make/modules/java.base/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.base/Lib.gmk b/make/modules/java.base/Lib.gmk index 6d63795f6de63..30261bab6e1eb 100644 --- a/make/modules/java.base/Lib.gmk +++ b/make/modules/java.base/Lib.gmk @@ -213,8 +213,10 @@ $(eval $(call SetupJdkLibrary, BUILD_SYSLOOKUPLIB, \ CXXFLAGS := $(CXXFLAGS_JDKLIB), \ LDFLAGS := $(LDFLAGS_JDKLIB), \ LDFLAGS_linux := -Wl$(COMMA)--no-as-needed, \ + LDFLAGS_aix := -brtl -bexpfull, \ LIBS := $(LIBCXX), \ LIBS_linux := -lc -lm -ldl, \ + LIBS_aix := -lc -lm -ldecNumber, \ )) TARGETS += $(BUILD_SYSLOOKUPLIB) diff --git a/make/modules/java.base/gensrc/GensrcCharacterData.gmk b/make/modules/java.base/gensrc/GensrcCharacterData.gmk index 23544c6a4cf70..beee18000f149 100644 --- a/make/modules/java.base/gensrc/GensrcCharacterData.gmk +++ b/make/modules/java.base/gensrc/GensrcCharacterData.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.datatransfer/Java.gmk b/make/modules/java.datatransfer/Java.gmk index 12eb67cff5153..6c5332f645737 100644 --- a/make/modules/java.datatransfer/Java.gmk +++ b/make/modules/java.datatransfer/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.desktop/Java.gmk b/make/modules/java.desktop/Java.gmk index 3b43862dd8049..66ef88dcf14d6 100644 --- a/make/modules/java.desktop/Java.gmk +++ b/make/modules/java.desktop/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.logging/Java.gmk b/make/modules/java.logging/Java.gmk index 04409a6f33173..781370b2e1879 100644 --- a/make/modules/java.logging/Java.gmk +++ b/make/modules/java.logging/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.management/Java.gmk b/make/modules/java.management/Java.gmk index 04409a6f33173..781370b2e1879 100644 --- a/make/modules/java.management/Java.gmk +++ b/make/modules/java.management/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.naming/Java.gmk b/make/modules/java.naming/Java.gmk index 519156b578e7b..a7ade637af579 100644 --- a/make/modules/java.naming/Java.gmk +++ b/make/modules/java.naming/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.rmi/Java.gmk b/make/modules/java.rmi/Java.gmk index caab120444132..6c607bd0572f2 100644 --- a/make/modules/java.rmi/Java.gmk +++ b/make/modules/java.rmi/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.security.jgss/Java.gmk b/make/modules/java.security.jgss/Java.gmk index 837cdd0809619..781370b2e1879 100644 --- a/make/modules/java.security.jgss/Java.gmk +++ b/make/modules/java.security.jgss/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.sql.rowset/Java.gmk b/make/modules/java.sql.rowset/Java.gmk index bfb6f8f034e47..6548dd46cae54 100644 --- a/make/modules/java.sql.rowset/Java.gmk +++ b/make/modules/java.sql.rowset/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.sql/Java.gmk b/make/modules/java.sql/Java.gmk index 837cdd0809619..781370b2e1879 100644 --- a/make/modules/java.sql/Java.gmk +++ b/make/modules/java.sql/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.xml.crypto/Java.gmk b/make/modules/java.xml.crypto/Java.gmk index ce9bb131d3c94..53e282aca272b 100644 --- a/make/modules/java.xml.crypto/Java.gmk +++ b/make/modules/java.xml.crypto/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/java.xml/Java.gmk b/make/modules/java.xml/Java.gmk index 7d64387548b77..cf6f1753f1c3d 100644 --- a/make/modules/java.xml/Java.gmk +++ b/make/modules/java.xml/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -27,4 +27,5 @@ DISABLED_WARNINGS_java += lossy-conversions this-escape DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:$(call CommaList, javax.xml.catalog javax.xml.datatype \ javax.xml.transform javax.xml.validation javax.xml.xpath)' +COPY += .dtd .xsd .xml CLEAN += .properties diff --git a/make/modules/jdk.charsets/Java.gmk b/make/modules/jdk.charsets/Java.gmk index 7198efd9fcf51..fad05cc29f147 100644 --- a/make/modules/jdk.charsets/Java.gmk +++ b/make/modules/jdk.charsets/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.compiler/Gendata.gmk b/make/modules/jdk.compiler/Gendata.gmk index e005e703f2c5e..df0ced743e7d6 100644 --- a/make/modules/jdk.compiler/Gendata.gmk +++ b/make/modules/jdk.compiler/Gendata.gmk @@ -35,59 +35,54 @@ $(eval $(call IncludeCustomExtension, modules/jdk.compiler/Gendata.gmk)) # This is needed to properly setup DOCS_MODULES. $(eval $(call ReadImportMetaData)) -# Modules that should be visible for 9 - the documented modules: -CT_MODULES := $(DOCS_MODULES) +# Modules that should be visible - the documented modules: +CT_MODULES := $(filter-out $(MODULES_FILTER), $(DOCS_MODULES)) +CT_TRANSITIVE_MODULES := $(call FindTransitiveIndirectDepsForModules, $(CT_MODULES)) +CT_MODULES_API_TARGETS := \ + $(foreach m, $(CT_MODULES) $(CT_TRANSITIVE_MODULES), \ + $(call SetupJavaCompilationApiTarget, $m, $(JDK_OUTPUTDIR)/modules/$m)) -# Get the complete module source path: -CT_MODULESOURCEPATH := $(call GetModuleSrcPath) +SYMBOL_FILES := $(wildcard $(MODULE_SRC)/share/data/symbols/*) CT_DATA_DESCRIPTION += $(MODULE_SRC)/share/data/symbols/symbols COMPILECREATESYMBOLS_ADD_EXPORTS := \ - --add-exports java.base/jdk.internal.javac=java.compiler.interim,jdk.compiler.interim \ - --add-exports jdk.internal.opt/jdk.internal.opt=jdk.compiler.interim,jdk.javadoc.interim \ - --add-exports jdk.compiler.interim/com.sun.tools.javac.api=ALL-UNNAMED \ - --add-exports jdk.compiler.interim/com.sun.tools.javac.code=ALL-UNNAMED \ - --add-exports jdk.compiler.interim/com.sun.tools.javac.util=ALL-UNNAMED \ - --add-exports jdk.compiler.interim/com.sun.tools.javac.jvm=ALL-UNNAMED \ + --add-modules jdk.compiler,jdk.jdeps \ + --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \ + --add-exports jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED \ # +# TODO: Unify with jdk.javadoc-gendata. Should only compile this once and share. $(eval $(call SetupJavaCompilation, COMPILE_CREATE_SYMBOLS, \ - TARGET_RELEASE := $(TARGET_RELEASE_BOOTJDK), \ - SRC := $(TOPDIR)/make/langtools/src/classes \ - $(TOPDIR)/src/jdk.jdeps/share/classes, \ + TARGET_RELEASE := $(TARGET_RELEASE_NEWJDK), \ + COMPILER := buildjdk, \ + SRC := $(TOPDIR)/make/langtools/src/classes, \ INCLUDES := build/tools/symbolgenerator com/sun/tools/classfile, \ - BIN := $(BUILDTOOLS_OUTPUTDIR)/create_symbols, \ + BIN := $(BUILDTOOLS_OUTPUTDIR)/create_symbols_javac, \ DISABLED_WARNINGS := options this-escape, \ JAVAC_FLAGS := \ - $(INTERIM_LANGTOOLS_ARGS) \ $(COMPILECREATESYMBOLS_ADD_EXPORTS), \ )) -$(SUPPORT_OUTPUTDIR)/symbols/ct.sym: \ - $(COMPILE_CREATE_SYMBOLS) \ - $(wildcard $(MODULE_SRC)/share/data/symbols/*) \ - $(MODULE_INFOS) +$(SUPPORT_OUTPUTDIR)/symbols/ct.sym: $(COMPILE_CREATE_SYMBOLS) $(SYMBOL_FILES) \ + $(MODULE_INFOS) $(CT_MODULES_API_TARGETS) $(RM) -r $(@D) $(MKDIR) -p $(@D) $(ECHO) Creating ct.sym classes - $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ + $(BUILD_JAVA_SMALL) \ $(COMPILECREATESYMBOLS_ADD_EXPORTS) \ - -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \ - build.tools.symbolgenerator.TransitiveDependencies \ - $(@D)/system-modules \ - $(CT_MODULESOURCEPATH) \ - $(CT_MODULES) - $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ - $(COMPILECREATESYMBOLS_ADD_EXPORTS) \ - -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols \ + -classpath $(BUILDTOOLS_OUTPUTDIR)/create_symbols_javac \ build.tools.symbolgenerator.CreateSymbols \ build-ctsym \ $(CT_DATA_DESCRIPTION) \ $(@D)/ct.sym \ $(SOURCE_DATE_EPOCH) \ $(JDK_SOURCE_TARGET_VERSION) \ - $(@D)/system-modules + "$(VERSION_PRE)" \ + $(JDK_OUTPUTDIR)/modules/ $(TOUCH) $@ # Copy ct.sym to the modules libs dir diff --git a/make/modules/jdk.compiler/Java.gmk b/make/modules/jdk.compiler/Java.gmk index 49572b6efb907..7e6793fc6379b 100644 --- a/make/modules/jdk.compiler/Java.gmk +++ b/make/modules/jdk.compiler/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.hotspot.agent/Java.gmk b/make/modules/jdk.hotspot.agent/Java.gmk index 2819813b4f484..52cb0c8d91a65 100644 --- a/make/modules/jdk.hotspot.agent/Java.gmk +++ b/make/modules/jdk.hotspot.agent/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.httpserver/Java.gmk b/make/modules/jdk.httpserver/Java.gmk index c1b5a0d6d0ca9..d7b2b2f70ca21 100644 --- a/make/modules/jdk.httpserver/Java.gmk +++ b/make/modules/jdk.httpserver/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.internal.jvmstat/Java.gmk b/make/modules/jdk.internal.jvmstat/Java.gmk index 697171c156bef..4376b7e348494 100644 --- a/make/modules/jdk.internal.jvmstat/Java.gmk +++ b/make/modules/jdk.internal.jvmstat/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.internal.le/Java.gmk b/make/modules/jdk.internal.le/Java.gmk index 17e7cdf71b4a3..75d99b8d2bb72 100644 --- a/make/modules/jdk.internal.le/Java.gmk +++ b/make/modules/jdk.internal.le/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.internal.le/Lib.gmk b/make/modules/jdk.internal.le/Lib.gmk index 42a2010527abf..75a2446cc5a18 100644 --- a/make/modules/jdk.internal.le/Lib.gmk +++ b/make/modules/jdk.internal.le/Lib.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.internal.opt/Java.gmk b/make/modules/jdk.internal.opt/Java.gmk index 4299496b1fc02..0c65d9ad94ee8 100644 --- a/make/modules/jdk.internal.opt/Java.gmk +++ b/make/modules/jdk.internal.opt/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.internal.vm.ci/Java.gmk b/make/modules/jdk.internal.vm.ci/Java.gmk index 7b846bc1bd9cd..0dd5af1f3ea72 100644 --- a/make/modules/jdk.internal.vm.ci/Java.gmk +++ b/make/modules/jdk.internal.vm.ci/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jartool/Java.gmk b/make/modules/jdk.jartool/Java.gmk index 1cf56a317e7cb..f9b4538838f3b 100644 --- a/make/modules/jdk.jartool/Java.gmk +++ b/make/modules/jdk.jartool/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.javadoc/Java.gmk b/make/modules/jdk.javadoc/Java.gmk index 92bbec738a036..3035864ecbacb 100644 --- a/make/modules/jdk.javadoc/Java.gmk +++ b/make/modules/jdk.javadoc/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jcmd/Java.gmk b/make/modules/jdk.jcmd/Java.gmk index 287bcbc6945c7..08209e9353821 100644 --- a/make/modules/jdk.jcmd/Java.gmk +++ b/make/modules/jdk.jcmd/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jconsole/Java.gmk b/make/modules/jdk.jconsole/Java.gmk index 7688f7f6b5c0b..9166d6a7c1187 100644 --- a/make/modules/jdk.jconsole/Java.gmk +++ b/make/modules/jdk.jconsole/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jdeps/Gensrc.gmk b/make/modules/jdk.jdeps/Gensrc.gmk index a5a72c543c72f..640143b1b56d7 100644 --- a/make/modules/jdk.jdeps/Gensrc.gmk +++ b/make/modules/jdk.jdeps/Gensrc.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jdeps/Java.gmk b/make/modules/jdk.jdeps/Java.gmk index 914b0c6658709..69c7ae0433fc7 100644 --- a/make/modules/jdk.jdeps/Java.gmk +++ b/make/modules/jdk.jdeps/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jdeps/Launcher.gmk b/make/modules/jdk.jdeps/Launcher.gmk index 4a5b5f408d554..9d7d3515fc2a7 100644 --- a/make/modules/jdk.jdeps/Launcher.gmk +++ b/make/modules/jdk.jdeps/Launcher.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jdi/Java.gmk b/make/modules/jdk.jdi/Java.gmk index 49e35d9c95105..76d05b06df48b 100644 --- a/make/modules/jdk.jdi/Java.gmk +++ b/make/modules/jdk.jdi/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jpackage/Java.gmk b/make/modules/jdk.jpackage/Java.gmk index 04cef0d52853b..60fe53475c939 100644 --- a/make/modules/jdk.jpackage/Java.gmk +++ b/make/modules/jdk.jpackage/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.jshell/Java.gmk b/make/modules/jdk.jshell/Java.gmk index a9db351c333ad..744c85d960a94 100644 --- a/make/modules/jdk.jshell/Java.gmk +++ b/make/modules/jdk.jshell/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.localedata/Java.gmk b/make/modules/jdk.localedata/Java.gmk index 9f8ee95addd9b..d794dde2124ea 100644 --- a/make/modules/jdk.localedata/Java.gmk +++ b/make/modules/jdk.localedata/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/modules/jdk.sctp/Java.gmk b/make/modules/jdk.sctp/Java.gmk index 08cd8fca1ce6e..9fc531689ee42 100644 --- a/make/modules/jdk.sctp/Java.gmk +++ b/make/modules/jdk.sctp/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/scripts/compare_exceptions.sh.incl b/make/scripts/compare_exceptions.sh.incl index d5043637145b2..cfbfeeb5be4f9 100644 --- a/make/scripts/compare_exceptions.sh.incl +++ b/make/scripts/compare_exceptions.sh.incl @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/scripts/fixpath.sh b/make/scripts/fixpath.sh index 9c8db7383f2de..3a886fee07c47 100644 --- a/make/scripts/fixpath.sh +++ b/make/scripts/fixpath.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/test/BuildFailureHandler.gmk b/make/test/BuildFailureHandler.gmk index ac133dc6a68e6..78eb3692e8a74 100644 --- a/make/test/BuildFailureHandler.gmk +++ b/make/test/BuildFailureHandler.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index b8a58849c01e5..360ef7a747a83 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1511,7 +1511,6 @@ void LIR_Assembler::casl(Register addr, Register newval, Register cmpval) { void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { - assert(VM_Version::supports_cx8(), "wrong machine"); Register addr; if (op->addr()->is_register()) { addr = as_reg(op->addr()); @@ -2700,7 +2699,10 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ verify_oop(obj); if (tmp != obj) { + assert_different_registers(obj, tmp, rscratch1, rscratch2, mdo_addr.base(), mdo_addr.index()); __ mov(tmp, obj); + } else { + assert_different_registers(obj, rscratch1, rscratch2, mdo_addr.base(), mdo_addr.index()); } if (do_null) { __ cbnz(tmp, update); @@ -2757,10 +2759,11 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ cbz(rscratch2, none); __ cmp(rscratch2, (u1)TypeEntries::null_seen); __ br(Assembler::EQ, none); - // There is a chance that the checks above (re-reading profiling - // data from memory) fail if another thread has just set the + // There is a chance that the checks above + // fail if another thread has just set the // profiling to this obj's klass __ dmb(Assembler::ISHLD); + __ eor(tmp, tmp, rscratch2); // get back original value before XOR __ ldr(rscratch2, mdo_addr); __ eor(tmp, tmp, rscratch2); __ andr(rscratch1, tmp, TypeEntries::type_klass_mask); @@ -2785,6 +2788,10 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ bind(none); // first time here. Set profile type. __ str(tmp, mdo_addr); +#ifdef ASSERT + __ andr(tmp, tmp, TypeEntries::type_mask); + __ verify_klass_ptr(tmp); +#endif } } else { // There's a single possible klass at this profile point @@ -2816,6 +2823,10 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { #endif // first time here. Set profile type. __ str(tmp, mdo_addr); +#ifdef ASSERT + __ andr(tmp, tmp, TypeEntries::type_mask); + __ verify_klass_ptr(tmp); +#endif } else { assert(ciTypeEntries::valid_ciklass(current_klass) != nullptr && ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "inconsistent"); diff --git a/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp new file mode 100644 index 0000000000000..d035ab21093da --- /dev/null +++ b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "logging/log.hpp" +#include "oops/compressedKlass.hpp" +#include "memory/metaspace.hpp" +#include "runtime/os.hpp" +#include "utilities/globalDefinitions.hpp" + +// Helper function; reserve at an address that is compatible with EOR +static char* reserve_at_eor_compatible_address(size_t size, bool aslr) { + char* result = nullptr; + + log_debug(metaspace, map)("Trying to reserve at an EOR-compatible address"); + + // We need immediates that are 32-bit aligned, since they should not intersect nKlass + // bits. They should not be larger than the addressable space either, but we still + // lack a good abstraction for that (see JDK-8320584), therefore we assume and hard-code + // 2^48 as a reasonable higher ceiling. + static const uint16_t immediates[] = { + 0x0001, 0x0002, 0x0003, 0x0004, 0x0006, 0x0007, 0x0008, 0x000c, 0x000e, + 0x000f, 0x0010, 0x0018, 0x001c, 0x001e, 0x001f, 0x0020, 0x0030, 0x0038, + 0x003c, 0x003e, 0x003f, 0x0040, 0x0060, 0x0070, 0x0078, 0x007c, 0x007e, + 0x007f, 0x0080, 0x00c0, 0x00e0, 0x00f0, 0x00f8, 0x00fc, 0x00fe, 0x00ff, + 0x0100, 0x0180, 0x01c0, 0x01e0, 0x01f0, 0x01f8, 0x01fc, 0x01fe, 0x01ff, + 0x0200, 0x0300, 0x0380, 0x03c0, 0x03e0, 0x03f0, 0x03f8, 0x03fc, 0x03fe, + 0x03ff, 0x0400, 0x0600, 0x0700, 0x0780, 0x07c0, 0x07e0, 0x07f0, 0x07f8, + 0x07fc, 0x07fe, 0x07ff, 0x0800, 0x0c00, 0x0e00, 0x0f00, 0x0f80, 0x0fc0, + 0x0fe0, 0x0ff0, 0x0ff8, 0x0ffc, 0x0ffe, 0x0fff, 0x1000, 0x1800, 0x1c00, + 0x1e00, 0x1f00, 0x1f80, 0x1fc0, 0x1fe0, 0x1ff0, 0x1ff8, 0x1ffc, 0x1ffe, + 0x1fff, 0x2000, 0x3000, 0x3800, 0x3c00, 0x3e00, 0x3f00, 0x3f80, 0x3fc0, + 0x3fe0, 0x3ff0, 0x3ff8, 0x3ffc, 0x3ffe, 0x3fff, 0x4000, 0x6000, 0x7000, + 0x7800, 0x7c00, 0x7e00, 0x7f00, 0x7f80, 0x7fc0, 0x7fe0, 0x7ff0, 0x7ff8, + 0x7ffc, 0x7ffe, 0x7fff + }; + static constexpr int num_immediates = sizeof(immediates) / sizeof(immediates[0]); + const int start_index = aslr ? os::random() : 0; + constexpr int max_tries = 64; + for (int ntry = 0; result == nullptr && ntry < max_tries; ntry ++) { + // As in os::attempt_reserve_memory_between, we alternate between higher and lower + // addresses; this maximizes the chance of early success if part of the address space + // is not accessible (e.g. 39-bit address space). + const int alt_index = (ntry & 1) ? 0 : num_immediates / 2; + const int index = (start_index + ntry + alt_index) % num_immediates; + const uint64_t immediate = ((uint64_t)immediates[index]) << 32; + assert(immediate > 0 && Assembler::operand_valid_for_logical_immediate(/*is32*/false, immediate), + "Invalid immediate %d " UINT64_FORMAT, index, immediate); + result = os::attempt_reserve_memory_at((char*)immediate, size, false); + if (result == nullptr) { + log_trace(metaspace, map)("Failed to attach at " UINT64_FORMAT_X, immediate); + } + } + if (result == nullptr) { + log_debug(metaspace, map)("Failed to reserve at any EOR-compatible address"); + } + return result; +} +char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base) { + + char* result = nullptr; + + // Optimize for base=0 shift=0 + if (optimize_for_zero_base) { + result = reserve_address_space_for_unscaled_encoding(size, aslr); + } + + // If this fails, we don't bother aiming for zero-based encoding (base=0 shift>0), since it has no + // advantages over EOR or movk mode. + + // EOR-compatible reservation + if (result == nullptr) { + result = reserve_at_eor_compatible_address(size, aslr); + } + + // Movk-compatible reservation via probing. + if (result == nullptr) { + result = reserve_address_space_for_16bit_move(size, aslr); + } + + // Movk-compatible reservation via overallocation. + // If that failed, attempt to allocate at any 4G-aligned address. Let the system decide where. For ASLR, + // we now rely on the system. + // Compared with the probing done above, this has two disadvantages: + // - on a kernel with 52-bit address space we may get an address that has bits set between [48, 52). + // In that case, we may need two movk moves (not yet implemented). + // - this technique leads to temporary over-reservation of address space; it will spike the vsize of + // the process. Therefore it may fail if a vsize limit is in place (e.g. ulimit -v). + if (result == nullptr) { + constexpr size_t alignment = nth_bit(32); + log_debug(metaspace, map)("Trying to reserve at a 32-bit-aligned address"); + result = os::reserve_memory_aligned(size, alignment, false); + } + + return result; +} + +void CompressedKlassPointers::initialize(address addr, size_t len) { + constexpr uintptr_t unscaled_max = nth_bit(32); + assert(len <= unscaled_max, "Klass range larger than 32 bits?"); + + // Shift is always 0 on aarch64. + _shift = 0; + + // On aarch64, we don't bother with zero-based encoding (base=0 shift>0). + address const end = addr + len; + _base = (end <= (address)unscaled_max) ? nullptr : addr; + + _range = end - _base; +} diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 3a9e5db2ef7ae..ebaf18299728d 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -1290,6 +1290,9 @@ static bool aarch64_test_and_branch_reachable(int branch_offset, int target_offs return test_and_branch_to_trampoline_delta < test_and_branch_delta_limit; } +ZLoadBarrierStubC2Aarch64::ZLoadBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register ref) + : ZLoadBarrierStubC2(node, ref_addr, ref), _test_and_branch_reachable_entry(), _offset(), _deferred_emit(false), _test_and_branch_reachable(false) {} + ZLoadBarrierStubC2Aarch64::ZLoadBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register ref, int offset) : ZLoadBarrierStubC2(node, ref_addr, ref), _test_and_branch_reachable_entry(), _offset(offset), _deferred_emit(false), _test_and_branch_reachable(false) { PhaseOutput* const output = Compile::current()->output(); @@ -1319,6 +1322,12 @@ int ZLoadBarrierStubC2Aarch64::get_stub_size() { return cb.insts_size(); } +ZLoadBarrierStubC2Aarch64* ZLoadBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register ref) { + ZLoadBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZLoadBarrierStubC2Aarch64(node, ref_addr, ref); + register_stub(stub); + return stub; +} + ZLoadBarrierStubC2Aarch64* ZLoadBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register ref, int offset) { ZLoadBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZLoadBarrierStubC2Aarch64(node, ref_addr, ref, offset); register_stub(stub); diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp index 00714e5c0c04b..82334b34adeca 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp @@ -265,10 +265,12 @@ class ZLoadBarrierStubC2Aarch64 : public ZLoadBarrierStubC2 { bool _deferred_emit; bool _test_and_branch_reachable; + ZLoadBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register ref); ZLoadBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register ref, int offset); int get_stub_size(); public: + static ZLoadBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register ref); static ZLoadBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register ref, int offset); virtual void emit_code(MacroAssembler& masm); diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 8c698635ad0f5..23564a3f23c38 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -48,7 +48,7 @@ static void z_keep_alive_load_barrier(MacroAssembler& _masm, const MachNode* nod __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadBeforeMov); __ movzw(tmp, barrier_Relocation::unpatched); __ tst(ref, tmp); - ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref); + ZLoadBarrierStubC2Aarch64* const stub = ZLoadBarrierStubC2Aarch64::create(node, ref_addr, ref); __ br(Assembler::NE, *stub->entry()); z_uncolor(_masm, node, ref); __ bind(*stub->continuation()); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index ecd97b36776eb..69a61e281f352 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1562,7 +1562,7 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result, } void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { - assert_different_registers(obj, rscratch1); + assert_different_registers(obj, rscratch1, mdo_addr.base(), mdo_addr.index()); Label update, next, none; verify_oop(obj); @@ -1584,13 +1584,13 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md tbnz(obj, exact_log2(TypeEntries::type_unknown), next); // already unknown. Nothing to do anymore. - ldr(rscratch1, mdo_addr); cbz(rscratch1, none); cmp(rscratch1, (u1)TypeEntries::null_seen); br(Assembler::EQ, none); - // There is a chance that the checks above (re-reading profiling - // data from memory) fail if another thread has just set the + // There is a chance that the checks above + // fail if another thread has just set the // profiling to this obj's klass + eor(obj, obj, rscratch1); // get back original value before XOR ldr(rscratch1, mdo_addr); eor(obj, obj, rscratch1); tst(obj, TypeEntries::type_klass_mask); @@ -1603,6 +1603,10 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md bind(none); // first time here. Set profile type. str(obj, mdo_addr); +#ifdef ASSERT + andr(obj, obj, TypeEntries::type_mask); + verify_klass_ptr(obj); +#endif bind(next); } diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 081e94341bde6..df28ca284c5cf 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -68,7 +68,6 @@ static SpinWait get_spin_wait_desc() { } void VM_Version::initialize() { - _supports_cx8 = true; _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; _supports_atomic_getset8 = true; diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index 45786898458bc..72993b3211f9d 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -1385,7 +1385,6 @@ void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { __ mov(dest, 1, eq); __ mov(dest, 0, ne); } else if (op->code() == lir_cas_long) { - assert(VM_Version::supports_cx8(), "wrong machine"); Register cmp_value_lo = op->cmp_value()->as_register_lo(); Register cmp_value_hi = op->cmp_value()->as_register_hi(); Register new_value_lo = op->new_value()->as_register_lo(); diff --git a/src/hotspot/cpu/arm/vm_version_arm_32.cpp b/src/hotspot/cpu/arm/vm_version_arm_32.cpp index 44f2179db986d..d3ba352f78b15 100644 --- a/src/hotspot/cpu/arm/vm_version_arm_32.cpp +++ b/src/hotspot/cpu/arm/vm_version_arm_32.cpp @@ -128,10 +128,16 @@ void VM_Version::early_initialize() { // use proper dmb instruction get_os_cpu_info(); + // Future cleanup: if SUPPORTS_NATIVE_CX8 is defined then we should not need + // any alternative solutions. At present this allows for the theoretical + // possibility of building for ARMv7 and then running on ARMv5 or 6. If that + // is impossible then the ARM port folk should clean this up. _kuser_helper_version = *(int*)KUSER_HELPER_VERSION_ADDR; +#ifndef SUPPORTS_NATIVE_CX8 // armv7 has the ldrexd instruction that can be used to implement cx8 // armv5 with linux >= 3.1 can use kernel helper routine _supports_cx8 = (supports_ldrexd() || supports_kuser_cmpxchg64()); +#endif } void VM_Version::initialize() { @@ -278,7 +284,7 @@ void VM_Version::initialize() { _supports_atomic_getadd8 = supports_ldrexd(); #ifdef COMPILER2 - assert(_supports_cx8 && _supports_atomic_getset4 && _supports_atomic_getadd4 + assert(supports_cx8() && _supports_atomic_getset4 && _supports_atomic_getadd4 && _supports_atomic_getset8 && _supports_atomic_getadd8, "C2: atomic operations must be supported"); #endif char buf[512]; diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 4973ec510d578..738511c6f5099 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -3106,7 +3106,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { // Klass seen before, nothing to do (regardless of unknown bit). //beq(CCR1, do_nothing); - __ andi_(R0, klass, TypeEntries::type_unknown); + __ andi_(R0, tmp, TypeEntries::type_unknown); // Already unknown. Nothing to do anymore. //bne(CCR0, do_nothing); __ crorc(CCR0, Assembler::equal, CCR1, Assembler::equal); // cr0 eq = cr1 eq or cr0 ne diff --git a/src/hotspot/cpu/ppc/compressedKlass_ppc.cpp b/src/hotspot/cpu/ppc/compressedKlass_ppc.cpp new file mode 100644 index 0000000000000..51012eef86594 --- /dev/null +++ b/src/hotspot/cpu/ppc/compressedKlass_ppc.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "oops/compressedKlass.hpp" +#include "utilities/globalDefinitions.hpp" + +char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base) { + + char* result = nullptr; + + // Optimize for base=0 shift=0; failing that, for base=0 shift>0 + if (optimize_for_zero_base) { + result = reserve_address_space_for_unscaled_encoding(size, aslr); + if (result == nullptr) { + result = reserve_address_space_for_zerobased_encoding(size, aslr); + } + } + + // Optimize for a single 16-bit move: a base that has only bits set in its third quadrant [32..48). + if (result == nullptr) { + result = reserve_address_space_for_16bit_move(size, aslr); + } + + return result; +} diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index 8265d06a5e357..54a31a16c8aa1 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -1774,7 +1774,7 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, Register mdo_addr // Klass seen before, nothing to do (regardless of unknown bit). //beq(CCR1, do_nothing); - andi_(R0, klass, TypeEntries::type_unknown); + andi_(R0, tmp, TypeEntries::type_unknown); // Already unknown. Nothing to do anymore. //bne(CCR0, do_nothing); crorc(CCR0, Assembler::equal, CCR1, Assembler::equal); // cr0 eq = cr1 eq or cr0 ne diff --git a/src/hotspot/cpu/ppc/templateTable_ppc.hpp b/src/hotspot/cpu/ppc/templateTable_ppc.hpp index 2245533be1298..3396acb192643 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc.hpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc.hpp @@ -27,7 +27,7 @@ #define CPU_PPC_TEMPLATETABLE_PPC_HPP static void prepare_invoke(Register Rcache, Register Rret_addr, Register Rrecv, Register Rscratch); - static void invokevfinal_helper(Register Rcache, Register Rscratch1, Register Rscratch2, Register Rscratch3); + static void invokevfinal_helper(Register Rcache, Register Rscratch1, Register Rscratch2, Register Rscratch3, Register Rscratch4); static void generate_vtable_call(Register Rrecv_klass, Register Rindex, Register Rret, Register Rtemp); static void invokeinterface_object_method(Register Rrecv_klass, Register Rret, Register Rflags, Register Rcache, Register Rtemp, Register Rtemp2); diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 7d6e14d26c127..84ecfc4f934e7 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -3487,7 +3487,7 @@ void TemplateTable::invokevirtual(int byte_no) { if (RewriteBytecodes && !UseSharedSpaces && !CDSConfig::is_dumping_static_archive()) { patch_bytecode(Bytecodes::_fast_invokevfinal, Rnew_bc, R12_scratch2); } - invokevfinal_helper(Rcache, R11_scratch1, R12_scratch2, Rflags /* tmp */); + invokevfinal_helper(Rcache, R11_scratch1, R12_scratch2, Rflags /* tmp */, Rrecv /* tmp */); __ align(32, 12); __ bind(LnotFinal); @@ -3510,36 +3510,24 @@ void TemplateTable::fast_invokevfinal(int byte_no) { assert(byte_no == f2_byte, "use this argument"); Register Rcache = R31; __ load_method_entry(Rcache, R11_scratch1); - invokevfinal_helper(Rcache, R11_scratch1, R12_scratch2, R22_tmp2); + invokevfinal_helper(Rcache, R11_scratch1, R12_scratch2, R22_tmp2, R23_tmp3); } -void TemplateTable::invokevfinal_helper(Register Rcache, Register Rscratch1, Register Rscratch2, Register Rscratch3) { +void TemplateTable::invokevfinal_helper(Register Rcache, + Register Rscratch1, Register Rscratch2, Register Rscratch3, Register Rscratch4) { - assert_different_registers(Rcache, Rscratch1, Rscratch2, Rscratch3); + assert_different_registers(Rcache, Rscratch1, Rscratch2, Rscratch3, Rscratch4); - // Load receiver from stack slot. - Register Rmethod = Rscratch3; - __ ld(Rmethod, in_bytes(ResolvedMethodEntry::method_offset()), Rcache); + Register Rrecv = Rscratch2, + Rmethod = Rscratch3, + Rret_addr = Rscratch4; + prepare_invoke(Rcache, Rret_addr, Rrecv, Rscratch1); - // Get return address. - Register Rtable_addr = Rscratch2, - Rret_addr = Rcache, - Rret_type = Rscratch1; - // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value. - __ lbz(Rret_type, in_bytes(ResolvedMethodEntry::type_offset()), Rcache); - __ load_dispatch_table(Rtable_addr, Interpreter::invoke_return_entry_table()); - __ sldi(Rret_type, Rret_type, LogBytesPerWord); - __ ldx(Rret_addr, Rret_type, Rtable_addr); // kills Rcache - - Register Rnum_params = Rscratch2, - Rrecv = Rscratch2; - __ ld(Rnum_params, in_bytes(Method::const_offset()), Rmethod); - __ lhz(Rnum_params /* number of params */, in_bytes(ConstMethod::size_of_parameters_offset()), Rnum_params); - - // Load receiver and receiver null check. - __ load_receiver(Rnum_params, Rrecv); + // Receiver null check. __ null_check_throw(Rrecv, -1, Rscratch1); + __ ld(Rmethod, in_bytes(ResolvedMethodEntry::method_offset()), Rcache); + __ profile_final_call(Rrecv, Rscratch1); // Argument and return type profiling. __ profile_arguments_type(Rmethod, Rscratch1, Rscratch2, true); @@ -3558,9 +3546,9 @@ void TemplateTable::invokespecial(int byte_no) { Rmethod = R31; load_resolved_method_entry_special_or_static(Rcache, // ResolvedMethodEntry* - Rmethod, // Method* or itable index + Rmethod, // Method* noreg); // flags - prepare_invoke(Rcache, Rret_addr, Rreceiver, R11_scratch1); // recv + prepare_invoke(Rcache, Rret_addr, Rreceiver, R11_scratch1); // Receiver null check. __ null_check_throw(Rreceiver, -1, R11_scratch1); @@ -3578,10 +3566,10 @@ void TemplateTable::invokestatic(int byte_no) { Register Rcache = R3_ARG1, Rret_addr = R4_ARG2; - load_resolved_method_entry_special_or_static(Rcache, // ResolvedMethodEntry* - R19_method, // Method* or itable index - noreg); // flags - prepare_invoke(Rcache, Rret_addr, noreg, R11_scratch1); // recv + load_resolved_method_entry_special_or_static(Rcache, // ResolvedMethodEntry* + R19_method, // Method* + noreg); // flags + prepare_invoke(Rcache, Rret_addr, noreg, R11_scratch1); __ profile_call(R11_scratch1, R12_scratch2); // Argument and return type profiling. @@ -3639,7 +3627,7 @@ void TemplateTable::invokeinterface(int byte_no) { Rcache = R31; load_resolved_method_entry_interface(Rcache, noreg, noreg, Rflags); - prepare_invoke(Rcache, Rret_addr, Rreceiver, Rscratch1); // recv + prepare_invoke(Rcache, Rret_addr, Rreceiver, Rscratch1); // First check for Object case, then private interface method, // then regular interface method. @@ -3757,7 +3745,7 @@ void TemplateTable::invokehandle(int byte_no) { Rcache = R31; load_resolved_method_entry_handle(Rcache, // ResolvedMethodEntry* - Rmethod, // Method* or itable index + Rmethod, // Method* Rscratch1, Rflags); prepare_invoke(Rcache, Rret_addr, Rrecv, Rscratch1); diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index 0b6824943b9ba..5a9d035be6034 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -200,10 +200,6 @@ void VM_Version::initialize() { print_features(); } - // PPC64 supports 8-byte compare-exchange operations (see Atomic::cmpxchg) - // and 'atomic long memory ops' (see Unsafe_GetLongVolatile). - _supports_cx8 = true; - // Used by C1. _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 953478d05ae7e..afdf25c58d234 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -1251,7 +1251,6 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { } void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { - assert(VM_Version::supports_cx8(), "wrong machine"); Register addr; if (op->addr()->is_register()) { addr = as_reg(op->addr()); @@ -1654,10 +1653,11 @@ void LIR_Assembler::check_conflict(ciKlass* exact_klass, intptr_t current_klass, __ beqz(t1, none); __ mv(t0, (u1)TypeEntries::null_seen); __ beq(t0, t1, none); - // There is a chance that the checks above (re-reading profiling - // data from memory) fail if another thread has just set the + // There is a chance that the checks above + // fail if another thread has just set the // profiling to this obj's klass __ membar(MacroAssembler::LoadLoad); + __ xorr(tmp, tmp, t1); // get back original value before XOR __ ld(t1, mdo_addr); __ xorr(tmp, tmp, t1); __ andi(t0, tmp, TypeEntries::type_klass_mask); @@ -1684,6 +1684,10 @@ void LIR_Assembler::check_conflict(ciKlass* exact_klass, intptr_t current_klass, __ bind(none); // first time here. Set profile type. __ sd(tmp, mdo_addr); +#ifdef ASSERT + __ andi(tmp, tmp, TypeEntries::type_mask); + __ verify_klass_ptr(tmp); +#endif } } @@ -1718,6 +1722,10 @@ void LIR_Assembler::check_no_conflict(ciKlass* exact_klass, intptr_t current_kla #endif // first time here. Set profile type. __ sd(tmp, mdo_addr); +#ifdef ASSERT + __ andi(tmp, tmp, TypeEntries::type_mask); + __ verify_klass_ptr(tmp); +#endif } else { assert(ciTypeEntries::valid_ciklass(current_klass) != nullptr && ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "inconsistent"); diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 647f56db73eed..5daeff511922c 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -1036,12 +1036,13 @@ void C2_MacroAssembler::string_indexof_linearscan(Register haystack, Register ne // Compare strings. void C2_MacroAssembler::string_compare(Register str1, Register str2, - Register cnt1, Register cnt2, Register result, Register tmp1, Register tmp2, - Register tmp3, int ae) + Register cnt1, Register cnt2, Register result, + Register tmp1, Register tmp2, Register tmp3, + int ae) { Label DONE, SHORT_LOOP, SHORT_STRING, SHORT_LAST, TAIL, STUB, - DIFFERENCE, NEXT_WORD, SHORT_LOOP_TAIL, SHORT_LAST2, SHORT_LAST_INIT, - SHORT_LOOP_START, TAIL_CHECK, L; + DIFFERENCE, NEXT_WORD, SHORT_LOOP_TAIL, SHORT_LAST2, SHORT_LAST_INIT, + SHORT_LOOP_START, TAIL_CHECK, L; const int STUB_THRESHOLD = 64 + 8; bool isLL = ae == StrIntrinsicNode::LL; diff --git a/src/hotspot/cpu/riscv/compressedKlass_riscv.cpp b/src/hotspot/cpu/riscv/compressedKlass_riscv.cpp new file mode 100644 index 0000000000000..cffadb4189b1c --- /dev/null +++ b/src/hotspot/cpu/riscv/compressedKlass_riscv.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "oops/compressedKlass.hpp" +#include "utilities/globalDefinitions.hpp" + +char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base) { + + char* result = nullptr; + + // RiscV loads a 64-bit immediate in up to four separate steps, splitting it into four different sections + // (two 32-bit sections, each split into two subsections of 20/12 bits). + // + // 63 ....... 44 43 ... 32 31 ....... 12 11 ... 0 + // D C B A + // + // A "good" base is, in this order: + // 1) only bits in A; this would be an address < 4KB, which is unrealistic on normal Linux boxes since + // the typical default for vm.mmap_min_address is 64KB. We ignore that. + // 2) only bits in B: a 12-bit-aligned address below 4GB. 12 bit = 4KB, but since mmap reserves at + // page boundaries, we can ignore the alignment. + // 3) only bits in C: a 4GB-aligned address that is lower than 16TB. + // 4) only bits in D: a 16TB-aligned address. + + // First, attempt to allocate < 4GB. We do this unconditionally: + // - if can_optimize_for_zero_base, a <4GB mapping start would allow us to run unscaled (base = 0, shift = 0) + // - if !can_optimize_for_zero_base, a <4GB mapping start is still good, the resulting immediate can be encoded + // with one instruction (2) + result = reserve_address_space_for_unscaled_encoding(size, aslr); + + // Failing that, attempt to reserve for base=zero shift>0 + if (result == nullptr && optimize_for_zero_base) { + result = reserve_address_space_for_zerobased_encoding(size, aslr); + } + + // Failing that, optimize for case (3) - a base with only bits set between [33-44) + if (result == nullptr) { + const uintptr_t from = nth_bit(32 + (optimize_for_zero_base ? LogKlassAlignmentInBytes : 0)); + constexpr uintptr_t to = nth_bit(44); + constexpr size_t alignment = nth_bit(32); + result = reserve_address_space_X(from, to, size, alignment, aslr); + } + + // Failing that, optimize for case (4) - a base with only bits set between [44-64) + if (result == nullptr) { + constexpr uintptr_t from = nth_bit(44); + constexpr uintptr_t to = UINT64_MAX; + constexpr size_t alignment = nth_bit(44); + result = reserve_address_space_X(from, to, size, alignment, aslr); + } + + return result; +} diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 1240566e26cc4..f9a23af41b09b 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -1627,8 +1627,8 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md bind(update); load_klass(obj, obj); - ld(t0, mdo_addr); - xorr(obj, obj, t0); + ld(tmp, mdo_addr); + xorr(obj, obj, tmp); andi(t0, obj, TypeEntries::type_klass_mask); beqz(t0, next); // klass seen before, nothing to // do. The unknown bit may have been @@ -1638,15 +1638,15 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md bnez(t0, next); // already unknown. Nothing to do anymore. - ld(t0, mdo_addr); - beqz(t0, none); - mv(tmp, (u1)TypeEntries::null_seen); - beq(t0, tmp, none); - // There is a chance that the checks above (re-reading profiling - // data from memory) fail if another thread has just set the + beqz(tmp, none); + mv(t0, (u1)TypeEntries::null_seen); + beq(tmp, t0, none); + // There is a chance that the checks above + // fail if another thread has just set the // profiling to this obj's klass - ld(t0, mdo_addr); - xorr(obj, obj, t0); + xorr(obj, obj, tmp); // get back original value before XOR + ld(tmp, mdo_addr); + xorr(obj, obj, tmp); andi(t0, obj, TypeEntries::type_klass_mask); beqz(t0, next); @@ -1657,6 +1657,10 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md bind(none); // first time here. Set profile type. sd(obj, mdo_addr); +#ifdef ASSERT + andi(obj, obj, TypeEntries::type_mask); + verify_klass_ptr(obj); +#endif bind(next); } diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 5201486c8c634..82a4df29326cb 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -2048,23 +2048,6 @@ void MacroAssembler::cmp_klass(Register oop, Register trial_klass, Register tmp1 beq(trial_klass, tmp1, L); } -// Multiply and multiply-accumulate unsigned 64-bit registers. -void MacroAssembler::wide_mul(Register prod_lo, Register prod_hi, Register n, Register m) { - assert_different_registers(prod_lo, prod_hi); - - mul(prod_lo, n, m); - mulhu(prod_hi, n, m); -} -void MacroAssembler::wide_madd(Register sum_lo, Register sum_hi, Register n, - Register m, Register tmp1, Register tmp2) { - assert_different_registers(sum_lo, sum_hi); - assert_different_registers(sum_hi, tmp2); - - wide_mul(tmp1, tmp2, n, m); - cad(sum_lo, sum_lo, tmp1, tmp1); // Add tmp1 to sum_lo with carry output to tmp1 - adc(sum_hi, sum_hi, tmp2, tmp1); // Add tmp2 with carry to sum_hi -} - // Move an oop into a register. void MacroAssembler::movoop(Register dst, jobject obj) { int oop_index; @@ -2536,6 +2519,109 @@ void MacroAssembler::lookup_interface_method(Register recv_klass, } } +// Look up the method for a megamorphic invokeinterface call in a single pass over itable: +// - check recv_klass (actual object class) is a subtype of resolved_klass from CompiledICHolder +// - find a holder_klass (class that implements the method) vtable offset and get the method from vtable by index +// The target method is determined by . +// The receiver klass is in recv_klass. +// On success, the result will be in method_result, and execution falls through. +// On failure, execution transfers to the given label. +void MacroAssembler::lookup_interface_method_stub(Register recv_klass, + Register holder_klass, + Register resolved_klass, + Register method_result, + Register temp_itbl_klass, + Register scan_temp, + int itable_index, + Label& L_no_such_interface) { + // 'method_result' is only used as output register at the very end of this method. + // Until then we can reuse it as 'holder_offset'. + Register holder_offset = method_result; + assert_different_registers(resolved_klass, recv_klass, holder_klass, temp_itbl_klass, scan_temp, holder_offset); + + int vtable_start_offset_bytes = in_bytes(Klass::vtable_start_offset()); + int scan_step = itableOffsetEntry::size() * wordSize; + int ioffset_bytes = in_bytes(itableOffsetEntry::interface_offset()); + int ooffset_bytes = in_bytes(itableOffsetEntry::offset_offset()); + int itmentry_off_bytes = in_bytes(itableMethodEntry::method_offset()); + const int vte_scale = exact_log2(vtableEntry::size_in_bytes()); + + Label L_loop_search_resolved_entry, L_resolved_found, L_holder_found; + + lwu(scan_temp, Address(recv_klass, Klass::vtable_length_offset())); + add(recv_klass, recv_klass, vtable_start_offset_bytes + ioffset_bytes); + // itableOffsetEntry[] itable = recv_klass + Klass::vtable_start_offset() + // + sizeof(vtableEntry) * (recv_klass->_vtable_len); + // scan_temp = &(itable[0]._interface) + // temp_itbl_klass = itable[0]._interface; + shadd(scan_temp, scan_temp, recv_klass, scan_temp, vte_scale); + ld(temp_itbl_klass, Address(scan_temp)); + mv(holder_offset, zr); + + // Initial checks: + // - if (holder_klass != resolved_klass), go to "scan for resolved" + // - if (itable[0] == holder_klass), shortcut to "holder found" + // - if (itable[0] == 0), no such interface + bne(resolved_klass, holder_klass, L_loop_search_resolved_entry); + beq(holder_klass, temp_itbl_klass, L_holder_found); + beqz(temp_itbl_klass, L_no_such_interface); + + // Loop: Look for holder_klass record in itable + // do { + // temp_itbl_klass = *(scan_temp += scan_step); + // if (temp_itbl_klass == holder_klass) { + // goto L_holder_found; // Found! + // } + // } while (temp_itbl_klass != 0); + // goto L_no_such_interface // Not found. + Label L_search_holder; + bind(L_search_holder); + add(scan_temp, scan_temp, scan_step); + ld(temp_itbl_klass, Address(scan_temp)); + beq(holder_klass, temp_itbl_klass, L_holder_found); + bnez(temp_itbl_klass, L_search_holder); + + j(L_no_such_interface); + + // Loop: Look for resolved_class record in itable + // while (true) { + // temp_itbl_klass = *(scan_temp += scan_step); + // if (temp_itbl_klass == 0) { + // goto L_no_such_interface; + // } + // if (temp_itbl_klass == resolved_klass) { + // goto L_resolved_found; // Found! + // } + // if (temp_itbl_klass == holder_klass) { + // holder_offset = scan_temp; + // } + // } + // + Label L_loop_search_resolved; + bind(L_loop_search_resolved); + add(scan_temp, scan_temp, scan_step); + ld(temp_itbl_klass, Address(scan_temp)); + bind(L_loop_search_resolved_entry); + beqz(temp_itbl_klass, L_no_such_interface); + beq(resolved_klass, temp_itbl_klass, L_resolved_found); + bne(holder_klass, temp_itbl_klass, L_loop_search_resolved); + mv(holder_offset, scan_temp); + j(L_loop_search_resolved); + + // See if we already have a holder klass. If not, go and scan for it. + bind(L_resolved_found); + beqz(holder_offset, L_search_holder); + mv(scan_temp, holder_offset); + + // Finally, scan_temp contains holder_klass vtable offset + bind(L_holder_found); + lwu(method_result, Address(scan_temp, ooffset_bytes - ioffset_bytes)); + add(recv_klass, recv_klass, itable_index * wordSize + itmentry_off_bytes + - vtable_start_offset_bytes - ioffset_bytes); // substract offsets to restore the original value of recv_klass + add(method_result, recv_klass, method_result); + ld(method_result, Address(method_result)); +} + // virtual method calling void MacroAssembler::lookup_virtual_method(Register recv_klass, RegisterOrConstant vtable_index, @@ -3576,6 +3662,24 @@ void MacroAssembler::mul_add(Register out, Register in, Register offset, bind(L_end); } +// Multiply and multiply-accumulate unsigned 64-bit registers. +void MacroAssembler::wide_mul(Register prod_lo, Register prod_hi, Register n, Register m) { + assert_different_registers(prod_lo, prod_hi); + + mul(prod_lo, n, m); + mulhu(prod_hi, n, m); +} + +void MacroAssembler::wide_madd(Register sum_lo, Register sum_hi, Register n, + Register m, Register tmp1, Register tmp2) { + assert_different_registers(sum_lo, sum_hi); + assert_different_registers(sum_hi, tmp2); + + wide_mul(tmp1, tmp2, n, m); + cad(sum_lo, sum_lo, tmp1, tmp1); // Add tmp1 to sum_lo with carry output to tmp1 + adc(sum_hi, sum_hi, tmp2, tmp1); // Add tmp2 with carry to sum_hi +} + // add two unsigned input and output carry void MacroAssembler::cad(Register dst, Register src1, Register src2, Register carry) { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 51bcba2f1a3fc..8308ea255c7e8 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -198,10 +198,6 @@ class MacroAssembler: public Assembler { void store_klass(Register dst, Register src, Register tmp = t0); void cmp_klass(Register oop, Register trial_klass, Register tmp1, Register tmp2, Label &L); - void wide_mul(Register prod_lo, Register prod_hi, Register n, Register m); - void wide_madd(Register sum_lo, Register sum_hi, Register n, - Register m, Register tmp1, Register tmp2); - void encode_klass_not_null(Register r, Register tmp = t0); void decode_klass_not_null(Register r, Register tmp = t0); void encode_klass_not_null(Register dst, Register src, Register tmp); @@ -258,6 +254,15 @@ class MacroAssembler: public Assembler { Label& no_such_interface, bool return_method = true); + void lookup_interface_method_stub(Register recv_klass, + Register holder_klass, + Register resolved_klass, + Register method_result, + Register temp_reg, + Register temp_reg2, + int itable_index, + Label& L_no_such_interface); + // virtual method calling // n.n. x86 allows RegisterOrConstant for vtable_index void lookup_virtual_method(Register recv_klass, @@ -1204,6 +1209,9 @@ class MacroAssembler: public Assembler { #ifdef COMPILER2 void mul_add(Register out, Register in, Register offset, Register len, Register k, Register tmp); + void wide_mul(Register prod_lo, Register prod_hi, Register n, Register m); + void wide_madd(Register sum_lo, Register sum_hi, Register n, + Register m, Register tmp1, Register tmp2); void cad(Register dst, Register src1, Register src2, Register carry); void cadc(Register dst, Register src1, Register src2, Register carry); void adc(Register dst, Register src1, Register src2, Register carry); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index cb2d540322aee..87f240873a85b 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -8645,7 +8645,7 @@ instruct MoveF2I_reg_reg(iRegINoSp dst, fRegF src) %{ ins_cost(XFER_COST); - format %{ "fmv.x.w $dst, $src\t#@MoveL2D_reg_stack" %} + format %{ "fmv.x.w $dst, $src\t#@MoveF2I_reg_reg" %} ins_encode %{ __ fmv_x_w(as_Register($dst$$reg), as_FloatRegister($src$$reg)); @@ -8699,7 +8699,7 @@ instruct MoveL2D_reg_reg(fRegD dst, iRegL src) %{ ins_cost(XFER_COST); - format %{ "fmv.d.x $dst, $src\t#@MoveD2L_reg_reg" %} + format %{ "fmv.d.x $dst, $src\t#@MoveL2D_reg_reg" %} ins_encode %{ __ fmv_d_x(as_FloatRegister($dst$$reg), as_Register($src$$reg)); @@ -8720,7 +8720,8 @@ instruct cmpF3_reg_reg(iRegINoSp dst, fRegF op1, fRegF op2) format %{ "flt.s $dst, $op2, $op1\t#@cmpF3_reg_reg\n\t" "bgtz $dst, done\n\t" "feq.s $dst, $op1, $op2\n\t" - "addi $dst, $dst, -1\t#@cmpF3_reg_reg" + "addi $dst, $dst, -1\n\t" + "done:" %} ins_encode %{ @@ -8740,7 +8741,8 @@ instruct cmpD3_reg_reg(iRegINoSp dst, fRegD op1, fRegD op2) format %{ "flt.d $dst, $op2, $op1\t#@cmpD3_reg_reg\n\t" "bgtz $dst, done\n\t" "feq.d $dst, $op1, $op2\n\t" - "addi $dst, $dst, -1\t#@cmpD3_reg_reg" + "addi $dst, $dst, -1\n\t" + "done:" %} ins_encode %{ @@ -8758,8 +8760,9 @@ instruct cmpL3_reg_reg(iRegINoSp dst, iRegL op1, iRegL op2) ins_cost(ALU_COST * 3 + BRANCH_COST); format %{ "slt $dst, $op2, $op1\t#@cmpL3_reg_reg\n\t" "bnez $dst, done\n\t" - "slt $dst, $op1, $op2\n\t" - "neg $dst, $dst\t#@cmpL3_reg_reg" + "slt $dst, $op1, $op2\n\t" + "neg $dst, $dst\n\t" + "done:" %} ins_encode %{ __ cmp_l2i(t0, as_Register($op1$$reg), as_Register($op2$$reg)); @@ -8777,7 +8780,8 @@ instruct cmpUL3_reg_reg(iRegINoSp dst, iRegL op1, iRegL op2) format %{ "sltu $dst, $op2, $op1\t#@cmpUL3_reg_reg\n\t" "bnez $dst, done\n\t" "sltu $dst, $op1, $op2\n\t" - "neg $dst, $dst\t#@cmpUL3_reg_reg" + "neg $dst, $dst\n\t" + "done:" %} ins_encode %{ __ cmp_ul2i(t0, as_Register($op1$$reg), as_Register($op2$$reg)); @@ -8795,7 +8799,8 @@ instruct cmpU3_reg_reg(iRegINoSp dst, iRegI op1, iRegI op2) format %{ "sltu $dst, $op2, $op1\t#@cmpU3_reg_reg\n\t" "bnez $dst, done\n\t" "sltu $dst, $op1, $op2\n\t" - "neg $dst, $dst\t#@cmpU3_reg_reg" + "neg $dst, $dst\n\t" + "done:" %} ins_encode %{ __ cmp_uw2i(t0, as_Register($op1$$reg), as_Register($op2$$reg)); @@ -8940,12 +8945,12 @@ instruct minI_rReg(iRegINoSp dst, iRegI src1, iRegI src2) ins_cost(BRANCH_COST + ALU_COST * 2); format %{ - "ble $src1, $src2, Lsrc1.\t#@minI_rReg\n\t" + "ble $src1, $src2, Lsrc1\t#@minI_rReg\n\t" "mv $dst, $src2\n\t" "j Ldone\n\t" - "bind Lsrc1\n\t" + "Lsrc1:\n\t" "mv $dst, $src1\n\t" - "bind\t#@minI_rReg" + "Ldone:" %} ins_encode %{ @@ -8972,9 +8977,9 @@ instruct maxI_rReg(iRegINoSp dst, iRegI src1, iRegI src2) "bge $src1, $src2, Lsrc1\t#@maxI_rReg\n\t" "mv $dst, $src2\n\t" "j Ldone\n\t" - "bind Lsrc1\n\t" + "Lsrc1:\n\t" "mv $dst, $src1\n\t" - "bind\t#@maxI_rReg" + "Ldone:" %} ins_encode %{ diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 2f1fb5055f609..fc83e62985837 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -46,7 +46,6 @@ RV_FEATURE_FLAGS(ADD_RV_FEATURE_IN_LIST) nullptr}; void VM_Version::initialize() { - _supports_cx8 = true; _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; _supports_atomic_getset8 = true; diff --git a/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp b/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp index 6ab3a26232345..9d08796681f3f 100644 --- a/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp +++ b/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp @@ -178,7 +178,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { // so all registers except arguments are free at this point. const Register recv_klass_reg = x18; const Register holder_klass_reg = x19; // declaring interface klass (DECC) - const Register resolved_klass_reg = xmethod; // resolved interface klass (REFC) + const Register resolved_klass_reg = x30; // resolved interface klass (REFC) const Register temp_reg = x28; const Register temp_reg2 = x29; const Register icholder_reg = t1; @@ -195,28 +195,13 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index) { __ load_klass(recv_klass_reg, j_rarg0); // Receiver subtype check against REFC. - __ lookup_interface_method(// inputs: rec. class, interface - recv_klass_reg, resolved_klass_reg, noreg, - // outputs: scan temp. reg1, scan temp. reg2 - temp_reg2, temp_reg, - L_no_such_interface, - /*return_method=*/false); - - const ptrdiff_t typecheckSize = __ pc() - start_pc; - start_pc = __ pc(); - // Get selected method from declaring class and itable index - __ lookup_interface_method(// inputs: rec. class, interface, itable index - recv_klass_reg, holder_klass_reg, itable_index, - // outputs: method, scan temp. reg - xmethod, temp_reg, - L_no_such_interface); - - const ptrdiff_t lookupSize = __ pc() - start_pc; + __ lookup_interface_method_stub(recv_klass_reg, holder_klass_reg, resolved_klass_reg, xmethod, + temp_reg, temp_reg2, itable_index, L_no_such_interface); // Reduce "estimate" such that "padding" does not drop below 8. const ptrdiff_t estimate = 256; - const ptrdiff_t codesize = typecheckSize + lookupSize; + const ptrdiff_t codesize = __ pc() - start_pc; slop_delta = (int)(estimate - codesize); slop_bytes += slop_delta; assert(slop_delta >= 0, "itable #%d: Code size estimate (%d) for lookup_interface_method too small, required: %d", itable_index, (int)estimate, (int)codesize); diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index f79d647b48c9b..542ade8ed0ec3 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -2670,7 +2670,6 @@ void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { Register addr = op->addr()->as_pointer_register(); Register t1_cmp = Z_R1_scratch; if (op->code() == lir_cas_long) { - assert(VM_Version::supports_cx8(), "wrong machine"); Register cmp_value_lo = op->cmp_value()->as_register_lo(); Register new_value_lo = op->new_value()->as_register_lo(); __ z_lgr(t1_cmp, cmp_value_lo); diff --git a/src/hotspot/cpu/s390/compressedKlass_s390.cpp b/src/hotspot/cpu/s390/compressedKlass_s390.cpp new file mode 100644 index 0000000000000..868df0f02d7cb --- /dev/null +++ b/src/hotspot/cpu/s390/compressedKlass_s390.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "oops/compressedKlass.hpp" +#include "utilities/globalDefinitions.hpp" + +char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base) { + + char* result = nullptr; + + uintptr_t tried_below = 0; + + // First, attempt to allocate < 4GB. We do this unconditionally: + // - if optimize_for_zero_base, a <4GB mapping start allows us to use base=0 shift=0 + // - if !optimize_for_zero_base, a <4GB mapping start allows us to use algfi + result = reserve_address_space_for_unscaled_encoding(size, aslr); + + // Failing that, try optimized for base=0 shift>0 + if (result == nullptr && optimize_for_zero_base) { + result = reserve_address_space_for_zerobased_encoding(size, aslr); + } + + // Failing that, aim for a base that is 4G-aligned; such a base can be set with aih. + if (result == nullptr) { + result = reserve_address_space_for_16bit_move(size, aslr); + } + + return result; +} diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index 23e9e975531bb..af0903884fb4f 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -287,11 +287,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UsePopCountInstruction, true); } - // z/Architecture supports 8-byte compare-exchange operations - // (see Atomic::cmpxchg) - // and 'atomic long memory ops' (see Unsafe_GetLongVolatile). - _supports_cx8 = true; - _supports_atomic_getadd4 = VM_Version::has_LoadAndALUAtomicV1(); _supports_atomic_getadd8 = VM_Version::has_LoadAndALUAtomicV1(); diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 0fc98a6ac1006..cedddaed97588 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -3417,6 +3417,27 @@ void Assembler::evmovdquq(XMMRegister dst, KRegister mask, Address src, bool mer emit_operand(dst, src, 0); } +void Assembler::evmovntdquq(Address dst, XMMRegister src, int vector_len) { + // Unmasked instruction + evmovntdquq(dst, k0, src, /*merge*/ true, vector_len); +} + +void Assembler::evmovntdquq(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(src != xnoreg, "sanity"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0xE7); + emit_operand(src, dst, 0); +} + void Assembler::evmovdquq(Address dst, XMMRegister src, int vector_len) { // Unmasked instruction evmovdquq(dst, k0, src, /*merge*/ true, vector_len); diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index c6083d844aa4f..3bcfee90b3537 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1615,6 +1615,9 @@ class Assembler : public AbstractAssembler { void evmovdqul(XMMRegister dst, KRegister mask, Address src, bool merge, int vector_len); void evmovdqul(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + void evmovntdquq(Address dst, KRegister mask, XMMRegister src, bool merge, int vector_len); + void evmovntdquq(Address dst, XMMRegister src, int vector_len); + void evmovdquq(Address dst, XMMRegister src, int vector_len); void evmovdquq(XMMRegister dst, Address src, int vector_len); void evmovdquq(XMMRegister dst, XMMRegister src, int vector_len); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index c6f469d433916..ff0726840d30a 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1929,7 +1929,7 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { void LIR_Assembler::emit_compare_and_swap(LIR_OpCompareAndSwap* op) { - if (LP64_ONLY(false &&) op->code() == lir_cas_long && VM_Version::supports_cx8()) { + if (LP64_ONLY(false &&) op->code() == lir_cas_long) { assert(op->cmp_value()->as_register_lo() == rax, "wrong register"); assert(op->cmp_value()->as_register_hi() == rdx, "wrong register"); assert(op->new_value()->as_register_lo() == rbx, "wrong register"); @@ -3629,13 +3629,33 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ verify_oop(obj); - if (tmp != obj) { - __ mov(tmp, obj); +#ifdef ASSERT + if (obj == tmp) { +#ifdef _LP64 + assert_different_registers(obj, rscratch1, mdo_addr.base(), mdo_addr.index()); +#else + assert_different_registers(obj, mdo_addr.base(), mdo_addr.index()); +#endif + } else { +#ifdef _LP64 + assert_different_registers(obj, tmp, rscratch1, mdo_addr.base(), mdo_addr.index()); +#else + assert_different_registers(obj, tmp, mdo_addr.base(), mdo_addr.index()); +#endif } +#endif if (do_null) { - __ testptr(tmp, tmp); + __ testptr(obj, obj); __ jccb(Assembler::notZero, update); if (!TypeEntries::was_null_seen(current_klass)) { + __ testptr(mdo_addr, TypeEntries::null_seen); +#ifndef ASSERT + __ jccb(Assembler::notZero, next); // already set +#else + __ jcc(Assembler::notZero, next); // already set +#endif + // atomic update to prevent overwriting Klass* with 0 + __ lock(); __ orptr(mdo_addr, TypeEntries::null_seen); } if (do_update) { @@ -3646,7 +3666,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ jmp(next); } } else { - __ testptr(tmp, tmp); + __ testptr(obj, obj); __ jcc(Assembler::notZero, update); __ stop("unexpected null obj"); #endif @@ -3658,7 +3678,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { #ifdef ASSERT if (exact_klass != nullptr) { Label ok; - __ load_klass(tmp, tmp, tmp_load_klass); + __ load_klass(tmp, obj, tmp_load_klass); __ push(tmp); __ mov_metadata(tmp, exact_klass->constant_encoding()); __ cmpptr(tmp, Address(rsp, 0)); @@ -3673,9 +3693,11 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { if (exact_klass != nullptr) { __ mov_metadata(tmp, exact_klass->constant_encoding()); } else { - __ load_klass(tmp, tmp, tmp_load_klass); + __ load_klass(tmp, obj, tmp_load_klass); } - +#ifdef _LP64 + __ mov(rscratch1, tmp); // save original value before XOR +#endif __ xorptr(tmp, mdo_addr); __ testptr(tmp, TypeEntries::type_klass_mask); // klass seen before, nothing to do. The unknown bit may have been @@ -3686,23 +3708,23 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore. if (TypeEntries::is_type_none(current_klass)) { - __ cmpptr(mdo_addr, 0); - __ jccb(Assembler::equal, none); - __ cmpptr(mdo_addr, TypeEntries::null_seen); - __ jccb(Assembler::equal, none); + __ testptr(mdo_addr, TypeEntries::type_mask); + __ jccb(Assembler::zero, none); +#ifdef _LP64 // There is a chance that the checks above (re-reading profiling // data from memory) fail if another thread has just set the // profiling to this obj's klass + __ mov(tmp, rscratch1); // get back original value before XOR __ xorptr(tmp, mdo_addr); __ testptr(tmp, TypeEntries::type_klass_mask); __ jccb(Assembler::zero, next); +#endif } } else { assert(ciTypeEntries::valid_ciklass(current_klass) != nullptr && ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "conflict only"); - __ movptr(tmp, mdo_addr); - __ testptr(tmp, TypeEntries::type_unknown); + __ testptr(mdo_addr, TypeEntries::type_unknown); __ jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore. } @@ -3715,6 +3737,10 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { __ bind(none); // first time here. Set profile type. __ movptr(mdo_addr, tmp); +#ifdef ASSERT + __ andptr(tmp, TypeEntries::type_klass_mask); + __ verify_klass_ptr(tmp); +#endif } } else { // There's a single possible klass at this profile point @@ -3729,10 +3755,8 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { { Label ok; __ push(tmp); - __ cmpptr(mdo_addr, 0); - __ jcc(Assembler::equal, ok); - __ cmpptr(mdo_addr, TypeEntries::null_seen); - __ jcc(Assembler::equal, ok); + __ testptr(mdo_addr, TypeEntries::type_mask); + __ jcc(Assembler::zero, ok); // may have been set by another thread __ mov_metadata(tmp, exact_klass->constant_encoding()); __ xorptr(tmp, mdo_addr); @@ -3748,20 +3772,22 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { #endif // first time here. Set profile type. __ movptr(mdo_addr, tmp); +#ifdef ASSERT + __ andptr(tmp, TypeEntries::type_klass_mask); + __ verify_klass_ptr(tmp); +#endif } else { assert(ciTypeEntries::valid_ciklass(current_klass) != nullptr && ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "inconsistent"); - __ movptr(tmp, mdo_addr); - __ testptr(tmp, TypeEntries::type_unknown); + __ testptr(mdo_addr, TypeEntries::type_unknown); __ jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore. __ orptr(mdo_addr, TypeEntries::type_unknown); } } - - __ bind(next); } + __ bind(next); } void LIR_Assembler::emit_delay(LIR_OpDelay*) { diff --git a/src/hotspot/cpu/x86/compressedKlass_x86.cpp b/src/hotspot/cpu/x86/compressedKlass_x86.cpp new file mode 100644 index 0000000000000..5b5a405bcef86 --- /dev/null +++ b/src/hotspot/cpu/x86/compressedKlass_x86.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +#ifdef _LP64 + +#include "oops/compressedKlass.hpp" +#include "utilities/globalDefinitions.hpp" + +char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base) { + + char* result = nullptr; + + // Optimize for unscaled encoding; failing that, for zero-based encoding: + if (optimize_for_zero_base) { + result = reserve_address_space_for_unscaled_encoding(size, aslr); + if (result == nullptr) { + result = reserve_address_space_for_zerobased_encoding(size, aslr); + } + } // end: low-address reservation + + // Nothing more to optimize for on x64. If base != 0, we will always emit the full 64-bit immediate. + return result; +} + +#endif // _LP64 diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoah_x86_64.ad b/src/hotspot/cpu/x86/gc/shenandoah/shenandoah_x86_64.ad index 3df65d037ae51..cb595f161686f 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoah_x86_64.ad +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoah_x86_64.ad @@ -33,7 +33,6 @@ instruct compareAndSwapP_shenandoah(rRegI res, rax_RegP oldval, rRegP newval, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); match(Set res (ShenandoahCompareAndSwapP mem_ptr (Binary oldval newval))); match(Set res (ShenandoahWeakCompareAndSwapP mem_ptr (Binary oldval newval))); effect(TEMP tmp1, TEMP tmp2, KILL cr, KILL oldval); @@ -95,7 +94,6 @@ instruct compareAndExchangeP_shenandoah(memory mem_ptr, rRegP tmp1, rRegP tmp2, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); match(Set oldval (ShenandoahCompareAndExchangeP mem_ptr (Binary oldval newval))); effect(KILL cr, TEMP tmp1, TEMP tmp2); ins_cost(1000); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 913ac4c1df95a..f5f83ae21f475 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -54,15 +54,28 @@ void InterpreterMacroAssembler::jump_to_entry(address entry) { void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { Label update, next, none; +#ifdef _LP64 + assert_different_registers(obj, rscratch1, mdo_addr.base(), mdo_addr.index()); +#else + assert_different_registers(obj, mdo_addr.base(), mdo_addr.index()); +#endif + interp_verify_oop(obj, atos); testptr(obj, obj); jccb(Assembler::notZero, update); + testptr(mdo_addr, TypeEntries::null_seen); + jccb(Assembler::notZero, next); // null already seen. Nothing to do anymore. + // atomic update to prevent overwriting Klass* with 0 + lock(); orptr(mdo_addr, TypeEntries::null_seen); jmpb(next); bind(update); load_klass(obj, obj, rscratch1); +#ifdef _LP64 + mov(rscratch1, obj); +#endif xorptr(obj, mdo_addr); testptr(obj, TypeEntries::type_klass_mask); @@ -77,12 +90,15 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md jccb(Assembler::equal, none); cmpptr(mdo_addr, TypeEntries::null_seen); jccb(Assembler::equal, none); +#ifdef _LP64 // There is a chance that the checks above (re-reading profiling // data from memory) fail if another thread has just set the // profiling to this obj's klass + mov(obj, rscratch1); xorptr(obj, mdo_addr); testptr(obj, TypeEntries::type_klass_mask); jccb(Assembler::zero, next); +#endif // different than before. Cannot keep accurate profile. orptr(mdo_addr, TypeEntries::type_unknown); @@ -91,6 +107,10 @@ void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& md bind(none); // first time here. Set profile type. movptr(mdo_addr, obj); +#ifdef ASSERT + andptr(obj, TypeEntries::type_klass_mask); + verify_klass_ptr(obj); +#endif bind(next); } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index f875089ac678a..47943576dcec9 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -894,6 +894,7 @@ class MacroAssembler: public Assembler { void testptr(Register src, int32_t imm32) { LP64_ONLY(testq(src, imm32)) NOT_LP64(testl(src, imm32)); } void testptr(Register src1, Address src2) { LP64_ONLY(testq(src1, src2)) NOT_LP64(testl(src1, src2)); } + void testptr(Address src, int32_t imm32) { LP64_ONLY(testq(src, imm32)) NOT_LP64(testl(src, imm32)); } void testptr(Register src1, Register src2); void xorptr(Register dst, Register src) { LP64_ONLY(xorq(dst, src)) NOT_LP64(xorl(dst, src)); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 109c98f83bd2a..6b7da7184988e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -187,11 +187,20 @@ class StubGenerator: public StubCodeGenerator { Register index, Register temp, bool use64byteVector, Label& L_entry, Label& L_exit); + void arraycopy_avx3_special_cases_256(XMMRegister xmm, KRegister mask, Register from, + Register to, Register count, int shift, + Register index, Register temp, Label& L_exit); + void arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegister mask, Register from, Register to, Register start_index, Register end_index, Register count, int shift, Register temp, bool use64byteVector, Label& L_entry, Label& L_exit); + void arraycopy_avx3_large(Register to, Register from, Register temp1, Register temp2, + Register temp3, Register temp4, Register count, + XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, + XMMRegister xmm4, int shift); + void copy32_avx(Register dst, Register src, Register index, XMMRegister xmm, int shift = Address::times_1, int offset = 0); @@ -199,6 +208,9 @@ class StubGenerator: public StubCodeGenerator { bool conjoint, int shift = Address::times_1, int offset = 0, bool use64byteVector = false); + void copy256_avx3(Register dst, Register src, Register index, XMMRegister xmm1, XMMRegister xmm2, + XMMRegister xmm3, XMMRegister xmm4, int shift, int offset = 0); + void copy64_masked_avx(Register dst, Register src, XMMRegister xmm, KRegister mask, Register length, Register index, Register temp, int shift = Address::times_1, int offset = 0, diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index 80d9b4844ea25..c802f953c9054 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -515,8 +515,10 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(address* entry, const int avx3threshold = VM_Version::avx3_threshold(); bool use64byteVector = (MaxVectorSize > 32) && (avx3threshold == 0); + const int large_threshold = 2621440; // 2.5 MB Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry; Label L_repmovs, L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64; + Label L_copy_large, L_finish; const Register from = rdi; // source array address const Register to = rsi; // destination array address const Register count = rdx; // elements count @@ -577,6 +579,12 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(address* entry, const // PRE-MAIN-POST loop for aligned copy. __ BIND(L_entry); + if (MaxVectorSize == 64) { + __ movq(temp2, temp1); + __ shlq(temp2, shift); + __ cmpq(temp2, large_threshold); + __ jcc(Assembler::greaterEqual, L_copy_large); + } if (avx3threshold != 0) { __ cmpq(count, threshold[shift]); if (MaxVectorSize == 64) { @@ -703,6 +711,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(address* entry, const __ BIND(L_exit); } + __ BIND(L_finish); address ucme_exit_pc = __ pc(); // When called from generic_arraycopy r11 contains specific values // used during arraycopy epilogue, re-initializing r11. @@ -717,9 +726,77 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(address* entry, const __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + if (MaxVectorSize == 64) { + __ BIND(L_copy_large); + arraycopy_avx3_large(to, from, temp1, temp2, temp3, temp4, count, xmm1, xmm2, xmm3, xmm4, shift); + __ jmp(L_finish); + } return start; } +void StubGenerator::arraycopy_avx3_large(Register to, Register from, Register temp1, Register temp2, + Register temp3, Register temp4, Register count, + XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3, + XMMRegister xmm4, int shift) { + + // Type(shift) byte(0), short(1), int(2), long(3) + int loop_size[] = { 256, 128, 64, 32}; + int threshold[] = { 4096, 2048, 1024, 512}; + + Label L_main_loop_large; + Label L_tail_large; + Label L_exit_large; + Label L_entry_large; + Label L_main_pre_loop_large; + Label L_pre_main_post_large; + + assert(MaxVectorSize == 64, "vector length != 64"); + __ BIND(L_entry_large); + + __ BIND(L_pre_main_post_large); + // Partial copy to make dst address 64 byte aligned. + __ movq(temp2, to); + __ andq(temp2, 63); + __ jcc(Assembler::equal, L_main_pre_loop_large); + + __ negptr(temp2); + __ addq(temp2, 64); + if (shift) { + __ shrq(temp2, shift); + } + __ movq(temp3, temp2); + copy64_masked_avx(to, from, xmm1, k2, temp3, temp4, temp1, shift, 0, true); + __ movq(temp4, temp2); + __ movq(temp1, count); + __ subq(temp1, temp2); + + __ cmpq(temp1, loop_size[shift]); + __ jcc(Assembler::less, L_tail_large); + + __ BIND(L_main_pre_loop_large); + __ subq(temp1, loop_size[shift]); + + // Main loop with aligned copy block size of 256 bytes at 64 byte copy granularity. + __ align32(); + __ BIND(L_main_loop_large); + copy256_avx3(to, from, temp4, xmm1, xmm2, xmm3, xmm4, shift, 0); + __ addptr(temp4, loop_size[shift]); + __ subq(temp1, loop_size[shift]); + __ jcc(Assembler::greater, L_main_loop_large); + // fence needed because copy256_avx3 uses non-temporal stores + __ sfence(); + + __ addq(temp1, loop_size[shift]); + // Zero length check. + __ jcc(Assembler::lessEqual, L_exit_large); + __ BIND(L_tail_large); + // Tail handling using 64 byte [masked] vector copy operations. + __ cmpq(temp1, 0); + __ jcc(Assembler::lessEqual, L_exit_large); + arraycopy_avx3_special_cases_256(xmm1, k2, from, to, temp1, shift, + temp4, temp3, L_exit_large); + __ BIND(L_exit_large); +} // Inputs: // c_rarg0 - source array address @@ -965,6 +1042,55 @@ void StubGenerator::arraycopy_avx3_special_cases(XMMRegister xmm, KRegister mask __ jmp(L_exit); } +void StubGenerator::arraycopy_avx3_special_cases_256(XMMRegister xmm, KRegister mask, Register from, + Register to, Register count, int shift, Register index, + Register temp, Label& L_exit) { + Label L_entry_64, L_entry_128, L_entry_192, L_entry_256; + + int size_mat[][4] = { + /* T_BYTE */ {64, 128, 192, 256}, + /* T_SHORT*/ {32, 64 , 96 , 128}, + /* T_INT */ {16, 32 , 48 , 64}, + /* T_LONG */ { 8, 16 , 24 , 32} + }; + + assert(MaxVectorSize == 64, "vector length != 64"); + // Case A) Special case for length less than or equal to 64 bytes. + __ BIND(L_entry_64); + __ cmpq(count, size_mat[shift][0]); + __ jccb(Assembler::greater, L_entry_128); + copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 0, true); + __ jmp(L_exit); + + // Case B) Special case for length less than or equal to 128 bytes. + __ BIND(L_entry_128); + __ cmpq(count, size_mat[shift][1]); + __ jccb(Assembler::greater, L_entry_192); + copy64_avx(to, from, index, xmm, false, shift, 0, true); + __ subq(count, 64 >> shift); + copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 64, true); + __ jmp(L_exit); + + // Case C) Special case for length less than or equal to 192 bytes. + __ BIND(L_entry_192); + __ cmpq(count, size_mat[shift][2]); + __ jcc(Assembler::greater, L_entry_256); + copy64_avx(to, from, index, xmm, false, shift, 0, true); + copy64_avx(to, from, index, xmm, false, shift, 64, true); + __ subq(count, 128 >> shift); + copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 128, true); + __ jmp(L_exit); + + // Case D) Special case for length less than or equal to 256 bytes. + __ BIND(L_entry_256); + copy64_avx(to, from, index, xmm, false, shift, 0, true); + copy64_avx(to, from, index, xmm, false, shift, 64, true); + copy64_avx(to, from, index, xmm, false, shift, 128, true); + __ subq(count, 192 >> shift); + copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 192, true); + __ jmp(L_exit); +} + void StubGenerator::arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegister mask, Register from, Register to, Register start_index, Register end_index, Register count, int shift, Register temp, @@ -1040,6 +1166,33 @@ void StubGenerator::arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegi __ jmp(L_exit); } +void StubGenerator::copy256_avx3(Register dst, Register src, Register index, XMMRegister xmm1, + XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4, + int shift, int offset) { + if (MaxVectorSize == 64) { + Address::ScaleFactor scale = (Address::ScaleFactor)(shift); + __ prefetcht0(Address(src, index, scale, offset + 0x200)); + __ prefetcht0(Address(src, index, scale, offset + 0x240)); + __ prefetcht0(Address(src, index, scale, offset + 0x280)); + __ prefetcht0(Address(src, index, scale, offset + 0x2C0)); + + __ prefetcht0(Address(src, index, scale, offset + 0x400)); + __ prefetcht0(Address(src, index, scale, offset + 0x440)); + __ prefetcht0(Address(src, index, scale, offset + 0x480)); + __ prefetcht0(Address(src, index, scale, offset + 0x4C0)); + + __ evmovdquq(xmm1, Address(src, index, scale, offset), Assembler::AVX_512bit); + __ evmovdquq(xmm2, Address(src, index, scale, offset + 0x40), Assembler::AVX_512bit); + __ evmovdquq(xmm3, Address(src, index, scale, offset + 0x80), Assembler::AVX_512bit); + __ evmovdquq(xmm4, Address(src, index, scale, offset + 0xC0), Assembler::AVX_512bit); + + __ evmovntdquq(Address(dst, index, scale, offset), xmm1, Assembler::AVX_512bit); + __ evmovntdquq(Address(dst, index, scale, offset + 0x40), xmm2, Assembler::AVX_512bit); + __ evmovntdquq(Address(dst, index, scale, offset + 0x80), xmm3, Assembler::AVX_512bit); + __ evmovntdquq(Address(dst, index, scale, offset + 0xC0), xmm4, Assembler::AVX_512bit); + } +} + void StubGenerator::copy64_masked_avx(Register dst, Register src, XMMRegister xmm, KRegister mask, Register length, Register index, Register temp, int shift, int offset, diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 0ead388415efe..1517e456e8230 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -816,7 +816,6 @@ void VM_Version::get_processor_features() { _L1_data_cache_line_size = L1_line_size(); } - _supports_cx8 = supports_cmpxchg8(); // xchg and xadd instructions _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; @@ -3236,4 +3235,3 @@ bool VM_Version::is_intrinsic_supported(vmIntrinsicID id) { } return true; } - diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 078f14221dd9b..454a8f312553a 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -643,7 +643,6 @@ class VM_Version : public Abstract_VM_Version { // Feature identification // static bool supports_cpuid() { return _features != 0; } - static bool supports_cmpxchg8() { return (_features & CPU_CX8) != 0; } static bool supports_cmov() { return (_features & CPU_CMOV) != 0; } static bool supports_fxsr() { return (_features & CPU_FXSR) != 0; } static bool supports_ht() { return (_features & CPU_HT) != 0; } diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 2567246b2060b..681abd7199fee 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1510,9 +1510,6 @@ bool Matcher::match_rule_supported(int opcode) { #ifdef _LP64 case Op_CompareAndSwapP: #endif - if (!VM_Version::supports_cx8()) { - return false; - } break; case Op_StrIndexOf: if (!UseSSE42Intrinsics) { @@ -10119,5 +10116,3 @@ instruct DoubleClassCheck_reg_reg_vfpclass(rRegI dst, regD src, kReg ktmp, rFlag %} ins_pipe(pipe_slow); %} - - diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index d2d3c6b6fa199..07b3cb35a388d 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -7281,7 +7281,6 @@ instruct castDD_PR( regDPR dst ) %{ // No flag versions for CompareAndSwap{P,I,L} because matcher can't match them instruct compareAndSwapL( rRegI res, eSIRegP mem_ptr, eADXRegL oldval, eBCXRegL newval, eFlagsReg cr ) %{ - predicate(VM_Version::supports_cx8()); match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval))); match(Set res (WeakCompareAndSwapL mem_ptr (Binary oldval newval))); effect(KILL cr, KILL oldval); @@ -7350,7 +7349,6 @@ instruct compareAndSwapI( rRegI res, pRegP mem_ptr, eAXRegI oldval, eCXRegI newv %} instruct compareAndExchangeL( eSIRegP mem_ptr, eADXRegL oldval, eBCXRegL newval, eFlagsReg cr ) %{ - predicate(VM_Version::supports_cx8()); match(Set oldval (CompareAndExchangeL mem_ptr (Binary oldval newval))); effect(KILL cr); format %{ "CMPXCHG8 [$mem_ptr],$newval\t# If EDX:EAX==[$mem_ptr] Then store $newval into [$mem_ptr]\n\t" %} diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index e41cadb4d4f96..94844cd4e7f9d 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -7174,7 +7174,7 @@ instruct compareAndSwapP(rRegI res, rax_RegP oldval, rRegP newval, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8() && n->as_LoadStore()->barrier_data() == 0); + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval))); match(Set res (WeakCompareAndSwapP mem_ptr (Binary oldval newval))); effect(KILL cr, KILL oldval); @@ -7197,7 +7197,6 @@ instruct compareAndSwapL(rRegI res, rax_RegL oldval, rRegL newval, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval))); match(Set res (WeakCompareAndSwapL mem_ptr (Binary oldval newval))); effect(KILL cr, KILL oldval); @@ -7358,7 +7357,6 @@ instruct compareAndExchangeL( rax_RegL oldval, rRegL newval, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8()); match(Set oldval (CompareAndExchangeL mem_ptr (Binary oldval newval))); effect(KILL cr); @@ -7392,7 +7390,7 @@ instruct compareAndExchangeP( rax_RegP oldval, rRegP newval, rFlagsReg cr) %{ - predicate(VM_Version::supports_cx8() && n->as_LoadStore()->barrier_data() == 0); + predicate(n->as_LoadStore()->barrier_data() == 0); match(Set oldval (CompareAndExchangeP mem_ptr (Binary oldval newval))); effect(KILL cr); diff --git a/src/hotspot/cpu/zero/vm_version_zero.cpp b/src/hotspot/cpu/zero/vm_version_zero.cpp index 07f99d647ac60..1fcf4b1086253 100644 --- a/src/hotspot/cpu/zero/vm_version_zero.cpp +++ b/src/hotspot/cpu/zero/vm_version_zero.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright 2009 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -137,6 +137,14 @@ void VM_Version::initialize() { #ifdef ASSERT UNSUPPORTED_OPTION(CountCompiledCalls); #endif + +#ifndef SUPPORTS_NATIVE_CX8 + // Supports 8-byte cmpxchg with compiler built-ins. + // These built-ins are supposed to be implemented on + // all platforms (even if not natively), so we claim + // the support unconditionally. + _supports_cx8 = true; +#endif } void VM_Version::initialize_cpu_information(void) { @@ -145,12 +153,6 @@ void VM_Version::initialize_cpu_information(void) { return; } - // Supports 8-byte cmpxchg with compiler built-ins. - // These built-ins are supposed to be implemented on - // all platforms (even if not natively), so we claim - // the support unconditionally. - _supports_cx8 = true; - _no_of_cores = os::processor_count(); _no_of_threads = _no_of_cores; _no_of_sockets = _no_of_cores; diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index c29e666c0fc4c..54c538cf367ed 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -1017,6 +1017,10 @@ int os::current_process_id() { // directory not the java application's temp directory, ala java.io.tmpdir. const char* os::get_temp_directory() { return "/tmp"; } +void os::prepare_native_symbols() { + LoadedLibraries::reload(); +} + // Check if addr is inside libjvm.so. bool os::address_is_in_vm(address addr) { @@ -1914,10 +1918,6 @@ bool os::numa_get_group_ids_for_range(const void** addresses, int* lgrp_ids, siz return false; } -char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { - return end; -} - // Reserves and attaches a shared memory segment. char* os::pd_reserve_memory(size_t bytes, bool exec) { // Always round to os::vm_page_size(), which may be larger than 4K. diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index f5968d20420d5..fc8e0da1a69de 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -895,6 +895,9 @@ bool os::address_is_in_vm(address addr) { return false; } +void os::prepare_native_symbols() { +} + bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset, bool demangle) { @@ -1674,11 +1677,6 @@ bool os::numa_get_group_ids_for_range(const void** addresses, int* lgrp_ids, siz return false; } -char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { - return end; -} - - bool os::pd_uncommit_memory(char* addr, size_t size, bool exec) { #if defined(__OpenBSD__) // XXX: Work-around mmap/MAP_FIXED bug temporarily on OpenBSD diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index f096de19acdd9..b8fb72867d6df 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1476,6 +1476,9 @@ bool os::address_is_in_vm(address addr) { return false; } +void os::prepare_native_symbols() { +} + bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset, bool demangle) { @@ -3024,12 +3027,6 @@ size_t os::numa_get_leaf_groups(uint *ids, size_t size) { return i; } -char *os::scan_pages(char *start, char* end, page_info* page_expected, - page_info* page_found) { - return end; -} - - int os::Linux::sched_getcpu_syscall(void) { unsigned int cpu = 0; long retval = -1; diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 1b75693b138a9..960fb465590cc 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -343,9 +343,10 @@ char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, in } static size_t calculate_aligned_extra_size(size_t size, size_t alignment) { - assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, + assert(is_aligned(alignment, os::vm_allocation_granularity()), "Alignment must be a multiple of allocation granularity (page size)"); - assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned"); + assert(is_aligned(size, os::vm_allocation_granularity()), + "Size must be a multiple of allocation granularity (page size)"); size_t extra_size = size + alignment; assert(extra_size >= size, "overflow, size is too large to allow alignment"); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 1b7fadab7dbfe..223bc0bd13514 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1398,6 +1398,9 @@ const char* os::get_current_directory(char *buf, size_t buflen) { return _getcwd(buf, n); } +void os::prepare_native_symbols() { +} + //----------------------------------------------------------- // Helper functions for fatal error handler #ifdef _WIN64 @@ -3328,9 +3331,10 @@ char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, in // virtual space to get requested alignment, like posix-like os's. // Windows prevents multiple thread from remapping over each other so this loop is thread-safe. static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { - assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, - "Alignment must be a multiple of allocation granularity (page size)"); - assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned"); + assert(is_aligned(alignment, os::vm_allocation_granularity()), + "Alignment must be a multiple of allocation granularity (page size)"); + assert(is_aligned(size, os::vm_allocation_granularity()), + "Size must be a multiple of allocation granularity (page size)"); size_t extra_size = size + alignment; assert(extra_size >= size, "overflow, size is too large to allow alignment"); @@ -3825,11 +3829,6 @@ bool os::numa_get_group_ids_for_range(const void** addresses, int* lgrp_ids, siz return false; } -char *os::scan_pages(char *start, char* end, page_info* page_expected, - page_info* page_found) { - return end; -} - char* os::non_memory_address_word() { // Must never look like an address returned by reserve_memory, // even in its subfields (as defined by the CPU immediate fields, diff --git a/src/hotspot/os_cpu/linux_arm/atomic_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/atomic_linux_arm.hpp index 513217649e633..4346920cd3765 100644 --- a/src/hotspot/os_cpu/linux_arm/atomic_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/atomic_linux_arm.hpp @@ -153,7 +153,6 @@ inline int32_t reorder_cmpxchg_func(int32_t exchange_value, inline int64_t reorder_cmpxchg_long_func(int64_t exchange_value, int64_t volatile* dest, int64_t compare_value) { - assert(VM_Version::supports_cx8(), "Atomic compare and exchange int64_t not supported on this architecture!"); // Warning: Arguments are swapped to avoid moving them for kernel call return (*ARMAtomicFuncs::_cmpxchg_long_func)(compare_value, exchange_value, dest); } diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index d471cf14d1381..6e93406b1a353 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -74,7 +74,7 @@ #define read_csr(csr) \ ({ \ - register unsigned long __v; \ + unsigned long __v; \ __asm__ __volatile__ ("csrr %0, %1" \ : "=r" (__v) \ : "i" (csr) \ diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index 9d9ff62f02381..c84f830eee72b 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -116,7 +116,6 @@ bool Compiler::is_intrinsic_supported(const methodHandle& method) { bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { switch (id) { case vmIntrinsics::_compareAndSetLong: - if (!VM_Version::supports_cx8()) return false; break; case vmIntrinsics::_getAndAddInt: if (!VM_Version::supports_atomic_getadd4()) return false; diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index 3f7f086220fef..edf6f29478c4d 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -2517,6 +2517,8 @@ XHandlers* GraphBuilder::handle_exception(Instruction* instruction) { // xhandler start with an empty expression stack if (cur_state->stack_size() != 0) { + // locals are preserved + // stack will be truncated cur_state = cur_state->copy(ValueStack::ExceptionState, cur_state->bci()); } if (instruction->exception_state() == nullptr) { @@ -2566,15 +2568,19 @@ XHandlers* GraphBuilder::handle_exception(Instruction* instruction) { // This scope and all callees do not handle exceptions, so the local // variables of this scope are not needed. However, the scope itself is // required for a correct exception stack trace -> clear out the locals. - if (_compilation->env()->should_retain_local_variables()) { - cur_state = cur_state->copy(ValueStack::ExceptionState, cur_state->bci()); - } else { - cur_state = cur_state->copy(ValueStack::EmptyExceptionState, cur_state->bci()); - } + // Stack and locals are invalidated but not truncated in caller state. if (prev_state != nullptr) { + assert(instruction->exception_state() != nullptr, "missed set?"); + ValueStack::Kind exc_kind = ValueStack::empty_exception_kind(true /* caller */); + cur_state = cur_state->copy(exc_kind, cur_state->bci()); + // reset caller exception state prev_state->set_caller_state(cur_state); - } - if (instruction->exception_state() == nullptr) { + } else { + assert(instruction->exception_state() == nullptr, "already set"); + // set instruction exception state + // truncate stack + ValueStack::Kind exc_kind = ValueStack::empty_exception_kind(); + cur_state = cur_state->copy(exc_kind, cur_state->bci()); instruction->set_exception_state(cur_state); } } @@ -3487,11 +3493,9 @@ ValueStack* GraphBuilder::copy_state_exhandling_with_bci(int bci) { ValueStack* GraphBuilder::copy_state_for_exception_with_bci(int bci) { ValueStack* s = copy_state_exhandling_with_bci(bci); if (s == nullptr) { - if (_compilation->env()->should_retain_local_variables()) { - s = state()->copy(ValueStack::ExceptionState, bci); - } else { - s = state()->copy(ValueStack::EmptyExceptionState, bci); - } + // no handler, no need to retain locals + ValueStack::Kind exc_kind = ValueStack::empty_exception_kind(); + s = state()->copy(exc_kind, bci); } return s; } @@ -3506,7 +3510,6 @@ int GraphBuilder::recursive_inline_level(ciMethod* cur_callee) const { return recur_level; } - bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, bool ignore_return, Bytecodes::Code bc, Value receiver) { const char* msg = nullptr; diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index afa36bde3d3a8..b17bed7e3f200 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -403,8 +403,20 @@ CodeEmitInfo* LIRGenerator::state_for(Instruction* x, ValueStack* state, bool ig ValueStack* s = state; for_each_state(s) { - if (s->kind() == ValueStack::EmptyExceptionState) { - assert(s->stack_size() == 0 && s->locals_size() == 0 && (s->locks_size() == 0 || s->locks_size() == 1), "state must be empty"); + if (s->kind() == ValueStack::EmptyExceptionState || + s->kind() == ValueStack::CallerEmptyExceptionState) + { +#ifdef ASSERT + int index; + Value value; + for_each_stack_value(s, index, value) { + fatal("state must be empty"); + } + for_each_local_value(s, index, value) { + fatal("state must be empty"); + } +#endif + assert(s->locks_size() == 0 || s->locks_size() == 1, "state must be empty"); continue; } diff --git a/src/hotspot/share/c1/c1_LinearScan.cpp b/src/hotspot/share/c1/c1_LinearScan.cpp index f779793584602..97f98d5a5e01e 100644 --- a/src/hotspot/share/c1/c1_LinearScan.cpp +++ b/src/hotspot/share/c1/c1_LinearScan.cpp @@ -2933,16 +2933,10 @@ IRScopeDebugInfo* LinearScan::compute_debug_info_for_scope(int op_id, IRScope* c assert(locals->length() == pos, "must match"); } - assert(locals->length() == cur_scope->method()->max_locals(), "wrong number of locals"); - assert(locals->length() == cur_state->locals_size(), "wrong number of locals"); - } else if (cur_scope->method()->max_locals() > 0) { - assert(cur_state->kind() == ValueStack::EmptyExceptionState, "should be"); - nof_locals = cur_scope->method()->max_locals(); - locals = new GrowableArray(nof_locals); - for(int i = 0; i < nof_locals; i++) { - locals->append(_illegal_value); - } + assert(locals->length() == nof_locals, "wrong number of locals"); } + assert(nof_locals == cur_scope->method()->max_locals(), "wrong number of locals"); + assert(nof_locals == cur_state->locals_size(), "wrong number of locals"); // describe expression stack int nof_stack = cur_state->stack_size(); @@ -2951,8 +2945,8 @@ IRScopeDebugInfo* LinearScan::compute_debug_info_for_scope(int op_id, IRScope* c int pos = 0; while (pos < nof_stack) { - Value expression = cur_state->stack_at_inc(pos); - append_scope_value(op_id, expression, expressions); + Value expression = cur_state->stack_at(pos); + pos += append_scope_value(op_id, expression, expressions); assert(expressions->length() == pos, "must match"); } diff --git a/src/hotspot/share/c1/c1_ValueStack.cpp b/src/hotspot/share/c1/c1_ValueStack.cpp index 58dc947764a79..e7bf714a3674d 100644 --- a/src/hotspot/share/c1/c1_ValueStack.cpp +++ b/src/hotspot/share/c1/c1_ValueStack.cpp @@ -51,12 +51,32 @@ ValueStack::ValueStack(ValueStack* copy_from, Kind kind, int bci) , _stack(copy_from->stack_size_for_copy(kind)) , _locks(copy_from->locks_size() == 0 ? nullptr : new Values(copy_from->locks_size())) { - assert(kind != EmptyExceptionState || !Compilation::current()->env()->should_retain_local_variables(), "need locals"); - if (kind != EmptyExceptionState) { + switch (kind) { + case EmptyExceptionState: + case CallerEmptyExceptionState: + assert(!Compilation::current()->env()->should_retain_local_variables(), "need locals"); + // set to all nulls, like clear_locals() + for (int i = 0; i < copy_from->locals_size(); ++i) { + _locals.append(nullptr); + } + break; + default: _locals.appendAll(©_from->_locals); } - if (kind != ExceptionState && kind != EmptyExceptionState) { + switch (kind) { + case ExceptionState: + case EmptyExceptionState: + assert(stack_size() == 0, "fix stack_size_for_copy"); + break; + case CallerExceptionState: + case CallerEmptyExceptionState: + // set to all nulls + for (int i = 0; i < copy_from->stack_size(); ++i) { + _stack.append(nullptr); + } + break; + default: _stack.appendAll(©_from->_stack); } @@ -68,10 +88,7 @@ ValueStack::ValueStack(ValueStack* copy_from, Kind kind, int bci) } int ValueStack::locals_size_for_copy(Kind kind) const { - if (kind != EmptyExceptionState) { - return locals_size(); - } - return 0; + return locals_size(); } int ValueStack::stack_size_for_copy(Kind kind) const { @@ -221,10 +238,15 @@ void ValueStack::print() { } else { InstructionPrinter ip; for (int i = 0; i < stack_size();) { + tty->print("stack %d ", i); Value t = stack_at_inc(i); - tty->print("%2d ", i); - tty->print("%c%d ", t->type()->tchar(), t->id()); - ip.print_instr(t); + if (t == nullptr) { + tty->print("null"); + } else { + tty->print("%2d ", i); + tty->print("%c%d ", t->type()->tchar(), t->id()); + ip.print_instr(t); + } tty->cr(); } } @@ -284,7 +306,9 @@ void ValueStack::verify() { int i; for (i = 0; i < stack_size(); i++) { Value v = _stack.at(i); - if (v == nullptr) { + if (kind() == empty_exception_kind(true /* caller */)) { + assert(v == nullptr, "should be empty"); + } else if (v == nullptr) { assert(_stack.at(i - 1)->type()->is_double_word(), "only hi-words are null on stack"); } else if (v->type()->is_double_word()) { assert(_stack.at(i + 1) == nullptr, "hi-word must be null"); @@ -293,7 +317,9 @@ void ValueStack::verify() { for (i = 0; i < locals_size(); i++) { Value v = _locals.at(i); - if (v != nullptr && v->type()->is_double_word()) { + if (kind() == EmptyExceptionState) { + assert(v == nullptr, "should be empty"); + } else if (v != nullptr && v->type()->is_double_word()) { assert(_locals.at(i + 1) == nullptr, "hi-word must be null"); } } diff --git a/src/hotspot/share/c1/c1_ValueStack.hpp b/src/hotspot/share/c1/c1_ValueStack.hpp index b1622b79622b8..74d5c2e3f3c65 100644 --- a/src/hotspot/share/c1/c1_ValueStack.hpp +++ b/src/hotspot/share/c1/c1_ValueStack.hpp @@ -34,8 +34,18 @@ class ValueStack: public CompilationResourceObj { CallerState, // Caller state when inlining StateBefore, // Before before execution of instruction StateAfter, // After execution of instruction - ExceptionState, // Exception handling of instruction - EmptyExceptionState, // Exception handling of instructions not covered by an xhandler + // Exception states for an instruction. + // Dead stack items or locals may be invalidated or cleared/removed. + // Locals are retained if needed for JVMTI. + // "empty" exception states are used when there is no handler, + // and invalidate the locals. + // "leaf" exception states clear the stack. + // "caller" exception states are used for the parent/caller, + // and invalidate the stack. + ExceptionState, // Exception state for leaf with handler, stack cleared + EmptyExceptionState, // Exception state for leaf w/o handler, stack cleared, locals invalidated + CallerExceptionState, // Exception state for parent with handler, stack invalidated + CallerEmptyExceptionState, // Exception state for parent w/o handler, stack+locals invalidated BlockBeginState // State of BlockBegin instruction with phi functions of this block }; @@ -75,10 +85,16 @@ class ValueStack: public CompilationResourceObj { ValueStack* copy(Kind new_kind, int new_bci) { return new ValueStack(this, new_kind, new_bci); } ValueStack* copy_for_parsing() { return new ValueStack(this, Parsing, -99); } + // Used when no exception handler is found + static Kind empty_exception_kind(bool caller = false) { + return Compilation::current()->env()->should_retain_local_variables() ? + (caller ? CallerExceptionState : ExceptionState) : // retain locals + (caller ? CallerEmptyExceptionState : EmptyExceptionState); // clear locals + } + void set_caller_state(ValueStack* s) { - assert(kind() == EmptyExceptionState || - (Compilation::current()->env()->should_retain_local_variables() && kind() == ExceptionState), - "only EmptyExceptionStates can be modified"); + assert(kind() == empty_exception_kind(false) || kind() == empty_exception_kind(true), + "only empty exception states can be modified"); _caller_state = s; } @@ -133,14 +149,14 @@ class ValueStack: public CompilationResourceObj { // stack access Value stack_at(int i) const { Value x = _stack.at(i); - assert(!x->type()->is_double_word() || + assert(x == nullptr || !x->type()->is_double_word() || _stack.at(i + 1) == nullptr, "hi-word of doubleword value must be null"); return x; } Value stack_at_inc(int& i) const { Value x = stack_at(i); - i += x->type()->size(); + i += ((x == nullptr) ? 1 : x->type()->size()); return x; } @@ -260,7 +276,8 @@ class ValueStack: public CompilationResourceObj { int temp_var = state->stack_size(); \ for (index = 0; \ index < temp_var && (value = state->stack_at(index), true); \ - index += value->type()->size()) + index += (value == nullptr ? 1 : value->type()->size())) \ + if (value != nullptr) #define for_each_lock_value(state, index, value) \ diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 0c70dab3f60c2..d85db121db34a 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -141,11 +141,16 @@ size_t MetaspaceShared::core_region_alignment() { } static bool shared_base_valid(char* shared_base) { -#ifdef _LP64 - return CompressedKlassPointers::is_valid_base((address)shared_base); -#else - return true; -#endif + // We check user input for SharedBaseAddress at dump time. We must weed out values + // we already know to be invalid later. + + // At CDS runtime, "shared_base" will be the (attempted) mapping start. It will also + // be the encoding base, since the the headers of archived base objects (and with Lilliput, + // the prototype mark words) carry pre-computed narrow Klass IDs that refer to the mapping + // start as base. + // + // Therefore, "shared_base" must be later usable as encoding base. + return AARCH64_ONLY(is_aligned(shared_base, 4 * G)) NOT_AARCH64(true); } class DumpClassListCLDClosure : public CLDClosure { @@ -1257,12 +1262,6 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma if (base_address != nullptr) { assert(is_aligned(base_address, archive_space_alignment), "Archive base address invalid: " PTR_FORMAT ".", p2i(base_address)); -#ifdef _LP64 - if (Metaspace::using_class_space()) { - assert(CompressedKlassPointers::is_valid_base(base_address), - "Archive base address invalid: " PTR_FORMAT ".", p2i(base_address)); - } -#endif } if (!Metaspace::using_class_space()) { @@ -1332,11 +1331,11 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma os::vm_page_size(), (char*) base_address); } else { // We did not manage to reserve at the preferred address, or were instructed to relocate. In that - // case we reserve whereever possible, but the start address needs to be encodable as narrow Klass - // encoding base since the archived heap objects contain nKlass IDs precalculated toward the start + // case we reserve wherever possible, but the start address needs to be encodable as narrow Klass + // encoding base since the archived heap objects contain nKlass IDs pre-calculated toward the start // of the shared Metaspace. That prevents us from using zero-based encoding and therefore we won't // try allocating in low-address regions. - total_space_rs = Metaspace::reserve_address_space_for_compressed_classes(total_range_size, false /* try_in_low_address_ranges */); + total_space_rs = Metaspace::reserve_address_space_for_compressed_classes(total_range_size, false /* optimize_for_zero_base */); } if (!total_space_rs.is_reserved()) { @@ -1348,7 +1347,6 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma "Sanity (" PTR_FORMAT " vs " PTR_FORMAT ")", p2i(base_address), p2i(total_space_rs.base())); assert(is_aligned(total_space_rs.base(), archive_space_alignment), "Sanity"); assert(total_space_rs.size() == total_range_size, "Sanity"); - assert(CompressedKlassPointers::is_valid_base((address)total_space_rs.base()), "Sanity"); // Now split up the space into ccs and cds archive. For simplicity, just leave // the gap reserved at the end of the archive space. Do not do real splitting. diff --git a/src/hotspot/share/ci/ciMethodData.cpp b/src/hotspot/share/ci/ciMethodData.cpp index 9bd73d32ff9a7..8d1772f655fa9 100644 --- a/src/hotspot/share/ci/ciMethodData.cpp +++ b/src/hotspot/share/ci/ciMethodData.cpp @@ -42,6 +42,8 @@ ciMethodData::ciMethodData(MethodData* md) : ciMetadata(md), _data_size(0), _extra_data_size(0), _data(nullptr), + _parameters_data_offset(0), + _exception_handlers_data_offset(0), // Set an initial hint. Don't use set_hint_di() because // first_di() may be out of bounds if data_size is 0. _hint_di(first_di()), @@ -50,8 +52,7 @@ ciMethodData::ciMethodData(MethodData* md) // Initialize the escape information (to "don't know."); _eflags(0), _arg_local(0), _arg_stack(0), _arg_returned(0), _invocation_counter(0), - _orig(), - _parameters(nullptr) {} + _orig() {} // Check for entries that reference an unloaded method class PrepareExtraDataClosure : public CleanExtraDataClosure { @@ -134,8 +135,16 @@ void ciMethodData::load_remaining_extra_data() { // Copy the extra data once it is prepared (i.e. cache populated, no release of extra data lock anymore) Copy::disjoint_words_atomic((HeapWord*) mdo->extra_data_base(), - (HeapWord*)((address) _data + _data_size), - (_extra_data_size - mdo->parameters_size_in_bytes()) / HeapWordSize); + (HeapWord*) extra_data_base(), + // copy everything from extra_data_base() up to parameters_data_base() + pointer_delta(parameters_data_base(), extra_data_base(), HeapWordSize)); + + // skip parameter data copying. Already done in 'load_data' + + // copy exception handler data + Copy::disjoint_words_atomic((HeapWord*) mdo->exception_handler_data_base(), + (HeapWord*) exception_handler_data_base(), + exception_handler_data_size() / HeapWordSize); // speculative trap entries also hold a pointer to a Method so need to be translated DataLayout* dp_src = mdo->extra_data_base(); @@ -195,12 +204,17 @@ bool ciMethodData::load_data() { // args_data_limit: --------------------------- // | parameter data entries | // | ... | + // param_data_limit: --------------------------- + // | ex handler data entries | + // | ... | // extra_data_limit: --------------------------- // // _data_size = extra_data_base - data_base // _extra_data_size = extra_data_limit - extra_data_base // total_size = _data_size + _extra_data_size - // args_data_limit = data_base + total_size - parameter_data_size + // args_data_limit = param_data_base + // param_data_limit = exception_handler_data_base + // extra_data_limit = extra_data_limit #ifndef ZERO // Some Zero platforms do not have expected alignment, and do not use @@ -218,12 +232,15 @@ bool ciMethodData::load_data() { Copy::disjoint_words_atomic((HeapWord*) mdo->data_base(), (HeapWord*) _data, _data_size / HeapWordSize); + // Copy offsets. This is used below + _parameters_data_offset = mdo->parameters_type_data_di(); + _exception_handlers_data_offset = mdo->exception_handlers_data_di(); int parameters_data_size = mdo->parameters_size_in_bytes(); if (parameters_data_size > 0) { // Snapshot the parameter data - Copy::disjoint_words_atomic((HeapWord*) mdo->args_data_limit(), - (HeapWord*) ((address)_data + total_size - parameters_data_size), + Copy::disjoint_words_atomic((HeapWord*) mdo->parameters_data_base(), + (HeapWord*) parameters_data_base(), parameters_data_size / HeapWordSize); } // Traverse the profile data, translating any oops into their @@ -237,12 +254,12 @@ bool ciMethodData::load_data() { data = mdo->next_data(data); } if (mdo->parameters_type_data() != nullptr) { - _parameters = data_layout_at(mdo->parameters_type_data_di()); - ciParametersTypeData* parameters = new ciParametersTypeData(_parameters); + DataLayout* parameters_data = data_layout_at(_parameters_data_offset); + ciParametersTypeData* parameters = new ciParametersTypeData(parameters_data); parameters->translate_from(mdo->parameters_type_data()); } - assert((DataLayout*) ((address)_data + total_size - parameters_data_size) == args_data_limit(), + assert((DataLayout*) ((address)_data + total_size - parameters_data_size - exception_handler_data_size()) == args_data_limit(), "sanity - parameter data starts after the argument data of the single ArgInfoData entry"); load_remaining_extra_data(); @@ -367,16 +384,24 @@ ciProfileData* ciMethodData::next_data(ciProfileData* current) { return next; } -DataLayout* ciMethodData::next_data_layout(DataLayout* current) { +DataLayout* ciMethodData::next_data_layout_helper(DataLayout* current, bool extra) { int current_index = dp_to_di((address)current); int next_index = current_index + current->size_in_bytes(); - if (out_of_bounds(next_index)) { + if (extra ? out_of_bounds_extra(next_index) : out_of_bounds(next_index)) { return nullptr; } DataLayout* next = data_layout_at(next_index); return next; } +DataLayout* ciMethodData::next_data_layout(DataLayout* current) { + return next_data_layout_helper(current, false); +} + +DataLayout* ciMethodData::next_extra_data_layout(DataLayout* current) { + return next_data_layout_helper(current, true); +} + ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots) { DataLayout* dp = extra_data_base(); DataLayout* end = args_data_limit(); @@ -438,6 +463,19 @@ ciProfileData* ciMethodData::bci_to_data(int bci, ciMethod* m) { return nullptr; } +ciBitData ciMethodData::exception_handler_bci_to_data(int bci) { + assert(ProfileExceptionHandlers, "not profiling"); + assert(_data != nullptr, "must be initialized"); + for (DataLayout* data = exception_handler_data_base(); data < exception_handler_data_limit(); data = next_extra_data_layout(data)) { + assert(data != nullptr, "out of bounds?"); + if (data->bci() == bci) { + return ciBitData(data); + } + } + // called with invalid bci or wrong Method/MethodData + ShouldNotReachHere(); +} + // Conservatively decode the trap_state of a ciProfileData. int ciMethodData::has_trap_at(ciProfileData* data, int reason) { typedef Deoptimization::DeoptReason DR_t; @@ -612,7 +650,7 @@ uint ciMethodData::arg_modified(int arg) const { } ciParametersTypeData* ciMethodData::parameters_type_data() const { - return _parameters != nullptr ? new ciParametersTypeData(_parameters) : nullptr; + return parameter_data_size() != 0 ? new ciParametersTypeData(data_layout_at(_parameters_data_offset)) : nullptr; } ByteSize ciMethodData::offset_of_slot(ciProfileData* data, ByteSize slot_offset_in_data) { diff --git a/src/hotspot/share/ci/ciMethodData.hpp b/src/hotspot/share/ci/ciMethodData.hpp index 005fa2146470c..dedbfc9f4d68a 100644 --- a/src/hotspot/share/ci/ciMethodData.hpp +++ b/src/hotspot/share/ci/ciMethodData.hpp @@ -379,6 +379,10 @@ class ciMethodData : public ciMetadata { // Data entries intptr_t* _data; + // layout of _data + int _parameters_data_offset; + int _exception_handlers_data_offset; + // Cached hint for data_layout_before() int _hint_di; @@ -403,17 +407,13 @@ class ciMethodData : public ciMetadata { // Coherent snapshot of original header. MethodData::CompilerCounters _orig; - // Area dedicated to parameters. null if no parameter profiling for this method. - DataLayout* _parameters; - int parameters_size() const { - return _parameters == nullptr ? 0 : parameters_type_data()->size_in_bytes(); - } - ciMethodData(MethodData* md = nullptr); // Accessors int data_size() const { return _data_size; } int extra_data_size() const { return _extra_data_size; } + int parameter_data_size() const { return _exception_handlers_data_offset - _parameters_data_offset; } + int exception_handler_data_size() const { return dp_to_di((address) exception_handler_data_limit()) - _exception_handlers_data_offset; } intptr_t * data() const { return _data; } MethodData* get_MethodData() const { @@ -425,7 +425,7 @@ class ciMethodData : public ciMetadata { void print_impl(outputStream* st); DataLayout* data_layout_at(int data_index) const { - assert(data_index % sizeof(intptr_t) == 0, "unaligned"); + assert(data_index % sizeof(intptr_t) == 0, "unaligned: %d", data_index); return (DataLayout*) (((address)_data) + data_index); } @@ -433,6 +433,12 @@ class ciMethodData : public ciMetadata { return data_index >= data_size(); } + bool out_of_bounds_extra(int data_index) { + return data_index < data_size() || data_index >= data_size() + extra_data_size(); + } + + DataLayout* next_data_layout_helper(DataLayout* current, bool extra); + // hint accessors int hint_di() const { return _hint_di; } void set_hint_di(int di) { @@ -500,7 +506,7 @@ class ciMethodData : public ciMetadata { bool load_data(); // Convert a dp (data pointer) to a di (data index). - int dp_to_di(address dp) { + int dp_to_di(address dp) const { return pointer_delta_as_int(dp, ((address)_data)); } @@ -511,17 +517,28 @@ class ciMethodData : public ciMetadata { ciProfileData* first_data() { return data_at(first_di()); } ciProfileData* next_data(ciProfileData* current); DataLayout* next_data_layout(DataLayout* current); + DataLayout* next_extra_data_layout(DataLayout* current); bool is_valid(ciProfileData* current) { return current != nullptr; } bool is_valid(DataLayout* current) { return current != nullptr; } + // pointers to sections in _data + // NOTE: these may be called before ciMethodData::load_data + // this works out since everything is initialized to 0 (i.e. there will appear to be no data) DataLayout* extra_data_base() const { return data_layout_at(data_size()); } - DataLayout* args_data_limit() const { return data_layout_at(data_size() + extra_data_size() - - parameters_size()); } + DataLayout* extra_data_limit() const { return data_layout_at(data_size() + extra_data_size()); } + // pointers to sections in extra data + DataLayout* args_data_limit() const { return parameters_data_base(); } + DataLayout* parameters_data_base() const { return data_layout_at(_parameters_data_offset); } + DataLayout* parameters_data_limit() const { return exception_handler_data_base(); } + DataLayout* exception_handler_data_base() const { return data_layout_at(_exception_handlers_data_offset); } + DataLayout* exception_handler_data_limit() const { return extra_data_limit(); } // Get the data at an arbitrary bci, or null if there is none. If m // is not null look for a SpeculativeTrapData if any first. ciProfileData* bci_to_data(int bci, ciMethod* m = nullptr); + ciBitData exception_handler_bci_to_data(int bci); + uint overflow_trap_count() const { return _orig.overflow_trap_count(); } diff --git a/src/hotspot/share/code/icBuffer.cpp b/src/hotspot/share/code/icBuffer.cpp index ba44df15c2e60..dbceefb7c8efc 100644 --- a/src/hotspot/share/code/icBuffer.cpp +++ b/src/hotspot/share/code/icBuffer.cpp @@ -141,7 +141,7 @@ void ICStub::print() { void InlineCacheBuffer::initialize() { if (_buffer != nullptr) return; // already initialized - _buffer = new StubQueue(new ICStubInterface, 10*K, InlineCacheBuffer_lock, "InlineCacheBuffer"); + _buffer = new StubQueue(new ICStubInterface, checked_cast(InlineCacheBufferSize), InlineCacheBuffer_lock, "InlineCacheBuffer"); assert (_buffer != nullptr, "cannot allocate InlineCacheBuffer"); } diff --git a/src/hotspot/share/code/stubs.cpp b/src/hotspot/share/code/stubs.cpp index b5f659a5c5a78..a24b3e6ce019a 100644 --- a/src/hotspot/share/code/stubs.cpp +++ b/src/hotspot/share/code/stubs.cpp @@ -218,8 +218,6 @@ void StubQueue::verify() { guarantee(0 <= _queue_begin && _queue_begin < _buffer_limit, "_queue_begin out of bounds"); guarantee(0 <= _queue_end && _queue_end <= _buffer_limit, "_queue_end out of bounds"); // verify alignment - guarantee(_buffer_size % stub_alignment() == 0, "_buffer_size not aligned"); - guarantee(_buffer_limit % stub_alignment() == 0, "_buffer_limit not aligned"); guarantee(_queue_begin % stub_alignment() == 0, "_queue_begin not aligned"); guarantee(_queue_end % stub_alignment() == 0, "_queue_end not aligned"); // verify buffer limit/size relationship diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 10b0f1f04a35c..0a04afad9aaee 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -498,6 +498,11 @@ bool CompilerConfig::check_args_consistency(bool status) { "Invalid NonNMethodCodeHeapSize=%dK. Must be at least %uK.\n", NonNMethodCodeHeapSize/K, min_code_cache_size/K); status = false; + } else if (InlineCacheBufferSize > NonNMethodCodeHeapSize / 2) { + jio_fprintf(defaultStream::error_stream(), + "Invalid InlineCacheBufferSize=" SIZE_FORMAT "K. Must be less than or equal to " SIZE_FORMAT "K.\n", + InlineCacheBufferSize/K, NonNMethodCodeHeapSize/2/K); + status = false; } #ifdef _LP64 diff --git a/src/hotspot/share/compiler/compilerDirectives.cpp b/src/hotspot/share/compiler/compilerDirectives.cpp index c76ef6de01d46..d7d4c5b60ed7f 100644 --- a/src/hotspot/share/compiler/compilerDirectives.cpp +++ b/src/hotspot/share/compiler/compilerDirectives.cpp @@ -297,8 +297,11 @@ void DirectiveSet::init_control_intrinsic() { } } -DirectiveSet::DirectiveSet(CompilerDirectives* d) :_inlinematchers(nullptr), _directive(d) { - _ideal_phase_name_mask = 0; +DirectiveSet::DirectiveSet(CompilerDirectives* d) : + _inlinematchers(nullptr), + _directive(d), + _ideal_phase_name_set(PHASE_NUM_TYPES, mtCompiler) +{ #define init_defaults_definition(name, type, dvalue, compiler) this->name##Option = dvalue; compilerdirectives_common_flags(init_defaults_definition) compilerdirectives_c2_flags(init_defaults_definition) @@ -427,18 +430,17 @@ DirectiveSet* DirectiveSet::compilecommand_compatibility_init(const methodHandle compilerdirectives_c1_flags(init_default_cc) #undef init_default_cc - // Parse PrintIdealPhaseName and create an efficient lookup mask + // Parse PrintIdealPhaseName and create a lookup set #ifndef PRODUCT #ifdef COMPILER2 if (!_modified[PrintIdealPhaseIndex]) { - // Parse ccstr and create mask + // Parse ccstr and create set ccstrlist option; if (CompilerOracle::has_option_value(method, CompileCommand::PrintIdealPhase, option)) { - uint64_t mask = 0; - PhaseNameValidator validator(option, mask); + PhaseNameValidator validator(option); if (validator.is_valid()) { - assert(mask != 0, "Must be set"); - set.cloned()->_ideal_phase_name_mask = mask; + assert(!validator.phase_name_set().is_empty(), "Phase name set must be non-empty"); + set.cloned()->set_ideal_phase_name_set(validator.phase_name_set()); } } } @@ -621,7 +623,7 @@ DirectiveSet* DirectiveSet::clone(DirectiveSet const* src) { #undef copy_string_members_definition set->_intrinsic_control_words = src->_intrinsic_control_words; - set->_ideal_phase_name_mask = src->_ideal_phase_name_mask; + set->set_ideal_phase_name_set(src->_ideal_phase_name_set); return set; } diff --git a/src/hotspot/share/compiler/compilerDirectives.hpp b/src/hotspot/share/compiler/compilerDirectives.hpp index a252ad02889c3..4c9b51724f9a3 100644 --- a/src/hotspot/share/compiler/compilerDirectives.hpp +++ b/src/hotspot/share/compiler/compilerDirectives.hpp @@ -31,6 +31,8 @@ #include "compiler/compiler_globals.hpp" #include "compiler/methodMatcher.hpp" #include "compiler/compilerOracle.hpp" +#include "opto/phasetype.hpp" +#include "utilities/bitMap.hpp" #include "utilities/exceptions.hpp" #include "utilities/tribool.hpp" @@ -128,7 +130,7 @@ class DirectiveSet : public CHeapObj { InlineMatcher* _inlinematchers; CompilerDirectives* _directive; TriBoolArray<(size_t)vmIntrinsics::number_of_intrinsics(), int> _intrinsic_control_words; - uint64_t _ideal_phase_name_mask; + CHeapBitMap _ideal_phase_name_set; public: DirectiveSet(CompilerDirectives* directive); @@ -197,8 +199,12 @@ void set_##name(void* value) { \ compilerdirectives_c1_string_flags(set_string_function_definition) #undef set_string_function_definition - void set_ideal_phase_mask(uint64_t mask) { _ideal_phase_name_mask = mask; }; - uint64_t ideal_phase_mask() { return _ideal_phase_name_mask; }; + void set_ideal_phase_name_set(const BitMap& set) { + _ideal_phase_name_set.set_from(set); + }; + bool should_print_phase(const CompilerPhaseType cpt) const { + return _ideal_phase_name_set.at(cpt); + }; void print_intx(outputStream* st, ccstr n, intx v, bool mod) { if (mod) { st->print("%s:" INTX_FORMAT " ", n, v); } } void print_uintx(outputStream* st, ccstr n, intx v, bool mod) { if (mod) { st->print("%s:" UINTX_FORMAT " ", n, v); } } diff --git a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp index 695d5eee0cc29..66eea29fcc136 100644 --- a/src/hotspot/share/compiler/compilerOracle.cpp +++ b/src/hotspot/share/compiler/compilerOracle.cpp @@ -777,8 +777,7 @@ static void scan_value(enum OptionType type, char* line, int& total_bytes_read, } #ifndef PRODUCT else if (option == CompileCommand::PrintIdealPhase) { - uint64_t mask = 0; - PhaseNameValidator validator(value, mask); + PhaseNameValidator validator(value); if (!validator.is_valid()) { jio_snprintf(errorbuf, buf_size, "Unrecognized phase name in %s: %s", option2name(option), validator.what()); diff --git a/src/hotspot/share/compiler/directivesParser.cpp b/src/hotspot/share/compiler/directivesParser.cpp index 0f6978bade86b..60955615133e2 100644 --- a/src/hotspot/share/compiler/directivesParser.cpp +++ b/src/hotspot/share/compiler/directivesParser.cpp @@ -336,12 +336,11 @@ bool DirectivesParser::set_option_flag(JSON_TYPE t, JSON_VAL* v, const key* opti error(VALUE_ERROR, "Unrecognized intrinsic detected in DisableIntrinsic: %s", validator.what()); } } else if (strncmp(option_key->name, "PrintIdealPhase", 15) == 0) { - uint64_t mask = 0; - PhaseNameValidator validator(s, mask); + PhaseNameValidator validator(s); valid = validator.is_valid(); if (valid) { - set->set_ideal_phase_mask(mask); + set->set_ideal_phase_name_set(validator.phase_name_set()); } else { error(VALUE_ERROR, "Unrecognized phase name detected in PrintIdealPhase: %s", validator.what()); } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index ba657e650a0d1..e9032d915a212 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -78,10 +78,8 @@ #include "gc/shared/gcBehaviours.hpp" #include "gc/shared/gcHeapSummary.hpp" #include "gc/shared/gcId.hpp" -#include "gc/shared/gcLocker.inline.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTraceTime.inline.hpp" -#include "gc/shared/generationSpec.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "gc/shared/locationPrinter.inline.hpp" #include "gc/shared/oopStorageParState.hpp" @@ -412,13 +410,11 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size) { // We should only get here after the first-level allocation attempt // (attempt_allocation()) failed to allocate. - // We will loop until a) we manage to successfully perform the - // allocation or b) we successfully schedule a collection which - // fails to perform the allocation. b) is the only case when we'll - // return null. + // We will loop until a) we manage to successfully perform the allocation or b) + // successfully schedule a collection which fails to perform the allocation. + // Case b) is the only case when we'll return null. HeapWord* result = nullptr; - for (uint try_count = 1, gclocker_retry_count = 0; /* we'll return */; try_count += 1) { - bool should_try_gc; + for (uint try_count = 1; /* we'll return */; try_count++) { uint gc_count_before; { @@ -431,67 +427,26 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size) { return result; } - // If the GCLocker is active and we are bound for a GC, try expanding young gen. - // This is different to when only GCLocker::needs_gc() is set: try to avoid - // waiting because the GCLocker is active to not wait too long. - if (GCLocker::is_active_and_needs_gc() && policy()->can_expand_young_list()) { - // No need for an ergo message here, can_expand_young_list() does this when - // it returns true. - result = _allocator->attempt_allocation_force(word_size); - if (result != nullptr) { - return result; - } - } - - // Only try a GC if the GCLocker does not signal the need for a GC. Wait until - // the GCLocker initiated GC has been performed and then retry. This includes - // the case when the GC Locker is not active but has not been performed. - should_try_gc = !GCLocker::needs_gc(); // Read the GC count while still holding the Heap_lock. gc_count_before = total_collections(); } - if (should_try_gc) { - bool succeeded; - result = do_collection_pause(word_size, gc_count_before, &succeeded, GCCause::_g1_inc_collection_pause); - if (result != nullptr) { - assert(succeeded, "only way to get back a non-null result"); - log_trace(gc, alloc)("%s: Successfully scheduled collection returning " PTR_FORMAT, - Thread::current()->name(), p2i(result)); - return result; - } - - if (succeeded) { - // We successfully scheduled a collection which failed to allocate. No - // point in trying to allocate further. We'll just return null. - log_trace(gc, alloc)("%s: Successfully scheduled collection failing to allocate " - SIZE_FORMAT " words", Thread::current()->name(), word_size); - return nullptr; - } - log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating " SIZE_FORMAT " words", - Thread::current()->name(), word_size); - } else { - // Failed to schedule a collection. - if (gclocker_retry_count > GCLockerRetryAllocationCount) { - log_warning(gc, alloc)("%s: Retried waiting for GCLocker too often allocating " - SIZE_FORMAT " words", Thread::current()->name(), word_size); - return nullptr; - } - log_trace(gc, alloc)("%s: Stall until clear", Thread::current()->name()); - // The GCLocker is either active or the GCLocker initiated - // GC has not yet been performed. Stall until it is and - // then retry the allocation. - GCLocker::stall_until_clear(); - gclocker_retry_count += 1; + bool succeeded; + result = do_collection_pause(word_size, gc_count_before, &succeeded, GCCause::_g1_inc_collection_pause); + if (succeeded) { + log_trace(gc, alloc)("%s: Successfully scheduled collection returning " PTR_FORMAT, + Thread::current()->name(), p2i(result)); + return result; } - // We can reach here if we were unsuccessful in scheduling a - // collection (because another thread beat us to it) or if we were - // stalled due to the GC locker. In either can we should retry the - // allocation attempt in case another thread successfully - // performed a collection and reclaimed enough space. We do the - // first attempt (without holding the Heap_lock) here and the - // follow-on attempt will be at the start of the next loop + log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating " SIZE_FORMAT " words", + Thread::current()->name(), word_size); + + // We can reach here if we were unsuccessful in scheduling a collection (because + // another thread beat us to it). In this case immeditealy retry the allocation + // attempt because another thread successfully performed a collection and possibly + // reclaimed enough space. The first attempt (without holding the Heap_lock) is + // here and the follow-on attempt will be at the start of the next loop // iteration (after taking the Heap_lock). size_t dummy = 0; result = _allocator->attempt_allocation(word_size, word_size, &dummy); @@ -674,13 +629,11 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { collect(GCCause::_g1_humongous_allocation); } - // We will loop until a) we manage to successfully perform the - // allocation or b) we successfully schedule a collection which - // fails to perform the allocation. b) is the only case when we'll - // return null. + // We will loop until a) we manage to successfully perform the allocation or b) + // successfully schedule a collection which fails to perform the allocation. + // Case b) is the only case when we'll return null. HeapWord* result = nullptr; - for (uint try_count = 1, gclocker_retry_count = 0; /* we'll return */; try_count += 1) { - bool should_try_gc; + for (uint try_count = 1; /* we'll return */; try_count++) { uint gc_count_before; @@ -698,64 +651,35 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { return result; } - // Only try a GC if the GCLocker does not signal the need for a GC. Wait until - // the GCLocker initiated GC has been performed and then retry. This includes - // the case when the GC Locker is not active but has not been performed. - should_try_gc = !GCLocker::needs_gc(); // Read the GC count while still holding the Heap_lock. gc_count_before = total_collections(); } - if (should_try_gc) { - bool succeeded; - result = do_collection_pause(word_size, gc_count_before, &succeeded, GCCause::_g1_humongous_allocation); + bool succeeded; + result = do_collection_pause(word_size, gc_count_before, &succeeded, GCCause::_g1_humongous_allocation); + if (succeeded) { + log_trace(gc, alloc)("%s: Successfully scheduled collection returning " PTR_FORMAT, + Thread::current()->name(), p2i(result)); if (result != nullptr) { - assert(succeeded, "only way to get back a non-null result"); - log_trace(gc, alloc)("%s: Successfully scheduled collection returning " PTR_FORMAT, - Thread::current()->name(), p2i(result)); size_t size_in_regions = humongous_obj_size_in_regions(word_size); policy()->old_gen_alloc_tracker()-> record_collection_pause_humongous_allocation(size_in_regions * HeapRegion::GrainBytes); - return result; - } - - if (succeeded) { - // We successfully scheduled a collection which failed to allocate. No - // point in trying to allocate further. We'll just return null. - log_trace(gc, alloc)("%s: Successfully scheduled collection failing to allocate " - SIZE_FORMAT " words", Thread::current()->name(), word_size); - return nullptr; - } - log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating " SIZE_FORMAT "", - Thread::current()->name(), word_size); - } else { - // Failed to schedule a collection. - if (gclocker_retry_count > GCLockerRetryAllocationCount) { - log_warning(gc, alloc)("%s: Retried waiting for GCLocker too often allocating " - SIZE_FORMAT " words", Thread::current()->name(), word_size); - return nullptr; } - log_trace(gc, alloc)("%s: Stall until clear", Thread::current()->name()); - // The GCLocker is either active or the GCLocker initiated - // GC has not yet been performed. Stall until it is and - // then retry the allocation. - GCLocker::stall_until_clear(); - gclocker_retry_count += 1; + return result; } + log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating " SIZE_FORMAT "", + Thread::current()->name(), word_size); - // We can reach here if we were unsuccessful in scheduling a - // collection (because another thread beat us to it) or if we were - // stalled due to the GC locker. In either can we should retry the - // allocation attempt in case another thread successfully - // performed a collection and reclaimed enough space. + // We can reach here if we were unsuccessful in scheduling a collection (because + // another thread beat us to it). // Humongous object allocation always needs a lock, so we wait for the retry // in the next iteration of the loop, unlike for the regular iteration case. // Give a warning if we seem to be looping forever. if ((QueuedAllocationWarningCount > 0) && (try_count % QueuedAllocationWarningCount == 0)) { - log_warning(gc, alloc)("%s: Retried allocation %u times for " SIZE_FORMAT " words", + log_warning(gc, alloc)("%s: Retried allocation %u times for %zu words", Thread::current()->name(), try_count, word_size); } } @@ -909,11 +833,6 @@ bool G1CollectedHeap::do_full_collection(bool clear_all_soft_refs, bool do_maximal_compaction) { assert_at_safepoint_on_vm_thread(); - if (GCLocker::check_active_before_gc()) { - // Full GC was not completed. - return false; - } - const bool do_clear_all_soft_refs = clear_all_soft_refs || soft_ref_policy()->should_clear_all_soft_refs(); @@ -1270,9 +1189,11 @@ G1CollectedHeap::G1CollectedHeap() : _humongous_object_threshold_in_words = humongous_threshold_for(HeapRegion::GrainWords); - // Override the default _filler_array_max_size so that no humongous filler - // objects are created. - _filler_array_max_size = _humongous_object_threshold_in_words; + // Since filler arrays are never referenced, we can make them region sized. + // This simplifies filling up the region in case we have some potentially + // unreferenced (by Java code, but still in use by native code) pinned objects + // in there. + _filler_array_max_size = HeapRegion::GrainWords; // Override the default _stack_chunk_max_size so that no humongous stack chunks are created _stack_chunk_max_size = _humongous_object_threshold_in_words; @@ -1905,12 +1826,6 @@ bool G1CollectedHeap::try_collect_concurrently(GCCause::Cause cause, // Collection failed and should be retried. assert(op.transient_failure(), "invariant"); - if (GCLocker::is_active_and_needs_gc()) { - // If GCLocker is active, wait until clear before retrying. - LOG_COLLECT_CONCURRENTLY(cause, "gc-locker stall"); - GCLocker::stall_until_clear(); - } - LOG_COLLECT_CONCURRENTLY(cause, "retry"); } } @@ -1936,11 +1851,6 @@ bool G1CollectedHeap::try_collect_fullgc(GCCause::Cause cause, return true; } } - - if (GCLocker::is_active_and_needs_gc()) { - // If GCLocker is active, wait until clear before retrying. - GCLocker::stall_until_clear(); - } } } @@ -1950,11 +1860,6 @@ bool G1CollectedHeap::try_collect(GCCause::Cause cause, return try_collect_concurrently(cause, counters_before.total_collections(), counters_before.old_marking_cycles_started()); - } else if (GCLocker::should_discard(cause, counters_before.total_collections())) { - // Indicate failure to be consistent with VMOp failure due to - // another collection slipping in after our gc_count but before - // our request is processed. - return false; } else if (cause == GCCause::_gc_locker || cause == GCCause::_wb_young_gc DEBUG_ONLY(|| cause == GCCause::_scavenge_alot)) { @@ -2180,14 +2085,6 @@ bool G1CollectedHeap::is_obj_dead_cond(const oop obj, return false; // keep some compilers happy } -void G1CollectedHeap::pin_object(JavaThread* thread, oop obj) { - GCLocker::lock_critical(thread); -} - -void G1CollectedHeap::unpin_object(JavaThread* thread, oop obj) { - GCLocker::unlock_critical(thread); -} - void G1CollectedHeap::print_heap_regions() const { LogTarget(Trace, gc, heap, region) lt; if (lt.is_enabled()) { @@ -2490,10 +2387,6 @@ bool G1CollectedHeap::do_collection_pause_at_safepoint() { assert_at_safepoint_on_vm_thread(); guarantee(!is_gc_active(), "collection is not reentrant"); - if (GCLocker::check_active_before_gc()) { - return false; - } - do_collection_pause_at_safepoint_helper(); return true; } @@ -2648,6 +2541,8 @@ void G1CollectedHeap::free_region(HeapRegion* hr, FreeRegionList* free_list) { assert(!hr->is_free(), "the region should not be free"); assert(!hr->is_empty(), "the region should not be empty"); assert(_hrm.is_available(hr->hrm_index()), "region should be committed"); + assert(!hr->has_pinned_objects(), + "must not free a region which contains pinned objects"); // Reset region metadata to allow reuse. hr->hr_clear(true /* clear_space */); @@ -2765,7 +2660,7 @@ bool G1CollectedHeap::check_young_list_empty() { // Remove the given HeapRegion from the appropriate region set. void G1CollectedHeap::prepare_region_for_full_compaction(HeapRegion* hr) { - if (hr->is_humongous()) { + if (hr->is_humongous()) { _humongous_set.remove(hr); } else if (hr->is_old()) { _old_set.remove(hr); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 96cb75777a6dd..d8b0f6cc8d910 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -560,6 +560,9 @@ class G1CollectedHeap : public CollectedHeap { return _monitoring_support; } + void pin_object(JavaThread* thread, oop obj) override; + void unpin_object(JavaThread* thread, oop obj) override; + void resize_heap_if_necessary(); // Check if there is memory to uncommit and if so schedule a task to do it. @@ -613,7 +616,7 @@ class G1CollectedHeap : public CollectedHeap { // We register a region with the fast "in collection set" test. We // simply set to true the array slot corresponding to this region. void register_young_region_with_region_attr(HeapRegion* r) { - _region_attr.set_in_young(r->hrm_index()); + _region_attr.set_in_young(r->hrm_index(), r->has_pinned_objects()); } inline void register_new_survivor_region_with_region_attr(HeapRegion* r); inline void register_region_with_region_attr(HeapRegion* r); @@ -1292,9 +1295,6 @@ class G1CollectedHeap : public CollectedHeap { G1HeapSummary create_g1_heap_summary(); G1EvacSummary create_g1_evac_summary(G1EvacStats* stats); - void pin_object(JavaThread* thread, oop obj) override; - void unpin_object(JavaThread* thread, oop obj) override; - // Printing private: void print_heap_regions() const; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 05d3a65fd59ef..7b81a93abe177 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -209,6 +209,8 @@ G1HeapRegionAttr G1CollectedHeap::region_attr(uint idx) const { } void G1CollectedHeap::register_humongous_candidate_region_with_region_attr(uint index) { + assert(!region_at(index)->has_pinned_objects(), "must be"); + assert(region_at(index)->rem_set()->is_complete(), "must be"); _region_attr.set_humongous_candidate(index); } @@ -218,9 +220,12 @@ void G1CollectedHeap::register_new_survivor_region_with_region_attr(HeapRegion* void G1CollectedHeap::register_region_with_region_attr(HeapRegion* r) { _region_attr.set_remset_is_tracked(r->hrm_index(), r->rem_set()->is_tracked()); + _region_attr.set_is_pinned(r->hrm_index(), r->has_pinned_objects()); } void G1CollectedHeap::register_old_region_with_region_attr(HeapRegion* r) { + assert(!r->has_pinned_objects(), "must be"); + assert(r->rem_set()->is_complete(), "must be"); _region_attr.set_in_old(r->hrm_index(), r->rem_set()->is_tracked()); _rem_set->exclude_region_from_scan(r->hrm_index()); } @@ -257,6 +262,21 @@ inline bool G1CollectedHeap::is_obj_dead(const oop obj, const HeapRegion* hr) co } } +inline void G1CollectedHeap::pin_object(JavaThread* thread, oop obj) { + assert(obj != nullptr, "obj must not be null"); + assert(!is_gc_active(), "must not pin objects during a GC"); + assert(obj->is_typeArray(), "must be typeArray"); + HeapRegion *r = heap_region_containing(obj); + r->increment_pinned_object_count(); +} + +inline void G1CollectedHeap::unpin_object(JavaThread* thread, oop obj) { + assert(obj != nullptr, "obj must not be null"); + assert(!is_gc_active(), "must not unpin objects during a GC"); + HeapRegion *r = heap_region_containing(obj); + r->decrement_pinned_object_count(); +} + inline bool G1CollectedHeap::is_obj_dead(const oop obj) const { assert(obj != nullptr, "precondition"); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 3c43d6ec10016..30787adbe543b 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -270,6 +270,9 @@ void G1CollectionSet::print(outputStream* st) { } #endif // !PRODUCT +// Always evacuate out pinned regions (apart from object types that can actually be +// pinned by JNI) to allow faster future evacuation. We already "paid" for this work +// when sizing the young generation. double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors) { Ticks start_time = Ticks::now(); @@ -317,6 +320,19 @@ static int compare_region_idx(const uint a, const uint b) { return static_cast(a-b); } +// The current mechanism skips evacuation of pinned old regions like g1 does for +// young regions: +// * evacuating pinned marking collection set candidate regions (available during mixed +// gc) like young regions would not result in any memory gain but only take additional +// time away from processing regions that would actually result in memory being freed. +// To advance mixed gc progress (we committed to evacuate all marking collection set +// candidate regions within the maximum number of mixed gcs in the phase), move them +// to the optional collection set candidates to reclaim them asap as time permits. +// * evacuating out retained collection set candidates would also just take up time with +// no actual space freed in old gen. Better to concentrate on others. +// Retained collection set candidates are aged out, ie. made to regular old regions +// without remembered sets after a few attempts to save computation costs of keeping +// them candidates for very long living pinned regions. void G1CollectionSet::finalize_old_part(double time_remaining_ms) { double non_young_start_time_sec = os::elapsedTime(); @@ -325,12 +341,15 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { G1CollectionCandidateRegionList initial_old_regions; assert(_optional_old_regions.length() == 0, "must be"); + G1CollectionCandidateRegionList pinned_marking_regions; + G1CollectionCandidateRegionList pinned_retained_regions; if (collector_state()->in_mixed_phase()) { time_remaining_ms = _policy->select_candidates_from_marking(&candidates()->marking_regions(), time_remaining_ms, &initial_old_regions, - &_optional_old_regions); + &_optional_old_regions, + &pinned_marking_regions); } else { log_debug(gc, ergo, cset)("Do not add marking candidates to collection set due to pause type."); } @@ -338,12 +357,20 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { _policy->select_candidates_from_retained(&candidates()->retained_regions(), time_remaining_ms, &initial_old_regions, - &_optional_old_regions); + &_optional_old_regions, + &pinned_retained_regions); // Move initially selected old regions to collection set directly. move_candidates_to_collection_set(&initial_old_regions); // Only prepare selected optional regions for now. prepare_optional_regions(&_optional_old_regions); + // Move pinned marking regions we came across to retained candidates so that + // there is progress in the mixed gc phase. + move_pinned_marking_to_retained(&pinned_marking_regions); + // Drop pinned retained regions to make progress with retained regions. Regions + // in that list must have been pinned for at least G1NumCollectionsKeepPinned + // GCs and hence are considered "long lived". + drop_pinned_retained_regions(&pinned_retained_regions); candidates()->verify(); } else { @@ -378,6 +405,32 @@ void G1CollectionSet::prepare_optional_regions(G1CollectionCandidateRegionList* } } +void G1CollectionSet::move_pinned_marking_to_retained(G1CollectionCandidateRegionList* regions) { + if (regions->length() == 0) { + return; + } + candidates()->remove(regions); + + for (HeapRegion* r : *regions) { + assert(r->has_pinned_objects(), "must be pinned"); + assert(r->rem_set()->is_complete(), "must be complete"); + candidates()->add_retained_region_unsorted(r); + } + candidates()->sort_by_efficiency(); +} + +void G1CollectionSet::drop_pinned_retained_regions(G1CollectionCandidateRegionList* regions) { + if (regions->length() == 0) { + return; + } + candidates()->remove(regions); + + // We can now drop these region's remembered sets. + for (HeapRegion* r : *regions) { + r->rem_set()->clear(true /* only_cardset */); + } +} + void G1CollectionSet::finalize_initial_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor) { double time_remaining_ms = finalize_young_part(target_pause_time_ms, survivor); finalize_old_part(time_remaining_ms); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index 133246a52081d..fba166e9c75b0 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -177,6 +177,12 @@ class G1CollectionSet { // Prepares old regions in the given set for optional collection later. Does not // add the region to collection set yet. void prepare_optional_regions(G1CollectionCandidateRegionList* regions); + // Moves given old regions from the marking candidates to the retained candidates. + // This makes sure that marking candidates will not remain there to unnecessarily + // prolong the mixed phase. + void move_pinned_marking_to_retained(G1CollectionCandidateRegionList* regions); + // Removes the given list of regions from the retained candidates. + void drop_pinned_retained_regions(G1CollectionCandidateRegionList* regions); // Finalize the young part of the initial collection set. Relabel survivor regions // as Eden and calculate a prediction on how long the evacuation of all young regions @@ -186,8 +192,8 @@ class G1CollectionSet { // can use them. void finalize_incremental_building(); - // Select the old regions of the initial collection set and determine how many optional - // regions we might be able to evacuate in this pause. + // Select the regions comprising the initial and optional collection set from marking + // and retained collection set candidates. void finalize_old_part(double time_remaining_ms); // Iterate the part of the collection set given by the offset and length applying the given diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 426e96ea779ba..9f903bc924f1e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -31,15 +31,15 @@ G1CollectionCandidateList::G1CollectionCandidateList() : _candidates(2, mtGC) { } -void G1CollectionCandidateList::set(G1CollectionCandidateList::CandidateInfo* candidate_infos, uint num_infos) { +void G1CollectionCandidateList::set(G1CollectionSetCandidateInfo* candidate_infos, uint num_infos) { assert(_candidates.is_empty(), "must be"); - GrowableArrayFromArray a(candidate_infos, (int)num_infos); + GrowableArrayFromArray a(candidate_infos, (int)num_infos); _candidates.appendAll(&a); } void G1CollectionCandidateList::append_unsorted(HeapRegion* r) { - CandidateInfo c(r, r->calc_gc_efficiency()); + G1CollectionSetCandidateInfo c(r, r->calc_gc_efficiency()); _candidates.append(c); } @@ -58,7 +58,7 @@ void G1CollectionCandidateList::remove(G1CollectionCandidateRegionList* other) { // Create a list from scratch, copying over the elements from the candidate // list not in the other list. Finally deallocate and overwrite the old list. int new_length = _candidates.length() - other->length(); - GrowableArray new_list(new_length, mtGC); + GrowableArray new_list(new_length, mtGC); uint other_idx = 0; @@ -81,10 +81,10 @@ void G1CollectionCandidateList::clear() { #ifndef PRODUCT void G1CollectionCandidateList::verify() { - CandidateInfo* prev = nullptr; + G1CollectionSetCandidateInfo* prev = nullptr; for (uint i = 0; i < (uint)_candidates.length(); i++) { - CandidateInfo& ci = _candidates.at(i); + G1CollectionSetCandidateInfo& ci = _candidates.at(i); assert(prev == nullptr || prev->_gc_efficiency >= ci._gc_efficiency, "Stored gc efficiency must be descending from region %u to %u", prev->_r->hrm_index(), ci._r->hrm_index()); @@ -94,7 +94,7 @@ void G1CollectionCandidateList::verify() { } #endif -int G1CollectionCandidateList::compare(CandidateInfo* ci1, CandidateInfo* ci2) { +int G1CollectionCandidateList::compare(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2) { // Make sure that null entries are moved to the end. if (ci1->_r == nullptr) { if (ci2->_r == nullptr) { @@ -182,7 +182,7 @@ void G1CollectionSetCandidates::clear() { _last_marking_candidates_length = 0; } -void G1CollectionSetCandidates::set_candidates_from_marking(G1CollectionCandidateList::CandidateInfo* candidate_infos, +void G1CollectionSetCandidates::set_candidates_from_marking(G1CollectionSetCandidateInfo* candidate_infos, uint num_infos) { assert(_marking_regions.length() == 0, "must be empty before adding new ones"); diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index 4b0ec643aa9a9..4eb5523d0399e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -26,6 +26,8 @@ #define SHARE_GC_G1_G1COLLECTIONSETCANDIDATES_HPP #include "gc/g1/g1CollectionSetCandidates.hpp" +#include "gc/g1/g1_globals.hpp" +#include "gc/shared/gc_globals.hpp" #include "gc/shared/workerThread.hpp" #include "memory/allocation.hpp" #include "runtime/globals.hpp" @@ -64,6 +66,20 @@ class G1CollectionCandidateRegionList { G1CollectionCandidateRegionListIterator end() const { return _regions.end(); } }; +struct G1CollectionSetCandidateInfo { + HeapRegion* _r; + double _gc_efficiency; + uint _num_unreclaimed; // Number of GCs this region has been found unreclaimable. + + G1CollectionSetCandidateInfo() : G1CollectionSetCandidateInfo(nullptr, 0.0) { } + G1CollectionSetCandidateInfo(HeapRegion* r, double gc_efficiency) : _r(r), _gc_efficiency(gc_efficiency), _num_unreclaimed(0) { } + + bool update_num_unreclaimed() { + ++_num_unreclaimed; + return _num_unreclaimed < G1NumCollectionsKeepPinned; + } +}; + class G1CollectionCandidateListIterator : public StackObj { G1CollectionCandidateList* _which; uint _position; @@ -72,7 +88,7 @@ class G1CollectionCandidateListIterator : public StackObj { G1CollectionCandidateListIterator(G1CollectionCandidateList* which, uint position); G1CollectionCandidateListIterator& operator++(); - HeapRegion* operator*(); + G1CollectionSetCandidateInfo* operator*(); bool operator==(const G1CollectionCandidateListIterator& rhs); bool operator!=(const G1CollectionCandidateListIterator& rhs); @@ -83,23 +99,13 @@ class G1CollectionCandidateListIterator : public StackObj { class G1CollectionCandidateList : public CHeapObj { friend class G1CollectionCandidateListIterator; -public: - struct CandidateInfo { - HeapRegion* _r; - double _gc_efficiency; - - CandidateInfo() : CandidateInfo(nullptr, 0.0) { } - CandidateInfo(HeapRegion* r, double gc_efficiency) : _r(r), _gc_efficiency(gc_efficiency) { } - }; - -private: - GrowableArray _candidates; + GrowableArray _candidates; public: G1CollectionCandidateList(); // Put the given set of candidates into this list, preserving the efficiency ordering. - void set(CandidateInfo* candidate_infos, uint num_infos); + void set(G1CollectionSetCandidateInfo* candidate_infos, uint num_infos); // Add the given HeapRegion to this list at the end, (potentially) making the list unsorted. void append_unsorted(HeapRegion* r); // Restore sorting order by decreasing gc efficiency, using the existing efficiency @@ -114,7 +120,7 @@ class G1CollectionCandidateList : public CHeapObj { void clear(); - CandidateInfo& at(uint position) { return _candidates.at(position); } + G1CollectionSetCandidateInfo& at(uint position) { return _candidates.at(position); } uint length() const { return (uint)_candidates.length(); } @@ -123,7 +129,7 @@ class G1CollectionCandidateList : public CHeapObj { // Comparison function to order regions in decreasing GC efficiency order. This // will cause regions with a lot of live objects and large remembered sets to end // up at the end of the list. - static int compare(CandidateInfo* ci1, CandidateInfo* ci2); + static int compare(G1CollectionSetCandidateInfo* ci1, G1CollectionSetCandidateInfo* ci2); G1CollectionCandidateListIterator begin() { return G1CollectionCandidateListIterator(this, 0); @@ -138,9 +144,9 @@ class G1CollectionCandidateList : public CHeapObj { // of the regions returned. class G1CollectionSetCandidatesIterator : public StackObj { G1CollectionSetCandidates* _which; - uint _position; + uint _position; -public: + public: G1CollectionSetCandidatesIterator(G1CollectionSetCandidates* which, uint position); G1CollectionSetCandidatesIterator& operator++(); @@ -198,7 +204,7 @@ class G1CollectionSetCandidates : public CHeapObj { // Merge collection set candidates from marking into the current marking list // (which needs to be empty). - void set_candidates_from_marking(G1CollectionCandidateList::CandidateInfo* candidate_infos, + void set_candidates_from_marking(G1CollectionSetCandidateInfo* candidate_infos, uint num_infos); // The most recent length of the list that had been merged last via // set_candidates_from_marking(). Used for calculating minimum collection set diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp index 0d391e6bebf70..638a50b474535 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.inline.hpp @@ -38,8 +38,8 @@ inline G1CollectionCandidateListIterator& G1CollectionCandidateListIterator::ope return *this; } -inline HeapRegion* G1CollectionCandidateListIterator::operator*() { - return _which->_candidates.at(_position)._r; +inline G1CollectionSetCandidateInfo* G1CollectionCandidateListIterator::operator*() { + return &_which->_candidates.at(_position); } inline bool G1CollectionCandidateListIterator::operator==(const G1CollectionCandidateListIterator& rhs) { diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp index 28132ea172b1a..f19488a49a8ae 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp @@ -39,7 +39,7 @@ // moved to the destination. class G1BuildCandidateRegionsTask : public WorkerTask { - using CandidateInfo = G1CollectionCandidateList::CandidateInfo; + using CandidateInfo = G1CollectionSetCandidateInfo; // Work area for building the set of collection set candidates. Contains references // to heap regions with their GC efficiencies calculated. To reduce contention diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index d41fd381e2365..fb99e028c624f 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1131,13 +1131,10 @@ class G1UpdateRemSetTrackingBeforeRebuildTask : public WorkerTask { // Distribute the given marked bytes across the humongous object starting // with hr and note end of marking for these regions. void distribute_marked_bytes(HeapRegion* hr, size_t marked_bytes) { - size_t const obj_size_in_words = cast_to_oop(hr->bottom())->size(); - - // "Distributing" zero words means that we only note end of marking for these - // regions. - assert(marked_bytes == 0 || obj_size_in_words * HeapWordSize == marked_bytes, + // Dead humongous objects (marked_bytes == 0) may have already been unloaded. + assert(marked_bytes == 0 || cast_to_oop(hr->bottom())->size() * HeapWordSize == marked_bytes, "Marked bytes should either be 0 or the same as humongous object (%zu) but is %zu", - obj_size_in_words * HeapWordSize, marked_bytes); + cast_to_oop(hr->bottom())->size() * HeapWordSize, marked_bytes); auto distribute_bytes = [&] (HeapRegion* r) { size_t const bytes_to_add = MIN2(HeapRegion::GrainBytes, marked_bytes); @@ -1148,10 +1145,6 @@ class G1UpdateRemSetTrackingBeforeRebuildTask : public WorkerTask { marked_bytes -= bytes_to_add; }; _g1h->humongous_obj_regions_iterate(hr, distribute_bytes); - - assert(marked_bytes == 0, - "%zu bytes left after distributing space across %zu regions", - marked_bytes, G1CollectedHeap::humongous_obj_size_in_regions(obj_size_in_words)); } void update_marked_bytes(HeapRegion* hr) { @@ -1370,7 +1363,10 @@ class G1ReclaimEmptyRegionsTask : public WorkerTask { uint humongous_regions_removed() { return _humongous_regions_removed; } bool do_heap_region(HeapRegion *hr) { - if (hr->used() > 0 && hr->live_bytes() == 0 && !hr->is_young()) { + bool can_reclaim = hr->used() > 0 && hr->live_bytes() == 0 && + !hr->is_young() && !hr->has_pinned_objects(); + + if (can_reclaim) { log_trace(gc, marking)("Reclaimed empty old gen region %u (%s) bot " PTR_FORMAT, hr->hrm_index(), hr->get_short_type_str(), p2i(hr->bottom())); _freed_bytes += hr->used(); diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp index 6964028d1f87d..a308fe7a3934a 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp @@ -33,29 +33,35 @@ #include "utilities/bitMap.inline.hpp" G1EvacFailureRegions::G1EvacFailureRegions() : - _regions_failed_evacuation(mtGC), - _evac_failure_regions(nullptr), - _evac_failure_regions_cur_length(0) { } + _regions_evac_failed(mtGC), + _regions_pinned(mtGC), + _regions_alloc_failed(mtGC), + _evac_failed_regions(nullptr), + _num_regions_evac_failed(0) { } G1EvacFailureRegions::~G1EvacFailureRegions() { - assert(_evac_failure_regions == nullptr, "not cleaned up"); + assert(_evac_failed_regions == nullptr, "not cleaned up"); } void G1EvacFailureRegions::pre_collection(uint max_regions) { - Atomic::store(&_evac_failure_regions_cur_length, 0u); - _regions_failed_evacuation.resize(max_regions); - _evac_failure_regions = NEW_C_HEAP_ARRAY(uint, max_regions, mtGC); + Atomic::store(&_num_regions_evac_failed, 0u); + _regions_evac_failed.resize(max_regions); + _regions_pinned.resize(max_regions); + _regions_alloc_failed.resize(max_regions); + _evac_failed_regions = NEW_C_HEAP_ARRAY(uint, max_regions, mtGC); } void G1EvacFailureRegions::post_collection() { - _regions_failed_evacuation.resize(0); + _regions_evac_failed.resize(0); + _regions_pinned.resize(0); + _regions_alloc_failed.resize(0); - FREE_C_HEAP_ARRAY(uint, _evac_failure_regions); - _evac_failure_regions = nullptr; + FREE_C_HEAP_ARRAY(uint, _evac_failed_regions); + _evac_failed_regions = nullptr; } bool G1EvacFailureRegions::contains(uint region_idx) const { - return _regions_failed_evacuation.par_at(region_idx, memory_order_relaxed); + return _regions_evac_failed.par_at(region_idx, memory_order_relaxed); } void G1EvacFailureRegions::par_iterate(HeapRegionClosure* closure, @@ -63,7 +69,7 @@ void G1EvacFailureRegions::par_iterate(HeapRegionClosure* closure, uint worker_id) const { G1CollectedHeap::heap()->par_iterate_regions_array(closure, hrclaimer, - _evac_failure_regions, - Atomic::load(&_evac_failure_regions_cur_length), + _evac_failed_regions, + Atomic::load(&_num_regions_evac_failed), worker_id); } diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp index 043d69ff143ba..b8f9ea8003884 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp @@ -25,32 +25,43 @@ #ifndef SHARE_GC_G1_G1EVACFAILUREREGIONS_HPP #define SHARE_GC_G1_G1EVACFAILUREREGIONS_HPP -#include "runtime/atomic.hpp" #include "utilities/bitMap.hpp" class G1AbstractSubTask; -class G1HeapRegionChunkClosure; class HeapRegionClosure; class HeapRegionClaimer; -// This class records for every region on the heap whether evacuation failed for it, -// and records for every evacuation failure region to speed up iteration of these -// regions in post evacuation phase. +// This class records for every region on the heap whether it had experienced an +// evacuation failure. +// An evacuation failure may occur due to pinning or due to allocation failure +// (not enough to-space). For every such occurrence the class records region +// information to speed up iteration of these regions in various gc phases. +// +// Pinned regions may experience an allocation failure at the same time as G1 +// tries to evacuate anything but objects that are possible to be pinned. So +// +// _num_regions_pinned + _num_regions_alloc_failed >= _num_regions_evac_failed +// class G1EvacFailureRegions { - // Records for every region on the heap whether evacuation failed for it. - CHeapBitMap _regions_failed_evacuation; - // Regions (index) of evacuation failed in the current collection. - uint* _evac_failure_regions; + // Records for every region on the heap whether the region has experienced an + // evacuation failure. + CHeapBitMap _regions_evac_failed; + // Records for every region on the heap whether the evacuation failure cause + // has been allocation failure or region pinning. + CHeapBitMap _regions_pinned; + CHeapBitMap _regions_alloc_failed; + // Evacuation failed regions (indexes) in the current collection. + uint* _evac_failed_regions; // Number of regions evacuation failed in the current collection. - volatile uint _evac_failure_regions_cur_length; + volatile uint _num_regions_evac_failed; public: G1EvacFailureRegions(); ~G1EvacFailureRegions(); uint get_region_idx(uint idx) const { - assert(idx < _evac_failure_regions_cur_length, "precondition"); - return _evac_failure_regions[idx]; + assert(idx < _num_regions_evac_failed, "precondition"); + return _evac_failed_regions[idx]; } // Sets up the bitmap and failed regions array for addition. @@ -66,18 +77,16 @@ class G1EvacFailureRegions { // Return a G1AbstractSubTask which does necessary preparation for evacuation failed regions G1AbstractSubTask* create_prepare_regions_task(); - uint num_regions_failed_evacuation() const { - return Atomic::load(&_evac_failure_regions_cur_length); - } + inline uint num_regions_evac_failed() const; - bool evacuation_failed() const { - return num_regions_failed_evacuation() > 0; - } + inline bool has_regions_evac_failed() const; + inline bool has_regions_evac_pinned() const; + inline bool has_regions_alloc_failed() const; // Record that the garbage collection encountered an evacuation failure in the // given region. Returns whether this has been the first occurrence of an evacuation // failure in that region. - inline bool record(uint region_idx); + inline bool record(uint worker_id, uint region_idx, bool cause_pinned); }; #endif //SHARE_GC_G1_G1EVACFAILUREREGIONS_HPP diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp index 48d9eb6deaac8..28be8562b523f 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp @@ -27,19 +27,60 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1EvacFailureRegions.hpp" +#include "gc/g1/g1GCPhaseTimes.hpp" #include "runtime/atomic.hpp" -bool G1EvacFailureRegions::record(uint region_idx) { - bool success = _regions_failed_evacuation.par_set_bit(region_idx, - memory_order_relaxed); +uint G1EvacFailureRegions::num_regions_evac_failed() const { + return Atomic::load(&_num_regions_evac_failed); +} + +bool G1EvacFailureRegions::has_regions_evac_failed() const { + return num_regions_evac_failed() > 0; +} + +bool G1EvacFailureRegions::has_regions_evac_pinned() const { + G1GCPhaseTimes* p = G1CollectedHeap::heap()->phase_times(); + size_t count = p->sum_thread_work_items(G1GCPhaseTimes::RestoreEvacuationFailedRegions, + G1GCPhaseTimes::RestoreEvacFailureRegionsPinnedNum); + return count != 0; +} + +bool G1EvacFailureRegions::has_regions_alloc_failed() const { + G1GCPhaseTimes* p = G1CollectedHeap::heap()->phase_times(); + size_t count = p->sum_thread_work_items(G1GCPhaseTimes::RestoreEvacuationFailedRegions, + G1GCPhaseTimes::RestoreEvacFailureRegionsAllocFailedNum); + return count != 0; +} + +bool G1EvacFailureRegions::record(uint worker_id, uint region_idx, bool cause_pinned) { + bool success = _regions_evac_failed.par_set_bit(region_idx, + memory_order_relaxed); if (success) { - size_t offset = Atomic::fetch_then_add(&_evac_failure_regions_cur_length, 1u); - _evac_failure_regions[offset] = region_idx; + size_t offset = Atomic::fetch_then_add(&_num_regions_evac_failed, 1u); + _evac_failed_regions[offset] = region_idx; G1CollectedHeap* g1h = G1CollectedHeap::heap(); HeapRegion* hr = g1h->region_at(region_idx); hr->note_evacuation_failure(); } + + if (cause_pinned) { + if (_regions_pinned.par_set_bit(region_idx, memory_order_relaxed)) { + G1GCPhaseTimes* p = G1CollectedHeap::heap()->phase_times(); + p->record_or_add_thread_work_item(G1GCPhaseTimes::RestoreEvacuationFailedRegions, + worker_id, + 1, + G1GCPhaseTimes::RestoreEvacFailureRegionsPinnedNum); + } + } else { + if (_regions_alloc_failed.par_set_bit(region_idx, memory_order_relaxed)) { + G1GCPhaseTimes* p = G1CollectedHeap::heap()->phase_times(); + p->record_or_add_thread_work_item(G1GCPhaseTimes::RestoreEvacuationFailedRegions, + worker_id, + 1, + G1GCPhaseTimes::RestoreEvacFailureRegionsAllocFailedNum); + } + } return success; } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 039f13019ab10..e4abb69caceec 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -171,6 +171,7 @@ class PrepareRegionsClosure : public HeapRegionClosure { PrepareRegionsClosure(G1FullCollector* collector) : _collector(collector) { } bool do_heap_region(HeapRegion* hr) { + hr->prepare_for_full_gc(); G1CollectedHeap::heap()->prepare_region_for_full_compaction(hr); _collector->before_marking_update_attribute_table(hr); return false; @@ -256,9 +257,9 @@ void G1FullCollector::complete_collection() { void G1FullCollector::before_marking_update_attribute_table(HeapRegion* hr) { if (hr->is_free()) { _region_attr_table.set_free(hr->hrm_index()); - } else if (hr->is_humongous()) { - // Humongous objects will never be moved in the "main" compaction phase, but - // afterwards in a special phase if needed. + } else if (hr->is_humongous() || hr->has_pinned_objects()) { + // Humongous objects or pinned regions will never be moved in the "main" + // compaction phase, but non-pinned regions might afterwards in a special phase. _region_attr_table.set_skip_compacting(hr->hrm_index()); } else { // Everything else should be compacted. @@ -453,10 +454,16 @@ void G1FullCollector::phase2d_prepare_humongous_compaction() { region_index++; continue; } else if (hr->is_starts_humongous()) { - uint num_regions = humongous_cp->forward_humongous(hr); - region_index += num_regions; // Skip over the continues humongous regions. + size_t obj_size = cast_to_oop(hr->bottom())->size(); + uint num_regions = (uint)G1CollectedHeap::humongous_obj_size_in_regions(obj_size); + // Even during last-ditch compaction we should not move pinned humongous objects. + if (!hr->has_pinned_objects()) { + humongous_cp->forward_humongous(hr); + } + region_index += num_regions; // Advance over all humongous regions. continue; } else if (is_compaction_target(region_index)) { + assert(!hr->has_pinned_objects(), "pinned regions should not be compaction targets"); // Add the region to the humongous compaction point. humongous_cp->add(hr); } diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp index 8c4fa4eb2e030..2ed6ccd4735fb 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp @@ -67,6 +67,7 @@ void G1FullGCCompactTask::copy_object_to_new_location(oop obj) { } void G1FullGCCompactTask::compact_region(HeapRegion* hr) { + assert(!hr->has_pinned_objects(), "Should be no region with pinned objects in compaction queue"); assert(!hr->is_humongous(), "Should be no humongous regions in compaction queue"); if (!collector()->is_free(hr->hrm_index())) { diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp index dabff2bc9fb5a..7669771eb6c3c 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.cpp @@ -145,7 +145,7 @@ void G1FullGCCompactionPoint::add_humongous(HeapRegion* hr) { }); } -uint G1FullGCCompactionPoint::forward_humongous(HeapRegion* hr) { +void G1FullGCCompactionPoint::forward_humongous(HeapRegion* hr) { assert(hr->is_starts_humongous(), "Sanity!"); oop obj = cast_to_oop(hr->bottom()); @@ -153,7 +153,7 @@ uint G1FullGCCompactionPoint::forward_humongous(HeapRegion* hr) { uint num_regions = (uint)G1CollectedHeap::humongous_obj_size_in_regions(obj_size); if (!has_regions()) { - return num_regions; + return; } // Find contiguous compaction target regions for the humongous object. @@ -161,7 +161,7 @@ uint G1FullGCCompactionPoint::forward_humongous(HeapRegion* hr) { if (range_begin == UINT_MAX) { // No contiguous compaction target regions found, so the object cannot be moved. - return num_regions; + return; } // Preserve the mark for the humongous object as the region was initially not compacting. @@ -177,7 +177,7 @@ uint G1FullGCCompactionPoint::forward_humongous(HeapRegion* hr) { // Remove covered regions from compaction target candidates. _compaction_regions->remove_range(range_begin, (range_begin + num_regions)); - return num_regions; + return; } uint G1FullGCCompactionPoint::find_contiguous_before(HeapRegion* hr, uint num_regions) { diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp index ea8351f1299f8..ca76f7e6b9408 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp @@ -55,7 +55,7 @@ class G1FullGCCompactionPoint : public CHeapObj { void initialize(HeapRegion* hr); void update(); void forward(oop object, size_t size); - uint forward_humongous(HeapRegion* hr); + void forward_humongous(HeapRegion* hr); void add(HeapRegion* hr); void add_humongous(HeapRegion* hr); diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.inline.hpp index 76a647a3da373..2a2add76f48c6 100644 --- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.inline.hpp @@ -42,7 +42,7 @@ void G1DetermineCompactionQueueClosure::free_empty_humongous_region(HeapRegion* inline bool G1DetermineCompactionQueueClosure::should_compact(HeapRegion* hr) const { // There is no need to iterate and forward objects in non-movable regions ie. // prepare them for compaction. - if (hr->is_humongous()) { + if (hr->is_humongous() || hr->has_pinned_objects()) { return false; } size_t live_words = _collector->live_words(hr->hrm_index()); @@ -73,29 +73,41 @@ inline void G1DetermineCompactionQueueClosure::add_to_compaction_queue(HeapRegio cp->add(hr); } +static bool has_pinned_objects(HeapRegion* hr) { + return hr->has_pinned_objects() || + (hr->is_humongous() && hr->humongous_start_region()->has_pinned_objects()); +} + inline bool G1DetermineCompactionQueueClosure::do_heap_region(HeapRegion* hr) { if (should_compact(hr)) { assert(!hr->is_humongous(), "moving humongous objects not supported."); add_to_compaction_queue(hr); - } else { - assert(hr->containing_set() == nullptr, "already cleared by PrepareRegionsClosure"); - if (hr->is_humongous()) { - oop obj = cast_to_oop(hr->humongous_start_region()->bottom()); - bool is_empty = !_collector->mark_bitmap()->is_marked(obj); - if (is_empty) { - free_empty_humongous_region(hr); - } else { - _collector->set_has_humongous(); - } + return false; + } + + assert(hr->containing_set() == nullptr, "already cleared by PrepareRegionsClosure"); + if (has_pinned_objects(hr)) { + // First check regions with pinned objects: they need to be skipped regardless + // of region type and never be considered for reclamation. + assert(_collector->is_skip_compacting(hr->hrm_index()), "pinned region %u must be skip_compacting", hr->hrm_index()); + log_trace(gc, phases)("Phase 2: skip compaction region index: %u (%s), has pinned objects", + hr->hrm_index(), hr->get_short_type_str()); + } else if (hr->is_humongous()) { + oop obj = cast_to_oop(hr->humongous_start_region()->bottom()); + bool is_empty = !_collector->mark_bitmap()->is_marked(obj); + if (is_empty) { + free_empty_humongous_region(hr); } else { - assert(MarkSweepDeadRatio > 0, - "only skip compaction for other regions when MarkSweepDeadRatio > 0"); + _collector->set_has_humongous(); + } + } else { + assert(MarkSweepDeadRatio > 0, + "only skip compaction for other regions when MarkSweepDeadRatio > 0"); - // Too many live objects in the region; skip compacting it. - _collector->update_from_compacting_to_skip_compacting(hr->hrm_index()); - log_trace(gc, phases)("Phase 2: skip compaction region index: %u, live words: " SIZE_FORMAT, + // Too many live objects in the region; skip compacting it. + _collector->update_from_compacting_to_skip_compacting(hr->hrm_index()); + log_trace(gc, phases)("Phase 2: skip compaction region index: %u, live words: " SIZE_FORMAT, hr->hrm_index(), _collector->live_words(hr->hrm_index())); - } } return false; diff --git a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp index 003cc2b3f2b89..ddbaaa0b75ef2 100644 --- a/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCResetMetadataTask.cpp @@ -89,10 +89,11 @@ void G1FullGCResetMetadataTask::G1ResetMetadataClosure::reset_skip_compacting(He if (hr->is_humongous()) { oop obj = cast_to_oop(hr->humongous_start_region()->bottom()); - assert(_collector->mark_bitmap()->is_marked(obj), "must be live"); + assert(hr->humongous_start_region()->has_pinned_objects() || + _collector->mark_bitmap()->is_marked(obj), "must be live"); } else { - assert(_collector->live_words(region_index) > _collector->scope()->region_compaction_threshold(), - "should be quite full"); + assert(hr->has_pinned_objects() || _collector->live_words(region_index) > _collector->scope()->region_compaction_threshold(), + "should be quite full or pinned %u", region_index); } assert(_collector->compaction_top(hr) == nullptr, diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index 83824c486562d..43b582b7dd901 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -97,7 +97,7 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[GCWorkerEnd] = new WorkerDataArray("GCWorkerEnd", "GC Worker End (ms):", max_gc_threads); _gc_par_phases[Other] = new WorkerDataArray("Other", "GC Worker Other (ms):", max_gc_threads); _gc_par_phases[MergePSS] = new WorkerDataArray("MergePSS", "Merge Per-Thread State (ms):", max_gc_threads); - _gc_par_phases[RestoreRetainedRegions] = new WorkerDataArray("RestoreRetainedRegions", "Restore Retained Regions (ms):", max_gc_threads); + _gc_par_phases[RestoreEvacuationFailedRegions] = new WorkerDataArray("RestoreEvacuationFailedRegions", "Restore Evacuation Failed Regions (ms):", max_gc_threads); _gc_par_phases[RemoveSelfForwards] = new WorkerDataArray("RemoveSelfForwards", "Remove Self Forwards (ms):", max_gc_threads); _gc_par_phases[ClearCardTable] = new WorkerDataArray("ClearLoggedCards", "Clear Logged Cards (ms):", max_gc_threads); _gc_par_phases[RecalculateUsed] = new WorkerDataArray("RecalculateUsed", "Recalculate Used Memory (ms):", max_gc_threads); @@ -132,8 +132,9 @@ G1GCPhaseTimes::G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads) : _gc_par_phases[MergePSS]->create_thread_work_items("LAB Undo Waste", MergePSSLABUndoWasteBytes); _gc_par_phases[MergePSS]->create_thread_work_items("Evac Fail Extra Cards", MergePSSEvacFailExtra); - _gc_par_phases[RestoreRetainedRegions]->create_thread_work_items("Evacuation Failed Regions:", RestoreRetainedRegionsFailedNum); - _gc_par_phases[RestoreRetainedRegions]->create_thread_work_items("New Retained Regions:", RestoreRetainedRegionsRetainedNum); + _gc_par_phases[RestoreEvacuationFailedRegions]->create_thread_work_items("Evacuation Failed Regions:", RestoreEvacFailureRegionsEvacFailedNum); + _gc_par_phases[RestoreEvacuationFailedRegions]->create_thread_work_items("Pinned Regions:", RestoreEvacFailureRegionsPinnedNum); + _gc_par_phases[RestoreEvacuationFailedRegions]->create_thread_work_items("Allocation Failed Regions:", RestoreEvacFailureRegionsAllocFailedNum); _gc_par_phases[RemoveSelfForwards]->create_thread_work_items("Forward Chunks:", RemoveSelfForwardChunksNum); _gc_par_phases[RemoveSelfForwards]->create_thread_work_items("Empty Forward Chunks:", RemoveSelfForwardEmptyChunksNum); @@ -502,7 +503,7 @@ double G1GCPhaseTimes::print_post_evacuate_collection_set(bool evacuation_failed debug_phase(_gc_par_phases[ClearCardTable], 1); debug_phase(_gc_par_phases[RecalculateUsed], 1); if (evacuation_failed) { - debug_phase(_gc_par_phases[RestoreRetainedRegions], 1); + debug_phase(_gc_par_phases[RestoreEvacuationFailedRegions], 1); debug_phase(_gc_par_phases[RemoveSelfForwards], 2); } diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp index de03ecab99ff6..713b411bda110 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp @@ -79,7 +79,7 @@ class G1GCPhaseTimes : public CHeapObj { RebuildFreeList, SampleCollectionSetCandidates, MergePSS, - RestoreRetainedRegions, + RestoreEvacuationFailedRegions, RemoveSelfForwards, ClearCardTable, RecalculateUsed, @@ -146,9 +146,10 @@ class G1GCPhaseTimes : public CHeapObj { MergePSSEvacFailExtra }; - enum RestoreRetainedRegionsWorkItems { - RestoreRetainedRegionsFailedNum, - RestoreRetainedRegionsRetainedNum + enum RestoreEvacFailureRegionsWorkItems { + RestoreEvacFailureRegionsEvacFailedNum, // How many regions experienced an evacuation failure (pinned or allocation failure) + RestoreEvacFailureRegionsPinnedNum, // How many regions were found as pinned. + RestoreEvacFailureRegionsAllocFailedNum // How many regions were found experiencing an allocation failure. }; enum RemoveSelfForwardsWorkItems { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp b/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp index 7d2ab4f26ea5b..e5a10858ed126 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp @@ -36,10 +36,14 @@ struct G1HeapRegionAttr { // remset_is_tracked_t is essentially bool, but we need precise control // on the size, and sizeof(bool) is implementation specific. typedef uint8_t remset_is_tracked_t; + // _is_pinned_t is essentially bool, but we want precise control + // on the size, and sizeof(bool) is implementation specific. + typedef uint8_t is_pinned_t; private: remset_is_tracked_t _remset_is_tracked; region_type_t _type; + is_pinned_t _is_pinned; public: // Selection of the values for the _type field were driven to micro-optimize the @@ -59,9 +63,8 @@ struct G1HeapRegionAttr { static const region_type_t Old = 1; // The region is in the collection set and an old region. static const region_type_t Num = 2; - G1HeapRegionAttr(region_type_t type = NotInCSet, bool remset_is_tracked = false) : - _remset_is_tracked(remset_is_tracked), _type(type) { - + G1HeapRegionAttr(region_type_t type = NotInCSet, bool remset_is_tracked = false, bool is_pinned = false) : + _remset_is_tracked(remset_is_tracked ? 1 : 0), _type(type), _is_pinned(is_pinned ? 1 : 0) { assert(is_valid(), "Invalid type %d", _type); } @@ -82,12 +85,16 @@ struct G1HeapRegionAttr { bool remset_is_tracked() const { return _remset_is_tracked != 0; } void set_new_survivor() { _type = NewSurvivor; } + bool is_pinned() const { return _is_pinned != 0; } + void set_old() { _type = Old; } void clear_humongous_candidate() { assert(is_humongous_candidate() || !is_in_cset(), "must be"); _type = NotInCSet; } + void set_remset_is_tracked(bool value) { _remset_is_tracked = value ? 1 : 0; } + void set_is_pinned(bool value) { _is_pinned = value ? 1 : 0; } bool is_in_cset_or_humongous_candidate() const { return is_in_cset() || is_humongous_candidate(); } bool is_in_cset() const { return type() >= Young; } @@ -136,7 +143,9 @@ class G1HeapRegionAttrBiasedMappedArray : public G1BiasedMappedArrayset_remset_is_tracked(remset_is_tracked); } - void set_in_young(uintptr_t index) { + void set_is_pinned(uintptr_t index, bool is_pinned) { + get_ref_by_index(index)->set_is_pinned(is_pinned); + } + + void set_in_young(uintptr_t index, bool is_pinned) { assert(get_by_index(index).is_default(), "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str()); - set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Young, true)); + set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Young, true, is_pinned)); } void set_in_old(uintptr_t index, bool remset_is_tracked) { assert(get_by_index(index).is_default(), "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str()); - set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Old, remset_is_tracked)); + // We do not select regions with pinned objects into the collection set. + const bool region_is_pinned = false; + set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Old, remset_is_tracked, region_is_pinned)); } bool is_in_cset_or_humongous_candidate(HeapWord* addr) const { return at(addr).is_in_cset_or_humongous_candidate(); } diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp index c4f2c1284032b..4e0a7dcfc3d6b 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp @@ -247,11 +247,10 @@ void G1MonitoringSupport::recalculate_sizes() { _old_gen_used = _overall_used - MIN2(_overall_used, _eden_space_used + _survivor_space_used); uint survivor_list_length = _g1h->survivor_regions_count(); - // Max length includes any potential extensions to the young gen - // we'll do when the GC locker is active. - uint young_list_max_length = _g1h->policy()->young_list_max_length(); - assert(young_list_max_length >= survivor_list_length, "invariant"); - uint eden_list_max_length = young_list_max_length - survivor_list_length; + + uint young_list_target_length = _g1h->policy()->young_list_target_length(); + assert(young_list_target_length >= survivor_list_length, "invariant"); + uint eden_list_max_length = young_list_target_length - survivor_list_length; // First calculate the committed sizes that can be calculated independently. _survivor_space_committed = survivor_list_length * HeapRegion::GrainBytes; diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index f9fc1fd019814..d011516fba146 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -428,7 +428,7 @@ HeapWord* G1ParScanThreadState::allocate_copy_slow(G1HeapRegionAttr* dest_attr, } #if EVAC_FAILURE_INJECTOR -bool G1ParScanThreadState::inject_evacuation_failure(uint region_idx) { +bool G1ParScanThreadState::inject_allocation_failure(uint region_idx) { return _g1h->evac_failure_injector()->evacuation_should_fail(_evac_failure_inject_counter, region_idx); } #endif @@ -461,6 +461,11 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio Klass* klass = old->klass(); const size_t word_sz = old->size_given_klass(klass); + // JNI only allows pinning of typeArrays, so we only need to keep those in place. + if (region_attr.is_pinned() && klass->is_typeArray_klass()) { + return handle_evacuation_failure_par(old, old_mark, word_sz, true /* cause_pinned */); + } + uint age = 0; G1HeapRegionAttr dest_attr = next_region_attr(region_attr, old_mark, age); HeapRegion* const from_region = _g1h->heap_region_containing(old); @@ -475,7 +480,7 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio if (obj_ptr == nullptr) { // This will either forward-to-self, or detect that someone else has // installed a forwarding pointer. - return handle_evacuation_failure_par(old, old_mark, word_sz); + return handle_evacuation_failure_par(old, old_mark, word_sz, false /* cause_pinned */); } } @@ -483,11 +488,11 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio assert(_g1h->is_in_reserved(obj_ptr), "Allocated memory should be in the heap"); // Should this evacuation fail? - if (inject_evacuation_failure(from_region->hrm_index())) { + if (inject_allocation_failure(from_region->hrm_index())) { // Doing this after all the allocation attempts also tests the // undo_allocation() method too. undo_allocation(dest_attr, obj_ptr, word_sz, node_index); - return handle_evacuation_failure_par(old, old_mark, word_sz); + return handle_evacuation_failure_par(old, old_mark, word_sz, false /* cause_pinned */); } // We're going to allocate linearly, so might as well prefetch ahead. @@ -624,7 +629,7 @@ void G1ParScanThreadStateSet::record_unused_optional_region(HeapRegion* hr) { } NOINLINE -oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, size_t word_sz) { +oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, size_t word_sz, bool cause_pinned) { assert(_g1h->is_in_cset(old), "Object " PTR_FORMAT " should be in the CSet", p2i(old)); oop forward_ptr = old->forward_to_atomic(old, m, memory_order_relaxed); @@ -632,7 +637,7 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, siz // Forward-to-self succeeded. We are the "owner" of the object. HeapRegion* r = _g1h->heap_region_containing(old); - if (_evac_failure_regions->record(r->hrm_index())) { + if (_evac_failure_regions->record(_worker_id, r->hrm_index(), cause_pinned)) { _g1h->hr_printer()->evac_failure(r); } diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index d1e8c0cb6d676..5347b6341f885 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -120,7 +120,7 @@ class G1ParScanThreadState : public CHeapObj { // Enqueue the card of p into the (evacuation failed) region. template void enqueue_card_into_evac_fail_region(T* p, oop obj); - bool inject_evacuation_failure(uint region_idx) EVAC_FAILURE_INJECTOR_RETURN_( return false; ); + bool inject_allocation_failure(uint region_idx) EVAC_FAILURE_INJECTOR_RETURN_( return false; ); public: G1ParScanThreadState(G1CollectedHeap* g1h, @@ -231,7 +231,7 @@ class G1ParScanThreadState : public CHeapObj { void reset_trim_ticks(); // An attempt to evacuate "obj" has failed; take necessary steps. - oop handle_evacuation_failure_par(oop obj, markWord m, size_t word_sz); + oop handle_evacuation_failure_par(oop obj, markWord m, size_t word_sz, bool cause_pinned); template inline void remember_root_into_optional_region(T* p); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 1bc1d44691bbb..9e22acc73ca33 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -63,7 +63,6 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) : _full_collection_start_sec(0.0), _young_list_desired_length(0), _young_list_target_length(0), - _young_list_max_length(0), _eden_surv_rate_group(new G1SurvRateGroup()), _survivor_surv_rate_group(new G1SurvRateGroup()), _reserve_factor((double) G1ReservePercent / 100.0), @@ -198,15 +197,13 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l uint new_young_list_desired_length = calculate_young_desired_length(pending_cards, card_rs_length, code_root_rs_length); uint new_young_list_target_length = calculate_young_target_length(new_young_list_desired_length); - uint new_young_list_max_length = calculate_young_max_length(new_young_list_target_length); - log_trace(gc, ergo, heap)("Young list length update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u max: %u", + log_trace(gc, ergo, heap)("Young list length update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u", pending_cards, card_rs_length, old_young_list_target_length, new_young_list_desired_length, - new_young_list_target_length, - new_young_list_max_length); + new_young_list_target_length); // Write back. This is not an attempt to control visibility order to other threads // here; all the revising of the young gen length are best effort to keep pause time. @@ -217,7 +214,6 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l // early or too late. Atomic::store(&_young_list_desired_length, new_young_list_desired_length); Atomic::store(&_young_list_target_length, new_young_list_target_length); - Atomic::store(&_young_list_max_length, new_young_list_max_length); } // Calculates desired young gen length. It is calculated from: @@ -321,8 +317,7 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length) const { uint receiving_additional_eden; if (allocated_young_length >= desired_young_length) { // Already used up all we actually want (may happen as G1 revises the - // young list length concurrently, or caused by gclocker). Do not allow more, - // potentially resulting in GC. + // young list length concurrently). Do not allow more, potentially resulting in GC. receiving_additional_eden = 0; log_trace(gc, ergo, heap)("Young target length: Already used up desired young %u allocated %u", desired_young_length, @@ -496,11 +491,13 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, uint min_marking_candidates = MIN2(calc_min_old_cset_length(candidates()->last_marking_candidates_length()), candidates()->marking_regions_length()); double predicted_region_evac_time_ms = base_time_ms; - for (HeapRegion* r : candidates()->marking_regions()) { + for (G1CollectionSetCandidateInfo* ci : candidates()->marking_regions()) { + // We optimistically assume that any of these marking candidate regions will + // not be pinned, so just consider them as normal. if (min_marking_candidates == 0) { break; } - predicted_region_evac_time_ms += predict_region_total_time_ms(r, false /* for_young_only_phase */); + predicted_region_evac_time_ms += predict_region_total_time_ms(ci->_r, false /* for_young_only_phase */); min_marking_candidates--; } @@ -523,13 +520,21 @@ double G1Policy::predict_survivor_regions_evac_time() const { double G1Policy::predict_retained_regions_evac_time() const { uint num_regions = 0; + uint num_pinned_regions = 0; + double result = 0.0; G1CollectionCandidateList& list = candidates()->retained_regions(); uint min_regions_left = MIN2(min_retained_old_cset_length(), list.length()); - for (HeapRegion* r : list) { + for (G1CollectionSetCandidateInfo* ci : list) { + HeapRegion* r = ci->_r; + // We optimistically assume that any of these marking candidate regions will + // be reclaimable the next gc, so just consider them as normal. + if (r->has_pinned_objects()) { + num_pinned_regions++; + } if (min_regions_left == 0) { // Minimum amount of regions considered. Exit. break; @@ -539,8 +544,8 @@ double G1Policy::predict_retained_regions_evac_time() const { num_regions++; } - log_trace(gc, ergo, heap)("Selected %u of %u retained candidates taking %1.3fms additional time", - num_regions, list.length(), result); + log_trace(gc, ergo, heap)("Selected %u of %u retained candidates (pinned %u) taking %1.3fms additional time", + num_regions, list.length(), num_pinned_regions, result); return result; } @@ -655,12 +660,14 @@ void G1Policy::record_concurrent_refinement_stats(size_t pending_cards, } bool G1Policy::should_retain_evac_failed_region(uint index) const { - size_t live_bytes= _g1h->region_at(index)->live_bytes(); + size_t live_bytes = _g1h->region_at(index)->live_bytes(); +#ifdef ASSERT + HeapRegion* r = _g1h->region_at(index); assert(live_bytes != 0, - "live bytes not set for %u used %zu garbage %zu cm-live %zu", - index, _g1h->region_at(index)->used(), _g1h->region_at(index)->garbage_bytes(), live_bytes); - + "live bytes not set for %u used %zu garbage %zu cm-live %zu pinned %d", + index, r->used(), r->garbage_bytes(), live_bytes, r->has_pinned_objects()); +#endif size_t threshold = G1RetainRegionLiveThresholdPercent * HeapRegion::GrainBytes / 100; return live_bytes < threshold; } @@ -784,7 +791,7 @@ double G1Policy::logged_cards_processing_time() const { // Anything below that is considered to be zero #define MIN_TIMER_GRANULARITY 0.0000001 -void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mark, bool evacuation_failure) { +void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mark, bool allocation_failure) { G1GCPhaseTimes* p = phase_times(); double start_time_sec = phase_times()->cur_collection_start_sec(); @@ -810,7 +817,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar // Evacuation failures skew the timing too much to be considered for some statistics updates. // We make the assumption that these are rare. - bool update_stats = !evacuation_failure; + bool update_stats = !allocation_failure; if (update_stats) { // We maintain the invariant that all objects allocated by mutator @@ -826,7 +833,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _analytics->report_alloc_rate_ms(alloc_rate_ms); } - record_pause(this_pause, start_time_sec, end_time_sec, evacuation_failure); + record_pause(this_pause, start_time_sec, end_time_sec, allocation_failure); if (G1GCPauseTypeHelper::is_last_young_pause(this_pause)) { assert(!G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause), @@ -1153,11 +1160,6 @@ bool G1Policy::should_allocate_mutator_region() const { return young_list_length < young_list_target_length(); } -bool G1Policy::can_expand_young_list() const { - uint young_list_length = _g1h->young_regions_count(); - return young_list_length < young_list_max_length(); -} - bool G1Policy::use_adaptive_young_list_length() const { return _young_gen_sizer.use_adaptive_young_list_length(); } @@ -1181,20 +1183,6 @@ void G1Policy::print_age_table() { _survivors_age_table.print_age_table(_tenuring_threshold); } -uint G1Policy::calculate_young_max_length(uint target_young_length) const { - uint expansion_region_num = 0; - if (GCLockerEdenExpansionPercent > 0) { - double perc = GCLockerEdenExpansionPercent / 100.0; - double expansion_region_num_d = perc * young_list_target_length(); - // We use ceiling so that if expansion_region_num_d is > 0.0 (but - // less than 1.0) we'll get 1. - expansion_region_num = (uint) ceil(expansion_region_num_d); - } - uint max_length = target_young_length + expansion_region_num; - assert(target_young_length <= max_length, "overflow"); - return max_length; -} - // Calculates survivor space parameters. void G1Policy::update_survivors_policy() { double max_survivor_regions_d = @@ -1377,13 +1365,13 @@ void G1Policy::update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_t void G1Policy::record_pause(G1GCPauseType gc_type, double start, double end, - bool evacuation_failure) { + bool allocation_failure) { // Manage the MMU tracker. For some reason it ignores Full GCs. if (gc_type != G1GCPauseType::FullGC) { _mmu_tracker->add_pause(start, end); } - if (!evacuation_failure) { + if (!allocation_failure) { update_gc_pause_time_ratios(gc_type, start, end); } @@ -1477,13 +1465,15 @@ static void print_finish_message(const char* reason, bool from_marking) { double G1Policy::select_candidates_from_marking(G1CollectionCandidateList* marking_list, double time_remaining_ms, G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions) { + G1CollectionCandidateRegionList* optional_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions) { assert(marking_list != nullptr, "must be"); uint num_expensive_regions = 0; uint num_initial_regions_selected = 0; uint num_optional_regions_selected = 0; + uint num_pinned_regions = 0; double predicted_initial_time_ms = 0.0; double predicted_optional_time_ms = 0.0; @@ -1496,9 +1486,9 @@ double G1Policy::select_candidates_from_marking(G1CollectionCandidateList* marki bool check_time_remaining = use_adaptive_young_list_length(); log_debug(gc, ergo, cset)("Start adding marking candidates to collection set. " - "Min %u regions, max %u regions, " + "Min %u regions, max %u regions, available %u regions" "time remaining %1.2fms, optional threshold %1.2fms", - min_old_cset_length, max_old_cset_length, time_remaining_ms, optional_threshold_ms); + min_old_cset_length, max_old_cset_length, marking_list->length(), time_remaining_ms, optional_threshold_ms); G1CollectionCandidateListIterator iter = marking_list->begin(); for (; iter != marking_list->end(); ++iter) { @@ -1507,7 +1497,18 @@ double G1Policy::select_candidates_from_marking(G1CollectionCandidateList* marki print_finish_message("Maximum number of regions reached", true); break; } - HeapRegion* hr = *iter; + HeapRegion* hr = (*iter)->_r; + // Skip evacuating pinned marking regions because we are not getting any free + // space from them (and we expect to get free space from marking candidates). + // Also prepare to move them to retained regions to be evacuated optionally later + // to not impact the mixed phase too much. + if (hr->has_pinned_objects()) { + num_pinned_regions++; + (*iter)->update_num_unreclaimed(); + log_trace(gc, ergo, cset)("Marking candidate %u can not be reclaimed currently. Skipping.", hr->hrm_index()); + pinned_old_regions->append(hr); + continue; + } double predicted_time_ms = predict_region_total_time_ms(hr, false); time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0); // Add regions to old set until we reach the minimum amount @@ -1551,9 +1552,9 @@ double G1Policy::select_candidates_from_marking(G1CollectionCandidateList* marki num_expensive_regions); } - log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u, optional: %u, " + log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u, optional: %u, pinned: %u, " "predicted initial time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, + num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms); assert(initial_old_regions->length() == num_initial_regions_selected, "must be"); @@ -1564,13 +1565,15 @@ double G1Policy::select_candidates_from_marking(G1CollectionCandidateList* marki void G1Policy::select_candidates_from_retained(G1CollectionCandidateList* retained_list, double time_remaining_ms, G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions) { + G1CollectionCandidateRegionList* optional_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions) { uint const min_regions = min_retained_old_cset_length(); uint num_initial_regions_selected = 0; uint num_optional_regions_selected = 0; uint num_expensive_regions_selected = 0; + uint num_pinned_regions = 0; double predicted_initial_time_ms = 0.0; double predicted_optional_time_ms = 0.0; @@ -1584,13 +1587,25 @@ void G1Policy::select_candidates_from_retained(G1CollectionCandidateList* retain time_remaining_ms = MIN2(time_remaining_ms, optional_time_remaining_ms); log_debug(gc, ergo, cset)("Start adding retained candidates to collection set. " - "Min %u regions, " + "Min %u regions, available %u, " "time remaining %1.2fms, optional remaining %1.2fms", - min_regions, time_remaining_ms, optional_time_remaining_ms); + min_regions, retained_list->length(), time_remaining_ms, optional_time_remaining_ms); - for (HeapRegion* r : *retained_list) { + for (G1CollectionSetCandidateInfo* ci : *retained_list) { + HeapRegion* r = ci->_r; double predicted_time_ms = predict_region_total_time_ms(r, collector_state()->in_young_only_phase()); bool fits_in_remaining_time = predicted_time_ms <= time_remaining_ms; + // If we can't reclaim that region ignore it for now. + if (r->has_pinned_objects()) { + num_pinned_regions++; + if (ci->update_num_unreclaimed()) { + log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Skipping.", r->hrm_index()); + } else { + log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Dropping.", r->hrm_index()); + pinned_old_regions->append(r); + } + continue; + } if (fits_in_remaining_time || (num_expensive_regions_selected < min_regions)) { predicted_initial_time_ms += predicted_time_ms; @@ -1620,10 +1635,10 @@ void G1Policy::select_candidates_from_retained(G1CollectionCandidateList* retain num_expensive_regions_selected); } - log_debug(gc, ergo, cset)("Finish adding retained candidates to collection set. Initial: %u, optional: %u, " + log_debug(gc, ergo, cset)("Finish adding retained candidates to collection set. Initial: %u, optional: %u, pinned: %u, " "predicted initial time: %1.2fms, predicted optional time: %1.2fms, " "time remaining: %1.2fms optional time remaining %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, + num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms, optional_time_remaining_ms); } diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index c5ab4e9e1de13..c951909788c2e 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -243,9 +243,6 @@ class G1Policy: public CHeapObj { uint calculate_young_desired_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) const; // Limit the given desired young length to available free regions. uint calculate_young_target_length(uint desired_young_length) const; - // The GCLocker might cause us to need more regions than the target. Calculate - // the maximum number of regions to use in that case. - uint calculate_young_max_length(uint target_young_length) const; size_t predict_bytes_to_copy(HeapRegion* hr) const; double predict_survivor_regions_evac_time() const; @@ -275,7 +272,7 @@ class G1Policy: public CHeapObj { void record_pause(G1GCPauseType gc_type, double start, double end, - bool evacuation_failure = false); + bool allocation_failure = false); void update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_sec, double end_sec); @@ -314,7 +311,7 @@ class G1Policy: public CHeapObj { // Record the start and end of the actual collection part of the evacuation pause. void record_young_collection_start(); - void record_young_collection_end(bool concurrent_operation_is_full_mark, bool evacuation_failure); + void record_young_collection_end(bool concurrent_operation_is_full_mark, bool allocation_failure); // Record the start and end of a full collection. void record_full_collection_start(); @@ -335,18 +332,20 @@ class G1Policy: public CHeapObj { // Amount of allowed waste in bytes in the collection set. size_t allowed_waste_in_collection_set() const; - // Calculate and fill in the initial and optional old gen candidate regions from + // Calculate and fill in the initial, optional and pinned old gen candidate regions from // the given candidate list and the remaining time. // Returns the remaining time. double select_candidates_from_marking(G1CollectionCandidateList* marking_list, double time_remaining_ms, G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions); + G1CollectionCandidateRegionList* optional_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions); void select_candidates_from_retained(G1CollectionCandidateList* retained_list, double time_remaining_ms, G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions); + G1CollectionCandidateRegionList* optional_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions); // Calculate the number of optional regions from the given collection set candidates, // the remaining time and the maximum number of these regions and return the number @@ -383,12 +382,9 @@ class G1Policy: public CHeapObj { uint young_list_desired_length() const { return Atomic::load(&_young_list_desired_length); } uint young_list_target_length() const { return Atomic::load(&_young_list_target_length); } - uint young_list_max_length() const { return Atomic::load(&_young_list_max_length); } bool should_allocate_mutator_region() const; - bool can_expand_young_list() const; - bool use_adaptive_young_list_length() const; // Return an estimate of the number of bytes used in young gen. diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 7820c9abff716..c2254e4287f38 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -68,11 +68,9 @@ VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(uint gc_count_before, bool VM_G1TryInitiateConcMark::doit_prologue() { bool result = VM_GC_Operation::doit_prologue(); // The prologue can fail for a couple of reasons. The first is that another GC - // got scheduled and prevented the scheduling of the concurrent start GC. The - // second is that the GC locker may be active and the heap can't be expanded. - // In both cases we want to retry the GC so that the concurrent start pause is - // actually scheduled. In the second case, however, we should stall until - // until the GC locker is no longer active and then retry the concurrent start GC. + // got scheduled and prevented the scheduling of the concurrent start GC. + // In this case we want to retry the GC so that the concurrent start pause is + // actually scheduled. if (!result) _transient_failure = true; return result; } @@ -103,16 +101,9 @@ void VM_G1TryInitiateConcMark::doit() { // request will be remembered for a later partial collection, even though // we've rejected this request. _whitebox_attached = true; - } else if (!g1h->do_collection_pause_at_safepoint()) { - // Failure to perform the collection at all occurs because GCLocker is - // active, and we have the bad luck to be the collection request that - // makes a later _gc_locker collection needed. (Else we would have hit - // the GCLocker check in the prologue.) - _transient_failure = true; - } else if (g1h->should_upgrade_to_full_gc()) { - _gc_succeeded = g1h->upgrade_to_full_collection(); } else { - _gc_succeeded = true; + _gc_succeeded = g1h->do_collection_pause_at_safepoint(); + assert(_gc_succeeded, "No reason to fail"); } } @@ -125,37 +116,20 @@ VM_G1CollectForAllocation::VM_G1CollectForAllocation(size_t word_size, void VM_G1CollectForAllocation::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (_word_size > 0) { - // An allocation has been requested. So, try to do that first. - // During the execution of this VM operation, there may have been a concurrent active - // GCLocker, potentially leading to expansion of the Eden space by other mutators. - // If the Eden space were expanded, this allocation request might succeed without - // the need for triggering a garbage collection. - _result = g1h->attempt_allocation_at_safepoint(_word_size, - false /* expect_null_cur_alloc_region */); - if (_result != nullptr) { - // If we can successfully allocate before we actually do the - // pause then we will consider this pause successful. - _gc_succeeded = true; - return; - } - } - GCCauseSetter x(g1h, _gc_cause); // Try a partial collection of some kind. _gc_succeeded = g1h->do_collection_pause_at_safepoint(); + assert(_gc_succeeded, "no reason to fail"); - if (_gc_succeeded) { - if (_word_size > 0) { - // An allocation had been requested. Do it, eventually trying a stronger - // kind of GC. - _result = g1h->satisfy_failed_allocation(_word_size, &_gc_succeeded); - } else if (g1h->should_upgrade_to_full_gc()) { - // There has been a request to perform a GC to free some space. We have no - // information on how much memory has been asked for. In case there are - // absolutely no regions left to allocate into, do a full compaction. - _gc_succeeded = g1h->upgrade_to_full_collection(); - } + if (_word_size > 0) { + // An allocation had been requested. Do it, eventually trying a stronger + // kind of GC. + _result = g1h->satisfy_failed_allocation(_word_size, &_gc_succeeded); + } else if (g1h->should_upgrade_to_full_gc()) { + // There has been a request to perform a GC to free some space. We have no + // information on how much memory has been asked for. In case there are + // absolutely no regions left to allocate into, do a full compaction. + _gc_succeeded = g1h->upgrade_to_full_collection(); } } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 351ecd06efb79..81d46aa39790c 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -35,6 +35,7 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1YoungGCEvacFailureInjector.hpp" +#include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1HRPrinter.hpp" #include "gc/g1/g1MonitoringSupport.hpp" @@ -77,12 +78,23 @@ class G1YoungGCTraceTime { GCTraceTime(Info, gc) _tt; const char* update_young_gc_name() { + char evacuation_failed_string[48]; + evacuation_failed_string[0] = '\0'; + + if (_collector->evacuation_failed()) { + snprintf(evacuation_failed_string, + ARRAY_SIZE(evacuation_failed_string), + " (Evacuation Failure: %s%s%s)", + _collector->evacuation_alloc_failed() ? "Allocation" : "", + _collector->evacuation_alloc_failed() && _collector->evacuation_pinned() ? " / " : "", + _collector->evacuation_pinned() ? "Pinned" : ""); + } snprintf(_young_gc_name_data, MaxYoungGCNameLength, "Pause Young (%s) (%s)%s", G1GCPauseTypeHelper::to_string(_pause_type), GCCause::to_string(_pause_cause), - _collector->evacuation_failed() ? " (Evacuation Failure)" : ""); + evacuation_failed_string); return _young_gc_name_data; } @@ -314,6 +326,10 @@ class G1PrepareEvacuationTask : public WorkerTask { if (!region->rem_set()->is_complete()) { return false; } + // We also cannot collect the humongous object if it is pinned. + if (region->has_pinned_objects()) { + return false; + } // Candidate selection must satisfy the following constraints // while concurrent marking is in progress: // @@ -386,13 +402,15 @@ class G1PrepareEvacuationTask : public WorkerTask { } else { _g1h->register_region_with_region_attr(hr); } - log_debug(gc, humongous)("Humongous region %u (object size %zu @ " PTR_FORMAT ") remset %zu code roots %zu marked %d reclaim candidate %d type array %d", + log_debug(gc, humongous)("Humongous region %u (object size %zu @ " PTR_FORMAT ") remset %zu code roots %zu " + "marked %d pinned count %u reclaim candidate %d type array %d", index, cast_to_oop(hr->bottom())->size() * HeapWordSize, p2i(hr->bottom()), hr->rem_set()->occupied(), hr->rem_set()->code_roots_list_length(), _g1h->concurrent_mark()->mark_bitmap()->is_marked(hr->bottom()), + hr->pinned_count(), _g1h->is_humongous_reclaim_candidate(index), cast_to_oop(hr->bottom())->is_typeArray() ); @@ -759,7 +777,7 @@ void G1YoungCollector::evacuate_next_optional_regions(G1ParScanThreadStateSet* p void G1YoungCollector::evacuate_optional_collection_set(G1ParScanThreadStateSet* per_thread_states) { const double collection_start_time_ms = phase_times()->cur_collection_start_sec() * 1000.0; - while (!evacuation_failed() && collection_set()->optional_region_length() > 0) { + while (!evacuation_alloc_failed() && collection_set()->optional_region_length() > 0) { double time_used_ms = os::elapsedTime() * 1000.0 - collection_start_time_ms; double time_left_ms = MaxGCPauseMillis - time_used_ms; @@ -1010,7 +1028,15 @@ void G1YoungCollector::post_evacuate_collection_set(G1EvacInfo* evacuation_info, } bool G1YoungCollector::evacuation_failed() const { - return _evac_failure_regions.evacuation_failed(); + return _evac_failure_regions.has_regions_evac_failed(); +} + +bool G1YoungCollector::evacuation_pinned() const { + return _evac_failure_regions.has_regions_evac_pinned(); +} + +bool G1YoungCollector::evacuation_alloc_failed() const { + return _evac_failure_regions.has_regions_alloc_failed(); } G1YoungCollector::G1YoungCollector(GCCause::Cause gc_cause) : @@ -1083,7 +1109,7 @@ void G1YoungCollector::collect() { // modifies it to the next state. jtm.report_pause_type(collector_state()->young_gc_pause_type(_concurrent_operation_is_full_mark)); - policy()->record_young_collection_end(_concurrent_operation_is_full_mark, evacuation_failed()); + policy()->record_young_collection_end(_concurrent_operation_is_full_mark, evacuation_alloc_failed()); } TASKQUEUE_STATS_ONLY(_g1h->task_queues()->print_and_reset_taskqueue_stats("Oop Queue");) } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.hpp b/src/hotspot/share/gc/g1/g1YoungCollector.hpp index 4a81ee97ed7da..506021a1bef25 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.hpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.hpp @@ -131,8 +131,12 @@ class G1YoungCollector { void post_evacuate_collection_set(G1EvacInfo* evacuation_info, G1ParScanThreadStateSet* per_thread_states); - // True iff an evacuation has failed in the most-recent collection. + // True iff an evacuation failure of any kind occurred in the most-recent collection. bool evacuation_failed() const; + // True iff an evacuation had pinned regions in the most-recent collection. + bool evacuation_pinned() const; + // True iff an evacuation had allocation failures in the most-recent collection. + bool evacuation_alloc_failed() const; public: G1YoungCollector(GCCause::Cause gc_cause); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index d47f0e86c3035..637860c81321e 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -31,7 +31,7 @@ #include "gc/g1/g1CollectionSetCandidates.inline.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" -#include "gc/g1/g1EvacFailureRegions.hpp" +#include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1OopClosures.inline.hpp" @@ -103,7 +103,7 @@ class G1PostEvacuateCollectionSetCleanupTask1::SampleCollectionSetCandidatesTask } }; -class G1PostEvacuateCollectionSetCleanupTask1::RestoreRetainedRegionsTask : public G1AbstractSubTask { +class G1PostEvacuateCollectionSetCleanupTask1::RestoreEvacFailureRegionsTask : public G1AbstractSubTask { G1CollectedHeap* _g1h; G1ConcurrentMark* _cm; @@ -265,14 +265,14 @@ class G1PostEvacuateCollectionSetCleanupTask1::RestoreRetainedRegionsTask : publ } public: - RestoreRetainedRegionsTask(G1EvacFailureRegions* evac_failure_regions) : - G1AbstractSubTask(G1GCPhaseTimes::RestoreRetainedRegions), + RestoreEvacFailureRegionsTask(G1EvacFailureRegions* evac_failure_regions) : + G1AbstractSubTask(G1GCPhaseTimes::RestoreEvacuationFailedRegions), _g1h(G1CollectedHeap::heap()), _cm(_g1h->concurrent_mark()), _evac_failure_regions(evac_failure_regions), _chunk_bitmap(mtGC) { - _num_evac_fail_regions = _evac_failure_regions->num_regions_failed_evacuation(); + _num_evac_fail_regions = _evac_failure_regions->num_regions_evac_failed(); _num_chunks_per_region = G1CollectedHeap::get_chunks_per_region(); _chunk_size = static_cast(HeapRegion::GrainWords / _num_chunks_per_region); @@ -284,10 +284,10 @@ class G1PostEvacuateCollectionSetCleanupTask1::RestoreRetainedRegionsTask : publ } double worker_cost() const override { - assert(_evac_failure_regions->evacuation_failed(), "Should not call this if not executed"); + assert(_evac_failure_regions->has_regions_evac_failed(), "Should not call this if there were no evacuation failures"); double workers_per_region = (double)G1CollectedHeap::get_chunks_per_region() / G1RestoreRetainedRegionChunksPerWorker; - return workers_per_region * _evac_failure_regions->num_regions_failed_evacuation(); + return workers_per_region * _evac_failure_regions->num_regions_evac_failed(); } void do_work(uint worker_id) override { @@ -308,16 +308,16 @@ G1PostEvacuateCollectionSetCleanupTask1::G1PostEvacuateCollectionSetCleanupTask1 G1EvacFailureRegions* evac_failure_regions) : G1BatchedTask("Post Evacuate Cleanup 1", G1CollectedHeap::heap()->phase_times()) { - bool evacuation_failed = evac_failure_regions->evacuation_failed(); + bool evac_failed = evac_failure_regions->has_regions_evac_failed(); add_serial_task(new MergePssTask(per_thread_states)); - add_serial_task(new RecalculateUsedTask(evacuation_failed)); + add_serial_task(new RecalculateUsedTask(evac_failed)); if (SampleCollectionSetCandidatesTask::should_execute()) { add_serial_task(new SampleCollectionSetCandidatesTask()); } add_parallel_task(G1CollectedHeap::heap()->rem_set()->create_cleanup_after_scan_heap_roots_task()); - if (evacuation_failed) { - add_parallel_task(new RestoreRetainedRegionsTask(evac_failure_regions)); + if (evac_failed) { + add_parallel_task(new RestoreEvacFailureRegionsTask(evac_failure_regions)); } } @@ -569,7 +569,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::ProcessEvacuationFailedRegionsTas } double worker_cost() const override { - return _evac_failure_regions->num_regions_failed_evacuation(); + return _evac_failure_regions->num_regions_evac_failed(); } void do_work(uint worker_id) override { @@ -757,10 +757,10 @@ class FreeCSetClosure : public HeapRegionClosure { G1GCPhaseTimes* p = _g1h->phase_times(); assert(r->in_collection_set(), "Failed evacuation of region %u not in collection set", r->hrm_index()); - p->record_or_add_thread_work_item(G1GCPhaseTimes::RestoreRetainedRegions, + p->record_or_add_thread_work_item(G1GCPhaseTimes::RestoreEvacuationFailedRegions, _worker_id, 1, - G1GCPhaseTimes::RestoreRetainedRegionsFailedNum); + G1GCPhaseTimes::RestoreEvacFailureRegionsEvacFailedNum); bool retain_region = _g1h->policy()->should_retain_evac_failed_region(r); // Update the region state due to the failed evacuation. @@ -844,6 +844,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1 const size_t* _surviving_young_words; uint _active_workers; G1EvacFailureRegions* _evac_failure_regions; + volatile uint _num_retained_regions; FreeCSetStats* worker_stats(uint worker) { return &_worker_stats[worker]; @@ -869,7 +870,8 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1 _claimer(0), _surviving_young_words(surviving_young_words), _active_workers(0), - _evac_failure_regions(evac_failure_regions) { + _evac_failure_regions(evac_failure_regions), + _num_retained_regions(0) { _g1h->clear_eden(); } @@ -877,10 +879,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1 virtual ~FreeCollectionSetTask() { Ticks serial_time = Ticks::now(); - G1GCPhaseTimes* p = _g1h->phase_times(); - bool has_new_retained_regions = - p->sum_thread_work_items(G1GCPhaseTimes::RestoreRetainedRegions, G1GCPhaseTimes::RestoreRetainedRegionsRetainedNum) != 0; - + bool has_new_retained_regions = Atomic::load(&_num_retained_regions) != 0; if (has_new_retained_regions) { G1CollectionSetCandidates* candidates = _g1h->collection_set()->candidates(); candidates->sort_by_efficiency(); @@ -891,7 +890,10 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1 _worker_stats[worker].~FreeCSetStats(); } FREE_C_HEAP_ARRAY(FreeCSetStats, _worker_stats); + + G1GCPhaseTimes* p = _g1h->phase_times(); p->record_serial_free_cset_time_ms((Ticks::now() - serial_time).seconds() * 1000.0); + _g1h->clear_collection_set(); } @@ -911,10 +913,8 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1 _g1h->collection_set_par_iterate_all(&cl, &_claimer, worker_id); // Report per-region type timings. cl.report_timing(); - _g1h->phase_times()->record_or_add_thread_work_item(G1GCPhaseTimes::RestoreRetainedRegions, - worker_id, - cl.num_retained_regions(), - G1GCPhaseTimes::RestoreRetainedRegionsRetainedNum); + + Atomic::add(&_num_retained_regions, cl.num_retained_regions(), memory_order_relaxed); } }; @@ -955,7 +955,7 @@ G1PostEvacuateCollectionSetCleanupTask2::G1PostEvacuateCollectionSetCleanupTask2 add_serial_task(new EagerlyReclaimHumongousObjectsTask()); } - if (evac_failure_regions->evacuation_failed()) { + if (evac_failure_regions->has_regions_evac_failed()) { add_parallel_task(new RestorePreservedMarksTask(per_thread_states->preserved_marks_set())); add_parallel_task(new ProcessEvacuationFailedRegionsTask(evac_failure_regions)); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp index 511eb1b4afbe8..868ab788b534a 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.hpp @@ -39,12 +39,12 @@ class G1ParScanThreadStateSet; // - Recalculate Used (s) // - Sample Collection Set Candidates (s) // - Clear Card Table -// - Restore retained regions (on evacuation failure) +// - Restore evac failure regions (on evacuation failure) class G1PostEvacuateCollectionSetCleanupTask1 : public G1BatchedTask { class MergePssTask; class RecalculateUsedTask; class SampleCollectionSetCandidatesTask; - class RestoreRetainedRegionsTask; + class RestoreEvacFailureRegionsTask; public: G1PostEvacuateCollectionSetCleanupTask1(G1ParScanThreadStateSet* per_thread_states, diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index 9ec0b45d22ae1..dc38047aa28f7 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -324,6 +324,10 @@ "retained region restore purposes.") \ range(1, 256) \ \ + product(uint, G1NumCollectionsKeepPinned, 8, DIAGNOSTIC, \ + "After how many GCs a region has been found pinned G1 should " \ + "give up reclaiming it.") \ + \ product(uint, G1NumCardsCostSampleThreshold, 1000, DIAGNOSTIC, \ "Threshold for the number of cards when reporting remembered set "\ "card cost related prediction samples. A sample must involve " \ diff --git a/src/hotspot/share/gc/g1/heapRegion.cpp b/src/hotspot/share/gc/g1/heapRegion.cpp index da11963317ef7..29fe5031a354a 100644 --- a/src/hotspot/share/gc/g1/heapRegion.cpp +++ b/src/hotspot/share/gc/g1/heapRegion.cpp @@ -232,7 +232,8 @@ HeapRegion::HeapRegion(uint hrm_index, _young_index_in_cset(-1), _surv_rate_group(nullptr), _age_index(G1SurvRateGroup::InvalidAgeIndex), - _node_index(G1NUMA::UnknownNodeIndex) + _node_index(G1NUMA::UnknownNodeIndex), + _pinned_object_count(0) { assert(Universe::on_page_boundary(mr.start()) && Universe::on_page_boundary(mr.end()), "invalid space boundaries"); @@ -423,6 +424,7 @@ void HeapRegion::print_on(outputStream* st) const { st->print("|-"); } } + st->print("|%3u", Atomic::load(&_pinned_object_count)); st->print_cr(""); } @@ -726,9 +728,20 @@ void HeapRegion::fill_with_dummy_object(HeapWord* address, size_t word_size, boo void HeapRegion::fill_range_with_dead_objects(HeapWord* start, HeapWord* end) { size_t range_size = pointer_delta(end, start); - // Fill the dead range with objects. G1 might need to create two objects if - // the range is larger than half a region, which is the max_fill_size(). - CollectedHeap::fill_with_objects(start, range_size); + // We must be a bit careful with regions that contain pinned objects. While the + // ranges passed in here corresponding to the space between live objects, it is + // possible that there is a pinned object that is not any more referenced by + // Java code (only by native). + // + // In this case we must not zap contents of such an array but we can overwrite + // the header; since only pinned typearrays are allowed, this fits nicely with + // putting filler arrays into the dead range as the object header sizes match and + // no user data is overwritten. + // + // In particular String Deduplication might change the reference to the character + // array of the j.l.String after native code obtained a raw reference to it (via + // GetStringCritical()). + CollectedHeap::fill_with_objects(start, range_size, !has_pinned_objects()); HeapWord* current = start; do { // Update the BOT if the a threshold is crossed. diff --git a/src/hotspot/share/gc/g1/heapRegion.hpp b/src/hotspot/share/gc/g1/heapRegion.hpp index 9d366e476b5fe..2706b814000a5 100644 --- a/src/hotspot/share/gc/g1/heapRegion.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.hpp @@ -174,6 +174,7 @@ class HeapRegion : public CHeapObj { void update_bot_for_block(HeapWord* start, HeapWord* end); + void prepare_for_full_gc(); // Update heap region that has been compacted to be consistent after Full GC. void reset_compacted_after_full_gc(HeapWord* new_top); // Update skip-compacting heap region to be consistent after Full GC. @@ -229,11 +230,17 @@ class HeapRegion : public CHeapObj { HeapWord* volatile _top_at_mark_start; // The area above this limit is fully parsable. This limit - // is equal to bottom except from Remark and until the region has been - // scrubbed concurrently. The scrubbing ensures that all dead objects (with - // possibly unloaded classes) have beenreplaced with filler objects that - // are parsable. Below this limit the marking bitmap must be used to - // determine size and liveness. + // is equal to bottom except + // + // * from Remark and until the region has been scrubbed concurrently. The + // scrubbing ensures that all dead objects (with possibly unloaded classes) + // have been replaced with filler objects that are parsable. + // * after the marking phase in the Full GC pause until the objects have been + // moved. Some (debug) code iterates over the heap after marking but before + // compaction. + // + // Below this limit the marking bitmap must be used to determine size and + // liveness. HeapWord* volatile _parsable_bottom; // Amount of dead data in the region. @@ -249,6 +256,9 @@ class HeapRegion : public CHeapObj { // NUMA node. uint _node_index; + // Number of objects in this region that are currently pinned. + volatile uint _pinned_object_count; + void report_region_type_change(G1HeapRegionTraceType::Type to); template @@ -292,6 +302,9 @@ class HeapRegion : public CHeapObj { static uint LogOfHRGrainBytes; static uint LogCardsPerRegion; + inline void increment_pinned_object_count(); + inline void decrement_pinned_object_count(); + static size_t GrainBytes; static size_t GrainWords; static size_t CardsPerRegion; @@ -395,6 +408,9 @@ class HeapRegion : public CHeapObj { bool is_old_or_humongous() const { return _type.is_old_or_humongous(); } + uint pinned_count() const { return Atomic::load(&_pinned_object_count); } + bool has_pinned_objects() const { return pinned_count() > 0; } + void set_free(); void set_eden(); diff --git a/src/hotspot/share/gc/g1/heapRegion.inline.hpp b/src/hotspot/share/gc/g1/heapRegion.inline.hpp index 625828b632e94..ec68407b546bd 100644 --- a/src/hotspot/share/gc/g1/heapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.inline.hpp @@ -167,6 +167,13 @@ inline size_t HeapRegion::block_size(const HeapWord* p, HeapWord* const pb) cons return cast_to_oop(p)->size(); } +inline void HeapRegion::prepare_for_full_gc() { + // After marking and class unloading the heap temporarily contains dead objects + // with unloaded klasses. Moving parsable_bottom makes some (debug) code correctly + // skip dead objects. + _parsable_bottom = top(); +} + inline void HeapRegion::reset_compacted_after_full_gc(HeapWord* new_top) { set_top(new_top); // After a compaction the mark bitmap in a movable region is invalid. @@ -188,7 +195,7 @@ inline void HeapRegion::reset_skip_compacting_after_full_gc() { inline void HeapRegion::reset_after_full_gc_common() { // Everything above bottom() is parsable and live. - _parsable_bottom = bottom(); + reset_parsable_bottom(); // Clear unused heap memory in debug builds. if (ZapUnusedHeapArea) { @@ -546,4 +553,12 @@ inline void HeapRegion::record_surv_words_in_group(size_t words_survived) { _surv_rate_group->record_surviving_words(age, words_survived); } +inline void HeapRegion::increment_pinned_object_count() { + Atomic::add(&_pinned_object_count, 1u, memory_order_relaxed); +} + +inline void HeapRegion::decrement_pinned_object_count() { + Atomic::sub(&_pinned_object_count, 1u, memory_order_relaxed); +} + #endif // SHARE_GC_G1_HEAPREGION_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/vmStructs_g1.hpp b/src/hotspot/share/gc/g1/vmStructs_g1.hpp index e7df5277e862a..94ade0f387c00 100644 --- a/src/hotspot/share/gc/g1/vmStructs_g1.hpp +++ b/src/hotspot/share/gc/g1/vmStructs_g1.hpp @@ -41,6 +41,7 @@ nonstatic_field(HeapRegion, _bottom, HeapWord* const) \ nonstatic_field(HeapRegion, _top, HeapWord* volatile) \ nonstatic_field(HeapRegion, _end, HeapWord* const) \ + nonstatic_field(HeapRegion, _pinned_object_count, volatile uint) \ \ nonstatic_field(HeapRegionType, _tag, HeapRegionType::Tag volatile) \ \ diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index a0bf561ab587f..2a39ce7b4700d 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -142,6 +142,11 @@ size_t MutableNUMASpace::free_in_words() const { return s; } +int MutableNUMASpace::lgrp_space_index(int lgrp_id) const { + return lgrp_spaces()->find_if([&](LGRPSpace* space) { + return space->lgrp_id() == checked_cast(lgrp_id); + }); +} size_t MutableNUMASpace::tlab_capacity(Thread *thr) const { guarantee(thr != nullptr, "No thread"); @@ -160,7 +165,7 @@ size_t MutableNUMASpace::tlab_capacity(Thread *thr) const { } } // That's the normal case, where we know the locality group of the thread. - int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + int i = lgrp_space_index(lgrp_id); if (i == -1) { return 0; } @@ -179,7 +184,7 @@ size_t MutableNUMASpace::tlab_used(Thread *thr) const { return 0; } } - int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + int i = lgrp_space_index(lgrp_id); if (i == -1) { return 0; } @@ -199,7 +204,7 @@ size_t MutableNUMASpace::unsafe_max_tlab_alloc(Thread *thr) const { return 0; } } - int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + int i = lgrp_space_index(lgrp_id); if (i == -1) { return 0; } @@ -236,20 +241,6 @@ void MutableNUMASpace::update() { SpaceDecorator::Clear, SpaceDecorator::DontMangle); } - - scan_pages(NUMAPageScanRate); -} - -// Scan pages. Free pages that have smaller size or wrong placement. -void MutableNUMASpace::scan_pages(size_t page_count) -{ - size_t pages_per_chunk = page_count / lgrp_spaces()->length(); - if (pages_per_chunk > 0) { - for (int i = 0; i < lgrp_spaces()->length(); i++) { - LGRPSpace *ls = lgrp_spaces()->at(i); - ls->scan_pages(page_size(), pages_per_chunk); - } - } } // Accumulate statistics about the allocation rate of each lgrp. @@ -583,7 +574,7 @@ HeapWord* MutableNUMASpace::cas_allocate(size_t size) { thr->set_lgrp_id(lgrp_id); } - int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + int i = lgrp_space_index(lgrp_id); // It is possible that a new CPU has been hotplugged and // we haven't reshaped the space accordingly. if (i == -1) { @@ -691,43 +682,3 @@ void MutableNUMASpace::LGRPSpace::accumulate_statistics(size_t page_size) { pointer_delta(space()->end(), end, sizeof(char)); } - -// Scan page_count pages and verify if they have the right size and right placement. -// If invalid pages are found they are freed in hope that subsequent reallocation -// will be more successful. -void MutableNUMASpace::LGRPSpace::scan_pages(size_t page_size, size_t page_count) -{ - char* range_start = (char*)align_up(space()->bottom(), page_size); - char* range_end = (char*)align_down(space()->end(), page_size); - - if (range_start > last_page_scanned() || last_page_scanned() >= range_end) { - set_last_page_scanned(range_start); - } - - char *scan_start = last_page_scanned(); - char* scan_end = MIN2(scan_start + page_size * page_count, range_end); - - os::page_info page_expected, page_found; - page_expected.size = page_size; - page_expected.lgrp_id = checked_cast(lgrp_id()); - - char *s = scan_start; - while (s < scan_end) { - char *e = os::scan_pages(s, (char*)scan_end, &page_expected, &page_found); - if (e == nullptr) { - break; - } - if (e != scan_end) { - assert(e < scan_end, "e: " PTR_FORMAT " scan_end: " PTR_FORMAT, p2i(e), p2i(scan_end)); - - if ((page_expected.size != page_size || checked_cast(page_expected.lgrp_id) != lgrp_id()) - && page_expected.size != 0) { - os::free_memory(s, pointer_delta(e, s, sizeof(char)), page_size); - } - page_expected = page_found; - } - s = e; - } - - set_last_page_scanned(scan_end); -} diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp index 721ad35b8bcf0..77ecb4da46671 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp @@ -83,11 +83,8 @@ class MutableNUMASpace : public MutableSpace { SpaceStats _space_stats; - char* _last_page_scanned; - char* last_page_scanned() { return _last_page_scanned; } - void set_last_page_scanned(char* p) { _last_page_scanned = p; } public: - LGRPSpace(uint l, size_t alignment) : _lgrp_id(l), _allocation_failed(false), _last_page_scanned(nullptr) { + LGRPSpace(uint l, size_t alignment) : _lgrp_id(l), _allocation_failed(false) { _space = new MutableSpace(alignment); _alloc_rate = new AdaptiveWeightedAverage(NUMAChunkResizeWeight); } @@ -96,10 +93,6 @@ class MutableNUMASpace : public MutableSpace { delete _alloc_rate; } - static bool equals(void* lgrp_id_value, LGRPSpace* p) { - return *(uint*)lgrp_id_value == p->lgrp_id(); - } - // Report a failed allocation. void set_allocation_failed() { _allocation_failed = true; } @@ -125,7 +118,6 @@ class MutableNUMASpace : public MutableSpace { void clear_space_stats() { _space_stats = SpaceStats(); } void accumulate_statistics(size_t page_size); - void scan_pages(size_t page_size, size_t page_count); }; GrowableArray* _lgrp_spaces; @@ -156,14 +148,14 @@ class MutableNUMASpace : public MutableSpace { size_t default_chunk_size(); // Adapt the chunk size to follow the allocation rate. size_t adaptive_chunk_size(int i, size_t limit); - // Scan and free invalid pages. - void scan_pages(size_t page_count); // Return the bottom_region and the top_region. Align them to page_size() boundary. // |------------------new_region---------------------------------| // |----bottom_region--|---intersection---|------top_region------| void select_tails(MemRegion new_region, MemRegion intersection, MemRegion* bottom_region, MemRegion *top_region); + int lgrp_space_index(int lgrp_id) const; + public: GrowableArray* lgrp_spaces() const { return _lgrp_spaces; } MutableNUMASpace(size_t alignment); diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index 34d6621c82073..3e255c15a4e0a 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -169,22 +169,6 @@ void PSAdaptiveSizePolicy::major_collection_end(size_t amount_live, _major_timer.start(); } -// If the remaining free space in the old generation is less that -// that expected to be needed by the next collection, do a full -// collection now. -bool PSAdaptiveSizePolicy::should_full_GC(size_t old_free_in_bytes) { - - // A similar test is done in the scavenge's should_attempt_scavenge(). If - // this is changed, decide if that test should also be changed. - bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes; - log_trace(gc, ergo)("%s after scavenge average_promoted " SIZE_FORMAT " padded_average_promoted " SIZE_FORMAT " free in old gen " SIZE_FORMAT, - result ? "Full" : "No full", - (size_t) average_promoted_in_bytes(), - (size_t) padded_average_promoted_in_bytes(), - old_free_in_bytes); - return result; -} - void PSAdaptiveSizePolicy::clear_generation_free_space_flags() { AdaptiveSizePolicy::clear_generation_free_space_flags(); diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp index fb5c30f824ad1..580ff9d06c005 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp @@ -306,10 +306,6 @@ class PSAdaptiveSizePolicy : public AdaptiveSizePolicy { } double major_collection_slope() { return _major_collection_estimator->slope();} - // Given the amount of live data in the heap, should we - // perform a Full GC? - bool should_full_GC(size_t live_in_old_gen); - // Calculates optimal (free) space sizes for both the young and old // generations. Stores results in _eden_size and _promo_size. // Takes current used space in all generations as input, as well diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index 2f114e1e2cecc..a783dfe7155bb 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -239,8 +239,7 @@ bool PSScavenge::invoke() { IsGCActiveMark mark; const bool scavenge_done = PSScavenge::invoke_no_policy(); - const bool need_full_gc = !scavenge_done || - policy->should_full_GC(heap->old_gen()->free_in_bytes()); + const bool need_full_gc = !scavenge_done; bool full_gc_done = false; if (UsePerfData) { @@ -703,8 +702,6 @@ bool PSScavenge::should_attempt_scavenge() { // Test to see if the scavenge will likely fail. PSAdaptiveSizePolicy* policy = heap->size_policy(); - // A similar test is done in the policy's should_full_GC(). If this is - // changed, decide if that test should also be changed. size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes(); size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes()); bool result = promotion_estimate < old_gen->free_in_bytes(); diff --git a/src/hotspot/share/gc/serial/cardTableRS.cpp b/src/hotspot/share/gc/serial/cardTableRS.cpp index e6682854b10af..375860c9a1f01 100644 --- a/src/hotspot/share/gc/serial/cardTableRS.cpp +++ b/src/hotspot/share/gc/serial/cardTableRS.cpp @@ -28,273 +28,17 @@ #include "gc/serial/generation.hpp" #include "gc/serial/serialHeap.hpp" #include "gc/shared/space.inline.hpp" -#include "memory/allocation.inline.hpp" #include "memory/iterator.inline.hpp" -#include "oops/access.inline.hpp" -#include "oops/oop.inline.hpp" -#include "runtime/atomic.hpp" -#include "runtime/java.hpp" -#include "runtime/os.hpp" -#include "utilities/macros.hpp" - -// A dirty card to oop closure for contiguous spaces (ContiguousSpace and -// sub-classes). It knows how to filter out objects that are outside of the -// _boundary. -// (Note that because of the imprecise nature of the write barrier, this may -// iterate over oops beyond the region.) -// -// Assumptions: -// 1. That the actual top of any area in a memory region -// contained by the space is bounded by the end of the contiguous -// region of the space. -// 2. That the space is really made up of objects and not just -// blocks. - -class DirtyCardToOopClosure: public MemRegionClosure { -protected: - OopIterateClosure* _cl; - TenuredSpace* _sp; - HeapWord* _min_done; // Need a downwards traversal to compensate - // imprecise write barrier; this is the - // lowest location already done (or, - // alternatively, the lowest address that - // shouldn't be done again. null means infinity.) - NOT_PRODUCT(HeapWord* _last_bottom;) - - // Get the actual top of the area on which the closure will - // operate, given where the top is assumed to be (the end of the - // memory region passed to do_MemRegion) and where the object - // at the top is assumed to start. For example, an object may - // start at the top but actually extend past the assumed top, - // in which case the top becomes the end of the object. - HeapWord* get_actual_top(HeapWord* top, HeapWord* top_obj); - - // Walk the given memory region from bottom to (actual) top - // looking for objects and applying the oop closure (_cl) to - // them. The base implementation of this treats the area as - // blocks, where a block may or may not be an object. Sub- - // classes should override this to provide more accurate - // or possibly more efficient walking. - void walk_mem_region(MemRegion mr, HeapWord* bottom, HeapWord* top); - - // Walk the given memory region, from bottom to top, applying - // the given oop closure to (possibly) all objects found. The - // given oop closure may or may not be the same as the oop - // closure with which this closure was created, as it may - // be a filtering closure which makes use of the _boundary. - // We offer two signatures, so the FilteringClosure static type is - // apparent. - void walk_mem_region_with_cl(MemRegion mr, - HeapWord* bottom, HeapWord* top, - OopIterateClosure* cl); -public: - DirtyCardToOopClosure(TenuredSpace* sp, OopIterateClosure* cl) : - _cl(cl), _sp(sp), _min_done(nullptr) { - NOT_PRODUCT(_last_bottom = nullptr); - } - - void do_MemRegion(MemRegion mr) override; -}; - -HeapWord* DirtyCardToOopClosure::get_actual_top(HeapWord* top, - HeapWord* top_obj) { - if (top_obj != nullptr && top_obj < _sp->top()) { - if (cast_to_oop(top_obj)->is_objArray() || cast_to_oop(top_obj)->is_typeArray()) { - // An arrayOop is starting on the dirty card - since we do exact - // store checks for objArrays we are done. - } else { - // Otherwise, it is possible that the object starting on the dirty - // card spans the entire card, and that the store happened on a - // later card. Figure out where the object ends. - top = top_obj + cast_to_oop(top_obj)->size(); - } - } else { - top = _sp->top(); - } - return top; -} - -void DirtyCardToOopClosure::walk_mem_region(MemRegion mr, - HeapWord* bottom, - HeapWord* top) { - // Note that this assumption won't hold if we have a concurrent - // collector in this space, which may have freed up objects after - // they were dirtied and before the stop-the-world GC that is - // examining cards here. - assert(bottom < top, "ought to be at least one obj on a dirty card."); - - walk_mem_region_with_cl(mr, bottom, top, _cl); -} - -// We get called with "mr" representing the dirty region -// that we want to process. Because of imprecise marking, -// we may need to extend the incoming "mr" to the right, -// and scan more. However, because we may already have -// scanned some of that extended region, we may need to -// trim its right-end back some so we do not scan what -// we (or another worker thread) may already have scanned -// or planning to scan. -void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) { - HeapWord* bottom = mr.start(); - HeapWord* last = mr.last(); - HeapWord* top = mr.end(); - HeapWord* bottom_obj; - HeapWord* top_obj; - - assert(_last_bottom == nullptr || top <= _last_bottom, - "Not decreasing"); - NOT_PRODUCT(_last_bottom = mr.start()); - - bottom_obj = _sp->block_start(bottom); - top_obj = _sp->block_start(last); - - assert(bottom_obj <= bottom, "just checking"); - assert(top_obj <= top, "just checking"); - - // Given what we think is the top of the memory region and - // the start of the object at the top, get the actual - // value of the top. - top = get_actual_top(top, top_obj); - - // If the previous call did some part of this region, don't redo. - if (_min_done != nullptr && _min_done < top) { - top = _min_done; - } - - // Top may have been reset, and in fact may be below bottom, - // e.g. the dirty card region is entirely in a now free object - // -- something that could happen with a concurrent sweeper. - bottom = MIN2(bottom, top); - MemRegion extended_mr = MemRegion(bottom, top); - assert(bottom <= top && - (_min_done == nullptr || top <= _min_done), - "overlap!"); - - // Walk the region if it is not empty; otherwise there is nothing to do. - if (!extended_mr.is_empty()) { - walk_mem_region(extended_mr, bottom_obj, top); - } - - _min_done = bottom; -} - -void DirtyCardToOopClosure::walk_mem_region_with_cl(MemRegion mr, - HeapWord* bottom, - HeapWord* top, - OopIterateClosure* cl) { - bottom += cast_to_oop(bottom)->oop_iterate_size(cl, mr); - if (bottom < top) { - HeapWord* next_obj = bottom + cast_to_oop(bottom)->size(); - while (next_obj < top) { - /* Bottom lies entirely below top, so we can call the */ - /* non-memRegion version of oop_iterate below. */ - cast_to_oop(bottom)->oop_iterate(cl); - bottom = next_obj; - next_obj = bottom + cast_to_oop(bottom)->size(); - } - /* Last object. */ - cast_to_oop(bottom)->oop_iterate(cl, mr); - } -} - -class ClearNoncleanCardWrapper: public MemRegionClosure { - DirtyCardToOopClosure* _dirty_card_closure; - CardTableRS* _ct; - -public: - - typedef CardTable::CardValue CardValue; -private: - // Clears the given card, return true if the corresponding card should be - // processed. - inline bool clear_card(CardValue* entry); - // check alignment of pointer - bool is_word_aligned(CardValue* entry); - -public: - ClearNoncleanCardWrapper(DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct); - void do_MemRegion(MemRegion mr) override; -}; - -inline bool ClearNoncleanCardWrapper::clear_card(CardValue* entry) { - assert(*entry == CardTableRS::dirty_card_val(), "Only look at dirty cards."); - *entry = CardTableRS::clean_card_val(); - return true; -} - -ClearNoncleanCardWrapper::ClearNoncleanCardWrapper( - DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct) : - _dirty_card_closure(dirty_card_closure), _ct(ct) { -} - -bool ClearNoncleanCardWrapper::is_word_aligned(CardTable::CardValue* entry) { - return (((intptr_t)entry) & (BytesPerWord-1)) == 0; -} - -// The regions are visited in *decreasing* address order. -// This order aids with imprecise card marking, where a dirty -// card may cause scanning, and summarization marking, of objects -// that extend onto subsequent cards. -void ClearNoncleanCardWrapper::do_MemRegion(MemRegion mr) { - assert(mr.word_size() > 0, "Error"); - assert(_ct->is_aligned(mr.start()), "mr.start() should be card aligned"); - // mr.end() may not necessarily be card aligned. - CardValue* cur_entry = _ct->byte_for(mr.last()); - const CardValue* limit = _ct->byte_for(mr.start()); - HeapWord* end_of_non_clean = mr.end(); - HeapWord* start_of_non_clean = end_of_non_clean; - while (cur_entry >= limit) { - HeapWord* cur_hw = _ct->addr_for(cur_entry); - if ((*cur_entry != CardTableRS::clean_card_val()) && clear_card(cur_entry)) { - // Continue the dirty range by opening the - // dirty window one card to the left. - start_of_non_clean = cur_hw; - } else { - // We hit a "clean" card; process any non-empty - // "dirty" range accumulated so far. - if (start_of_non_clean < end_of_non_clean) { - const MemRegion mrd(start_of_non_clean, end_of_non_clean); - _dirty_card_closure->do_MemRegion(mrd); - } - - // fast forward through potential continuous whole-word range of clean cards beginning at a word-boundary - if (is_word_aligned(cur_entry)) { - CardValue* cur_row = cur_entry - BytesPerWord; - while (cur_row >= limit && *((intptr_t*)cur_row) == CardTableRS::clean_card_row_val()) { - cur_row -= BytesPerWord; - } - cur_entry = cur_row + BytesPerWord; - cur_hw = _ct->addr_for(cur_entry); - } - - // Reset the dirty window, while continuing to look - // for the next dirty card that will start a - // new dirty window. - end_of_non_clean = cur_hw; - start_of_non_clean = cur_hw; - } - // Note that "cur_entry" leads "start_of_non_clean" in - // its leftward excursion after this point - // in the loop and, when we hit the left end of "mr", - // will point off of the left end of the card-table - // for "mr". - cur_entry--; - } - // If the first card of "mr" was dirty, we will have - // been left with a dirty window, co-initial with "mr", - // which we now process. - if (start_of_non_clean < end_of_non_clean) { - const MemRegion mrd(start_of_non_clean, end_of_non_clean); - _dirty_card_closure->do_MemRegion(mrd); - } -} +#include "utilities/align.hpp" void CardTableRS::younger_refs_in_space_iterate(TenuredSpace* sp, OopIterateClosure* cl) { verify_used_region_at_save_marks(sp); const MemRegion urasm = sp->used_region_at_save_marks(); - non_clean_card_iterate(sp, urasm, cl, this); + if (!urasm.is_empty()) { + non_clean_card_iterate(sp, urasm, cl, this); + } } #ifdef ASSERT @@ -601,20 +345,185 @@ void CardTableRS::verify() { CardTableRS::CardTableRS(MemRegion whole_heap) : CardTable(whole_heap) { } +// Implemented word-iteration to skip long consecutive clean cards. +CardTable::CardValue* CardTableRS::find_first_dirty_card(CardValue* const start_card, + CardValue* const end_card) { + using Word = uintptr_t; + + CardValue* current_card = start_card; + + while (!is_aligned(current_card, sizeof(Word))) { + if (current_card >= end_card) { + return end_card; + } + if (is_dirty(current_card)) { + return current_card; + } + ++current_card; + } + + // Word comparison + while (current_card + sizeof(Word) <= end_card) { + Word* current_word = reinterpret_cast(current_card); + if (*current_word != (Word)clean_card_row_val()) { + // Found a dirty card in this word; fall back to per-CardValue comparison. + break; + } + current_card += sizeof(Word); + } + + // Per-CardValue comparison. + for (/* empty */; current_card < end_card; ++current_card) { + if (is_dirty(current_card)) { + return current_card; + } + } + + return end_card; +} + +// Because non-objArray objs can be imprecisely-marked (only obj-start card is +// dirty instead of the part containing old-to-young pointers), if the +// obj-start of a non-objArray is dirty, all cards that obj completely resides +// on are considered as dirty, since that obj will be iterated (scanned for +// old-to-young pointers) as a whole. +template +CardTable::CardValue* CardTableRS::find_first_clean_card(CardValue* const start_card, + CardValue* const end_card, + CardTableRS* ct, + Func& object_start) { + for (CardValue* current_card = start_card; current_card < end_card; /* empty */) { + if (is_dirty(current_card)) { + current_card++; + continue; + } + + // A potential candidate. + HeapWord* addr = ct->addr_for(current_card); + HeapWord* obj_start_addr = object_start(addr); + + if (obj_start_addr == addr) { + return current_card; + } + + // Final obj in dirty-chunk crosses card-boundary. + oop obj = cast_to_oop(obj_start_addr); + if (obj->is_objArray()) { + // ObjArrays are always precisely-marked so we are not allowed to jump to + // the end of the current object. + return current_card; + } + + // Card occupied by next obj. + CardValue* next_obj_card = ct->byte_for(obj_start_addr + obj->size()); + if (is_clean(next_obj_card)) { + return next_obj_card; + } + + // Continue the search after this known-dirty card... + current_card = next_obj_card + 1; + } + + return end_card; +} + +void CardTableRS::clear_cards(CardValue* start, CardValue* end) { + size_t num_cards = pointer_delta(end, start, sizeof(CardValue)); + memset(start, clean_card_val(), num_cards); +} + +static void prefetch_write(void *p) { + if (PrefetchScanIntervalInBytes >= 0) { + Prefetch::write(p, PrefetchScanIntervalInBytes); + } +} + +static void scan_obj_with_limit(oop obj, + OopIterateClosure* cl, + HeapWord* start, + HeapWord* end) { + if (!obj->is_typeArray()) { + prefetch_write(start); + obj->oop_iterate(cl, MemRegion(start, end)); + } +} + void CardTableRS::non_clean_card_iterate(TenuredSpace* sp, MemRegion mr, OopIterateClosure* cl, - CardTableRS* ct) -{ - if (mr.is_empty()) { - return; - } - // clear_cl finds contiguous dirty ranges of cards to process and clear. + CardTableRS* ct) { + struct { + HeapWord* start_addr; + HeapWord* end_addr; + } cached_obj { nullptr, mr.start() }; + + auto object_start = [&] (const HeapWord* const addr) { + if (addr < cached_obj.end_addr) { + assert(cached_obj.start_addr != nullptr, "inv"); + return cached_obj.start_addr; + } + HeapWord* result = sp->block_start_const(addr); + + cached_obj.start_addr = result; + cached_obj.end_addr = result + cast_to_oop(result)->size(); + + return result; + }; + + CardValue* const start_card = ct->byte_for(mr.start()); + CardValue* const end_card = ct->byte_for(mr.last()) + 1; + + // if mr.end() is not card-aligned, that final card should not be cleared + // because it can be annotated dirty due to old-to-young pointers in + // newly-promoted objs on that card. + CardValue* const clear_limit_card = is_card_aligned(mr.end()) ? end_card - 1 + : end_card - 2; + + for (CardValue* current_card = start_card; current_card < end_card; /* empty */) { + CardValue* const dirty_l = find_first_dirty_card(current_card, end_card); - DirtyCardToOopClosure dcto_cl{sp, cl}; - ClearNoncleanCardWrapper clear_cl(&dcto_cl, ct); + if (dirty_l == end_card) { + // No dirty cards to iterate. + return; + } + + HeapWord* const addr_l = ct->addr_for(dirty_l); + HeapWord* obj_addr = object_start(addr_l); + + CardValue* const dirty_r = find_first_clean_card(dirty_l + 1, + end_card, + ct, + object_start); + assert(dirty_l < dirty_r, "inv"); + HeapWord* const addr_r = dirty_r == end_card ? mr.end() + : ct->addr_for(dirty_r); + + clear_cards(MIN2(dirty_l, clear_limit_card), + MIN2(dirty_r, clear_limit_card)); + + while (true) { + assert(obj_addr < addr_r, "inv"); + + oop obj = cast_to_oop(obj_addr); + const bool is_obj_array = obj->is_objArray(); + HeapWord* const obj_end_addr = obj_addr + obj->size(); - clear_cl.do_MemRegion(mr); + if (is_obj_array) { + // ObjArrays are always precise-marked. + scan_obj_with_limit(obj, cl, addr_l, addr_r); + } else { + scan_obj_with_limit(obj, cl, addr_l, obj_end_addr); + } + + if (obj_end_addr >= addr_r) { + current_card = dirty_r + 1; + break; + } + + // Move to next obj inside this dirty chunk. + obj_addr = obj_end_addr; + } + } } bool CardTableRS::is_in_young(const void* p) const { diff --git a/src/hotspot/share/gc/serial/cardTableRS.hpp b/src/hotspot/share/gc/serial/cardTableRS.hpp index 822914fcf79f6..a4f333803a0ce 100644 --- a/src/hotspot/share/gc/serial/cardTableRS.hpp +++ b/src/hotspot/share/gc/serial/cardTableRS.hpp @@ -29,7 +29,6 @@ #include "memory/memRegion.hpp" #include "oops/oop.hpp" -class DirtyCardToOopClosure; class Generation; class Space; class TenuredSpace; @@ -41,10 +40,28 @@ class CardTableRS : public CardTable { friend class VMStructs; // Below are private classes used in impl. friend class VerifyCTSpaceClosure; - friend class ClearNoncleanCardWrapper; void verify_space(Space* s, HeapWord* gen_start); + static bool is_dirty(const CardValue* const v) { + return !is_clean(v); + } + + static bool is_clean(const CardValue* const v) { + return *v == clean_card_val(); + } + + static void clear_cards(CardValue* start, CardValue* end); + + static CardValue* find_first_dirty_card(CardValue* start_card, + CardValue* end_card); + + template + CardValue* find_first_clean_card(CardValue* start_card, + CardValue* end_card, + CardTableRS* ct, + Func& object_start); + public: CardTableRS(MemRegion whole_heap); @@ -57,10 +74,6 @@ class CardTableRS : public CardTable { *byte = dirty_card_val(); } - bool is_aligned(HeapWord* addr) { - return is_card_aligned(addr); - } - void verify(); // Update old gen cards to maintain old-to-young-pointer invariant: Clear diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index 3a012b639fde0..b569fbac48f0f 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -40,7 +40,6 @@ #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcTraceTime.inline.hpp" -#include "gc/shared/generationSpec.hpp" #include "gc/shared/preservedMarks.inline.hpp" #include "gc/shared/referencePolicy.hpp" #include "gc/shared/referenceProcessorPhaseTimes.hpp" @@ -560,7 +559,7 @@ void DefNewGeneration::compute_new_size() { size_t old_size = gch->old_gen()->capacity(); size_t new_size_before = _virtual_space.committed_size(); - size_t min_new_size = initial_size(); + size_t min_new_size = NewSize; size_t max_new_size = reserved().byte_size(); assert(min_new_size <= new_size_before && new_size_before <= max_new_size, diff --git a/src/hotspot/share/gc/serial/generation.cpp b/src/hotspot/share/gc/serial/generation.cpp index daad60f406d4e..aa3c804ba3c20 100644 --- a/src/hotspot/share/gc/serial/generation.cpp +++ b/src/hotspot/share/gc/serial/generation.cpp @@ -31,7 +31,6 @@ #include "gc/shared/gcLocker.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" -#include "gc/shared/generationSpec.hpp" #include "gc/shared/space.inline.hpp" #include "gc/shared/spaceDecorator.inline.hpp" #include "logging/log.hpp" @@ -57,14 +56,6 @@ Generation::Generation(ReservedSpace rs, size_t initial_size) : (HeapWord*)_virtual_space.high_boundary()); } -size_t Generation::initial_size() { - SerialHeap* serial_heap = SerialHeap::heap(); - if (serial_heap->is_young_gen(this)) { - return serial_heap->young_gen_spec()->init_size(); - } - return serial_heap->old_gen_spec()->init_size(); -} - size_t Generation::max_capacity() const { return reserved().byte_size(); } diff --git a/src/hotspot/share/gc/serial/generation.hpp b/src/hotspot/share/gc/serial/generation.hpp index 54e9d188a5bb2..63eefae720bbb 100644 --- a/src/hotspot/share/gc/serial/generation.hpp +++ b/src/hotspot/share/gc/serial/generation.hpp @@ -50,7 +50,6 @@ class DefNewGeneration; class GCMemoryManager; -class GenerationSpec; class ContiguousSpace; class CompactPoint; class OopClosure; @@ -113,8 +112,6 @@ class Generation: public CHeapObj { virtual Generation::Name kind() { return Generation::Other; } - // Space inquiries (results in bytes) - size_t initial_size(); virtual size_t capacity() const = 0; // The maximum number of object bytes the // generation can currently hold. virtual size_t used() const = 0; // The number of used bytes in the gen. diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index f42973691f342..981322eb14140 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp @@ -32,7 +32,6 @@ #include "gc/shared/gcLocker.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" -#include "gc/shared/generationSpec.hpp" #include "gc/shared/space.hpp" #include "logging/log.hpp" #include "memory/allocation.inline.hpp" @@ -165,7 +164,7 @@ void TenuredGeneration::compute_new_size_inner() { const double min_tmp = used_after_gc / maximum_used_percentage; size_t minimum_desired_capacity = (size_t)MIN2(min_tmp, double(max_uintx)); // Don't shrink less than the initial generation size - minimum_desired_capacity = MAX2(minimum_desired_capacity, initial_size()); + minimum_desired_capacity = MAX2(minimum_desired_capacity, OldSize); assert(used_after_gc <= minimum_desired_capacity, "sanity check"); const size_t free_after_gc = free(); @@ -204,7 +203,7 @@ void TenuredGeneration::compute_new_size_inner() { const double minimum_used_percentage = 1.0 - maximum_free_percentage; const double max_tmp = used_after_gc / minimum_used_percentage; size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx)); - maximum_desired_capacity = MAX2(maximum_desired_capacity, initial_size()); + maximum_desired_capacity = MAX2(maximum_desired_capacity, OldSize); log_trace(gc, heap)(" maximum_free_percentage: %6.2f minimum_used_percentage: %6.2f", maximum_free_percentage, minimum_used_percentage); log_trace(gc, heap)(" _capacity_at_prologue: %6.1fK minimum_desired_capacity: %6.1fK maximum_desired_capacity: %6.1fK", @@ -234,7 +233,7 @@ void TenuredGeneration::compute_new_size_inner() { } assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size"); log_trace(gc, heap)(" shrinking: initSize: %.1fK maximum_desired_capacity: %.1fK", - initial_size() / (double) K, maximum_desired_capacity / (double) K); + OldSize / (double) K, maximum_desired_capacity / (double) K); log_trace(gc, heap)(" shrink_bytes: %.1fK current_shrink_factor: " SIZE_FORMAT " new shrink factor: " SIZE_FORMAT " _min_heap_delta_bytes: %.1fK", shrink_bytes / (double) K, current_shrink_factor, @@ -304,12 +303,8 @@ TenuredGeneration::TenuredGeneration(ReservedSpace rs, // If this wasn't true, a single card could span more than on generation, // which would cause problems when we commit/uncommit memory, and when we // clear and dirty cards. - guarantee(_rs->is_aligned(reserved_mr.start()), "generation must be card aligned"); - if (reserved_mr.end() != SerialHeap::heap()->reserved_region().end()) { - // Don't check at the very end of the heap as we'll assert that we're probing off - // the end if we try. - guarantee(_rs->is_aligned(reserved_mr.end()), "generation must be card aligned"); - } + guarantee(_rs->is_card_aligned(reserved_mr.start()), "generation must be card aligned"); + guarantee(_rs->is_card_aligned(reserved_mr.end()), "generation must be card aligned"); _min_heap_delta_bytes = MinHeapDeltaBytes; _capacity_at_prologue = initial_byte_size; _used_at_prologue = 0; diff --git a/src/hotspot/share/gc/shared/cardTable.hpp b/src/hotspot/share/gc/shared/cardTable.hpp index f66c5b7c5087d..dfa2fe39ccb77 100644 --- a/src/hotspot/share/gc/shared/cardTable.hpp +++ b/src/hotspot/share/gc/shared/cardTable.hpp @@ -112,8 +112,7 @@ class CardTable: public CHeapObj { // Return true if "p" is at the start of a card. bool is_card_aligned(HeapWord* p) { - CardValue* pcard = byte_for(p); - return (addr_for(pcard) == p); + return is_aligned(p, card_size()); } // Mapping from address to card marking array entry @@ -196,7 +195,7 @@ class CardTable: public CHeapObj { static constexpr CardValue clean_card_val() { return clean_card; } static constexpr CardValue dirty_card_val() { return dirty_card; } - static intptr_t clean_card_row_val() { return clean_card_row; } + static constexpr intptr_t clean_card_row_val() { return clean_card_row; } // Initialize card size static void initialize_card_size(); diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 19923ca9bc413..cccc7b168832a 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -165,8 +165,10 @@ class CollectedHeap : public CHeapObj { // Filler object utilities. static inline size_t filler_array_hdr_size(); - static inline size_t filler_array_min_size(); + static size_t filler_array_min_size(); + +protected: static inline void zap_filler_array_with(HeapWord* start, size_t words, juint value); DEBUG_ONLY(static void fill_args_check(HeapWord* start, size_t words);) DEBUG_ONLY(static void zap_filler_array(HeapWord* start, size_t words, bool zap = true);) diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.cpp b/src/hotspot/share/gc/shared/genCollectedHeap.cpp index 610c259f5299a..54946d75bc9de 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp @@ -34,6 +34,7 @@ #include "gc/serial/defNewGeneration.hpp" #include "gc/serial/genMarkSweep.hpp" #include "gc/serial/markSweep.hpp" +#include "gc/serial/tenuredGeneration.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/collectedHeap.inline.hpp" #include "gc/shared/collectorCounters.hpp" @@ -47,7 +48,6 @@ #include "gc/shared/gcVMOperations.hpp" #include "gc/shared/genArguments.hpp" #include "gc/shared/genCollectedHeap.hpp" -#include "gc/shared/generationSpec.hpp" #include "gc/shared/locationPrinter.inline.hpp" #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageParState.inline.hpp" @@ -85,14 +85,6 @@ GenCollectedHeap::GenCollectedHeap(Generation::Name young, CollectedHeap(), _young_gen(nullptr), _old_gen(nullptr), - _young_gen_spec(new GenerationSpec(young, - NewSize, - MaxNewSize, - GenAlignment)), - _old_gen_spec(new GenerationSpec(old, - OldSize, - MaxOldSize, - GenAlignment)), _rem_set(nullptr), _soft_ref_policy(), _gc_policy_counters(new GCPolicyCounters(policy_counters_name, 2, 2)), @@ -115,8 +107,8 @@ jint GenCollectedHeap::initialize() { initialize_reserved_region(heap_rs); - ReservedSpace young_rs = heap_rs.first_part(_young_gen_spec->max_size()); - ReservedSpace old_rs = heap_rs.last_part(_young_gen_spec->max_size()); + ReservedSpace young_rs = heap_rs.first_part(MaxNewSize); + ReservedSpace old_rs = heap_rs.last_part(MaxNewSize); _rem_set = create_rem_set(heap_rs.region()); _rem_set->initialize(young_rs.base(), old_rs.base()); @@ -125,8 +117,8 @@ jint GenCollectedHeap::initialize() { bs->initialize(); BarrierSet::set_barrier_set(bs); - _young_gen = _young_gen_spec->init(young_rs, rem_set()); - _old_gen = _old_gen_spec->init(old_rs, rem_set()); + _young_gen = new DefNewGeneration(young_rs, NewSize, MinNewSize, MaxNewSize); + _old_gen = new TenuredGeneration(old_rs, OldSize, MinOldSize, MaxOldSize, rem_set()); GCInitLogger::print(); @@ -143,8 +135,8 @@ ReservedHeapSpace GenCollectedHeap::allocate(size_t alignment) { assert(alignment % pageSize == 0, "Must be"); // Check for overflow. - size_t total_reserved = _young_gen_spec->max_size() + _old_gen_spec->max_size(); - if (total_reserved < _young_gen_spec->max_size()) { + size_t total_reserved = MaxNewSize + MaxOldSize; + if (total_reserved < MaxNewSize) { vm_exit_during_initialization("The size of the object heap + VM data exceeds " "the maximum representable size"); } @@ -199,14 +191,6 @@ PreGenGCValues GenCollectedHeap::get_pre_gc_values() const { old_gen()->capacity()); } -GenerationSpec* GenCollectedHeap::young_gen_spec() const { - return _young_gen_spec; -} - -GenerationSpec* GenCollectedHeap::old_gen_spec() const { - return _old_gen_spec; -} - size_t GenCollectedHeap::capacity() const { return _young_gen->capacity() + _old_gen->capacity(); } diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.hpp b/src/hotspot/share/gc/shared/genCollectedHeap.hpp index bdeeede64b820..0f4f95f90f7c8 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp @@ -33,7 +33,6 @@ class CardTableRS; class GCPolicyCounters; -class GenerationSpec; // A "GenCollectedHeap" is a CollectedHeap that uses generational // collection. It has two generations, young and old. @@ -62,9 +61,6 @@ class GenCollectedHeap : public CollectedHeap { Generation* _old_gen; private: - GenerationSpec* _young_gen_spec; - GenerationSpec* _old_gen_spec; - // The singleton CardTable Remembered Set. CardTableRS* _rem_set; @@ -144,9 +140,6 @@ class GenCollectedHeap : public CollectedHeap { MemRegion reserved_region() const { return _reserved; } bool is_in_reserved(const void* addr) const { return _reserved.contains(addr); } - GenerationSpec* young_gen_spec() const; - GenerationSpec* old_gen_spec() const; - SoftRefPolicy* soft_ref_policy() override { return &_soft_ref_policy; } // Performance Counter support diff --git a/src/hotspot/share/gc/shared/generationSpec.hpp b/src/hotspot/share/gc/shared/generationSpec.hpp deleted file mode 100644 index 662e24d4e0413..0000000000000 --- a/src/hotspot/share/gc/shared/generationSpec.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2001, 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_GC_SHARED_GENERATIONSPEC_HPP -#define SHARE_GC_SHARED_GENERATIONSPEC_HPP - -#include "gc/serial/generation.hpp" -#include "utilities/align.hpp" - -// The specification of a generation. This class also encapsulates -// some generation-specific behavior. This is done here rather than as a -// virtual function of Generation because these methods are needed in -// initialization of the Generations. -class GenerationSpec : public CHeapObj { - friend class VMStructs; -private: - Generation::Name _name; - size_t _init_size; - size_t _min_size; - size_t _max_size; - -public: - GenerationSpec(Generation::Name name, size_t init_size, size_t max_size, size_t alignment) : - _name(name), - _init_size(align_up(init_size, alignment)), - _min_size(_init_size), - _max_size(align_up(max_size, alignment)) - { } - - Generation* init(ReservedSpace rs, CardTableRS* remset); - - Generation::Name name() const { return _name; } - size_t init_size() const { return _init_size; } - size_t min_size() const { return _min_size; } - size_t max_size() const { return _max_size; } -}; - -typedef GenerationSpec* GenerationSpecPtr; - -#endif // SHARE_GC_SHARED_GENERATIONSPEC_HPP diff --git a/src/hotspot/share/gc/shared/vmStructs_gc.hpp b/src/hotspot/share/gc/shared/vmStructs_gc.hpp index 13f23d5d4394f..696cdc00dc520 100644 --- a/src/hotspot/share/gc/shared/vmStructs_gc.hpp +++ b/src/hotspot/share/gc/shared/vmStructs_gc.hpp @@ -29,7 +29,6 @@ #include "gc/shared/cardTable.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/genCollectedHeap.hpp" -#include "gc/shared/generationSpec.hpp" #include "gc/shared/oopStorage.hpp" #include "gc/shared/space.hpp" #if INCLUDE_EPSILONGC @@ -114,14 +113,8 @@ nonstatic_field(Generation::StatRecord, invocations, int) \ nonstatic_field(Generation::StatRecord, accumulated_time, elapsedTimer) \ \ - nonstatic_field(GenerationSpec, _name, Generation::Name) \ - nonstatic_field(GenerationSpec, _init_size, size_t) \ - nonstatic_field(GenerationSpec, _max_size, size_t) \ - \ nonstatic_field(GenCollectedHeap, _young_gen, Generation*) \ nonstatic_field(GenCollectedHeap, _old_gen, Generation*) \ - nonstatic_field(GenCollectedHeap, _young_gen_spec, GenerationSpec*) \ - nonstatic_field(GenCollectedHeap, _old_gen_spec, GenerationSpec*) \ \ nonstatic_field(MemRegion, _start, HeapWord*) \ nonstatic_field(MemRegion, _word_size, size_t) \ @@ -172,7 +165,6 @@ declare_toplevel_type(AgeTable) \ declare_toplevel_type(CardTable::CardValue) \ declare_toplevel_type(Generation::StatRecord) \ - declare_toplevel_type(GenerationSpec) \ declare_toplevel_type(HeapWord) \ declare_toplevel_type(MemRegion) \ declare_toplevel_type(ThreadLocalAllocBuffer) \ @@ -190,7 +182,6 @@ declare_toplevel_type(DefNewGeneration*) \ declare_toplevel_type(GenCollectedHeap*) \ declare_toplevel_type(Generation*) \ - declare_toplevel_type(GenerationSpec**) \ declare_toplevel_type(HeapWord*) \ declare_toplevel_type(HeapWord* volatile) \ declare_toplevel_type(MemRegion*) \ diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 4e041eaffe20c..07dd900d989fe 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -1392,11 +1392,9 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { Node* result_mem = nullptr; Node* addr; - if (ShenandoahSelfFixing) { + { VectorSet visited; addr = get_load_addr(phase, visited, lrb); - } else { - addr = phase->igvn().zerocon(T_OBJECT); } if (addr->Opcode() == Op_AddP) { Node* orig_base = addr->in(AddPNode::Base); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp index 396b6ee89666a..fa6b3e67fee82 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp @@ -38,12 +38,6 @@ ShenandoahAggressiveHeuristics::ShenandoahAggressiveHeuristics(ShenandoahSpaceIn // Aggressive evacuates everything, so it needs as much evac space as it can get SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahEvacReserveOverflow); - - // If class unloading is globally enabled, aggressive does unloading even with - // concurrent cycles. - if (ClassUnloading) { - SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahUnloadClassesFrequency, 1); - } } void ShenandoahAggressiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, @@ -63,7 +57,7 @@ bool ShenandoahAggressiveHeuristics::should_start_gc() { } bool ShenandoahAggressiveHeuristics::should_unload_classes() { - if (!can_unload_classes_normal()) return false; + if (!can_unload_classes()) return false; if (has_metaspace_oom()) return true; // Randomly unload classes with 50% chance. return (os::random() & 1) == 1; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 2a18ae95a7bfc..c2620a228115f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -54,11 +54,6 @@ ShenandoahHeuristics::ShenandoahHeuristics(ShenandoahSpaceInfo* space_info) : _gc_time_history(new TruncatedSeq(10, ShenandoahAdaptiveDecayFactor)), _metaspace_oom() { - // No unloading during concurrent mark? Communicate that to heuristics - if (!ClassUnloadingWithConcurrentMark) { - FLAG_SET_DEFAULT(ShenandoahUnloadClassesFrequency, 0); - } - size_t num_regions = ShenandoahHeap::heap()->num_regions(); assert(num_regions > 0, "Sanity"); @@ -263,23 +258,10 @@ bool ShenandoahHeuristics::can_unload_classes() { return true; } -bool ShenandoahHeuristics::can_unload_classes_normal() { - if (!can_unload_classes()) return false; - if (has_metaspace_oom()) return true; - if (!ClassUnloadingWithConcurrentMark) return false; - if (ShenandoahUnloadClassesFrequency == 0) return false; - return true; -} - bool ShenandoahHeuristics::should_unload_classes() { - if (!can_unload_classes_normal()) return false; + if (!can_unload_classes()) return false; if (has_metaspace_oom()) return true; - size_t cycle = ShenandoahHeap::heap()->shenandoah_policy()->cycle_counter(); - // Unload classes every Nth GC cycle. - // This should not happen in the same cycle as process_references to amortize costs. - // Offsetting by one is enough to break the rendezvous when periods are equal. - // When periods are not equal, offsetting by one is just as good as any other guess. - return (cycle + 1) % ShenandoahUnloadClassesFrequency == 0; + return ClassUnloadingWithConcurrentMark; } void ShenandoahHeuristics::initialize() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index 4deb134a4b5fa..bc6d2dc40c54c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -130,7 +130,6 @@ class ShenandoahHeuristics : public CHeapObj { virtual void choose_collection_set(ShenandoahCollectionSet* collection_set); virtual bool can_unload_classes(); - virtual bool can_unload_classes_normal(); virtual bool should_unload_classes(); virtual const char* name() = 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 413dfe10faad9..b8da50dd6e109 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -123,7 +123,7 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(DecoratorSet decorators, } oop fwd = load_reference_barrier(obj); - if (ShenandoahSelfFixing && load_addr != nullptr && fwd != obj) { + if (load_addr != nullptr && fwd != obj) { // Since we are here and we know the load address, update the reference. ShenandoahHeap::atomic_update_oop(fwd, load_addr, obj); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 1ac9047706dc3..d94200f401c3d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -76,11 +76,6 @@ " compact - run GC more frequently and with deeper targets to " \ "free up more memory.") \ \ - product(uintx, ShenandoahUnloadClassesFrequency, 1, EXPERIMENTAL, \ - "Unload the classes every Nth cycle. Normally affects concurrent "\ - "GC cycles, as degenerated and full GCs would try to unload " \ - "classes regardless. Set to zero to disable class unloading.") \ - \ product(uintx, ShenandoahGarbageThreshold, 25, EXPERIMENTAL, \ "How much garbage a region has to contain before it would be " \ "taken for collection. This a guideline only, as GC heuristics " \ @@ -366,9 +361,6 @@ product(bool, ShenandoahLoopOptsAfterExpansion, true, DIAGNOSTIC, \ "Attempt more loop opts after barrier expansion.") \ \ - product(bool, ShenandoahSelfFixing, true, DIAGNOSTIC, \ - "Fix references with load reference barrier. Disabling this " \ - "might degrade performance.") // end of GC_SHENANDOAH_FLAGS diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index e5fb50a26e4fb..70b9bd6eaa7de 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -42,6 +42,7 @@ #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/type.hpp" +#include "utilities/debug.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" @@ -226,6 +227,7 @@ Label* ZBarrierStubC2::continuation() { } ZLoadBarrierStubC2* ZLoadBarrierStubC2::create(const MachNode* node, Address ref_addr, Register ref) { + AARCH64_ONLY(fatal("Should use ZLoadBarrierStubC2Aarch64::create")); ZLoadBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZLoadBarrierStubC2(node, ref_addr, ref); register_stub(stub); @@ -275,6 +277,7 @@ void ZLoadBarrierStubC2::emit_code(MacroAssembler& masm) { } ZStoreBarrierStubC2* ZStoreBarrierStubC2::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) { + AARCH64_ONLY(fatal("Should use ZStoreBarrierStubC2Aarch64::create")); ZStoreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic); register_stub(stub); diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp index a0f29fbc51076..7af70c6409678 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp @@ -52,9 +52,9 @@ static void inc_trampoline_stubs_count(); static int trampoline_stubs_count(); static int stubs_start_offset(); -public: ZBarrierStubC2(const MachNode* node); +public: RegMask& live() const; Label* entry(); Label* continuation(); diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 049c454c241a6..cc1b3d94cbd32 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -781,12 +781,6 @@ JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls); JNIEXPORT jobject JNICALL JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused); -/* - * java.util.concurrent.atomic.AtomicLong - */ -JNIEXPORT jboolean JNICALL -JVM_SupportsCX8(void); - /* * java.lang.ref.Finalizer */ diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index ddfb9c4092112..a9ebc00190091 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -466,6 +466,7 @@ JRT_END // bci where the exception happened. If the exception was propagated back // from a call, the expression stack contains the values for the bci at the // invoke w/o arguments (i.e., as if one were inside the call). +// Note that the implementation of this method assumes it's only called when an exception has actually occured JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThread* current, oopDesc* exception)) // We get here after we have unwound from a callee throwing an exception // into the interpreter. Any deferred stack processing is notified of @@ -580,6 +581,7 @@ JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThrea } else { // handler in this method => change bci/bcp to handler bci/bcp and continue there handler_pc = h_method->code_base() + handler_bci; + h_method->set_exception_handler_entered(handler_bci); // profiling #ifndef ZERO set_bcp_and_mdp(handler_pc, current); continuation = Interpreter::dispatch_table(vtos)[*handler_pc]; diff --git a/src/hotspot/share/interpreter/templateInterpreter.cpp b/src/hotspot/share/interpreter/templateInterpreter.cpp index 8f8abd30288e7..58559481e5ddc 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.cpp +++ b/src/hotspot/share/interpreter/templateInterpreter.cpp @@ -244,6 +244,7 @@ address* TemplateInterpreter::invoke_return_entry_table_for(Bytecodes::Code code case Bytecodes::_invokespecial: case Bytecodes::_invokevirtual: case Bytecodes::_invokehandle: + case Bytecodes::_fast_invokevfinal: return Interpreter::invoke_return_entry_table(); case Bytecodes::_invokeinterface: return Interpreter::invokeinterface_return_entry_table(); diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp index 0f147f137dc40..866f8c1df1363 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.cpp @@ -525,6 +525,33 @@ const char* JfrJavaSupport::c_str(jstring string, Thread* thread, bool c_heap /* return string != nullptr ? c_str(resolve_non_null(string), thread, c_heap) : nullptr; } +static Symbol** allocate_symbol_array(bool c_heap, int length, Thread* thread) { + return c_heap ? + NEW_C_HEAP_ARRAY(Symbol*, length, mtTracing) : + NEW_RESOURCE_ARRAY_IN_THREAD(thread, Symbol*, length); +} + +Symbol** JfrJavaSupport::symbol_array(jobjectArray string_array, JavaThread* thread, intptr_t* result_array_size, bool c_heap /* false */) { + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread)); + assert(string_array != nullptr, "invariant"); + assert(result_array_size != nullptr, "invariant"); + objArrayOop arrayOop = objArrayOop(resolve_non_null(string_array)); + const int length = arrayOop->length(); + *result_array_size = length; + Symbol** result_array = allocate_symbol_array(c_heap, length, thread); + assert(result_array != nullptr, "invariant"); + for (int i = 0; i < length; i++) { + oop object = arrayOop->obj_at(i); + Symbol* symbol = nullptr; + if (object != nullptr) { + const char* text = c_str(arrayOop->obj_at(i), thread, c_heap); + symbol = SymbolTable::new_symbol(text); + } + result_array[i] = symbol; + } + return result_array; +} + /* * Exceptions and errors */ diff --git a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp index 05559573e4a0e..8b4ecf18dc148 100644 --- a/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp +++ b/src/hotspot/share/jfr/jni/jfrJavaSupport.hpp @@ -86,6 +86,7 @@ class JfrJavaSupport : public AllStatic { static Klass* klass(const jobject handle); static const char* c_str(jstring string, Thread* thread, bool c_heap = false); static const char* c_str(oop string, Thread* thread, bool c_heap = false); + static Symbol** symbol_array(jobjectArray string_array, JavaThread* thread, intptr_t* result_size, bool c_heap = false); // exceptions static void throw_illegal_state_exception(const char* message, TRAPS); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index 4951e74dfd48c..82f26d7bdd062 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -36,6 +36,8 @@ #include "jfr/recorder/repository/jfrEmergencyDump.hpp" #include "jfr/recorder/service/jfrEventThrottler.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/recorder/stacktrace/jfrStackFilter.hpp" +#include "jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp" #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "jfr/recorder/stringpool/jfrStringPool.hpp" #include "jfr/jni/jfrJavaSupport.hpp" @@ -239,8 +241,8 @@ JVM_ENTRY_NO_ENV(jlong, jfr_class_id(JNIEnv* env, jclass jvm, jclass jc)) return JfrTraceId::load(jc); JVM_END -JVM_ENTRY_NO_ENV(jlong, jfr_stacktrace_id(JNIEnv* env, jclass jvm, jint skip)) - return JfrStackTraceRepository::record(thread, skip); +JVM_ENTRY_NO_ENV(jlong, jfr_stacktrace_id(JNIEnv* env, jclass jvm, jint skip, jlong stack_filter_id)) + return JfrStackTraceRepository::record(thread, skip, stack_filter_id); JVM_END JVM_ENTRY_NO_ENV(void, jfr_log(JNIEnv* env, jclass jvm, jint tag_set, jint level, jstring message)) @@ -397,3 +399,11 @@ JVM_END JVM_ENTRY_NO_ENV(void, jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes)) EventDataLoss::commit(bytes, min_jlong); JVM_END + +JVM_ENTRY_NO_ENV(jlong, jfr_register_stack_filter(JNIEnv* env, jclass jvm, jobjectArray classes, jobjectArray methods)) + return JfrStackFilterRegistry::add(classes, methods, thread); +JVM_END + +JVM_ENTRY_NO_ENV(void, jfr_unregister_stack_filter(JNIEnv* env, jclass jvm, jlong id)) + JfrStackFilterRegistry::remove(id); +JVM_END diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp index 28bafc4b73f21..b37841aeac297 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp @@ -57,7 +57,7 @@ jlong JNICALL jfr_class_id(JNIEnv* env, jclass jvm, jclass jc); jstring JNICALL jfr_get_pid(JNIEnv* env, jclass jvm); -jlong JNICALL jfr_stacktrace_id(JNIEnv* env, jclass jvm, jint skip); +jlong JNICALL jfr_stacktrace_id(JNIEnv* env, jclass jvm, jint skip, jlong stack_filter_id); jlong JNICALL jfr_elapsed_frequency(JNIEnv* env, jclass jvm); @@ -159,6 +159,10 @@ jlong JNICALL jfr_host_total_memory(JNIEnv* env, jclass jvm); void JNICALL jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes); +jlong JNICALL jfr_register_stack_filter(JNIEnv* env, jobject classes, jobject methods); + +jlong JNICALL jfr_unregister_stack_filter(JNIEnv* env, jlong start_filter_id); + #ifdef __cplusplus } #endif diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp index 7c571abbd695f..f8475881ff8ad 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp @@ -46,7 +46,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (char*)"getAllEventClasses", (char*)"()Ljava/util/List;", (void*)jfr_get_all_event_classes, (char*)"getClassId", (char*)"(Ljava/lang/Class;)J", (void*)jfr_class_id, (char*)"getPid", (char*)"()Ljava/lang/String;", (void*)jfr_get_pid, - (char*)"getStackTraceId", (char*)"(I)J", (void*)jfr_stacktrace_id, + (char*)"getStackTraceId", (char*)"(IJ)J", (void*)jfr_stacktrace_id, (char*)"getThreadId", (char*)"(Ljava/lang/Thread;)J", (void*)jfr_id_for_thread, (char*)"getTicksFrequency", (char*)"()J", (void*)jfr_elapsed_frequency, (char*)"subscribeLogLevel", (char*)"(Ljdk/jfr/internal/LogTag;I)V", (void*)jfr_subscribe_log_level, @@ -97,7 +97,9 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (char*)"isInstrumented", (char*)"(Ljava/lang/Class;)Z", (void*) jfr_is_class_instrumented, (char*)"isContainerized", (char*)"()Z", (void*) jfr_is_containerized, (char*)"hostTotalMemory", (char*)"()J", (void*) jfr_host_total_memory, - (char*)"emitDataLoss", (char*)"(J)V", (void*)jfr_emit_data_loss + (char*)"emitDataLoss", (char*)"(J)V", (void*)jfr_emit_data_loss, + (char*)"registerStackFilter", (char*)"([Ljava/lang/String;[Ljava/lang/String;)J", (void*)jfr_register_stack_filter, + (char*)"unregisterStackFilter", (char*)"(J)V", (void*)jfr_unregister_stack_filter }; const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod); diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp index fd50628602334..f07078eaf06de 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp @@ -35,12 +35,10 @@ #include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/jniHandles.inline.hpp" -#include "runtime/vm_version.hpp" #include "utilities/growableArray.hpp" // returns updated value static traceid atomic_inc(traceid volatile* const dest, traceid stride = 1) { - assert(VM_Version::supports_cx8(), "invariant"); traceid compare_value; traceid exchange_value; do { @@ -294,4 +292,3 @@ void JfrTraceId::untag_jdk_jfr_event_sub(const Klass* k) { } assert(IS_NOT_AN_EVENT_SUB_KLASS(k), "invariant"); } - diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.cpp new file mode 100644 index 0000000000000..bf10c531c637a --- /dev/null +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "jfr/recorder/stacktrace/jfrStackFilter.hpp" +#include "oops/method.hpp" +#include "oops/symbol.hpp" + +JfrStackFilter::JfrStackFilter(Symbol** class_names, Symbol** method_names, size_t count) + : _count(count), + _class_names(class_names), + _method_names(method_names) { + assert(_class_names != nullptr, "invariant"); + assert(_method_names != nullptr, "invariant"); +} + +bool JfrStackFilter::match(const Method* method) const { + assert(method != nullptr, "Invariant"); + const Symbol* const method_name = method->name(); + const Symbol* const klass_name = method->klass_name(); + for (size_t i = 0; i < _count; i++) { + const Symbol* m = _method_names[i]; + if (m == nullptr || m == method_name) { + const Symbol* c = _class_names[i]; + if (c == nullptr || c == klass_name) { + return true; + } + } + } + return false; +} + +JfrStackFilter::~JfrStackFilter() { + for (size_t i = 0; i < _count; i++) { + Symbol::maybe_decrement_refcount(_method_names[i]); + Symbol::maybe_decrement_refcount(_class_names[i]); + } + FREE_C_HEAP_ARRAY(Symbol*, _method_names); + FREE_C_HEAP_ARRAY(Symbol*, _class_names); +} diff --git a/src/hotspot/share/gc/shared/generationSpec.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.hpp similarity index 55% rename from src/hotspot/share/gc/shared/generationSpec.cpp rename to src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.hpp index 422bea62ae37f..19c1821149ca3 100644 --- a/src/hotspot/share/gc/shared/generationSpec.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,28 +22,24 @@ * */ -#include "precompiled.hpp" -#include "gc/serial/cardTableRS.hpp" -#include "gc/shared/generationSpec.hpp" -#include "runtime/java.hpp" -#include "utilities/macros.hpp" -#if INCLUDE_SERIALGC -#include "gc/serial/defNewGeneration.hpp" -#include "gc/serial/tenuredGeneration.hpp" -#endif +#ifndef SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKFILTER_HPP +#define SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKFILTER_HPP -Generation* GenerationSpec::init(ReservedSpace rs, CardTableRS* remset) { - switch (name()) { -#if INCLUDE_SERIALGC - case Generation::DefNew: - return new DefNewGeneration(rs, _init_size, _min_size, _max_size); +#include "jfr/utilities/jfrAllocation.hpp" - case Generation::MarkSweepCompact: - return new TenuredGeneration(rs, _init_size, _min_size, _max_size, remset); -#endif +class Mathod; +class Symbol; - default: - guarantee(false, "unrecognized GenerationName"); - return nullptr; - } -} +class JfrStackFilter : public JfrCHeapObj { + private: + size_t _count; + Symbol** _class_names; + Symbol** _method_names; + + public: + JfrStackFilter(Symbol** class_names, Symbol** method_names, size_t count); + ~JfrStackFilter(); + bool match(const Method* method) const; +}; + +#endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKFILTER_HPP diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.cpp new file mode 100644 index 0000000000000..481dcbdc840c6 --- /dev/null +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +#include "precompiled.hpp" +#include "jfr/jni/jfrJavaSupport.hpp" +#include "jfr/recorder/stacktrace/jfrStackFilter.hpp" +#include "jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp" +#include "logging/log.hpp" + +static const intptr_t STACK_FILTER_ELEMENTS_SIZE = 4096; +static const intptr_t STACK_FILTER_ERROR_CODE = -1; +static const JfrStackFilter* _elements[STACK_FILTER_ELEMENTS_SIZE]; +static intptr_t _free_list[STACK_FILTER_ELEMENTS_SIZE]; +static intptr_t _index = 0; +static intptr_t _free_list_index = 0; + +int64_t JfrStackFilterRegistry::add(jobjectArray classes, jobjectArray methods, JavaThread* jt) { + intptr_t c_size = 0; + Symbol** class_names = JfrJavaSupport::symbol_array(classes, jt, &c_size, true); + assert(class_names != nullptr, "invariant"); + intptr_t m_size = 0; + Symbol** method_names = JfrJavaSupport::symbol_array(methods, jt, &m_size, true); + assert(method_names != nullptr, "invariant"); + if (c_size != m_size) { + FREE_C_HEAP_ARRAY(Symbol*, class_names); + FREE_C_HEAP_ARRAY(Symbol*, method_names); + JfrJavaSupport::throw_internal_error("Method array size doesn't match class array size", jt); + return STACK_FILTER_ERROR_CODE; + } + assert(c_size >= 0, "invariant"); + const JfrStackFilter* filter = new JfrStackFilter(class_names, method_names, static_cast(c_size)); + return JfrStackFilterRegistry::add(filter); +} + +#ifdef ASSERT +static bool range_check(int64_t idx) { + return idx < STACK_FILTER_ELEMENTS_SIZE && idx >= 0; +} +#endif + +int64_t JfrStackFilterRegistry::add(const JfrStackFilter* filter) { + if (_free_list_index > 0) { + assert(range_check(_free_list_index), "invariant"); + const intptr_t free_index = _free_list[_free_list_index - 1]; + _elements[free_index] = filter; + _free_list_index--; + return free_index; + } + if (_index >= STACK_FILTER_ELEMENTS_SIZE - 1) { + log_warning(jfr)("Maximum number of @StackFrame in use has been reached."); + return STACK_FILTER_ERROR_CODE; + } + assert(range_check(_index), "invariant"); + _elements[_index] = filter; + return _index++; +} + +const JfrStackFilter* JfrStackFilterRegistry::lookup(int64_t id) { + if (id < 0) { + return nullptr; + } + assert(range_check(id), "invariant"); + return _elements[id]; +} + +void JfrStackFilterRegistry::remove(int64_t index) { + assert(range_check(index), "invariant"); + delete _elements[index]; + if (_free_list_index < STACK_FILTER_ELEMENTS_SIZE - 1) { + assert(range_check(_free_list_index), "invariant"); + _free_list[_free_list_index++] = index; + } +} diff --git a/test/hotspot/jtreg/gc/stress/TestJNIBlockFullGC/libTestJNIBlockFullGC.c b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp similarity index 59% rename from test/hotspot/jtreg/gc/stress/TestJNIBlockFullGC/libTestJNIBlockFullGC.c rename to src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp index 76448c825d83e..e35fb90938fe6 100644 --- a/test/hotspot/jtreg/gc/stress/TestJNIBlockFullGC/libTestJNIBlockFullGC.c +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017 SAP SE and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,28 +19,25 @@ * Please 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 "jni.h" - -JNIEXPORT jint JNICALL -Java_gc_stress_TestJNIBlockFullGC_TestJNIBlockFullGC_TestCriticalArray0(JNIEnv *env, jclass jCls, jintArray jIn) { - jint *bufIn = NULL; - jint jInLen = (*env)->GetArrayLength(env, jIn); - jint result = 0; - jint i; +#ifndef SHARE_JFR_RECORDER_STACKTRACE_JFRSTCKFILTERREGISTRY_HPP +#define SHARE_JFR_RECORDER_STACKTRACE_JFRSTCKFILTERREGISTRY_HPP - if (jInLen != 0) { - bufIn = (jint*)(*env)->GetPrimitiveArrayCritical(env, jIn, 0); - } +#include "jni.h" +#include "jfr/utilities/jfrAllocation.hpp" - for (i = 0; i < jInLen; ++i) { - result += bufIn[i]; // result = sum of all array elements - } +class JavaThread; +class JfrStackFilter; - if (bufIn != NULL) { - (*env)->ReleasePrimitiveArrayCritical(env, jIn, bufIn, 0); - } +class JfrStackFilterRegistry : AllStatic { + private: + static int64_t add(const JfrStackFilter* frame); + public: + static int64_t add(jobjectArray classes, jobjectArray methods, JavaThread* jt); + static void remove(int64_t id); + static const JfrStackFilter* lookup(int64_t id); +}; - return result; -} +#endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTCKFILTERREGISTRY_HPP diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp index c70f626a2fe3a..55e1e2ac3747d 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.cpp @@ -30,6 +30,8 @@ #include "jfr/recorder/storage/jfrBuffer.hpp" #include "jfr/support/jfrMethodLookup.hpp" #include "jfr/support/jfrThreadLocal.hpp" +#include "jfrStackFilter.hpp" +#include "jfrStackFilterRegistry.hpp" #include "memory/allocation.inline.hpp" #include "oops/instanceKlass.inline.hpp" #include "runtime/continuation.hpp" @@ -284,7 +286,7 @@ bool JfrStackTrace::record_async(JavaThread* jt, const frame& frame) { return count > 0; } -bool JfrStackTrace::record(JavaThread* jt, const frame& frame, int skip) { +bool JfrStackTrace::record(JavaThread* jt, const frame& frame, int skip, int64_t stack_filter_id) { assert(jt != nullptr, "invariant"); assert(jt == Thread::current(), "invariant"); assert(jt->thread_state() != _thread_in_native, "invariant"); @@ -302,6 +304,7 @@ bool JfrStackTrace::record(JavaThread* jt, const frame& frame, int skip) { } vfs.next_vframe(); } + const JfrStackFilter* stack_filter = JfrStackFilterRegistry::lookup(stack_filter_id); _hash = 1; while (!vfs.at_end()) { if (count >= _max_frames) { @@ -309,6 +312,12 @@ bool JfrStackTrace::record(JavaThread* jt, const frame& frame, int skip) { break; } const Method* method = vfs.method(); + if (stack_filter != nullptr) { + if (stack_filter->match(method)) { + vfs.next_vframe(); + continue; + } + } const traceid mid = JfrTraceId::load(method); u1 type = vfs.is_interpreted_frame() ? JfrStackFrame::FRAME_INTERPRETER : JfrStackFrame::FRAME_JIT; int bci = 0; @@ -335,13 +344,13 @@ bool JfrStackTrace::record(JavaThread* jt, const frame& frame, int skip) { return count > 0; } -bool JfrStackTrace::record(JavaThread* current_thread, int skip) { +bool JfrStackTrace::record(JavaThread* current_thread, int skip, int64_t stack_filter_id) { assert(current_thread != nullptr, "invariant"); assert(current_thread == Thread::current(), "invariant"); if (!current_thread->has_last_Java_frame()) { return false; } - return record(current_thread, current_thread->last_frame(), skip); + return record(current_thread, current_thread->last_frame(), skip, stack_filter_id); } void JfrStackFrame::resolve_lineno() const { diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp index acd9d41fbf71c..8b2ca022443fe 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTrace.hpp @@ -93,8 +93,8 @@ class JfrStackTrace : public JfrCHeapObj { void set_reached_root(bool reached_root) { _reached_root = reached_root; } void resolve_linenos() const; - bool record(JavaThread* current_thread, int skip); - bool record(JavaThread* current_thread, const frame& frame, int skip); + bool record(JavaThread* current_thread, int skip, int64_t stack_frame_id); + bool record(JavaThread* current_thread, const frame& frame, int skip, int64_t stack_frame_id); bool record_async(JavaThread* other_thread, const frame& frame); bool have_lineno() const { return _lineno; } diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp index 26056141a72ed..1c207f5bb2b47 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp @@ -146,7 +146,7 @@ size_t JfrStackTraceRepository::clear(JfrStackTraceRepository& repo) { return processed; } -traceid JfrStackTraceRepository::record(Thread* current_thread, int skip /* 0 */) { +traceid JfrStackTraceRepository::record(Thread* current_thread, int skip /* 0 */, int64_t stack_filter_id /* -1 */) { assert(current_thread == Thread::current(), "invariant"); JfrThreadLocal* const tl = current_thread->jfr_thread_local(); assert(tl != nullptr, "invariant"); @@ -163,13 +163,14 @@ traceid JfrStackTraceRepository::record(Thread* current_thread, int skip /* 0 */ } assert(frames != nullptr, "invariant"); assert(tl->stackframes() == frames, "invariant"); - return instance().record(JavaThread::cast(current_thread), skip, frames, tl->stackdepth()); + return instance().record(JavaThread::cast(current_thread), skip, stack_filter_id, frames, tl->stackdepth()); } -traceid JfrStackTraceRepository::record(JavaThread* current_thread, int skip, JfrStackFrame *frames, u4 max_frames) { +traceid JfrStackTraceRepository::record(JavaThread* current_thread, int skip, int64_t stack_filter_id, JfrStackFrame *frames, u4 max_frames) { JfrStackTrace stacktrace(frames, max_frames); - return stacktrace.record(current_thread, skip) ? add(instance(), stacktrace) : 0; + return stacktrace.record(current_thread, skip, stack_filter_id) ? add(instance(), stacktrace) : 0; } + traceid JfrStackTraceRepository::add(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace) { traceid tid = repo.add_trace(stacktrace); if (tid == 0) { @@ -191,7 +192,7 @@ void JfrStackTraceRepository::record_for_leak_profiler(JavaThread* current_threa assert(tl != nullptr, "invariant"); assert(!tl->has_cached_stack_trace(), "invariant"); JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth()); - stacktrace.record(current_thread, skip); + stacktrace.record(current_thread, skip, -1); const traceid hash = stacktrace.hash(); if (hash != 0) { tl->set_cached_stack_trace_id(add(leak_profiler_instance(), stacktrace), hash); diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp index d59dd5f57e0a8..abcf88450ffea 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.hpp @@ -67,10 +67,10 @@ class JfrStackTraceRepository : public JfrCHeapObj { traceid add_trace(const JfrStackTrace& stacktrace); static traceid add(JfrStackTraceRepository& repo, const JfrStackTrace& stacktrace); static traceid add(const JfrStackTrace& stacktrace); - traceid record(JavaThread* current_thread, int skip, JfrStackFrame* frames, u4 max_frames); + traceid record(JavaThread* current_thread, int skip, int64_t stack_filter_id, JfrStackFrame* frames, u4 max_frames); public: - static traceid record(Thread* current_thread, int skip = 0); + static traceid record(Thread* current_thread, int skip = 0, int64_t stack_filter_id = -1); }; #endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKTRACEREPOSITORY_HPP diff --git a/src/hotspot/share/jfr/utilities/jfrAllocation.cpp b/src/hotspot/share/jfr/utilities/jfrAllocation.cpp index e9e41ac7cd875..c94765bbc8fe7 100644 --- a/src/hotspot/share/jfr/utilities/jfrAllocation.cpp +++ b/src/hotspot/share/jfr/utilities/jfrAllocation.cpp @@ -29,14 +29,12 @@ #include "memory/allocation.inline.hpp" #include "nmt/memTracker.hpp" #include "runtime/atomic.hpp" -#include "runtime/vm_version.hpp" #include "utilities/debug.hpp" #include "utilities/macros.hpp" #include "utilities/nativeCallStack.hpp" #ifdef ASSERT static jlong atomic_add_jlong(jlong value, jlong volatile* const dest) { - assert(VM_Version::supports_cx8(), "unsupported"); jlong compare_value; jlong exchange_value; do { diff --git a/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp b/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp index a155b2690a054..8d381a646ee07 100644 --- a/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp @@ -27,8 +27,6 @@ #include "runtime/javaThread.hpp" -// this utility could be useful for non cx8 platforms - class JfrSpinlockHelper { private: volatile int* const _lock; diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp index 47db09d6922c7..b060bd8479fe4 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.cpp +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp @@ -189,7 +189,7 @@ void JVMCIEnv::init_env_mode_runtime(JavaThread* thread, JNIEnv* parent_env) { JNIAccessMark jni(this, thread); jint result = _env->PushLocalFrame(32); if (result != JNI_OK) { - JVMCI_event_1("[%s:%d] Error pushing local JNI frame (err: %d)", _file, _line, _init_error); + JVMCI_event_1("[%s:%d] Error pushing local JNI frame (err: %d)", _file, _line, result); return; } _pop_frame_on_close = true; diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index cbb7bf9e3f15a..573df165d0dff 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021 SAP SE. All rights reserved. + * Copyright (c) 2023, 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 @@ -23,12 +24,6 @@ * */ -/* - * This file has been modified by Loongson Technology in 2022. These - * modifications are Copyright (c) 2019, 2022, Loongson Technology, and are made - * available on the same license terms set forth above. - */ - #include "precompiled.hpp" #include "cds/cdsConfig.hpp" #include "cds/metaspaceShared.hpp" @@ -584,54 +579,15 @@ bool Metaspace::class_space_is_initialized() { // Reserve a range of memory that is to contain narrow Klass IDs. If "try_in_low_address_ranges" // is true, we will attempt to reserve memory suitable for zero-based encoding. -ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t size, bool try_in_low_address_ranges) { - +ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t size, bool optimize_for_zero_base) { char* result = nullptr; - const bool randomize = RandomizeClassSpaceLocation; - - // First try to reserve in low address ranges. - if (try_in_low_address_ranges) { - constexpr uintptr_t unscaled_max = ((uintptr_t)UINT_MAX + 1); - log_debug(metaspace, map)("Trying below " SIZE_FORMAT_X " for unscaled narrow Klass encoding", unscaled_max); - result = os::attempt_reserve_memory_between(nullptr, (char*)unscaled_max, - size, Metaspace::reserve_alignment(), randomize); - if (result == nullptr) { - constexpr uintptr_t zerobased_max = unscaled_max << LogKlassAlignmentInBytes; - log_debug(metaspace, map)("Trying below " SIZE_FORMAT_X " for zero-based narrow Klass encoding", zerobased_max); - result = os::attempt_reserve_memory_between((char*)unscaled_max, (char*)zerobased_max, - size, Metaspace::reserve_alignment(), randomize); - } - } // end: low-address reservation -#if defined(AARCH64) || defined(PPC64) || defined(S390) || defined(LOONGARCH64) - if (result == nullptr) { - // Failing zero-based allocation, or in strict_base mode, try to come up with - // an optimized start address that is amenable to JITs that use 16-bit moves to - // load the encoding base as a short immediate. - // Therefore we try here for an address that when right-shifted by - // LogKlassAlignmentInBytes has only 1s in the third 16-bit quadrant. - // - // Example: for shift=3, the address space searched would be - // [0x0080_0000_0000 - 0xFFF8_0000_0000]. - - // Number of least significant bits that should be zero - constexpr int lo_zero_bits = 32 + LogKlassAlignmentInBytes; - // Number of most significant bits that should be zero - constexpr int hi_zero_bits = 16; - - constexpr size_t alignment = nth_bit(lo_zero_bits); - assert(alignment >= Metaspace::reserve_alignment(), "Sanity"); - constexpr uint64_t min = alignment; - constexpr uint64_t max = nth_bit(64 - hi_zero_bits); - - log_debug(metaspace, map)("Trying between " UINT64_FORMAT_X " and " UINT64_FORMAT_X - " with " SIZE_FORMAT_X " alignment", min, max, alignment); - result = os::attempt_reserve_memory_between((char*)min, (char*)max, size, alignment, randomize); - } -#endif // defined(AARCH64) || defined(PPC64) || defined(S390) || defined(LOONGARCH64) + NOT_ZERO(result = + (char*) CompressedKlassPointers::reserve_address_space_for_compressed_classes(size, RandomizeClassSpaceLocation, + optimize_for_zero_base)); if (result == nullptr) { - // Fallback: reserve anywhere and hope the resulting block is usable. + // Fallback: reserve anywhere log_debug(metaspace, map)("Trying anywhere..."); result = os::reserve_memory_aligned(size, Metaspace::reserve_alignment(), false); } @@ -639,10 +595,12 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz // Wrap resulting range in ReservedSpace ReservedSpace rs; if (result != nullptr) { + log_debug(metaspace, map)("Mapped at " PTR_FORMAT, p2i(result)); assert(is_aligned(result, Metaspace::reserve_alignment()), "Alignment too small for metaspace"); rs = ReservedSpace::space_for_range(result, size, Metaspace::reserve_alignment(), os::vm_page_size(), false, false); } else { + log_debug(metaspace, map)("Failed to map."); rs = ReservedSpace(); } return rs; diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp index 652a2be35d9ad..a90ff77564763 100644 --- a/src/hotspot/share/memory/metaspace.hpp +++ b/src/hotspot/share/memory/metaspace.hpp @@ -76,7 +76,7 @@ class Metaspace : public AllStatic { // Reserve a range of memory that is to contain narrow Klass IDs. If "try_in_low_address_ranges" // is true, we will attempt to reserve memory suitable for zero-based encoding. - static ReservedSpace reserve_address_space_for_compressed_classes(size_t size, bool try_in_low_address_ranges); + static ReservedSpace reserve_address_space_for_compressed_classes(size_t size, bool optimize_for_zero_base); // Given a prereserved space, use that to set up the compressed class space list. static void initialize_class_space(ReservedSpace rs); diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index 48470478b9bb8..964911bb5954e 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -55,7 +55,7 @@ void MemReporterBase::print_total(size_t reserved, size_t committed, size_t peak output()->print("reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s", amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale); if (peak != 0) { - output()->print(", largest_committed=" SIZE_FORMAT "%s", amount_in_current_scale(peak), scale); + output()->print(", peak=" SIZE_FORMAT "%s", amount_in_current_scale(peak), scale); } } @@ -93,9 +93,15 @@ void MemReporterBase::print_malloc(const MemoryCounter* c, MEMFLAGS flag) const } void MemReporterBase::print_virtual_memory(size_t reserved, size_t committed, size_t peak) const { + outputStream* out = output(); const char* scale = current_scale(); - output()->print("(mmap: reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s, largest_committed=" SIZE_FORMAT "%s)", - amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale, amount_in_current_scale(peak), scale); + out->print("(mmap: reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s, ", + amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale); + if (peak == committed) { + out->print_raw("at peak)"); + } else { + out->print("peak=" SIZE_FORMAT "%s)", amount_in_current_scale(peak), scale); + } } void MemReporterBase::print_malloc_line(const MemoryCounter* c) const { @@ -204,74 +210,79 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, committed_amount += _malloc_snapshot->malloc_overhead(); } - if (amount_in_current_scale(reserved_amount) > 0) { - outputStream* out = output(); - const char* scale = current_scale(); - out->print("-%26s (", NMTUtil::flag_to_name(flag)); - print_total(reserved_amount, committed_amount); + // Omit printing if the current reserved value as well as all historical peaks (malloc, mmap committed, arena) + // fall below scale threshold + const size_t pk_vm = virtual_memory->peak_size(); + const size_t pk_malloc = malloc_memory->malloc_peak_size(); + const size_t pk_arena = malloc_memory->arena_peak_size(); + + if (amount_in_current_scale(MAX4(reserved_amount, pk_vm, pk_malloc, pk_arena)) == 0) { + return; + } + + outputStream* out = output(); + const char* scale = current_scale(); + out->print("-%26s (", NMTUtil::flag_to_name(flag)); + print_total(reserved_amount, committed_amount); #if INCLUDE_CDS - if (flag == mtClassShared) { - size_t read_only_bytes = FileMapInfo::readonly_total(); - output()->print(", readonly=" SIZE_FORMAT "%s", - amount_in_current_scale(read_only_bytes), scale); - } + if (flag == mtClassShared) { + size_t read_only_bytes = FileMapInfo::readonly_total(); + output()->print(", readonly=" SIZE_FORMAT "%s", + amount_in_current_scale(read_only_bytes), scale); + } #endif - out->print_cr(")"); + out->print_cr(")"); - if (flag == mtClass) { - // report class count - out->print_cr("%27s (classes #" SIZE_FORMAT ")", - " ", (_instance_class_count + _array_class_count)); - out->print_cr("%27s ( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")", - " ", _instance_class_count, _array_class_count); - } else if (flag == mtThread) { - if (ThreadStackTracker::track_as_vm()) { - const VirtualMemory* thread_stack_usage = - _vm_snapshot->by_type(mtThreadStack); - // report thread count - out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", ThreadStackTracker::thread_count()); - out->print("%27s (stack: ", " "); - print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size()); - } else { - MallocMemory* thread_stack_memory = _malloc_snapshot->by_type(mtThreadStack); - const char* scale = current_scale(); - // report thread count - out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", thread_stack_memory->malloc_count()); - out->print("%27s (Stack: " SIZE_FORMAT "%s", " ", - amount_in_current_scale(thread_stack_memory->malloc_size()), scale); - } - out->print_cr(")"); + if (flag == mtClass) { + // report class count + out->print_cr("%27s (classes #" SIZE_FORMAT ")", + " ", (_instance_class_count + _array_class_count)); + out->print_cr("%27s ( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")", + " ", _instance_class_count, _array_class_count); + } else if (flag == mtThread) { + if (ThreadStackTracker::track_as_vm()) { + const VirtualMemory* thread_stack_usage = + _vm_snapshot->by_type(mtThreadStack); + // report thread count + out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", ThreadStackTracker::thread_count()); + out->print("%27s (stack: ", " "); + print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size()); + } else { + MallocMemory* thread_stack_memory = _malloc_snapshot->by_type(mtThreadStack); + const char* scale = current_scale(); + // report thread count + out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", thread_stack_memory->malloc_count()); + out->print("%27s (Stack: " SIZE_FORMAT "%s", " ", + amount_in_current_scale(thread_stack_memory->malloc_size()), scale); } + out->print_cr(")"); + } - // report malloc'd memory - if (amount_in_current_scale(malloc_memory->malloc_size()) > 0 - || amount_in_current_scale(malloc_memory->malloc_peak_size()) > 0) { - print_malloc_line(malloc_memory->malloc_counter()); - } + // report malloc'd memory + if (amount_in_current_scale(MAX2(malloc_memory->malloc_size(), pk_malloc)) > 0) { + print_malloc_line(malloc_memory->malloc_counter()); + } - if (amount_in_current_scale(virtual_memory->reserved()) > 0 - DEBUG_ONLY(|| amount_in_current_scale(virtual_memory->peak_size()) > 0)) { - print_virtual_memory_line(virtual_memory->reserved(), virtual_memory->committed(), virtual_memory->peak_size()); - } + if (amount_in_current_scale(MAX2(virtual_memory->reserved(), pk_vm)) > 0) { + print_virtual_memory_line(virtual_memory->reserved(), virtual_memory->committed(), virtual_memory->peak_size()); + } - if (amount_in_current_scale(malloc_memory->arena_size()) > 0 - DEBUG_ONLY(|| amount_in_current_scale(malloc_memory->arena_peak_size()) > 0)) { - print_arena_line(malloc_memory->arena_counter()); - } + if (amount_in_current_scale(MAX2(malloc_memory->arena_size(), pk_arena)) > 0) { + print_arena_line(malloc_memory->arena_counter()); + } - if (flag == mtNMT && - amount_in_current_scale(_malloc_snapshot->malloc_overhead()) > 0) { - out->print_cr("%27s (tracking overhead=" SIZE_FORMAT "%s)", " ", - amount_in_current_scale(_malloc_snapshot->malloc_overhead()), scale); - } else if (flag == mtClass) { - // Metadata information - report_metadata(Metaspace::NonClassType); - if (Metaspace::using_class_space()) { - report_metadata(Metaspace::ClassType); - } + if (flag == mtNMT && + amount_in_current_scale(_malloc_snapshot->malloc_overhead()) > 0) { + out->print_cr("%27s (tracking overhead=" SIZE_FORMAT "%s)", " ", + amount_in_current_scale(_malloc_snapshot->malloc_overhead()), scale); + } else if (flag == mtClass) { + // Metadata information + report_metadata(Metaspace::NonClassType); + if (Metaspace::using_class_space()) { + report_metadata(Metaspace::ClassType); } - out->print_cr(" "); } + out->print_cr(" "); } void MemSummaryReporter::report_metadata(Metaspace::MetadataType type) const { @@ -321,9 +332,8 @@ int MemDetailReporter::report_malloc_sites() { const MallocSite* malloc_site; int num_omitted = 0; while ((malloc_site = malloc_itr.next()) != nullptr) { - // Don't report if site has never allocated less than one unit of whatever our scale is - if (scale() > 1 && amount_in_current_scale(malloc_site->size()) == 0 - DEBUG_ONLY(&& amount_in_current_scale(malloc_site->peak_size()) == 0)) { + // Omit printing if the current value and the historic peak value both fall below the reporting scale threshold + if (amount_in_current_scale(MAX2(malloc_site->size(), malloc_site->peak_size())) == 0) { num_omitted ++; continue; } @@ -353,8 +363,10 @@ int MemDetailReporter::report_virtual_memory_allocation_sites() { if (virtual_memory_site->reserved() == 0) { continue; } - // Don't report if site has reserved less than one unit of whatever our scale is - if (scale() > 1 && amount_in_current_scale(virtual_memory_site->reserved()) == 0) { + // Omit printing if the current value and the historic peak value both fall below the + // reporting scale threshold + if (amount_in_current_scale(MAX2(virtual_memory_site->reserved(), + virtual_memory_site->peak_size())) == 0) { num_omitted++; continue; } @@ -386,7 +398,16 @@ void MemDetailReporter::report_virtual_memory_map() { void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* reserved_rgn) { assert(reserved_rgn != nullptr, "null pointer"); - // Don't report if size is too small + // We don't bother about reporting peaks here. + // That is because peaks - in the context of virtual memory, peak of committed areas - make little sense + // when we report *by region*, which are identified by their location in memory. There is a philosophical + // question about identity here: e.g. a committed region that has been split into three regions by + // uncommitting a middle section of it, should that still count as "having peaked" before the split? If + // yes, which of the three new regions would be the spiritual successor? Rather than introducing more + // complexity, we avoid printing peaks altogether. Note that peaks should still be printed when reporting + // usage *by callsite*. + + // Don't report if size is too small. if (amount_in_current_scale(reserved_rgn->size()) == 0) return; outputStream* out = output(); diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index 65bd9afa2d661..5bb9072666064 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -34,7 +34,6 @@ size_t VirtualMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(VirtualMemorySnapshot, size_t)]; -#ifdef ASSERT void VirtualMemory::update_peak(size_t size) { size_t peak_sz = peak_size(); while (peak_sz < size) { @@ -46,7 +45,6 @@ void VirtualMemory::update_peak(size_t size) { } } } -#endif // ASSERT void VirtualMemorySummary::initialize() { assert(sizeof(_snapshot) >= sizeof(VirtualMemorySnapshot), "Sanity Check"); diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index b6f89c278ce15..06ca960febe1e 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp @@ -42,21 +42,17 @@ class VirtualMemory { size_t _reserved; size_t _committed; -#ifdef ASSERT volatile size_t _peak_size; void update_peak(size_t size); -#endif // ASSERT public: - VirtualMemory() : _reserved(0), _committed(0) { - DEBUG_ONLY(_peak_size = 0;) - } + VirtualMemory() : _reserved(0), _committed(0), _peak_size(0) {} inline void reserve_memory(size_t sz) { _reserved += sz; } inline void commit_memory (size_t sz) { _committed += sz; - DEBUG_ONLY(update_peak(sz);) assert(_committed <= _reserved, "Sanity check"); + update_peak(_committed); } inline void release_memory (size_t sz) { @@ -72,7 +68,7 @@ class VirtualMemory { inline size_t reserved() const { return _reserved; } inline size_t committed() const { return _committed; } inline size_t peak_size() const { - return DEBUG_ONLY(Atomic::load(&_peak_size)) NOT_DEBUG(0); + return Atomic::load(&_peak_size); } }; @@ -85,10 +81,9 @@ class VirtualMemoryAllocationSite : public AllocationSite { inline void reserve_memory(size_t sz) { _c.reserve_memory(sz); } inline void commit_memory (size_t sz) { _c.commit_memory(sz); } - inline void uncommit_memory(size_t sz) { _c.uncommit_memory(sz); } - inline void release_memory(size_t sz) { _c.release_memory(sz); } inline size_t reserved() const { return _c.reserved(); } inline size_t committed() const { return _c.committed(); } + inline size_t peak_size() const { return _c.peak_size(); } }; class VirtualMemorySummary; diff --git a/src/hotspot/share/oops/accessBackend.cpp b/src/hotspot/share/oops/accessBackend.cpp index 61f58f1d33526..853f42f6f7e4c 100644 --- a/src/hotspot/share/oops/accessBackend.cpp +++ b/src/hotspot/share/oops/accessBackend.cpp @@ -28,60 +28,11 @@ #include "oops/oop.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/mutexLocker.hpp" -#include "runtime/vm_version.hpp" #include "utilities/copy.hpp" #include "utilities/debug.hpp" #include "utilities/vmError.hpp" namespace AccessInternal { - // VM_Version::supports_cx8() is a surrogate for 'supports atomic long memory ops'. - // - // On platforms which do not support atomic compare-and-swap of jlong (8 byte) - // values we have to use a lock-based scheme to enforce atomicity. This has to be - // applied to all Unsafe operations that set the value of a jlong field. Even so - // the compareAndSwapLong operation will not be atomic with respect to direct stores - // to the field from Java code. It is important therefore that any Java code that - // utilizes these Unsafe jlong operations does not perform direct stores. To permit - // direct loads of the field from Java code we must also use Atomic::store within the - // locked regions. And for good measure, in case there are direct stores, we also - // employ Atomic::load within those regions. Note that the field in question must be - // volatile and so must have atomic load/store accesses applied at the Java level. - // - // The locking scheme could utilize a range of strategies for controlling the locking - // granularity: from a lock per-field through to a single global lock. The latter is - // the simplest and is used for the current implementation. Note that the Java object - // that contains the field, can not, in general, be used for locking. To do so can lead - // to deadlocks as we may introduce locking into what appears to the Java code to be a - // lock-free path. - // - // As all the locked-regions are very short and themselves non-blocking we can treat - // them as leaf routines and elide safepoint checks (ie we don't perform any thread - // state transitions even when blocking for the lock). Note that if we do choose to - // add safepoint checks and thread state transitions, we must ensure that we calculate - // the address of the field _after_ we have acquired the lock, else the object may have - // been moved by the GC - -#ifndef SUPPORTS_NATIVE_CX8 - - // This is intentionally in the cpp file rather than the .inline.hpp file. It seems - // desirable to trade faster JDK build times (not propagating vm_version.hpp) - // for slightly worse runtime atomic jlong performance on 32 bit machines with - // support for 64 bit atomics. - bool wide_atomic_needs_locking() { - return !VM_Version::supports_cx8(); - } - - AccessLocker::AccessLocker() { - assert(!VM_Version::supports_cx8(), "why else?"); - UnsafeJlong_lock->lock_without_safepoint_check(); - } - - AccessLocker::~AccessLocker() { - UnsafeJlong_lock->unlock(); - } - -#endif - // These forward copying calls to Copy without exposing the Copy type in headers unnecessarily void arraycopy_arrayof_conjoint_oops(void* src, void* dst, size_t length) { diff --git a/src/hotspot/share/oops/accessBackend.hpp b/src/hotspot/share/oops/accessBackend.hpp index c711a241ef947..5299b57ca4637 100644 --- a/src/hotspot/share/oops/accessBackend.hpp +++ b/src/hotspot/share/oops/accessBackend.hpp @@ -81,15 +81,6 @@ namespace AccessInternal { reinterpret_cast((void*)base) + byte_offset); } - // This metafunction returns whether it is possible for a type T to require - // locking to support wide atomics or not. - template -#ifdef SUPPORTS_NATIVE_CX8 - struct PossiblyLockedAccess: public std::false_type {}; -#else - struct PossiblyLockedAccess: public std::integral_constant 4)> {}; -#endif - template struct AccessFunctionTypes { typedef T (*load_at_func_t)(oop base, ptrdiff_t offset); @@ -140,13 +131,6 @@ namespace AccessInternal { template typename AccessFunction::type resolve_oop_barrier(); - class AccessLocker { - public: - AccessLocker(); - ~AccessLocker(); - }; - bool wide_atomic_needs_locking(); - void* field_addr(oop base, ptrdiff_t offset); // Forward calls to Copy:: in the cpp file to reduce dependencies and allow @@ -281,34 +265,6 @@ class RawAccessBarrier: public AllStatic { HasDecorator::value, T>::type atomic_xchg_internal(void* addr, T new_value); - // The following *_locked mechanisms serve the purpose of handling atomic operations - // that are larger than a machine can handle, and then possibly opt for using - // a slower path using a mutex to perform the operation. - - template - static inline typename EnableIf< - !AccessInternal::PossiblyLockedAccess::value, T>::type - atomic_cmpxchg_maybe_locked(void* addr, T compare_value, T new_value) { - return atomic_cmpxchg_internal(addr, compare_value, new_value); - } - - template - static typename EnableIf< - AccessInternal::PossiblyLockedAccess::value, T>::type - atomic_cmpxchg_maybe_locked(void* addr, T compare_value, T new_value); - - template - static inline typename EnableIf< - !AccessInternal::PossiblyLockedAccess::value, T>::type - atomic_xchg_maybe_locked(void* addr, T new_value) { - return atomic_xchg_internal(addr, new_value); - } - - template - static typename EnableIf< - AccessInternal::PossiblyLockedAccess::value, T>::type - atomic_xchg_maybe_locked(void* addr, T new_value); - public: template static inline void store(void* addr, T value) { @@ -322,12 +278,12 @@ class RawAccessBarrier: public AllStatic { template static inline T atomic_cmpxchg(void* addr, T compare_value, T new_value) { - return atomic_cmpxchg_maybe_locked(addr, compare_value, new_value); + return atomic_cmpxchg_internal(addr, compare_value, new_value); } template static inline T atomic_xchg(void* addr, T new_value) { - return atomic_xchg_maybe_locked(addr, new_value); + return atomic_xchg_internal(addr, new_value); } template diff --git a/src/hotspot/share/oops/accessBackend.inline.hpp b/src/hotspot/share/oops/accessBackend.inline.hpp index 677af8115c3d2..e0d4f5aca92a4 100644 --- a/src/hotspot/share/oops/accessBackend.inline.hpp +++ b/src/hotspot/share/oops/accessBackend.inline.hpp @@ -214,44 +214,6 @@ RawAccessBarrier::atomic_xchg_internal(void* addr, T new_value) { new_value); } -// For platforms that do not have native support for wide atomics, -// we can emulate the atomicity using a lock. So here we check -// whether that is necessary or not. - -template -template -inline typename EnableIf< - AccessInternal::PossiblyLockedAccess::value, T>::type -RawAccessBarrier::atomic_xchg_maybe_locked(void* addr, T new_value) { - if (!AccessInternal::wide_atomic_needs_locking()) { - return atomic_xchg_internal(addr, new_value); - } else { - AccessInternal::AccessLocker access_lock; - volatile T* p = reinterpret_cast(addr); - T old_val = RawAccess<>::load(p); - RawAccess<>::store(p, new_value); - return old_val; - } -} - -template -template -inline typename EnableIf< - AccessInternal::PossiblyLockedAccess::value, T>::type -RawAccessBarrier::atomic_cmpxchg_maybe_locked(void* addr, T compare_value, T new_value) { - if (!AccessInternal::wide_atomic_needs_locking()) { - return atomic_cmpxchg_internal(addr, compare_value, new_value); - } else { - AccessInternal::AccessLocker access_lock; - volatile T* p = reinterpret_cast(addr); - T old_val = RawAccess<>::load(p); - if (old_val == compare_value) { - RawAccess<>::store(p, new_value); - } - return old_val; - } -} - class RawAccessBarrierArrayCopy: public AllStatic { template struct IsHeapWordSized: public std::integral_constant { }; public: diff --git a/src/hotspot/share/oops/compressedKlass.cpp b/src/hotspot/share/oops/compressedKlass.cpp index 947085b02e3fa..f9d0466dda4ec 100644 --- a/src/hotspot/share/oops/compressedKlass.cpp +++ b/src/hotspot/share/oops/compressedKlass.cpp @@ -23,8 +23,11 @@ */ #include "precompiled.hpp" +#include "logging/log.hpp" +#include "memory/metaspace.hpp" #include "oops/compressedKlass.hpp" #include "runtime/globals.hpp" +#include "runtime/os.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" @@ -35,11 +38,17 @@ size_t CompressedKlassPointers::_range = 0; #ifdef _LP64 +#ifdef ASSERT +void CompressedKlassPointers::assert_is_valid_encoding(address addr, size_t len, address base, int shift) { + assert(base + nth_bit(32 + shift) >= addr + len, "Encoding (base=" PTR_FORMAT ", shift=%d) does not " + "fully cover the class range " PTR_FORMAT "-" PTR_FORMAT, p2i(base), shift, p2i(addr), p2i(addr + len)); +} +#endif + // Given a klass range [addr, addr+len) and a given encoding scheme, assert that this scheme covers the range, then // set this encoding scheme. Used by CDS at runtime to re-instate the scheme used to pre-compute klass ids for // archived heap objects. void CompressedKlassPointers::initialize_for_given_encoding(address addr, size_t len, address requested_base, int requested_shift) { - assert(is_valid_base(requested_base), "Address must be a valid encoding base"); address const end = addr + len; const int narrow_klasspointer_bits = sizeof(narrowKlass) * 8; @@ -51,62 +60,62 @@ void CompressedKlassPointers::initialize_for_given_encoding(address addr, size_t assert(requested_base == addr, "Invalid requested base"); assert(encoding_range_end >= end, "Encoding does not cover the full Klass range"); - set_base(requested_base); - set_shift(requested_shift); - set_range(encoding_range_size); -} + _base = requested_base; + _shift = requested_shift; + _range = encoding_range_size; -// Given an address range [addr, addr+len) which the encoding is supposed to -// cover, choose base, shift and range. -// The address range is the expected range of uncompressed Klass pointers we -// will encounter (and the implicit promise that there will be no Klass -// structures outside this range). -void CompressedKlassPointers::initialize(address addr, size_t len) { - address const end = addr + len; + DEBUG_ONLY(assert_is_valid_encoding(addr, len, _base, _shift);) +} - address base; - int shift; - size_t range; +char* CompressedKlassPointers::reserve_address_space_X(uintptr_t from, uintptr_t to, size_t size, size_t alignment, bool aslr) { + alignment = MAX2(Metaspace::reserve_alignment(), alignment); + return os::attempt_reserve_memory_between((char*)from, (char*)to, size, alignment, aslr); +} - // Attempt to run with encoding base == zero - if (end <= (address)KlassEncodingMetaspaceMax) { - base = 0; - } else { - base = addr; - } +char* CompressedKlassPointers::reserve_address_space_for_unscaled_encoding(size_t size, bool aslr) { + return reserve_address_space_X(0, nth_bit(32), size, Metaspace::reserve_alignment(), aslr); +} - // Highest offset a Klass* can ever have in relation to base. - range = end - base; +char* CompressedKlassPointers::reserve_address_space_for_zerobased_encoding(size_t size, bool aslr) { + return reserve_address_space_X(nth_bit(32), nth_bit(32 + LogKlassAlignmentInBytes), size, Metaspace::reserve_alignment(), aslr); +} - // We may not even need a shift if the range fits into 32bit: - const uint64_t UnscaledClassSpaceMax = (uint64_t(max_juint) + 1); - if (range <= UnscaledClassSpaceMax) { - shift = 0; - } else { - shift = LogKlassAlignmentInBytes; - } +char* CompressedKlassPointers::reserve_address_space_for_16bit_move(size_t size, bool aslr) { + return reserve_address_space_X(nth_bit(32), nth_bit(48), size, nth_bit(32), aslr); +} - set_base(base); - set_shift(shift); - set_range(range); +#ifndef AARCH64 +// On aarch64 we have an own version; all other platforms use the default version +void CompressedKlassPointers::initialize(address addr, size_t len) { + // The default version of this code tries, in order of preference: + // -unscaled (base=0 shift=0) + // -zero-based (base=0 shift>0) + // -nonzero-base (base>0 shift=0) + // Note that base>0 shift>0 should never be needed, since the klass range will + // never exceed 4GB. + constexpr uintptr_t unscaled_max = nth_bit(32); + assert(len <= unscaled_max, "Klass range larger than 32 bits?"); - assert(is_valid_base(_base), "Address must be a valid encoding base"); -} + constexpr uintptr_t zerobased_max = nth_bit(32 + LogKlassAlignmentInBytes); -// Given an address p, return true if p can be used as an encoding base. -// (Some platforms have restrictions of what constitutes a valid base address). -bool CompressedKlassPointers::is_valid_base(address p) { -#ifdef AARCH64 - // Below 32G, base must be aligned to 4G. - // Above that point, base must be aligned to 32G - if (p < (address)(32 * G)) { - return is_aligned(p, 4 * G); + address const end = addr + len; + if (end <= (address)unscaled_max) { + _base = nullptr; + _shift = 0; + } else { + if (end <= (address)zerobased_max) { + _base = nullptr; + _shift = LogKlassAlignmentInBytes; + } else { + _base = addr; + _shift = 0; + } } - return is_aligned(p, (4 << LogKlassAlignmentInBytes) * G); -#else - return true; -#endif + _range = end - _base; + + DEBUG_ONLY(assert_is_valid_encoding(addr, len, _base, _shift);) } +#endif // !AARCH64 void CompressedKlassPointers::print_mode(outputStream* st) { st->print_cr("Narrow klass base: " PTR_FORMAT ", Narrow klass shift: %d, " @@ -114,19 +123,4 @@ void CompressedKlassPointers::print_mode(outputStream* st) { range()); } -void CompressedKlassPointers::set_base(address base) { - assert(UseCompressedClassPointers, "no compressed klass ptrs?"); - _base = base; -} - -void CompressedKlassPointers::set_shift(int shift) { - assert(shift == 0 || shift == LogKlassAlignmentInBytes, "invalid shift for klass ptrs"); - _shift = shift; -} - -void CompressedKlassPointers::set_range(size_t range) { - assert(UseCompressedClassPointers, "no compressed klass ptrs?"); - _range = range; -} - #endif // _LP64 diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index e4a2550a5996b..e871fe5bdcc0e 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -56,20 +56,24 @@ class CompressedKlassPointers : public AllStatic { // could use this info to optimize encoding. static size_t _range; - static void set_base(address base); - static void set_range(size_t range); - static void set_shift(int shift); + // Helper function for common cases. + static char* reserve_address_space_X(uintptr_t from, uintptr_t to, size_t size, size_t alignment, bool aslr); + static char* reserve_address_space_for_unscaled_encoding(size_t size, bool aslr); + static char* reserve_address_space_for_zerobased_encoding(size_t size, bool aslr); + static char* reserve_address_space_for_16bit_move(size_t size, bool aslr); + + DEBUG_ONLY(static void assert_is_valid_encoding(address addr, size_t len, address base, int shift);) public: - // Given an address p, return true if p can be used as an encoding base. - // (Some platforms have restrictions of what constitutes a valid base - // address). - static bool is_valid_base(address p); + // Reserve a range of memory that is to contain Klass strucutures which are referenced by narrow Klass IDs. + // If optimize_for_zero_base is true, the implementation will attempt to reserve optimized for zero-based encoding. + static char* reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base); // Given a klass range [addr, addr+len) and a given encoding scheme, assert that this scheme covers the range, then // set this encoding scheme. Used by CDS at runtime to re-instate the scheme used to pre-compute klass ids for - // archived heap objects. + // archived heap objects. In this case, we don't have the freedom to choose base and shift; they are handed to + // us from CDS. static void initialize_for_given_encoding(address addr, size_t len, address requested_base, int requested_shift); // Given an address range [addr, addr+len) which the encoding is supposed to diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index fa595142c68f6..2178a506e5ceb 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -4225,6 +4225,23 @@ bool InstanceKlass::should_clean_previous_versions_and_reset() { return ret; } +// This nulls out jmethodIDs for all methods in 'klass' +// It needs to be called explicitly for all previous versions of a class because these may not be cleaned up +// during class unloading. +// We can not use the jmethodID cache associated with klass directly because the 'previous' versions +// do not have the jmethodID cache filled in. Instead, we need to lookup jmethodID for each method and this +// is expensive - O(n) for one jmethodID lookup. For all contained methods it is O(n^2). +// The reason for expensive jmethodID lookup for each method is that there is no direct link between method and jmethodID. +void InstanceKlass::clear_jmethod_ids(InstanceKlass* klass) { + Array* method_refs = klass->methods(); + for (int k = 0; k < method_refs->length(); k++) { + Method* method = method_refs->at(k); + if (method != nullptr && method->is_obsolete()) { + method->clear_jmethod_id(); + } + } +} + // Purge previous versions before adding new previous versions of the class and // during class unloading. void InstanceKlass::purge_previous_version_list() { @@ -4268,6 +4285,7 @@ void InstanceKlass::purge_previous_version_list() { // Unlink from previous version list. assert(pv_node->class_loader_data() == loader_data, "wrong loader_data"); InstanceKlass* next = pv_node->previous_versions(); + clear_jmethod_ids(pv_node); // jmethodID maintenance for the unloaded class pv_node->link_previous_versions(nullptr); // point next to null last->link_previous_versions(next); // Delete this node directly. Nothing is referring to it and we don't diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 13cc080be5b06..62c861be89f47 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -1080,6 +1080,8 @@ class InstanceKlass: public Klass { bool idnum_can_increment() const { return has_been_redefined(); } inline jmethodID* methods_jmethod_ids_acquire() const; inline void release_set_methods_jmethod_ids(jmethodID* jmeths); + // This nulls out jmethodIDs for all methods in 'klass' + static void clear_jmethod_ids(InstanceKlass* klass); // Lock during initialization public: diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index c408bc23be7c7..0922fc4eac773 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -648,6 +648,16 @@ bool Method::init_method_counters(MethodCounters* counters) { return Atomic::replace_if_null(&_method_counters, counters); } +void Method::set_exception_handler_entered(int handler_bci) { + if (ProfileExceptionHandlers) { + MethodData* mdo = method_data(); + if (mdo != nullptr) { + BitData handler_data = mdo->exception_handler_bci_to_data(handler_bci); + handler_data.set_exception_handler_entered(); + } + } +} + int Method::extra_stack_words() { // not an inline function, to avoid a header dependency on Interpreter return extra_stack_entries() * Interpreter::stackElementSize; @@ -2263,6 +2273,20 @@ void Method::clear_jmethod_ids(ClassLoaderData* loader_data) { loader_data->jmethod_ids()->clear_all_methods(); } +void Method::clear_jmethod_id() { + // Being at a safepoint prevents racing against other class redefinitions + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); + // The jmethodID is not stored in the Method instance, we need to look it up first + jmethodID methodid = find_jmethod_id_or_null(); + // We need to make sure that jmethodID actually resolves to this method + // - multiple redefined versions may share jmethodID slots and if a method + // has already been rewired to a newer version we could be removing reference + // to a still existing method instance + if (methodid != nullptr && *((Method**)methodid) == this) { + *((Method**)methodid) = nullptr; + } +} + bool Method::has_method_vptr(const void* ptr) { Method m; // This assumes that the vtbl pointer is the first word of a C++ object. diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index 1dab585a66069..f05c54b992b1f 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -308,6 +308,9 @@ class Method : public Metadata { return _method_data; } + // mark an exception handler as entered (used to prune dead catch blocks in C2) + void set_exception_handler_entered(int handler_bci); + MethodCounters* method_counters() const { return _method_counters; } @@ -715,6 +718,7 @@ class Method : public Metadata { // Clear methods static void clear_jmethod_ids(ClassLoaderData* loader_data); + void clear_jmethod_id(); static void print_jmethod_ids_count(const ClassLoaderData* loader_data, outputStream* out) PRODUCT_RETURN; // Get this method's jmethodID -- allocate if it doesn't exist diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp index 71f950bafec1e..af0fcdeeb5f77 100644 --- a/src/hotspot/share/oops/methodData.cpp +++ b/src/hotspot/share/oops/methodData.cpp @@ -965,6 +965,12 @@ int MethodData::compute_allocation_size_in_bytes(const methodHandle& method) { if (args_cell > 0) { object_size += DataLayout::compute_size_in_bytes(args_cell); } + + if (ProfileExceptionHandlers && method()->has_exception_handler()) { + int num_exception_handlers = method()->exception_table_length(); + object_size += num_exception_handlers * single_exception_handler_data_size(); + } + return object_size; } @@ -1275,8 +1281,10 @@ void MethodData::initialize() { // for method entry so they don't fit with the framework for the // profiling of bytecodes). We store the offset within the MDO of // this area (or -1 if no parameter is profiled) + int parm_data_size = 0; if (parms_cell > 0) { - object_size += DataLayout::compute_size_in_bytes(parms_cell); + parm_data_size = DataLayout::compute_size_in_bytes(parms_cell); + object_size += parm_data_size; _parameters_type_data_di = data_size + extra_size + arg_data_size; DataLayout *dp = data_layout_at(data_size + extra_size + arg_data_size); dp->initialize(DataLayout::parameters_type_data_tag, 0, parms_cell); @@ -1284,6 +1292,17 @@ void MethodData::initialize() { _parameters_type_data_di = no_parameters; } + _exception_handler_data_di = data_size + extra_size + arg_data_size + parm_data_size; + if (ProfileExceptionHandlers && method()->has_exception_handler()) { + int num_exception_handlers = method()->exception_table_length(); + object_size += num_exception_handlers * single_exception_handler_data_size(); + ExceptionTableElement* exception_handlers = method()->exception_table_start(); + for (int i = 0; i < num_exception_handlers; i++) { + DataLayout *dp = exception_handler_data_at(i); + dp->initialize(DataLayout::bit_data_tag, exception_handlers[i].handler_pc, single_exception_handler_data_cell_count()); + } + } + // Set an initial hint. Don't use set_hint_di() because // first_di() may be out of bounds if data_size is 0. // In that situation, _hint_di is never used, but at @@ -1378,6 +1397,28 @@ ProfileData* MethodData::bci_to_data(int bci) { return bci_to_extra_data(bci, nullptr, false); } +DataLayout* MethodData::exception_handler_bci_to_data_helper(int bci) { + assert(ProfileExceptionHandlers, "not profiling"); + for (int i = 0; i < num_exception_handler_data(); i++) { + DataLayout* exception_handler_data = exception_handler_data_at(i); + if (exception_handler_data->bci() == bci) { + return exception_handler_data; + } + } + return nullptr; +} + +BitData* MethodData::exception_handler_bci_to_data_or_null(int bci) { + DataLayout* data = exception_handler_bci_to_data_helper(bci); + return data != nullptr ? new BitData(data) : nullptr; +} + +BitData MethodData::exception_handler_bci_to_data(int bci) { + DataLayout* data = exception_handler_bci_to_data_helper(bci); + assert(data != nullptr, "invalid bci"); + return BitData(data); +} + DataLayout* MethodData::next_extra(DataLayout* dp) { int nb_cells = 0; switch(dp->tag()) { diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp index 1d4dd7f19e98a..ee49bd189498c 100644 --- a/src/hotspot/share/oops/methodData.hpp +++ b/src/hotspot/share/oops/methodData.hpp @@ -491,10 +491,11 @@ class BitData : public ProfileData { enum : u1 { // null_seen: // saw a null operand (cast/aastore/instanceof) - null_seen_flag = DataLayout::first_flag + 0 + null_seen_flag = DataLayout::first_flag + 0, + exception_handler_entered_flag = null_seen_flag + 1 #if INCLUDE_JVMCI // bytecode threw any exception - , exception_seen_flag = null_seen_flag + 1 + , exception_seen_flag = exception_handler_entered_flag + 1 #endif }; enum { bit_cell_count = 0 }; // no additional data fields needed. @@ -525,6 +526,10 @@ class BitData : public ProfileData { void set_exception_seen() { set_flag_at(exception_seen_flag); } #endif + // true if a ex handler block at this bci was entered + bool exception_handler_entered() { return flag_at(exception_handler_entered_flag); } + void set_exception_handler_entered() { set_flag_at(exception_handler_entered_flag); } + // Code generation support static u1 null_seen_byte_constant() { return flag_number_to_constant(null_seen_flag); @@ -2063,7 +2068,11 @@ class MethodData : public Metadata { enum { no_parameters = -2, parameters_uninitialized = -1 }; int _parameters_type_data_di; + // data index of exception handler profiling data + int _exception_handler_data_di; + // Beginning of the data entries + // See comment in ciMethodData::load_data intptr_t _data[1]; // Helper for size computation @@ -2078,6 +2087,22 @@ class MethodData : public Metadata { return (DataLayout*) (((address)_data) + data_index); } + static int single_exception_handler_data_cell_count() { + return BitData::static_cell_count(); + } + + static int single_exception_handler_data_size() { + return DataLayout::compute_size_in_bytes(single_exception_handler_data_cell_count()); + } + + DataLayout* exception_handler_data_at(int exception_handler_index) const { + return data_layout_at(_exception_handler_data_di + (exception_handler_index * single_exception_handler_data_size())); + } + + int num_exception_handler_data() const { + return exception_handlers_data_size() / single_exception_handler_data_size(); + } + // Initialize an individual data segment. Returns the size of // the segment in bytes. int initialize_data(BytecodeStream* stream, int data_index); @@ -2143,6 +2168,8 @@ class MethodData : public Metadata { void clean_extra_data_helper(DataLayout* dp, int shift, bool reset = false); void verify_extra_data_clean(CleanExtraDataClosure* cl); + DataLayout* exception_handler_bci_to_data_helper(int bci); + public: void clean_extra_data(CleanExtraDataClosure* cl); @@ -2279,8 +2306,11 @@ class MethodData : public Metadata { } int parameters_size_in_bytes() const { - ParametersTypeData* param = parameters_type_data(); - return param == nullptr ? 0 : param->size_in_bytes(); + return pointer_delta_as_int((address) parameters_data_limit(), (address) parameters_data_base()); + } + + int exception_handlers_data_size() const { + return pointer_delta_as_int((address) exception_handler_data_limit(), (address) exception_handler_data_base()); } // Accessors @@ -2333,11 +2363,26 @@ class MethodData : public Metadata { return bci_to_extra_data(bci, nullptr, true); } + BitData* exception_handler_bci_to_data_or_null(int bci); + BitData exception_handler_bci_to_data(int bci); + // Add a handful of extra data records, for trap tracking. + // Only valid after 'set_size' is called at the end of MethodData::initialize DataLayout* extra_data_base() const { return limit_data_position(); } DataLayout* extra_data_limit() const { return (DataLayout*)((address)this + size_in_bytes()); } - DataLayout* args_data_limit() const { return (DataLayout*)((address)this + size_in_bytes() - - parameters_size_in_bytes()); } + // pointers to sections in extra data + DataLayout* args_data_limit() const { return parameters_data_base(); } + DataLayout* parameters_data_base() const { + assert(_parameters_type_data_di != parameters_uninitialized, "called too early"); + return _parameters_type_data_di != no_parameters ? data_layout_at(_parameters_type_data_di) : parameters_data_limit(); + } + DataLayout* parameters_data_limit() const { + assert(_parameters_type_data_di != parameters_uninitialized, "called too early"); + return exception_handler_data_base(); + } + DataLayout* exception_handler_data_base() const { return data_layout_at(_exception_handler_data_di); } + DataLayout* exception_handler_data_limit() const { return extra_data_limit(); } + int extra_data_size() const { return (int)((address)extra_data_limit() - (address)extra_data_base()); } static DataLayout* next_extra(DataLayout* dp); @@ -2385,8 +2430,12 @@ class MethodData : public Metadata { } int parameters_type_data_di() const { - assert(_parameters_type_data_di != parameters_uninitialized && _parameters_type_data_di != no_parameters, "no args type data"); - return _parameters_type_data_di; + assert(_parameters_type_data_di != parameters_uninitialized, "called too early"); + return _parameters_type_data_di != no_parameters ? _parameters_type_data_di : exception_handlers_data_di(); + } + + int exception_handlers_data_di() const { + return _exception_handler_data_di; } // Support for code generation diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 3e466539c8771..9a398e0dbb5ac 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -783,6 +783,8 @@ "more than this threshold") \ range(0, 100) \ \ + develop(bool, StressPrunedExceptionHandlers, false, \ + "Always prune exception handlers") \ // end of C2_FLAGS diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 00d3e24bce87f..a572e02bb9a22 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -596,10 +596,12 @@ void Compile::print_ideal_ir(const char* phase_name) { compile_id(), is_osr_compilation() ? " compile_kind='osr'" : "", phase_name); - xtty->print("%s", ss.as_string()); // print to tty would use xml escape encoding + } + + tty->print("%s", ss.as_string()); + + if (xtty != nullptr) { xtty->tail("ideal"); - } else { - tty->print("%s", ss.as_string()); } } #endif @@ -5148,7 +5150,7 @@ void Compile::end_method() { bool Compile::should_print_phase(CompilerPhaseType cpt) { #ifndef PRODUCT - if ((_directive->ideal_phase_mask() & CompilerPhaseTypeHelper::to_bitmask(cpt)) != 0) { + if (_directive->should_print_phase(cpt)) { return true; } #endif diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index bad44ca46d473..122da9b447c5e 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -941,7 +941,7 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { // Get the exception oop klass from its header Node* ex_klass_node = nullptr; - if (has_ex_handler() && !ex_type->klass_is_exact()) { + if (has_exception_handler() && !ex_type->klass_is_exact()) { Node* p = basic_plus_adr( ex_node, ex_node, oopDesc::klass_offset_in_bytes()); ex_klass_node = _gvn.transform(LoadKlassNode::make(_gvn, nullptr, immutable_memory(), p, TypeInstPtr::KLASS, TypeInstKlassPtr::OBJECT)); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index b271dd6f63ceb..1a8ae950305cf 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -181,9 +181,9 @@ bool GraphKit::stopped() { } -//-----------------------------has_ex_handler---------------------------------- +//-----------------------------has_exception_handler---------------------------------- // Tell if this method or any caller method has exception handlers. -bool GraphKit::has_ex_handler() { +bool GraphKit::has_exception_handler() { for (JVMState* jvmsp = jvms(); jvmsp != nullptr; jvmsp = jvmsp->caller()) { if (jvmsp->has_method() && jvmsp->method()->has_exception_handlers()) { return true; @@ -548,7 +548,7 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason) { // as hot if there has been at least one in this method. if (C->trap_count(reason) != 0 && method()->method_data()->trap_count(reason) != 0 - && has_ex_handler()) { + && has_exception_handler()) { treat_throw_as_hot = true; } } diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 92180371c067b..3f917ec882b07 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -194,7 +194,7 @@ class GraphKit : public Phase { bool stopped(); // Tell if this method or any caller method has exception handlers. - bool has_ex_handler(); + bool has_exception_handler(); // Save an exception without blowing stack contents or other JVM state. // (The extra pointer is stuck with add_req on the map, beyond the JVMS.) diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 6709f8314478f..3b493b5efa277 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -1297,15 +1297,12 @@ const Type* RShiftINode::Value(PhaseGVN* phase) const { if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) return TypeInt::INT; - if (t2 == TypeInt::INT) - return TypeInt::INT; - const TypeInt *r1 = t1->is_int(); // Handy access const TypeInt *r2 = t2->is_int(); // Handy access // If the shift is a constant, just shift the bounds of the type. // For example, if the shift is 31, we just propagate sign bits. - if (r2->is_con()) { + if (!r1->is_con() && r2->is_con()) { uint shift = r2->get_con(); shift &= BitsPerJavaInteger-1; // semantics of Java shifts // Shift by a multiple of 32 does nothing: @@ -1327,11 +1324,22 @@ const Type* RShiftINode::Value(PhaseGVN* phase) const { return ti; } - if( !r1->is_con() || !r2->is_con() ) + if (!r1->is_con() || !r2->is_con()) { + // If the left input is non-negative the result must also be non-negative, regardless of what the right input is. + if (r1->_lo >= 0) { + return TypeInt::make(0, r1->_hi, MAX2(r1->_widen, r2->_widen)); + } + + // Conversely, if the left input is negative then the result must be negative. + if (r1->_hi <= -1) { + return TypeInt::make(r1->_lo, -1, MAX2(r1->_widen, r2->_widen)); + } + return TypeInt::INT; + } // Signed shift right - return TypeInt::make( r1->get_con() >> (r2->get_con()&31) ); + return TypeInt::make(r1->get_con() >> (r2->get_con() & 31)); } //============================================================================= @@ -1359,15 +1367,12 @@ const Type* RShiftLNode::Value(PhaseGVN* phase) const { if (t1 == Type::BOTTOM || t2 == Type::BOTTOM) return TypeLong::LONG; - if (t2 == TypeInt::INT) - return TypeLong::LONG; - const TypeLong *r1 = t1->is_long(); // Handy access const TypeInt *r2 = t2->is_int (); // Handy access // If the shift is a constant, just shift the bounds of the type. // For example, if the shift is 63, we just propagate sign bits. - if (r2->is_con()) { + if (!r1->is_con() && r2->is_con()) { uint shift = r2->get_con(); shift &= (2*BitsPerJavaInteger)-1; // semantics of Java shifts // Shift by a multiple of 64 does nothing: @@ -1389,7 +1394,21 @@ const Type* RShiftLNode::Value(PhaseGVN* phase) const { return tl; } - return TypeLong::LONG; // Give up + if (!r1->is_con() || !r2->is_con()) { + // If the left input is non-negative the result must also be non-negative, regardless of what the right input is. + if (r1->_lo >= 0) { + return TypeLong::make(0, r1->_hi, MAX2(r1->_widen, r2->_widen)); + } + + // Conversely, if the left input is negative then the result must be negative. + if (r1->_hi <= -1) { + return TypeLong::make(r1->_lo, -1, MAX2(r1->_widen, r2->_widen)); + } + + return TypeLong::LONG; + } + + return TypeLong::make(r1->get_con() >> (r2->get_con() & 63)); } //============================================================================= diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index d2200c5591d1c..d214d69e2e1cd 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -175,6 +175,11 @@ class Scheduling { // Add a node to the current bundle void AddNodeToBundle(Node *n, const Block *bb); + // Return an integer less than, equal to, or greater than zero + // if the stack offset of the first argument is respectively + // less than, equal to, or greater than the second. + int compare_two_spill_nodes(Node* first, Node* second); + // Add a node to the list of available nodes void AddNodeToAvailableList(Node *n); @@ -2293,6 +2298,29 @@ Node * Scheduling::ChooseNodeToBundle() { return _available[0]; } +int Scheduling::compare_two_spill_nodes(Node* first, Node* second) { + assert(first->is_MachSpillCopy() && second->is_MachSpillCopy(), ""); + + OptoReg::Name first_src_lo = _regalloc->get_reg_first(first->in(1)); + OptoReg::Name first_dst_lo = _regalloc->get_reg_first(first); + OptoReg::Name second_src_lo = _regalloc->get_reg_first(second->in(1)); + OptoReg::Name second_dst_lo = _regalloc->get_reg_first(second); + + // Comparison between stack -> reg and stack -> reg + if (OptoReg::is_stack(first_src_lo) && OptoReg::is_stack(second_src_lo) && + OptoReg::is_reg(first_dst_lo) && OptoReg::is_reg(second_dst_lo)) { + return _regalloc->reg2offset(first_src_lo) - _regalloc->reg2offset(second_src_lo); + } + + // Comparison between reg -> stack and reg -> stack + if (OptoReg::is_stack(first_dst_lo) && OptoReg::is_stack(second_dst_lo) && + OptoReg::is_reg(first_src_lo) && OptoReg::is_reg(second_src_lo)) { + return _regalloc->reg2offset(first_dst_lo) - _regalloc->reg2offset(second_dst_lo); + } + + return 0; // Not comparable +} + void Scheduling::AddNodeToAvailableList(Node *n) { assert( !n->is_Proj(), "projections never directly made available" ); #ifndef PRODUCT @@ -2304,11 +2332,20 @@ void Scheduling::AddNodeToAvailableList(Node *n) { int latency = _current_latency[n->_idx]; - // Insert in latency order (insertion sort) + // Insert in latency order (insertion sort). If two MachSpillCopyNodes + // for stack spilling or unspilling have the same latency, we sort + // them in the order of stack offset. Some ports (e.g. aarch64) may also + // have more opportunities to do ld/st merging uint i; - for ( i=0; i < _available.size(); i++ ) - if (_current_latency[_available[i]->_idx] > latency) + for (i = 0; i < _available.size(); i++) { + if (_current_latency[_available[i]->_idx] > latency) { break; + } else if (_current_latency[_available[i]->_idx] == latency && + n->is_MachSpillCopy() && _available[i]->is_MachSpillCopy() && + compare_two_spill_nodes(n, _available[i]) > 0) { + break; + } + } // Special Check for compares following branches if( n->is_Mach() && _scheduled.size() > 0 ) { diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 0b867c69ebc74..1e2edbb4bf807 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1532,6 +1532,22 @@ void Parse::do_one_block() { // Set iterator to start of block. iter().reset_to_bci(block()->start()); + if (ProfileExceptionHandlers && block()->is_handler()) { + ciMethodData* methodData = method()->method_data(); + if (methodData->is_mature()) { + ciBitData data = methodData->exception_handler_bci_to_data(block()->start()); + if (!data.exception_handler_entered() || StressPrunedExceptionHandlers) { + // dead catch block + // Emit an uncommon trap instead of processing the block. + set_parse_bci(block()->start()); + uncommon_trap(Deoptimization::Reason_unreached, + Deoptimization::Action_reinterpret, + nullptr, "dead catch block"); + return; + } + } + } + CompileLog* log = C->log(); // Parse bytecodes diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index ce27cd2a7f172..c21f2b50418c8 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -25,6 +25,8 @@ #ifndef SHARE_OPTO_PHASETYPE_HPP #define SHARE_OPTO_PHASETYPE_HPP +#include "utilities/bitMap.inline.hpp" + #define COMPILER_PHASES(flags) \ flags(BEFORE_STRINGOPTS, "Before StringOpts") \ flags(AFTER_STRINGOPTS, "After StringOpts") \ @@ -100,9 +102,6 @@ class CompilerPhaseTypeHelper { static const char* to_description(CompilerPhaseType cpt) { return phase_descriptions[cpt]; } - static uint64_t to_bitmask(CompilerPhaseType cpt) { - return (UINT64_C(1) << cpt); - } }; static CompilerPhaseType find_phase(const char* str) { @@ -157,11 +156,16 @@ class PhaseNameIter { class PhaseNameValidator { private: + CHeapBitMap _phase_name_set; bool _valid; char* _bad; public: - PhaseNameValidator(ccstrlist option, uint64_t& mask) : _valid(true), _bad(nullptr) { + PhaseNameValidator(ccstrlist option) : + _phase_name_set(PHASE_NUM_TYPES, mtCompiler), + _valid(true), + _bad(nullptr) + { for (PhaseNameIter iter(option); *iter != nullptr && _valid; ++iter) { CompilerPhaseType cpt = find_phase(*iter); @@ -172,10 +176,10 @@ class PhaseNameValidator { strncpy(_bad, *iter, len); _valid = false; } else if (PHASE_ALL == cpt) { - mask = ~(UINT64_C(0)); + _phase_name_set.set_range(0, PHASE_NUM_TYPES); } else { - assert(cpt < 64, "out of bounds"); - mask |= CompilerPhaseTypeHelper::to_bitmask(cpt); + assert(cpt < PHASE_NUM_TYPES, "out of bounds"); + _phase_name_set.set_bit(cpt); } } } @@ -186,6 +190,11 @@ class PhaseNameValidator { } } + const BitMap& phase_name_set() const { + assert(is_valid(), "Use of invalid phase name set"); + return _phase_name_set; + } + bool is_valid() const { return _valid; } diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 539122746b2f5..f0a30ee0311ad 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3586,12 +3586,6 @@ JVM_ENTRY(jobject, JVM_NewInstanceFromConstructor(JNIEnv *env, jobject c, jobjec return res; JVM_END -// Atomic /////////////////////////////////////////////////////////////////////////////////////////// - -JVM_LEAF(jboolean, JVM_SupportsCX8()) - return VM_Version::supports_cx8(); -JVM_END - JVM_ENTRY(void, JVM_InitializeFromArchive(JNIEnv* env, jclass cls)) Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls)); assert(k->is_klass(), "just checking"); diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 26b7abf68594f..eee5b2a70fa5d 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -3139,6 +3139,13 @@ bool JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample() { return false; } + // If the current thread is attaching from native and its Java thread object + // is being allocated, things are not ready for allocation sampling. + JavaThread* jt = JavaThread::cast(thread); + if (jt->is_attaching_via_jni() && jt->threadObj() == nullptr) { + return false; + } + if (MultiArray_lock->owner() == thread) { return false; } diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index c9c3974b591a5..21122539af847 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -119,14 +119,6 @@ void GrowableCache::recache() { _listener_fun(_this_obj,_cache); } -bool GrowableCache::equals(void* v, GrowableElement *e2) { - GrowableElement *e1 = (GrowableElement *) v; - assert(e1 != nullptr, "e1 != nullptr"); - assert(e2 != nullptr, "e2 != nullptr"); - - return e1->equals(e2); -} - // // class GrowableCache - public methods // @@ -163,8 +155,8 @@ GrowableElement* GrowableCache::at(int index) { return e; } -int GrowableCache::find(GrowableElement* e) { - return _elements->find(e, GrowableCache::equals); +int GrowableCache::find(const GrowableElement* e) const { + return _elements->find_if([&](const GrowableElement* other_e) { return e->equals(other_e); }); } // append a copy of the element to the end of the collection @@ -216,7 +208,7 @@ void JvmtiBreakpoint::copy(JvmtiBreakpoint& bp) { _class_holder = OopHandle(JvmtiExport::jvmti_oop_storage(), bp._class_holder.resolve()); } -bool JvmtiBreakpoint::equals(JvmtiBreakpoint& bp) { +bool JvmtiBreakpoint::equals(const JvmtiBreakpoint& bp) const { return _method == bp._method && _bci == bp._bci; } diff --git a/src/hotspot/share/prims/jvmtiImpl.hpp b/src/hotspot/share/prims/jvmtiImpl.hpp index 851dec72ee7db..c93abe5eedf88 100644 --- a/src/hotspot/share/prims/jvmtiImpl.hpp +++ b/src/hotspot/share/prims/jvmtiImpl.hpp @@ -66,9 +66,9 @@ class JvmtiBreakpoints; class GrowableElement : public CHeapObj { public: virtual ~GrowableElement() {} - virtual address getCacheValue() =0; - virtual bool equals(GrowableElement* e) =0; - virtual GrowableElement *clone() =0; + virtual address getCacheValue() =0; + virtual bool equals(const GrowableElement* e) const =0; + virtual GrowableElement* clone() =0; }; class GrowableCache { @@ -88,8 +88,6 @@ class GrowableCache { // (but NOT when cached elements are recomputed). void (*_listener_fun)(void *, address*); - static bool equals(void *, GrowableElement *); - // recache all elements after size change, notify listener void recache(); @@ -104,7 +102,7 @@ class GrowableCache { // get the value of the index element in the collection GrowableElement* at(int index); // find the index of the element, -1 if it doesn't exist - int find(GrowableElement* e); + int find(const GrowableElement* e) const; // append a copy of the element to the end of the collection, notify listener void append(GrowableElement* e); // remove the element at index, notify listener @@ -165,7 +163,7 @@ class JvmtiBreakpoint : public GrowableElement { JvmtiBreakpoint() : _method(nullptr), _bci(0) {} JvmtiBreakpoint(Method* m_method, jlocation location); virtual ~JvmtiBreakpoint(); - bool equals(JvmtiBreakpoint& bp); + bool equals(const JvmtiBreakpoint& bp) const; void copy(JvmtiBreakpoint& bp); address getBcp() const; void each_method_version_do(method_action meth_act); @@ -177,7 +175,7 @@ class JvmtiBreakpoint : public GrowableElement { // GrowableElement implementation address getCacheValue() { return getBcp(); } - bool equals(GrowableElement* e) { return equals((JvmtiBreakpoint&) *e); } + bool equals(const GrowableElement* e) const { return equals((const JvmtiBreakpoint&) *e); } GrowableElement *clone() { JvmtiBreakpoint *bp = new JvmtiBreakpoint(); diff --git a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp index 1737bfd6a9f4c..0df26a1b1c531 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp @@ -78,17 +78,27 @@ inline JvmtiThreadState* JvmtiThreadState::state_for_while_locked(JavaThread *th NoSafepointVerifier nsv; // oop is safe to use. - if (thread_oop == nullptr) { // Then thread should not be null (see assert above). - thread_oop = thread->jvmti_vthread() != nullptr ? thread->jvmti_vthread() : thread->threadObj(); - } - // In a case of unmounted virtual thread the thread can be null. JvmtiThreadState *state = thread == nullptr ? nullptr : thread->jvmti_thread_state(); - if (state == nullptr && thread != nullptr && thread->is_exiting()) { - // Don't add a JvmtiThreadState to a thread that is exiting. + if (state == nullptr && thread != nullptr && + (thread->is_exiting() || thread->is_attaching_via_jni())) { + // Don't add a JvmtiThreadState to a thread that is exiting or is attaching. + // When a thread is attaching, it may not have a Java level thread object + // created yet. return nullptr; } + + // Make sure we don't see an incomplete state. An incomplete state can cause + // a duplicate JvmtiThreadState being created below and bound to the 'thread' + // incorrectly, which leads to stale JavaThread* from the JvmtiThreadState + // after the thread exits. + assert(state == nullptr || state->get_thread_oop() != nullptr, + "incomplete state"); + + if (thread_oop == nullptr) { // Then thread should not be null (see assert above). + thread_oop = thread->jvmti_vthread() != nullptr ? thread->jvmti_vthread() : thread->threadObj(); + } if (state == nullptr || state->get_thread_oop() != thread_oop) { // Check if java_lang_Thread already has a link to the JvmtiThreadState. if (thread_oop != nullptr) { // thread_oop can be null during early VMStart. @@ -98,6 +108,7 @@ inline JvmtiThreadState* JvmtiThreadState::state_for_while_locked(JavaThread *th state = new JvmtiThreadState(thread, thread_oop); } } + assert(state != nullptr, "sanity check"); return state; } diff --git a/src/hotspot/share/prims/scopedMemoryAccess.cpp b/src/hotspot/share/prims/scopedMemoryAccess.cpp index 76bfd3a5a976d..62546c4fe51f6 100644 --- a/src/hotspot/share/prims/scopedMemoryAccess.cpp +++ b/src/hotspot/share/prims/scopedMemoryAccess.cpp @@ -35,54 +35,72 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/vframe.inline.hpp" -class CloseScopedMemoryFindOopClosure : public OopClosure { - oop _deopt; - bool _found; - -public: - CloseScopedMemoryFindOopClosure(jobject deopt) : - _deopt(JNIHandles::resolve(deopt)), - _found(false) {} - - template - void do_oop_work(T* p) { - if (_found) { - return; +static bool is_in_scoped_access(JavaThread* jt, oop session) { + const int max_critical_stack_depth = 10; + int depth = 0; + for (vframeStream stream(jt); !stream.at_end(); stream.next()) { + Method* m = stream.method(); + if (m->is_scoped()) { + StackValueCollection* locals = stream.asJavaVFrame()->locals(); + for (int i = 0; i < locals->size(); i++) { + StackValue* var = locals->at(i); + if (var->type() == T_OBJECT) { + if (var->get_obj() == session) { + assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth); + return true; + } + } + } + break; } - if (RawAccess<>::oop_load(p) == _deopt) { - _found = true; + depth++; +#ifndef ASSERT + if (depth >= max_critical_stack_depth) { + break; } +#endif } - virtual void do_oop(oop* p) { - do_oop_work(p); - } + return false; +} + +class ScopedAsyncExceptionHandshake : public AsyncExceptionHandshake { + OopHandle _session; - virtual void do_oop(narrowOop* p) { - do_oop_work(p); +public: + ScopedAsyncExceptionHandshake(OopHandle& session, OopHandle& error) + : AsyncExceptionHandshake(error), + _session(session) {} + + ~ScopedAsyncExceptionHandshake() { + _session.release(Universe::vm_global()); } - bool found() { - return _found; + virtual void do_thread(Thread* thread) { + JavaThread* jt = JavaThread::cast(thread); + ResourceMark rm; + if (is_in_scoped_access(jt, _session.resolve())) { + // Throw exception to unwind out from the scoped access + AsyncExceptionHandshake::do_thread(thread); + } } }; class CloseScopedMemoryClosure : public HandshakeClosure { - jobject _deopt; + jobject _session; + jobject _error; public: - jboolean _found; - - CloseScopedMemoryClosure(jobject deopt, jobject exception) + CloseScopedMemoryClosure(jobject session, jobject error) : HandshakeClosure("CloseScopedMemory") - , _deopt(deopt) - , _found(false) {} + , _session(session) + , _error(error) {} void do_thread(Thread* thread) { - JavaThread* jt = JavaThread::cast(thread); if (!jt->has_last_Java_frame()) { + // No frames; not in a scoped memory access return; } @@ -97,44 +115,27 @@ class CloseScopedMemoryClosure : public HandshakeClosure { } ResourceMark rm; - if (_deopt != nullptr && last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) { - CloseScopedMemoryFindOopClosure cl(_deopt); - CompiledMethod* cm = last_frame.cb()->as_compiled_method(); - - /* FIXME: this doesn't work if reachability fences are violated by C2 - last_frame.oops_do(&cl, nullptr, ®ister_map); - if (cl.found()) { - //Found the deopt oop in a compiled method; deoptimize. - Deoptimization::deoptimize(jt, last_frame); - } - so... we unconditionally deoptimize, for now: */ + if (last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) { + // FIXME: we would like to conditionally deoptimize only if the corresponding + // _session is reachable from the frame, but reachabilityFence doesn't currently + // work the way it should. Therefore we deopt unconditionally for now. Deoptimization::deoptimize(jt, last_frame); } - const int max_critical_stack_depth = 10; - int depth = 0; - for (vframeStream stream(jt); !stream.at_end(); stream.next()) { - Method* m = stream.method(); - if (m->is_scoped()) { - StackValueCollection* locals = stream.asJavaVFrame()->locals(); - for (int i = 0; i < locals->size(); i++) { - StackValue* var = locals->at(i); - if (var->type() == T_OBJECT) { - if (var->get_obj() == JNIHandles::resolve(_deopt)) { - assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth); - _found = true; - return; - } - } - } - break; - } - depth++; -#ifndef ASSERT - if (depth >= max_critical_stack_depth) { - break; - } -#endif + if (jt->has_async_exception_condition()) { + // Target thread just about to throw an async exception using async handshakes, + // we will then unwind out from the scoped memory access. + return; + } + + if (is_in_scoped_access(jt, JNIHandles::resolve(_session))) { + // We have found that the target thread is inside of a scoped access. + // An asynchronous handshake is sent to the target thread, telling it + // to throw an exception, which will unwind the target thread out from + // the scoped access. + OopHandle session(Universe::vm_global(), JNIHandles::resolve(_session)); + OopHandle error(Universe::vm_global(), JNIHandles::resolve(_error)); + jt->install_async_exception(new ScopedAsyncExceptionHandshake(session, error)); } } }; @@ -146,10 +147,9 @@ class CloseScopedMemoryClosure : public HandshakeClosure { * class annotated with the '@Scoped' annotation), and whose local variables mention the session being * closed (deopt), this method returns false, signalling that the session cannot be closed safely. */ -JVM_ENTRY(jboolean, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject deopt, jobject exception)) - CloseScopedMemoryClosure cl(deopt, exception); +JVM_ENTRY(void, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject session, jobject error)) + CloseScopedMemoryClosure cl(session, error); Handshake::execute(&cl); - return !cl._found; JVM_END /// JVM_RegisterUnsafeMethods @@ -157,14 +157,14 @@ JVM_END #define PKG_MISC "Ljdk/internal/misc/" #define PKG_FOREIGN "Ljdk/internal/foreign/" -#define MEMACCESS "ScopedMemoryAccess" -#define SCOPE PKG_FOREIGN "MemorySessionImpl;" +#define SCOPED_SESSION PKG_FOREIGN "MemorySessionImpl;" +#define SCOPED_ERROR PKG_MISC "ScopedMemoryAccess$ScopedAccessError;" #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) static JNINativeMethod jdk_internal_misc_ScopedMemoryAccess_methods[] = { - {CC "closeScope0", CC "(" SCOPE ")Z", FN_PTR(ScopedMemoryAccess_closeScope)}, + {CC "closeScope0", CC "(" SCOPED_SESSION SCOPED_ERROR ")V", FN_PTR(ScopedMemoryAccess_closeScope)}, }; #undef CC @@ -172,8 +172,8 @@ static JNINativeMethod jdk_internal_misc_ScopedMemoryAccess_methods[] = { #undef PKG_MISC #undef PKG_FOREIGN -#undef MEMACCESS -#undef SCOPE +#undef SCOPED_SESSION +#undef SCOPED_ERROR // This function is exported, used by NativeLookup. diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index 74c56f169d6f4..3d732f3292d66 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -70,13 +70,36 @@ ( arrayOopDesc::header_size(T_DOUBLE) * HeapWordSize \ + ((julong)max_jint * sizeof(double)) ) - #define UNSAFE_ENTRY(result_type, header) \ JVM_ENTRY(static result_type, header) #define UNSAFE_LEAF(result_type, header) \ JVM_LEAF(static result_type, header) +// Note that scoped accesses (cf. scopedMemoryAccess.cpp) can install +// an async handshake on the entry to an Unsafe method. When that happens, +// it is expected that we are not allowed to touch the underlying memory +// that might have gotten unmapped. Therefore, we check at the entry +// to unsafe functions, if we have such async exception conditions, +// and return immediately if that is the case. +// +// We also use NoSafepointVerifier to block potential safepoints. +// It would be problematic if an async exception handshake were installed later on +// during another safepoint in the function, but before the memory access happens, +// as the memory will be freed after the handshake is installed. We must notice +// the installed handshake and return early before doing the memory access to prevent +// accesses to freed memory. +// +// Note also that we MUST do a scoped memory access in the VM (or Java) thread +// state. Since we rely on a handshake to check for threads that are accessing +// scoped memory, and we need the handshaking thread to wait until we get to a +// safepoint, in order to make sure we are not in the middle of accessing memory +// that is about to be freed. (i.e. there can be no UNSAFE_LEAF_SCOPED) +#define UNSAFE_ENTRY_SCOPED(result_type, header) \ + JVM_ENTRY(static result_type, header) \ + if (thread->has_async_exception_condition()) {return (result_type)0;} \ + NoSafepointVerifier nsv; + #define UNSAFE_END JVM_END @@ -279,11 +302,11 @@ UNSAFE_ENTRY(jobject, Unsafe_GetUncompressedObject(JNIEnv *env, jobject unsafe, #define DEFINE_GETSETOOP(java_type, Type) \ \ -UNSAFE_ENTRY(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \ +UNSAFE_ENTRY_SCOPED(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \ return MemoryAccess(thread, obj, offset).get(); \ } UNSAFE_END \ \ -UNSAFE_ENTRY(void, Unsafe_Put##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \ +UNSAFE_ENTRY_SCOPED(void, Unsafe_Put##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \ MemoryAccess(thread, obj, offset).put(x); \ } UNSAFE_END \ \ @@ -302,11 +325,11 @@ DEFINE_GETSETOOP(jdouble, Double); #define DEFINE_GETSETOOP_VOLATILE(java_type, Type) \ \ -UNSAFE_ENTRY(java_type, Unsafe_Get##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \ +UNSAFE_ENTRY_SCOPED(java_type, Unsafe_Get##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \ return MemoryAccess(thread, obj, offset).get_volatile(); \ } UNSAFE_END \ \ -UNSAFE_ENTRY(void, Unsafe_Put##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \ +UNSAFE_ENTRY_SCOPED(void, Unsafe_Put##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \ MemoryAccess(thread, obj, offset).put_volatile(x); \ } UNSAFE_END \ \ @@ -362,7 +385,7 @@ UNSAFE_LEAF(void, Unsafe_FreeMemory0(JNIEnv *env, jobject unsafe, jlong addr)) { os::free(p); } UNSAFE_END -UNSAFE_ENTRY(void, Unsafe_SetMemory0(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong size, jbyte value)) { +UNSAFE_ENTRY_SCOPED(void, Unsafe_SetMemory0(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong size, jbyte value)) { size_t sz = (size_t)size; oop base = JNIHandles::resolve(obj); @@ -371,7 +394,7 @@ UNSAFE_ENTRY(void, Unsafe_SetMemory0(JNIEnv *env, jobject unsafe, jobject obj, j Copy::fill_to_memory_atomic(p, sz, value); } UNSAFE_END -UNSAFE_ENTRY(void, Unsafe_CopyMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size)) { +UNSAFE_ENTRY_SCOPED(void, Unsafe_CopyMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size)) { size_t sz = (size_t)size; oop srcp = JNIHandles::resolve(srcObj); @@ -390,39 +413,19 @@ UNSAFE_ENTRY(void, Unsafe_CopyMemory0(JNIEnv *env, jobject unsafe, jobject srcOb } } UNSAFE_END -// This function is a leaf since if the source and destination are both in native memory -// the copy may potentially be very large, and we don't want to disable GC if we can avoid it. -// If either source or destination (or both) are on the heap, the function will enter VM using -// JVM_ENTRY_FROM_LEAF -UNSAFE_LEAF(void, Unsafe_CopySwapMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size, jlong elemSize)) { +UNSAFE_ENTRY_SCOPED(void, Unsafe_CopySwapMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size, jlong elemSize)) { size_t sz = (size_t)size; size_t esz = (size_t)elemSize; - if (srcObj == nullptr && dstObj == nullptr) { - // Both src & dst are in native memory - address src = (address)srcOffset; - address dst = (address)dstOffset; - - { - JavaThread* thread = JavaThread::thread_from_jni_environment(env); - GuardUnsafeAccess guard(thread); - Copy::conjoint_swap(src, dst, sz, esz); - } - } else { - // At least one of src/dst are on heap, transition to VM to access raw pointers - - JVM_ENTRY_FROM_LEAF(env, void, Unsafe_CopySwapMemory0) { - oop srcp = JNIHandles::resolve(srcObj); - oop dstp = JNIHandles::resolve(dstObj); + oop srcp = JNIHandles::resolve(srcObj); + oop dstp = JNIHandles::resolve(dstObj); - address src = (address)index_oop_from_field_offset_long(srcp, srcOffset); - address dst = (address)index_oop_from_field_offset_long(dstp, dstOffset); + address src = (address)index_oop_from_field_offset_long(srcp, srcOffset); + address dst = (address)index_oop_from_field_offset_long(dstp, dstOffset); - { - GuardUnsafeAccess guard(thread); - Copy::conjoint_swap(src, dst, sz, esz); - } - } JVM_END + { + GuardUnsafeAccess guard(thread); + Copy::conjoint_swap(src, dst, sz, esz); } } UNSAFE_END @@ -718,13 +721,13 @@ UNSAFE_ENTRY(jobject, Unsafe_CompareAndExchangeReference(JNIEnv *env, jobject un return JNIHandles::make_local(THREAD, res); } UNSAFE_END -UNSAFE_ENTRY(jint, Unsafe_CompareAndExchangeInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) { +UNSAFE_ENTRY_SCOPED(jint, Unsafe_CompareAndExchangeInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) { oop p = JNIHandles::resolve(obj); volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset); return Atomic::cmpxchg(addr, e, x); } UNSAFE_END -UNSAFE_ENTRY(jlong, Unsafe_CompareAndExchangeLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) { +UNSAFE_ENTRY_SCOPED(jlong, Unsafe_CompareAndExchangeLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) { oop p = JNIHandles::resolve(obj); volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset); return Atomic::cmpxchg(addr, e, x); @@ -739,13 +742,13 @@ UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetReference(JNIEnv *env, jobject unsafe return ret == e; } UNSAFE_END -UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) { +UNSAFE_ENTRY_SCOPED(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) { oop p = JNIHandles::resolve(obj); volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset); return Atomic::cmpxchg(addr, e, x) == e; } UNSAFE_END -UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) { +UNSAFE_ENTRY_SCOPED(jboolean, Unsafe_CompareAndSetLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) { oop p = JNIHandles::resolve(obj); volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset); return Atomic::cmpxchg(addr, e, x) == e; diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index c15686dc87c1b..230129ea2df31 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -401,7 +401,7 @@ WB_ENTRY(jboolean, WB_isObjectInOldGen(JNIEnv* env, jobject o, jobject obj)) if (UseG1GC) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); const HeapRegion* hr = g1h->heap_region_containing(p); - return !(hr->is_young()); + return hr->is_old_or_humongous(); } #endif #if INCLUDE_PARALLELGC @@ -2593,6 +2593,32 @@ WB_ENTRY(void, WB_UnlockCritical(JNIEnv* env, jobject wb)) GCLocker::unlock_critical(thread); WB_END +WB_ENTRY(void, WB_PinObject(JNIEnv* env, jobject wb, jobject o)) +#if INCLUDE_G1GC + if (!UseG1GC) { + ShouldNotReachHere(); + return; + } + oop obj = JNIHandles::resolve(o); + G1CollectedHeap::heap()->pin_object(thread, obj); +#else + ShouldNotReachHere(); +#endif // INCLUDE_G1GC +WB_END + +WB_ENTRY(void, WB_UnpinObject(JNIEnv* env, jobject wb, jobject o)) +#if INCLUDE_G1GC + if (!UseG1GC) { + ShouldNotReachHere(); + return; + } + oop obj = JNIHandles::resolve(o); + G1CollectedHeap::heap()->unpin_object(thread, obj); +#else + ShouldNotReachHere(); +#endif // INCLUDE_G1GC +WB_END + WB_ENTRY(jboolean, WB_SetVirtualThreadsNotifyJvmtiMode(JNIEnv* env, jobject wb, jboolean enable)) if (!Continuations::enabled()) { tty->print_cr("WB error: must be Continuations::enabled()!"); @@ -2617,6 +2643,10 @@ WB_ENTRY(void, WB_PreTouchMemory(JNIEnv* env, jobject wb, jlong addr, jlong size } WB_END +WB_ENTRY(void, WB_CleanMetaspaces(JNIEnv* env, jobject target)) + ClassLoaderDataGraph::safepoint_and_clean_metaspaces(); +WB_END + #define CC (char*) static JNINativeMethod methods[] = { @@ -2899,8 +2929,11 @@ static JNINativeMethod methods[] = { {CC"lockCritical", CC"()V", (void*)&WB_LockCritical}, {CC"unlockCritical", CC"()V", (void*)&WB_UnlockCritical}, + {CC"pinObject", CC"(Ljava/lang/Object;)V", (void*)&WB_PinObject}, + {CC"unpinObject", CC"(Ljava/lang/Object;)V", (void*)&WB_UnpinObject}, {CC"setVirtualThreadsNotifyJvmtiMode", CC"(Z)Z", (void*)&WB_SetVirtualThreadsNotifyJvmtiMode}, {CC"preTouchMemory", CC"(JJ)V", (void*)&WB_PreTouchMemory}, + {CC"cleanMetaspaces", CC"()V", (void*)&WB_CleanMetaspaces}, }; diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index 2fdb820982a93..f1f8888653e4a 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -35,7 +35,9 @@ const char* Abstract_VM_Version::_s_internal_vm_info_string = Abstract_VM_Versio uint64_t Abstract_VM_Version::_features = 0; const char* Abstract_VM_Version::_features_string = ""; +#ifndef SUPPORTS_NATIVE_CX8 bool Abstract_VM_Version::_supports_cx8 = false; +#endif bool Abstract_VM_Version::_supports_atomic_getset4 = false; bool Abstract_VM_Version::_supports_atomic_getset8 = false; bool Abstract_VM_Version::_supports_atomic_getadd4 = false; @@ -240,6 +242,16 @@ const char* Abstract_VM_Version::internal_vm_info_string() { #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.2 (VS2022)" #elif _MSC_VER == 1933 #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.3 (VS2022)" + #elif _MSC_VER == 1934 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.4 (VS2022)" + #elif _MSC_VER == 1935 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.5 (VS2022)" + #elif _MSC_VER == 1936 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.6 (VS2022)" + #elif _MSC_VER == 1937 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.7 (VS2022)" + #elif _MSC_VER == 1938 + #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.8 (VS2022)" #else #define HOTSPOT_BUILD_COMPILER "unknown MS VC++:" XSTR(_MSC_VER) #endif diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index d9ed84f47510a..d8ffca8de81fb 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -59,7 +59,9 @@ class Abstract_VM_Version: AllStatic { static const char* _features_string; // These are set by machine-dependent initializations +#ifndef SUPPORTS_NATIVE_CX8 static bool _supports_cx8; +#endif static bool _supports_atomic_getset4; static bool _supports_atomic_getset8; static bool _supports_atomic_getadd4; @@ -133,6 +135,8 @@ class Abstract_VM_Version: AllStatic { static void print_platform_virtualization_info(outputStream*) { } // does HW support an 8-byte compare-exchange operation? + // Required to be true but still dynamically checked at runtime + // for platforms that don't set SUPPORTS_NATIVE_CX8 static bool supports_cx8() { #ifdef SUPPORTS_NATIVE_CX8 return true; diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 8b50389c5c3b1..23ec9fd157328 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -515,6 +515,7 @@ static SpecialFlag const special_jvm_flags[] = { { "DynamicDumpSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, + { "RegisterFinalizersAtInit", JDK_Version::jdk(22), JDK_Version::jdk(23), JDK_Version::jdk(24) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "DefaultMaxRAMFraction", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() }, diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index ac0ce49d26e56..399a78fd3fce1 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.hpp @@ -55,11 +55,13 @@ enum ScopedFenceType { class Atomic : AllStatic { public: - // Atomic operations on int64 types are not available on all 32-bit - // platforms. If atomic ops on int64 are defined here they must only - // be used from code that verifies they are available at runtime and - // can provide an alternative action if not - see supports_cx8() for - // a means to test availability. + // Atomic operations on int64 types are required to be available on + // all platforms. At a minimum a 64-bit cmpxchg must be available + // from which other atomic operations can be constructed if needed. + // The legacy `Abstract_VMVersion::supports_cx8()` function used to + // indicate if this support existed, allowing for alternative lock- + // based mechanism to be used. But today this function is required + // to return true and in the future will be removed entirely. // The memory operations that are mentioned with each of the atomic // function families come from src/share/vm/runtime/orderAccess.hpp, diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 8d76c35af2a46..626c65755818e 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -1504,21 +1504,21 @@ static void jvmti_yield_cleanup(JavaThread* thread, ContinuationWrapper& cont) { #endif // INCLUDE_JVMTI #ifdef ASSERT -static bool monitors_on_stack(JavaThread* thread) { - ContinuationEntry* ce = thread->last_continuation(); - RegisterMap map(thread, - RegisterMap::UpdateMap::include, - RegisterMap::ProcessFrames::include, - RegisterMap::WalkContinuation::skip); - map.set_include_argument_oops(false); - for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) { - if ((f.is_interpreted_frame() && ContinuationHelper::InterpretedFrame::is_owning_locks(f)) || - (f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f))) { - return true; - } - } - return false; -} +// static bool monitors_on_stack(JavaThread* thread) { +// ContinuationEntry* ce = thread->last_continuation(); +// RegisterMap map(thread, +// RegisterMap::UpdateMap::include, +// RegisterMap::ProcessFrames::include, +// RegisterMap::WalkContinuation::skip); +// map.set_include_argument_oops(false); +// for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) { +// if ((f.is_interpreted_frame() && ContinuationHelper::InterpretedFrame::is_owning_locks(f)) || +// (f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f))) { +// return true; +// } +// } +// return false; +// } bool FreezeBase::interpreted_native_or_deoptimized_on_stack() { ContinuationEntry* ce = _thread->last_continuation(); @@ -1581,8 +1581,8 @@ static inline int freeze_internal(JavaThread* current, intptr_t* const sp) { assert(entry->is_virtual_thread() == (entry->scope(current) == java_lang_VirtualThread::vthread_scope()), ""); - assert(monitors_on_stack(current) == ((current->held_monitor_count() - current->jni_monitor_count()) > 0), - "Held monitor count and locks on stack invariant: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count()); + // assert(monitors_on_stack(current) == ((current->held_monitor_count() - current->jni_monitor_count()) > 0), + // "Held monitor count and locks on stack invariant: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count()); if (entry->is_pinned() || current->held_monitor_count() > 0) { log_develop_debug(continuations)("PINNED due to critical section/hold monitor"); diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index f809cd031c358..a5f2e09afaa8e 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -2444,6 +2444,17 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr nm->method()->set_not_compilable("give up compiling", CompLevel_full_optimization); } + if (ProfileExceptionHandlers && trap_mdo != nullptr) { + BitData* exception_handler_data = trap_mdo->exception_handler_bci_to_data_or_null(trap_bci); + if (exception_handler_data != nullptr) { + // uncommon trap at the start of an exception handler. + // C2 generates these for un-entered exception handlers. + // mark the handler as entered to avoid generating + // another uncommon trap the next time the handler is compiled + exception_handler_data->set_exception_handler_entered(); + } + } + } // Free marked resources } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index b780b040f9416..dce90c78eb475 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -295,6 +295,9 @@ const int ObjectAlignmentInBytes = 8; product(bool, UseInlineCaches, true, \ "Use Inline Caches for virtual calls ") \ \ + product(size_t, InlineCacheBufferSize, 10*K, EXPERIMENTAL, \ + "InlineCacheBuffer size") \ + \ product(bool, InlineArrayCopy, true, DIAGNOSTIC, \ "Inline arraycopy native that is known to be part of " \ "base library DLL") \ @@ -667,8 +670,8 @@ const int ObjectAlignmentInBytes = 8; "Print JVM warnings to output stream") \ \ product(bool, RegisterFinalizersAtInit, true, \ - "Register finalizable objects at end of Object. or " \ - "after allocation") \ + "(Deprecated) Register finalizable objects at end of " \ + "Object. or after allocation") \ \ develop(bool, RegisterReferences, true, \ "Tell whether the VM should register soft/weak/final/phantom " \ @@ -733,6 +736,10 @@ const int ObjectAlignmentInBytes = 8; "at one time (minimum is 1024).") \ range(1024, max_jint) \ \ + product(intx, MonitorUnlinkBatch, 500, DIAGNOSTIC, \ + "The maximum number of monitors to unlink in one batch. ") \ + range(1, max_jint) \ + \ product(int, MonitorUsedDeflationThreshold, 90, DIAGNOSTIC, \ "Percentage of used monitors before triggering deflation (0 is " \ "off). The check is performed on GuaranteedSafepointInterval, " \ @@ -835,6 +842,13 @@ const int ObjectAlignmentInBytes = 8; "JVM aborts, producing an error log and core/mini dump, on the " \ "first occurrence of an out-of-memory error thrown from JVM") \ \ + product(intx, UserThreadWaitAttemptsAtExit, 30, \ + "The number of times to wait for user threads to stop executing " \ + "native code during JVM exit. Each wait lasts 10 milliseconds. " \ + "The maximum number of waits is 1000, to wait at most 10 " \ + "seconds.") \ + range(0, 1000) \ + \ /* tracing */ \ \ develop(bool, StressRewriter, false, \ @@ -1984,6 +1998,13 @@ const int ObjectAlignmentInBytes = 8; "more eagerly at the cost of higher overhead. A value of 0 " \ "(default) disables native heap trimming.") \ range(0, UINT_MAX) \ + \ + develop(bool, SimulateFullAddressSpace, false, \ + "Simulates a very populated, fragmented address space; no " \ + "targeted reservations will succeed.") \ + \ + product(bool, ProfileExceptionHandlers, true, \ + "Profile exception handlers") \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp index 9de1f8126d100..403ff1d9ea292 100644 --- a/src/hotspot/share/runtime/interfaceSupport.inline.hpp +++ b/src/hotspot/share/runtime/interfaceSupport.inline.hpp @@ -406,14 +406,6 @@ extern "C" { \ VM_LEAF_BASE(result_type, header) -#define JVM_ENTRY_FROM_LEAF(env, result_type, header) \ - { { \ - JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ - ThreadInVMfromNative __tiv(thread); \ - debug_only(VMNativeEntryWrapper __vew;) \ - VM_ENTRY_BASE_FROM_LEAF(result_type, header, thread) - - #define JVM_END } } #endif // SHARE_RUNTIME_INTERFACESUPPORT_INLINE_HPP diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index d7f6d7b3d03c6..71bf0cc1d49d3 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -225,9 +225,9 @@ class JavaThread: public Thread { friend class AsyncExceptionHandshake; friend class HandshakeState; - void install_async_exception(AsyncExceptionHandshake* aec = nullptr); void handle_async_exception(oop java_throwable); public: + void install_async_exception(AsyncExceptionHandshake* aec = nullptr); bool has_async_exception_condition(); inline void set_pending_unsafe_access_error(); static void send_async_exception(JavaThread* jt, oop java_throwable); diff --git a/src/hotspot/share/runtime/lockStack.hpp b/src/hotspot/share/runtime/lockStack.hpp index b8e2e4136e3d3..d5216793aea10 100644 --- a/src/hotspot/share/runtime/lockStack.hpp +++ b/src/hotspot/share/runtime/lockStack.hpp @@ -81,9 +81,6 @@ class LockStack { // Pushes an oop on this lock-stack. inline void push(oop o); - // Pops an oop from this lock-stack. - inline oop pop(); - // Removes an oop from an arbitrary location of this lock-stack. inline void remove(oop o); diff --git a/src/hotspot/share/runtime/lockStack.inline.hpp b/src/hotspot/share/runtime/lockStack.inline.hpp index b36be2f72de0e..be63c51d8e5e1 100644 --- a/src/hotspot/share/runtime/lockStack.inline.hpp +++ b/src/hotspot/share/runtime/lockStack.inline.hpp @@ -68,19 +68,6 @@ inline void LockStack::push(oop o) { verify("post-push"); } -inline oop LockStack::pop() { - verify("pre-pop"); - assert(to_index(_top) > 0, "underflow, probably unbalanced push/pop"); - _top -= oopSize; - oop o = _base[to_index(_top)]; -#ifdef ASSERT - _base[to_index(_top)] = nullptr; -#endif - assert(!contains(o), "entries must be unique: " PTR_FORMAT, p2i(o)); - verify("post-pop"); - return o; -} - inline void LockStack::remove(oop o) { verify("pre-remove"); assert(contains(o), "entry must be present: " PTR_FORMAT, p2i(o)); diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 62a685736c2c2..efd8490cd80e5 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -123,9 +123,6 @@ Mutex* JfrBuffer_lock = nullptr; Monitor* JfrThreadSampler_lock = nullptr; #endif -#ifndef SUPPORTS_NATIVE_CX8 -Mutex* UnsafeJlong_lock = nullptr; -#endif Mutex* CodeHeapStateAnalytics_lock = nullptr; Monitor* ContinuationRelativize_lock = nullptr; @@ -298,10 +295,6 @@ void mutex_init() { MUTEX_DEFN(JfrThreadSampler_lock , PaddedMonitor, nosafepoint); #endif -#ifndef SUPPORTS_NATIVE_CX8 - MUTEX_DEFN(UnsafeJlong_lock , PaddedMutex , nosafepoint); -#endif - MUTEX_DEFN(ContinuationRelativize_lock , PaddedMonitor, nosafepoint-3); MUTEX_DEFN(CodeHeapStateAnalytics_lock , PaddedMutex , safepoint); MUTEX_DEFN(ThreadsSMRDelete_lock , PaddedMonitor, service-2); // Holds ConcurrentHashTableResize_lock diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 9a0f9b9ab1ebe..840d01e62fa05 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -136,10 +136,6 @@ extern Mutex* JfrBuffer_lock; // protects JFR buffer operatio extern Monitor* JfrThreadSampler_lock; // used to suspend/resume JFR thread sampler #endif -#ifndef SUPPORTS_NATIVE_CX8 -extern Mutex* UnsafeJlong_lock; // provides Unsafe atomic updates to jlongs on platforms that don't support cx8 -#endif - extern Mutex* Metaspace_lock; // protects Metaspace virtualspace and chunk expansions extern Monitor* MetaspaceCritical_lock; // synchronizes failed metaspace allocations that risk throwing metaspace OOM extern Mutex* ClassLoaderDataGraph_lock; // protects CLDG list, needed for concurrent unloading diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index a648e0447cf33..462e3e8031b4d 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -282,24 +282,15 @@ ObjectMonitor::ObjectMonitor(oop object) : { } ObjectMonitor::~ObjectMonitor() { - if (!_object.is_null()) { - // Release object's oop storage if it hasn't already been done. - release_object(); - } + _object.release(_oop_storage); } oop ObjectMonitor::object() const { check_object_context(); - if (_object.is_null()) { - return nullptr; - } return _object.resolve(); } oop ObjectMonitor::object_peek() const { - if (_object.is_null()) { - return nullptr; - } return _object.peek(); } @@ -597,9 +588,6 @@ bool ObjectMonitor::deflate_monitor() { install_displaced_markword_in_object(obj); } - // Release object's oop storage since the ObjectMonitor has been deflated: - release_object(); - // We leave owner == DEFLATER_MARKER and contentions < 0 // to force any racing threads to retry. return true; // Success, ObjectMonitor has been deflated. diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index afcad7f0cdf44..fc15e20309f77 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -364,7 +364,6 @@ class ObjectMonitor : public CHeapObj { // Deflation support bool deflate_monitor(); void install_displaced_markword_in_object(const oop obj); - void release_object() { _object.release(_oop_storage); } }; #endif // SHARE_RUNTIME_OBJECTMONITOR_HPP diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index da1659adc63d8..9aa9d7ef86fc3 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1817,7 +1817,7 @@ char* os::reserve_memory(size_t bytes, bool executable, MEMFLAGS flags) { } char* os::attempt_reserve_memory_at(char* addr, size_t bytes, bool executable) { - char* result = pd_attempt_reserve_memory_at(addr, bytes, executable); + char* result = SimulateFullAddressSpace ? nullptr : pd_attempt_reserve_memory_at(addr, bytes, executable); if (result != nullptr) { MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); log_debug(os)("Reserved memory at " INTPTR_FORMAT " for " SIZE_FORMAT " bytes.", p2i(addr), bytes); @@ -1825,7 +1825,6 @@ char* os::attempt_reserve_memory_at(char* addr, size_t bytes, bool executable) { log_debug(os)("Attempt to reserve memory at " INTPTR_FORMAT " for " SIZE_FORMAT " bytes failed, errno %d", p2i(addr), bytes, get_last_error()); } - return result; } @@ -1881,10 +1880,10 @@ char* os::attempt_reserve_memory_between(char* min, char* max, size_t bytes, siz // we attempt to minimize fragmentation. constexpr unsigned total_shuffle_threshold = 1024; -#define ARGSFMT " range [" PTR_FORMAT "-" PTR_FORMAT "), size " SIZE_FORMAT_X ", alignment " SIZE_FORMAT_X ", randomize: %d" +#define ARGSFMT "range [" PTR_FORMAT "-" PTR_FORMAT "), size " SIZE_FORMAT_X ", alignment " SIZE_FORMAT_X ", randomize: %d" #define ARGSFMTARGS p2i(min), p2i(max), bytes, alignment, randomize - log_trace(os, map) ("reserve_between (" ARGSFMT ")", ARGSFMTARGS); + log_debug(os, map) ("reserve_between (" ARGSFMT ")", ARGSFMTARGS); assert(is_power_of_2(alignment), "alignment invalid (" ARGSFMT ")", ARGSFMTARGS); assert(alignment < SIZE_MAX / 2, "alignment too large (" ARGSFMT ")", ARGSFMTARGS); @@ -1994,7 +1993,7 @@ char* os::attempt_reserve_memory_between(char* min, char* max, size_t bytes, siz const unsigned candidate_offset = points[i]; char* const candidate = lo_att + candidate_offset * alignment_adjusted; assert(candidate <= hi_att, "Invalid offset %u (" ARGSFMT ")", candidate_offset, ARGSFMTARGS); - result = os::pd_attempt_reserve_memory_at(candidate, bytes, false); + result = SimulateFullAddressSpace ? nullptr : os::pd_attempt_reserve_memory_at(candidate, bytes, false); if (!result) { log_trace(os, map)("Failed to attach at " PTR_FORMAT, p2i(candidate)); } @@ -2012,6 +2011,8 @@ char* os::attempt_reserve_memory_between(char* min, char* max, size_t bytes, siz log_trace(os, map)(ERRFMT, ERRFMTARGS); log_debug(os, map)("successfully attached at " PTR_FORMAT, p2i(result)); MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); + } else { + log_debug(os, map)("failed to attach anywhere in [" PTR_FORMAT "-" PTR_FORMAT ")", p2i(min), p2i(max)); } return result; #undef ARGSFMT diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 3df2aa27c6af8..094fd2509a05b 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -523,8 +523,6 @@ class os: AllStatic { size_t size; int lgrp_id; }; - static char* scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found); - static char* non_memory_address_word(); // reserve, commit and pin the entire memory region static char* reserve_memory_special(size_t size, size_t alignment, size_t page_size, @@ -673,6 +671,8 @@ class os: AllStatic { static const char* get_temp_directory(); static const char* get_current_directory(char *buf, size_t buflen); + static void prepare_native_symbols(); + // Builds the platform-specific name of a library. // Returns false if the buffer is too small. static bool dll_build_name(char* buffer, size_t size, diff --git a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp index d1caa2edeabcb..e2549a11f10ea 100644 --- a/src/hotspot/share/runtime/perfData.cpp +++ b/src/hotspot/share/runtime/perfData.cpp @@ -185,6 +185,10 @@ void PerfData::create_entry(BasicType dtype, size_t dsize, size_t vlen) { PerfMemory::mark_updated(); } +bool PerfData::name_equals(const char* name) const { + return strcmp(name, this->name()) == 0; +} + PerfLong::PerfLong(CounterNS ns, const char* namep, Units u, Variability v) : PerfData(ns, namep, u, v) { @@ -501,17 +505,9 @@ PerfDataList::~PerfDataList() { } -bool PerfDataList::by_name(void* name, PerfData* pd) { - - if (pd == nullptr) - return false; - - return strcmp((const char*)name, pd->name()) == 0; -} - PerfData* PerfDataList::find_by_name(const char* name) { - int i = _set->find((void*)name, PerfDataList::by_name); + int i = _set->find_if([&](PerfData* pd) { return pd->name_equals(name); }); if (i >= 0 && i <= _set->length()) return _set->at(i); diff --git a/src/hotspot/share/runtime/perfData.hpp b/src/hotspot/share/runtime/perfData.hpp index 5c7b3d104e972..968e0813bdbc3 100644 --- a/src/hotspot/share/runtime/perfData.hpp +++ b/src/hotspot/share/runtime/perfData.hpp @@ -319,7 +319,8 @@ class PerfData : public CHeapObj { // PerfData memory region. This redundancy is maintained for // security reasons as the PerfMemory region may be in shared // memory. - const char* name() { return _name; } + const char* name() const { return _name; } + bool name_equals(const char* name) const; // returns the variability classification associated with this item Variability variability() { return _v; } @@ -576,7 +577,7 @@ class PerfDataList : public CHeapObj { PerfDataArray* _set; // method to search for a instrumentation object by name - static bool by_name(void* name, PerfData* pd); + static bool by_name(const char* name, PerfData* pd); protected: // we expose the implementation here to facilitate the clone diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 536944a61dcca..cbb7c84d0d94f 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -677,6 +677,7 @@ JRT_END // ret_pc points into caller; we are returning caller's exception handler // for given exception +// Note that the implementation of this method assumes it's only called when an exception has actually occured address SharedRuntime::compute_compiled_exc_handler(CompiledMethod* cm, address ret_pc, Handle& exception, bool force_unwind, bool top_frame_only, bool& recursive_exception_occurred) { assert(cm != nullptr, "must exist"); @@ -779,6 +780,9 @@ address SharedRuntime::compute_compiled_exc_handler(CompiledMethod* cm, address return nullptr; } + if (handler_bci != -1) { // did we find a handler in this method? + sd->method()->set_exception_handler_entered(handler_bci); // profile + } return nm->code_begin() + t->pco(); } diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index b3375bc5f25e9..6a502d2b2513a 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -90,44 +90,68 @@ size_t MonitorList::max() const { return Atomic::load(&_max); } -// Walk the in-use list and unlink (at most MonitorDeflationMax) deflated -// ObjectMonitors. Returns the number of unlinked ObjectMonitors. +// Walk the in-use list and unlink deflated ObjectMonitors. +// Returns the number of unlinked ObjectMonitors. size_t MonitorList::unlink_deflated(Thread* current, LogStream* ls, elapsedTimer* timer_p, + size_t deflated_count, GrowableArray* unlinked_list) { size_t unlinked_count = 0; ObjectMonitor* prev = nullptr; - ObjectMonitor* head = Atomic::load_acquire(&_head); - ObjectMonitor* m = head; + ObjectMonitor* m = Atomic::load_acquire(&_head); + // The in-use list head can be null during the final audit. while (m != nullptr) { if (m->is_being_async_deflated()) { - // Find next live ObjectMonitor. + // Find next live ObjectMonitor. Batch up the unlinkable monitors, so we can + // modify the list once per batch. The batch starts at "m". + size_t unlinked_batch = 0; ObjectMonitor* next = m; + // Look for at most MonitorUnlinkBatch monitors, or the number of + // deflated and not unlinked monitors, whatever comes first. + assert(deflated_count >= unlinked_count, "Sanity: underflow"); + size_t unlinked_batch_limit = MIN2(deflated_count - unlinked_count, MonitorUnlinkBatch); do { ObjectMonitor* next_next = next->next_om(); - unlinked_count++; + unlinked_batch++; unlinked_list->append(next); next = next_next; - if (unlinked_count >= (size_t)MonitorDeflationMax) { - // Reached the max so bail out on the gathering loop. + if (unlinked_batch >= unlinked_batch_limit) { + // Reached the max batch, so bail out of the gathering loop. + break; + } + if (prev == nullptr && Atomic::load(&_head) != m) { + // Current batch used to be at head, but it is not at head anymore. + // Bail out and figure out where we currently are. This avoids long + // walks searching for new prev during unlink under heavy list inserts. break; } } while (next != nullptr && next->is_being_async_deflated()); + + // Unlink the found batch. if (prev == nullptr) { - ObjectMonitor* prev_head = Atomic::cmpxchg(&_head, head, next); - if (prev_head != head) { - // Find new prev ObjectMonitor that just got inserted. + // The current batch is the first batch, so there is a chance that it starts at head. + // Optimistically assume no inserts happened, and try to unlink the entire batch from the head. + ObjectMonitor* prev_head = Atomic::cmpxchg(&_head, m, next); + if (prev_head != m) { + // Something must have updated the head. Figure out the actual prev for this batch. for (ObjectMonitor* n = prev_head; n != m; n = n->next_om()) { prev = n; } + assert(prev != nullptr, "Should have found the prev for the current batch"); prev->set_next_om(next); } } else { + // The current batch is preceded by another batch. This guarantees the current batch + // does not start at head. Unlink the entire current batch without updating the head. + assert(Atomic::load(&_head) != m, "Sanity"); prev->set_next_om(next); } - if (unlinked_count >= (size_t)MonitorDeflationMax) { - // Reached the max so bail out on the searching loop. + + unlinked_count += unlinked_batch; + if (unlinked_count >= deflated_count) { + // Reached the max so bail out of the searching loop. + // There should be no more deflated monitors left. break; } m = next; @@ -143,6 +167,20 @@ size_t MonitorList::unlink_deflated(Thread* current, LogStream* ls, ls, timer_p); } } + +#ifdef ASSERT + // Invariant: the code above should unlink all deflated monitors. + // The code that runs after this unlinking does not expect deflated monitors. + // Notably, attempting to deflate the already deflated monitor would break. + { + ObjectMonitor* m = Atomic::load_acquire(&_head); + while (m != nullptr) { + assert(!m->is_being_async_deflated(), "All deflated monitors should be unlinked"); + m = m->next_om(); + } + } +#endif + Atomic::sub(&_count, unlinked_count); return unlinked_count; } @@ -605,13 +643,7 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) // The ObjectMonitor* can't be async deflated until ownership is // dropped inside exit() and the ObjectMonitor* must be !is_busy(). ObjectMonitor* monitor = inflate(current, object, inflate_cause_vm_internal); - if (LockingMode == LM_LIGHTWEIGHT && monitor->is_owner_anonymous()) { - // It must be owned by us. Pop lock object from lock stack. - LockStack& lock_stack = current->lock_stack(); - oop popped = lock_stack.pop(); - assert(popped == object, "must be owned by this thread"); - monitor->set_owner_from_anonymous(current); - } + assert(!monitor->is_owner_anonymous(), "must not be"); monitor->exit(current); } @@ -1587,14 +1619,18 @@ class VM_RendezvousGCThreads : public VM_Operation { }; }; -static size_t delete_monitors(GrowableArray* delete_list) { +static size_t delete_monitors(JavaThread* current, GrowableArray* delete_list, + LogStream* ls, elapsedTimer* timer_p) { NativeHeapTrimmer::SuspendMark sm("monitor deletion"); - size_t count = 0; + size_t deleted_count = 0; for (ObjectMonitor* monitor: *delete_list) { delete monitor; - count++; + deleted_count++; + // A JavaThread must check for a safepoint/handshake and honor it. + ObjectSynchronizer::chk_for_block_req(current, "deletion", "deleted_count", + deleted_count, ls, timer_p); } - return count; + return deleted_count; } // This function is called by the MonitorDeflationThread to deflate @@ -1633,7 +1669,7 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { // Unlink deflated ObjectMonitors from the in-use list. ResourceMark rm; GrowableArray delete_list((int)deflated_count); - unlinked_count = _in_use_list.unlink_deflated(current, ls, &timer, &delete_list); + unlinked_count = _in_use_list.unlink_deflated(current, ls, &timer, deflated_count, &delete_list); if (current->is_monitor_deflation_thread()) { if (ls != nullptr) { timer.stop(); @@ -1668,30 +1704,7 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { // After the handshake, safely free the ObjectMonitors that were // deflated and unlinked in this cycle. - if (current->is_Java_thread()) { - if (ls != NULL) { - timer.stop(); - ls->print_cr("before setting blocked: unlinked_count=" SIZE_FORMAT - ", in_use_list stats: ceiling=" SIZE_FORMAT ", count=" - SIZE_FORMAT ", max=" SIZE_FORMAT, - unlinked_count, in_use_list_ceiling(), - _in_use_list.count(), _in_use_list.max()); - } - // Mark the calling JavaThread blocked (safepoint safe) while we free - // the ObjectMonitors so we don't delay safepoints whilst doing that. - ThreadBlockInVM tbivm(JavaThread::cast(current)); - if (ls != NULL) { - ls->print_cr("after setting blocked: in_use_list stats: ceiling=" - SIZE_FORMAT ", count=" SIZE_FORMAT ", max=" SIZE_FORMAT, - in_use_list_ceiling(), _in_use_list.count(), _in_use_list.max()); - timer.start(); - } - deleted_count = delete_monitors(&delete_list); - // ThreadBlockInVM is destroyed here - } else { - // A non-JavaThread can just free the ObjectMonitors: - deleted_count = delete_monitors(&delete_list); - } + deleted_count = delete_monitors(JavaThread::cast(current), &delete_list, ls, &timer); assert(unlinked_count == deleted_count, "must be"); } diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index d65a1e14bfa68..4dea13432e78e 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -47,6 +47,7 @@ class MonitorList { public: void add(ObjectMonitor* monitor); size_t unlink_deflated(Thread* current, LogStream* ls, elapsedTimer* timer_p, + size_t deflated_count, GrowableArray* unlinked_list); size_t count() const; size_t max() const; diff --git a/src/hotspot/share/runtime/unhandledOops.cpp b/src/hotspot/share/runtime/unhandledOops.cpp index 11c8160fe03fc..cd1bde76c775f 100644 --- a/src/hotspot/share/runtime/unhandledOops.cpp +++ b/src/hotspot/share/runtime/unhandledOops.cpp @@ -71,11 +71,6 @@ void UnhandledOops::register_unhandled_oop(oop* op) { _oop_list->push(entry); } - -bool match_oop_entry(void *op, UnhandledOopEntry e) { - return (e.oop_ptr() == op); -} - // Mark unhandled oop as okay for GC - the containing struct has an oops_do and // for some reason the oop has to be on the stack. // May not be called for the current thread, as in the case of @@ -83,7 +78,9 @@ bool match_oop_entry(void *op, UnhandledOopEntry e) { void UnhandledOops::allow_unhandled_oop(oop* op) { assert (CheckUnhandledOops, "should only be called with checking option"); - int i = _oop_list->find_from_end(op, match_oop_entry); + int i = _oop_list->find_from_end_if([&](const UnhandledOopEntry& e) { + return e.match_oop_entry(op); + }); assert(i!=-1, "safe for gc oop not in unhandled_oop_list"); UnhandledOopEntry entry = _oop_list->at(i); @@ -105,7 +102,9 @@ void UnhandledOops::unregister_unhandled_oop(oop* op) { } _level--; - int i = _oop_list->find_from_end(op, match_oop_entry); + int i = _oop_list->find_from_end_if([&](const UnhandledOopEntry& e) { + return e.match_oop_entry(op); + }); assert(i!=-1, "oop not in unhandled_oop_list"); _oop_list->remove_at(i); } diff --git a/src/hotspot/share/runtime/unhandledOops.hpp b/src/hotspot/share/runtime/unhandledOops.hpp index 400e6dd554382..09ebbe68be357 100644 --- a/src/hotspot/share/runtime/unhandledOops.hpp +++ b/src/hotspot/share/runtime/unhandledOops.hpp @@ -53,14 +53,17 @@ class UnhandledOopEntry : public CHeapObj { private: oop* _oop_ptr; bool _ok_for_gc; + + bool match_oop_entry(oop* op) const { + return _oop_ptr == op; + } + public: - oop* oop_ptr() { return _oop_ptr; } UnhandledOopEntry() : _oop_ptr(nullptr), _ok_for_gc(false) {} UnhandledOopEntry(oop* op) : _oop_ptr(op), _ok_for_gc(false) {} }; - class UnhandledOops : public CHeapObj { friend class Thread; private: diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp index 7438814255c66..cad9dd2ad54c5 100644 --- a/src/hotspot/share/runtime/vmOperations.cpp +++ b/src/hotspot/share/runtime/vmOperations.cpp @@ -494,10 +494,9 @@ int VM_Exit::wait_for_threads_in_native_to_block() { // don't have to wait for user threads to be quiescent, but it's always // better to terminate VM when current thread is the only active thread, so // wait for user threads too. Numbers are in 10 milliseconds. - int max_wait_user_thread = 30; // at least 300 milliseconds - int max_wait_compiler_thread = 1000; // at least 10 seconds - - int max_wait = max_wait_compiler_thread; + int wait_time_per_attempt = 10; // in milliseconds + int max_wait_attempts_user_thread = UserThreadWaitAttemptsAtExit; + int max_wait_attempts_compiler_thread = 1000; // at least 10 seconds int attempts = 0; JavaThreadIteratorWithHandle jtiwh; @@ -530,16 +529,17 @@ int VM_Exit::wait_for_threads_in_native_to_block() { if (num_active == 0) { return 0; - } else if (attempts > max_wait) { + } else if (attempts >= max_wait_attempts_compiler_thread) { return num_active; - } else if (num_active_compiler_thread == 0 && attempts > max_wait_user_thread) { + } else if (num_active_compiler_thread == 0 && + attempts >= max_wait_attempts_user_thread) { return num_active; } attempts++; MonitorLocker ml(&timer, Mutex::_no_safepoint_check_flag); - ml.wait(10); + ml.wait(wait_time_per_attempt); } } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 6ff843336061d..425daacb94794 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1114,6 +1114,7 @@ declare_unsigned_integer_type(u_char) \ declare_unsigned_integer_type(unsigned int) \ declare_unsigned_integer_type(uint) \ + declare_unsigned_integer_type(volatile uint) \ declare_unsigned_integer_type(unsigned short) \ declare_unsigned_integer_type(jushort) \ declare_unsigned_integer_type(unsigned long) \ diff --git a/src/hotspot/share/runtime/vm_version.cpp b/src/hotspot/share/runtime/vm_version.cpp index 33a5c792c87d5..e2a7aa565d6b8 100644 --- a/src/hotspot/share/runtime/vm_version.cpp +++ b/src/hotspot/share/runtime/vm_version.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ void VM_Version_init() { VM_Version::initialize(); - + guarantee(VM_Version::supports_cx8(), "Support for 64-bit atomic operations is required"); if (log_is_enabled(Info, os, cpu)) { char buf[1024]; ResourceMark rm; diff --git a/src/hotspot/share/services/diagnosticFramework.cpp b/src/hotspot/share/services/diagnosticFramework.cpp index d17e426ee8a86..006c08cb63ff8 100644 --- a/src/hotspot/share/services/diagnosticFramework.cpp +++ b/src/hotspot/share/services/diagnosticFramework.cpp @@ -144,9 +144,8 @@ bool DCmdArgIter::next(TRAPS) { return _key_len != 0; } -bool DCmdInfo::by_name(void* cmd_name, DCmdInfo* info) { - if (info == nullptr) return false; - return strcmp((const char*)cmd_name, info->name()) == 0; +bool DCmdInfo::name_equals(const char* name) const { + return strcmp(name, this->name()) == 0; } void DCmdParser::add_dcmd_option(GenDCmdArgument* arg) { diff --git a/src/hotspot/share/services/diagnosticFramework.hpp b/src/hotspot/share/services/diagnosticFramework.hpp index 8313954aaecbe..898f29274eaa1 100644 --- a/src/hotspot/share/services/diagnosticFramework.hpp +++ b/src/hotspot/share/services/diagnosticFramework.hpp @@ -140,13 +140,12 @@ class DCmdInfo : public ResourceObj { : _name(name), _description(description), _impact(impact), _permission(permission), _num_arguments(num_arguments), _is_enabled(enabled) {} const char* name() const { return _name; } + bool name_equals(const char* cmd_name) const; const char* description() const { return _description; } const char* impact() const { return _impact; } const JavaPermission& permission() const { return _permission; } int num_arguments() const { return _num_arguments; } bool is_enabled() const { return _is_enabled; } - - static bool by_name(void* name, DCmdInfo* info); }; // A DCmdArgumentInfo instance provides a description of a diagnostic command diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index a9c50a8bf06df..8e9249f5a61bc 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -2004,7 +2004,9 @@ JVM_ENTRY(void, jmm_GetDiagnosticCommandInfo(JNIEnv *env, jobjectArray cmds, THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Command name cannot be null."); } - int pos = info_list->find((void*)cmd_name,DCmdInfo::by_name); + int pos = info_list->find_if([&](DCmdInfo* info) { + return info->name_equals(cmd_name); + }); if (pos == -1) { THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Unknown diagnostic command"); diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index d9c47e8360fdb..e9abd9fae9a3a 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -209,17 +209,29 @@ class GrowableArrayView : public GrowableArrayBase { return -1; } - int find(void* token, bool f(void*, E)) const { + // Find first element that matches the given predicate. + // + // Predicate: bool predicate(const E& elem) + // + // Returns the index of the element or -1 if no element matches the predicate + template + int find_if(Predicate predicate) const { for (int i = 0; i < _len; i++) { - if (f(token, _data[i])) return i; + if (predicate(_data[i])) return i; } return -1; } - int find_from_end(void* token, bool f(void*, E)) const { + // Find last element that matches the given predicate. + // + // Predicate: bool predicate(const E& elem) + // + // Returns the index of the element or -1 if no element matches the predicate + template + int find_from_end_if(Predicate predicate) const { // start at the end of the array for (int i = _len-1; i >= 0; i--) { - if (f(token, _data[i])) return i; + if (predicate(_data[i])) return i; } return -1; } diff --git a/src/hotspot/share/utilities/nativeCallStack.cpp b/src/hotspot/share/utilities/nativeCallStack.cpp index 0fb0303fb908b..3ddf296506c2a 100644 --- a/src/hotspot/share/utilities/nativeCallStack.cpp +++ b/src/hotspot/share/utilities/nativeCallStack.cpp @@ -82,18 +82,33 @@ void NativeCallStack::print_on(outputStream* out, int indent) const { char buf[1024]; int offset; if (is_empty()) { - for (int index = 0; index < indent; index ++) out->print(" "); + out->fill_to(indent); out->print("[BOOTSTRAP]"); } else { for (int frame = 0; frame < NMT_TrackingStackDepth; frame ++) { pc = get_frame(frame); if (pc == nullptr) break; - // Print indent - for (int index = 0; index < indent; index ++) out->print(" "); + out->fill_to(indent); + out->print("[" PTR_FORMAT "]", p2i(pc)); + // Print function and library; shorten library name to just its last component + // for brevity, and omit it completely for libjvm.so + bool function_printed = false; if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { - out->print("[" PTR_FORMAT "] %s+0x%x", p2i(pc), buf, offset); - } else { - out->print("[" PTR_FORMAT "]", p2i(pc)); + out->print("%s+0x%x", buf, offset); + function_printed = true; + } + if ((!function_printed || !os::address_is_in_vm(pc)) && + os::dll_address_to_library_name(pc, buf, sizeof(buf), &offset)) { + const char* libname = strrchr(buf, os::file_separator()[0]); + if (libname != nullptr) { + libname++; + } else { + libname = buf; + } + out->print(" in %s", libname); + if (!function_printed) { + out->print("+0x%x", offset); + } } // Note: we deliberately omit printing source information here. NativeCallStack::print_on() diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 34372701f6e6f..d586c6f4eeacf 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -74,10 +74,6 @@ #include "jvmci/jvmci.hpp" #endif -#ifdef AIX -#include "loadlib_aix.hpp" -#endif - #ifndef PRODUCT #include #endif // PRODUCT @@ -725,6 +721,11 @@ void VMError::report(outputStream* st, bool _verbose) { "Runtime Environment to continue."); } + // avoid the cache update for malloc/mmap errors + if (should_report_bug(_id)) { + os::prepare_native_symbols(); + } + #ifdef ASSERT // Error handler self tests // Meaning of codes passed through in the tests. @@ -1347,7 +1348,7 @@ void VMError::report(outputStream* st, bool _verbose) { void VMError::print_vm_info(outputStream* st) { char buf[O_BUFLEN]; - AIX_ONLY(LoadedLibraries::reload()); + os::prepare_native_symbols(); report_vm_version(st, buf, sizeof(buf)); diff --git a/src/hotspot/share/utilities/waitBarrier_generic.cpp b/src/hotspot/share/utilities/waitBarrier_generic.cpp index b5d9ff67eb782..dbf4db336c2f5 100644 --- a/src/hotspot/share/utilities/waitBarrier_generic.cpp +++ b/src/hotspot/share/utilities/waitBarrier_generic.cpp @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,66 +30,228 @@ #include "utilities/waitBarrier_generic.hpp" #include "utilities/spinYield.hpp" +// Implements the striped semaphore wait barrier. +// +// To guarantee progress and safety, we need to make sure that new barrier tag +// starts with the completely empty set of waiters and free semaphore. This +// requires either waiting for all threads to leave wait() for current barrier +// tag on disarm(), or waiting for all threads to leave the previous tag before +// reusing the semaphore in arm(). +// +// When there are multiple threads, it is normal for some threads to take +// significant time to leave the barrier. Waiting for these threads introduces +// stalls on barrier reuse. +// +// If we wait on disarm(), this stall is nearly guaranteed to happen if some threads +// are de-scheduled by prior wait(). It would be especially bad if there are more +// waiting threads than CPUs: every thread would need to wake up and register itself +// as leaving, before we can unblock from disarm(). +// +// If we wait on arm(), we can get lucky that most threads would be able to catch up, +// exit wait(), and so we arrive to arm() with semaphore ready for reuse. However, +// that is still insufficient in practice. +// +// Therefore, this implementation goes a step further and implements the _striped_ +// semaphores. We maintain several semaphores in cells. The barrier tags are assigned +// to cells in some simple manner. Most of the current uses have sequential barrier +// tags, so simple modulo works well. We then operate on a cell like we would operate +// on a single semaphore: we wait at arm() for all threads to catch up before reusing +// the cell. For the cost of maintaining just a few cells, we have enough window for +// threads to catch up. +// +// The correctness is guaranteed by using a single atomic state variable per cell, +// with updates always done with CASes: +// +// [.......... barrier tag ..........][.......... waiters ..........] +// 63 31 0 +// +// Cell starts with zero tag and zero waiters. Arming the cell swings barrier tag from +// zero to some tag, while checking that no waiters have appeared. Disarming swings +// the barrier tag back from tag to zero. Every waiter registers itself by incrementing +// the "waiters", while checking that barrier tag is still the same. Every completing waiter +// decrements the "waiters". When all waiters complete, a cell ends up in initial state, +// ready to be armed again. This allows accurate tracking of how many signals +// to issue and does not race with disarm. +// +// The implementation uses the strongest (default) barriers for extra safety, even +// when not strictly required to do so for correctness. Extra barrier overhead is +// dominated by the actual wait/notify latency anyway. +// + void GenericWaitBarrier::arm(int barrier_tag) { - assert(_barrier_tag == 0, "Already armed"); - assert(_waiters == 0, "We left a thread hanging"); - _barrier_tag = barrier_tag; - _waiters = 0; + assert(barrier_tag != 0, "Pre arm: Should be arming with armed value"); + assert(Atomic::load(&_barrier_tag) == 0, + "Pre arm: Should not be already armed. Tag: %d", + Atomic::load(&_barrier_tag)); + Atomic::release_store(&_barrier_tag, barrier_tag); + + Cell &cell = tag_to_cell(barrier_tag); + cell.arm(barrier_tag); + + // API specifies arm() must provide a trailing fence. OrderAccess::fence(); } -int GenericWaitBarrier::wake_if_needed() { - assert(_barrier_tag == 0, "Not disarmed"); - int w = _waiters; - if (w == 0) { - // Load of _barrier_threads in caller must not pass the load of _waiters. - OrderAccess::loadload(); - return 0; - } - assert(w > 0, "Bad counting"); - // We need an exact count which never goes below zero, - // otherwise the semaphore may be signalled too many times. - if (Atomic::cmpxchg(&_waiters, w, w - 1) == w) { - _sem_barrier.signal(); - return w - 1; - } - return w; +void GenericWaitBarrier::disarm() { + int barrier_tag = Atomic::load_acquire(&_barrier_tag); + assert(barrier_tag != 0, "Pre disarm: Should be armed. Tag: %d", barrier_tag); + Atomic::release_store(&_barrier_tag, 0); + + Cell &cell = tag_to_cell(barrier_tag); + cell.disarm(barrier_tag); + + // API specifies disarm() must provide a trailing fence. + OrderAccess::fence(); } -void GenericWaitBarrier::disarm() { - assert(_barrier_tag != 0, "Not armed"); - _barrier_tag = 0; - // Loads of _barrier_threads/_waiters must not float above disarm store and - // disarm store must not sink below. +void GenericWaitBarrier::wait(int barrier_tag) { + assert(barrier_tag != 0, "Pre wait: Should be waiting on armed value"); + + Cell &cell = tag_to_cell(barrier_tag); + cell.wait(barrier_tag); + + // API specifies wait() must provide a trailing fence. OrderAccess::fence(); - int left; +} + +void GenericWaitBarrier::Cell::arm(int32_t requested_tag) { + // Before we continue to arm, we need to make sure that all threads + // have left the previous cell. + + int64_t state; + SpinYield sp; - do { - left = GenericWaitBarrier::wake_if_needed(); - if (left == 0 && _barrier_threads > 0) { - // There is no thread to wake but we still have barrier threads. + while (true) { + state = Atomic::load_acquire(&_state); + assert(decode_tag(state) == 0, + "Pre arm: Should not be armed. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + decode_tag(state), decode_waiters(state)); + if (decode_waiters(state) == 0) { + break; + } + sp.wait(); + } + + // Try to swing cell to armed. This should always succeed after the check above. + int64_t new_state = encode(requested_tag, 0); + int64_t prev_state = Atomic::cmpxchg(&_state, state, new_state); + if (prev_state != state) { + fatal("Cannot arm the wait barrier. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + decode_tag(prev_state), decode_waiters(prev_state)); + } +} + +int GenericWaitBarrier::Cell::signal_if_needed(int max) { + int signals = 0; + while (true) { + int cur = Atomic::load_acquire(&_outstanding_wakeups); + if (cur == 0) { + // All done, no more waiters. + return 0; + } + assert(cur > 0, "Sanity"); + + int prev = Atomic::cmpxchg(&_outstanding_wakeups, cur, cur - 1); + if (prev != cur) { + // Contention, return to caller for early return or backoff. + return prev; + } + + // Signal! + _sem.signal(); + + if (++signals >= max) { + // Signalled requested number of times, break out. + return prev; + } + } +} + +void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) { + int32_t waiters; + + while (true) { + int64_t state = Atomic::load_acquire(&_state); + int32_t tag = decode_tag(state); + waiters = decode_waiters(state); + + assert((tag == expected_tag) && (waiters >= 0), + "Mid disarm: Should be armed with expected tag and have sane waiters. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + tag, waiters); + + int64_t new_state = encode(0, waiters); + if (Atomic::cmpxchg(&_state, state, new_state) == state) { + // Successfully disarmed. + break; + } + } + + // Wake up waiters, if we have at least one. + // Allow other threads to assist with wakeups, if possible. + if (waiters > 0) { + Atomic::release_store(&_outstanding_wakeups, waiters); + SpinYield sp; + while (signal_if_needed(INT_MAX) > 0) { sp.wait(); } - // We must loop here until there are no waiters or potential waiters. - } while (left > 0 || _barrier_threads > 0); - // API specifies disarm() must provide a trailing fence. - OrderAccess::fence(); + } + assert(Atomic::load(&_outstanding_wakeups) == 0, "Post disarm: Should not have outstanding wakeups"); } -void GenericWaitBarrier::wait(int barrier_tag) { - assert(barrier_tag != 0, "Trying to wait on disarmed value"); - if (barrier_tag != _barrier_tag) { - // API specifies wait() must provide a trailing fence. - OrderAccess::fence(); - return; +void GenericWaitBarrier::Cell::wait(int32_t expected_tag) { + // Try to register ourselves as pending waiter. + while (true) { + int64_t state = Atomic::load_acquire(&_state); + int32_t tag = decode_tag(state); + if (tag != expected_tag) { + // Cell tag had changed while waiting here. This means either the cell had + // been disarmed, or we are late and the cell was armed with a new tag. + // Exit without touching anything else. + return; + } + int32_t waiters = decode_waiters(state); + + assert((tag == expected_tag) && (waiters >= 0 && waiters < INT32_MAX), + "Before wait: Should be armed with expected tag and waiters are in range. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + tag, waiters); + + int64_t new_state = encode(tag, waiters + 1); + if (Atomic::cmpxchg(&_state, state, new_state) == state) { + // Success! Proceed to wait. + break; + } } - Atomic::add(&_barrier_threads, 1); - if (barrier_tag != 0 && barrier_tag == _barrier_tag) { - Atomic::add(&_waiters, 1); - _sem_barrier.wait(); - // We help out with posting, but we need to do so before we decrement the - // _barrier_threads otherwise we might wake threads up in next wait. - GenericWaitBarrier::wake_if_needed(); + + // Wait for notification. + _sem.wait(); + + // Unblocked! We help out with waking up two siblings. This allows to avalanche + // the wakeups for many threads, even if some threads are lagging behind. + // Note that we can only do this *before* reporting back as completed waiter, + // otherwise we might prematurely wake up threads for another barrier tag. + // Current arm() sequence protects us from this trouble by waiting until all waiters + // leave. + signal_if_needed(2); + + // Register ourselves as completed waiter before leaving. + while (true) { + int64_t state = Atomic::load_acquire(&_state); + int32_t tag = decode_tag(state); + int32_t waiters = decode_waiters(state); + + assert((tag == 0) && (waiters > 0), + "After wait: Should be not armed and have non-complete waiters. " + "Tag: " INT32_FORMAT "; Waiters: " INT32_FORMAT, + tag, waiters); + + int64_t new_state = encode(tag, waiters - 1); + if (Atomic::cmpxchg(&_state, state, new_state) == state) { + // Success! + break; + } } - Atomic::add(&_barrier_threads, -1); } diff --git a/src/hotspot/share/utilities/waitBarrier_generic.hpp b/src/hotspot/share/utilities/waitBarrier_generic.hpp index 50bfea6aebfb7..d3a45b33b82ef 100644 --- a/src/hotspot/share/utilities/waitBarrier_generic.hpp +++ b/src/hotspot/share/utilities/waitBarrier_generic.hpp @@ -26,29 +26,79 @@ #define SHARE_UTILITIES_WAITBARRIER_GENERIC_HPP #include "memory/allocation.hpp" +#include "memory/padded.hpp" #include "runtime/semaphore.hpp" #include "utilities/globalDefinitions.hpp" -// In addition to the barrier tag, it uses two counters to keep the semaphore -// count correct and not leave any late thread waiting. class GenericWaitBarrier : public CHeapObj { +private: + class Cell : public CHeapObj { + private: + // Pad out the cells to avoid interference between the cells. + // This would insulate from stalls when adjacent cells have returning + // workers and contend over the cache line for current latency-critical + // cell. + DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); + + Semaphore _sem; + + // Cell state, tracks the arming + waiters status + volatile int64_t _state; + + // Wakeups to deliver for current waiters + volatile int _outstanding_wakeups; + + int signal_if_needed(int max); + + static int64_t encode(int32_t barrier_tag, int32_t waiters) { + int64_t val = (((int64_t) barrier_tag) << 32) | + (((int64_t) waiters) & 0xFFFFFFFF); + assert(decode_tag(val) == barrier_tag, "Encoding is reversible"); + assert(decode_waiters(val) == waiters, "Encoding is reversible"); + return val; + } + + static int32_t decode_tag(int64_t value) { + return (int32_t)(value >> 32); + } + + static int32_t decode_waiters(int64_t value) { + return (int32_t)(value & 0xFFFFFFFF); + } + + public: + Cell() : _sem(0), _state(encode(0, 0)), _outstanding_wakeups(0) {} + NONCOPYABLE(Cell); + + void arm(int32_t requested_tag); + void disarm(int32_t expected_tag); + void wait(int32_t expected_tag); + }; + + // Should be enough for most uses without exploding the footprint. + static constexpr int CELLS_COUNT = 16; + + Cell _cells[CELLS_COUNT]; + + // Trailing padding to protect the last cell. + DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0); + volatile int _barrier_tag; - // The number of threads waiting on or about to wait on the semaphore. - volatile int _waiters; - // The number of threads in the wait path, before or after the tag check. - // These threads can become waiters. - volatile int _barrier_threads; - Semaphore _sem_barrier; + + // Trailing padding to insulate the rest of the barrier from adjacent + // data structures. The leading padding is not needed, as cell padding + // handles this for us. + DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, 0); NONCOPYABLE(GenericWaitBarrier); - int wake_if_needed(); + Cell& tag_to_cell(int tag) { return _cells[tag & (CELLS_COUNT - 1)]; } - public: - GenericWaitBarrier() : _barrier_tag(0), _waiters(0), _barrier_threads(0), _sem_barrier(0) {} +public: + GenericWaitBarrier() : _cells(), _barrier_tag(0) {} ~GenericWaitBarrier() {} - const char* description() { return "semaphore"; } + const char* description() { return "striped semaphore"; } void arm(int barrier_tag); void disarm(); diff --git a/src/java.base/aix/native/libsyslookup/syslookup.c b/src/java.base/aix/native/libsyslookup/syslookup.c new file mode 100644 index 0000000000000..66bd50e01a0ef --- /dev/null +++ b/src/java.base/aix/native/libsyslookup/syslookup.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute 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. + */ + +// Note: the include below is not strictly required, as dependencies will be pulled using linker flags. +// Adding at least one #include removes unwanted warnings on some platforms. +#include +#include + +// Addresses of functions to be referenced using static linking. +void* funcs[] = { + //string.h + &strlen, + &strcat, + //math.h + &abs, + &fabs, + &fabsf, + &fabsl, + &fmod, + &fmodf, + &fmodl, + &remainder, + &remainderf, + &remainderl, + &remquo, + &remquof, + &remquol, + &fma, + &fmaf, + &fmal, + &fmax, + &fmaxf, + &fmaxl, + &fmin, + &fminf, + &fminl, + &fdim, + &fdimf, + &fdiml, + &nan, + &nanf, + &nanl, + &exp, + &expf, + &expl, + &exp2, + &exp2f, + &exp2l, + &expm1, + &expm1f, + &expm1l, + &log, + &logf, + &logl, + &log10, + &log10f, + &log10l, + &log2, + &log2f, + &log2l, + &log1p, + &log1pf, + &log1pl, + &pow, + &powf, + &powl, + &sqrt, + &sqrtf, + &sqrtl, + &cbrt, + &cbrtf, + &cbrtl, + &hypot, + &hypotf, + &hypotl, + &sin, + &sinf, + &sinl, + &cos, + &cosf, + &cosl, + &tan, + &tanf, + &tanl, + &asin, + &asinf, + &asinl, + &acos, + &acosf, + &acosl, + &atan, + &atanf, + &atanl, + &atan2, + &atan2f, + &atan2l, + &sinh, + &sinhf, + &sinhl, + &cosh, + &coshf, + &coshl, + &tanh, + &tanhf, + &tanhl, + &asinh, + &asinhf, + &asinhl, + &acosh, + &acoshf, + &acoshl, + &atanh, + &atanhf, + &atanhl, + &erf, + &erff, + &erfl, + &erfc, + &erfcf, + &erfcl, + &tgamma, + &tgammaf, + &tgammal, + &lgamma, + &lgammaf, + &lgammal, + &ceil, + &ceilf, + &ceill, + &floor, + &floorf, + &floorl, + &trunc, + &truncf, + &truncl, + &round, + &roundf, + &roundl, + &lround, + &lroundf, + &lroundl, + &llround, + &llroundf, + &llroundl, + &nearbyint, + &nearbyintf, + &nearbyintl, + &rintf, + &rintl, + &lrint, + &lrintf, + &lrintl, + &llrint, + &llrintf, + &llrintl, + &frexpf, + &ldexpf, + &modff, + &scalbn, + &scalbnf, + &scalbnl, + &scalbln, + &scalblnf, + &scalblnl, + &ilogb, + &ilogbf, + &ilogbl, + &logb, + &logbf, + &logbl, + &nextafter, + &nextafterf, + &nextafterl, + &nexttoward, + &nexttowardf, + &nexttowardl, + ©sign, + ©signf, + ©signl, + &isnan +}; diff --git a/src/java.base/share/classes/java/lang/Double.java b/src/java.base/share/classes/java/lang/Double.java index 14ddcd0de2a5e..580f8b26cab22 100644 --- a/src/java.base/share/classes/java/lang/Double.java +++ b/src/java.base/share/classes/java/lang/Double.java @@ -234,7 +234,7 @@ * equivalent of 15 to 17 digits of decimal precision. (The * equivalent precision varies according to the different relative * densities of binary and decimal values at different points along the - * real number line). + * real number line.) * *

This representation hazard of decimal fractions is one reason to * use caution when storing monetary values as {@code float} or {@code @@ -316,7 +316,7 @@ * * {@snippet lang="java" : * double d = 0.0; - * while(d != 1.0) { // Surprising infinite loop + * while (d != 1.0) { // Surprising infinite loop * d += 0.1; // Sum never _exactly_ equals 1.0 * } * } @@ -325,7 +325,7 @@ * * {@snippet lang="java" : * double d = 0.0; - * for(int i = 0; i < 10; i++) { + * for (int i = 0; i < 10; i++) { * d += 0.1; * } // Value of d is equal to Math.nextDown(1.0). * } @@ -335,7 +335,7 @@ * * {@snippet lang="java" : * double d = 0.0; - * while(d <= 1.0) { + * while (d <= 1.0) { * d += 0.1; * } // Value of d approximately 1.0999999999999999 * } diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index 6810fa6222d1a..79dc47e68169c 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -26,17 +26,29 @@ package java.lang.runtime; import java.lang.Enum.EnumDesc; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.reflect.AccessFlag; +import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.function.BiPredicate; import java.util.stream.Stream; import jdk.internal.access.SharedSecrets; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.Label; +import jdk.internal.classfile.instruction.SwitchCase; import jdk.internal.vm.annotation.Stable; +import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; +import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; import static java.util.Objects.requireNonNull; /** @@ -54,26 +66,16 @@ private SwitchBootstraps() {} private static final Object SENTINEL = new Object(); private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - private static final MethodHandle INSTANCEOF_CHECK; - private static final MethodHandle INTEGER_EQ_CHECK; - private static final MethodHandle OBJECT_EQ_CHECK; - private static final MethodHandle ENUM_EQ_CHECK; private static final MethodHandle NULL_CHECK; private static final MethodHandle IS_ZERO; private static final MethodHandle CHECK_INDEX; private static final MethodHandle MAPPED_ENUM_LOOKUP; + private static final MethodTypeDesc TYPES_SWITCH_DESCRIPTOR = + MethodTypeDesc.ofDescriptor("(Ljava/lang/Object;ILjava/util/function/BiPredicate;Ljava/util/List;)I"); + static { try { - INSTANCEOF_CHECK = MethodHandles.permuteArguments(LOOKUP.findVirtual(Class.class, "isInstance", - MethodType.methodType(boolean.class, Object.class)), - MethodType.methodType(boolean.class, Object.class, Class.class), 1, 0); - INTEGER_EQ_CHECK = LOOKUP.findStatic(SwitchBootstraps.class, "integerEqCheck", - MethodType.methodType(boolean.class, Object.class, Integer.class)); - OBJECT_EQ_CHECK = LOOKUP.findStatic(Objects.class, "equals", - MethodType.methodType(boolean.class, Object.class, Object.class)); - ENUM_EQ_CHECK = LOOKUP.findStatic(SwitchBootstraps.class, "enumEqCheck", - MethodType.methodType(boolean.class, Object.class, EnumDesc.class, MethodHandles.Lookup.class, ResolvedEnumLabel.class)); NULL_CHECK = LOOKUP.findStatic(Objects.class, "isNull", MethodType.methodType(boolean.class, Object.class)); IS_ZERO = LOOKUP.findStatic(SwitchBootstraps.class, "isZero", @@ -155,7 +157,9 @@ public static CallSite typeSwitch(MethodHandles.Lookup lookup, labels = labels.clone(); Stream.of(labels).forEach(SwitchBootstraps::verifyLabel); - MethodHandle target = createMethodHandleSwitch(lookup, labels); + MethodHandle target = generateInnerClass(lookup, labels); + + target = withIndexCheck(target, labels.length); return new ConstantCallSite(target); } @@ -173,79 +177,6 @@ private static void verifyLabel(Object label) { } } - /* - * Construct test chains for labels inside switch, to handle switch repeats: - * switch (idx) { - * case 0 -> if (selector matches label[0]) return 0; else if (selector matches label[1]) return 1; else ... - * case 1 -> if (selector matches label[1]) return 1; else ... - * ... - * } - */ - private static MethodHandle createRepeatIndexSwitch(MethodHandles.Lookup lookup, Object[] labels) { - MethodHandle def = MethodHandles.dropArguments(MethodHandles.constant(int.class, labels.length), 0, Object.class); - MethodHandle[] testChains = new MethodHandle[labels.length]; - List labelsList = List.of(labels).reversed(); - - for (int i = 0; i < labels.length; i++) { - MethodHandle test = def; - int idx = labels.length - 1; - List currentLabels = labelsList.subList(0, labels.length - i); - - for (int j = 0; j < currentLabels.size(); j++, idx--) { - Object currentLabel = currentLabels.get(j); - if (j + 1 < currentLabels.size() && currentLabels.get(j + 1) == currentLabel) continue; - MethodHandle currentTest; - if (currentLabel instanceof Class) { - currentTest = INSTANCEOF_CHECK; - } else if (currentLabel instanceof Integer) { - currentTest = INTEGER_EQ_CHECK; - } else if (currentLabel instanceof EnumDesc) { - currentTest = MethodHandles.insertArguments(ENUM_EQ_CHECK, 2, lookup, new ResolvedEnumLabel()); - } else { - currentTest = OBJECT_EQ_CHECK; - } - test = MethodHandles.guardWithTest(MethodHandles.insertArguments(currentTest, 1, currentLabel), - MethodHandles.dropArguments(MethodHandles.constant(int.class, idx), 0, Object.class), - test); - } - testChains[i] = MethodHandles.dropArguments(test, 0, int.class); - } - - return MethodHandles.tableSwitch(MethodHandles.dropArguments(def, 0, int.class), testChains); - } - - /* - * Construct code that maps the given selector and repeat index to a case label number: - * if (selector == null) return -1; - * else return "createRepeatIndexSwitch(labels)" - */ - private static MethodHandle createMethodHandleSwitch(MethodHandles.Lookup lookup, Object[] labels) { - MethodHandle mainTest; - MethodHandle def = MethodHandles.dropArguments(MethodHandles.constant(int.class, labels.length), 0, Object.class); - if (labels.length > 0) { - mainTest = createRepeatIndexSwitch(lookup, labels); - } else { - mainTest = MethodHandles.dropArguments(def, 0, int.class); - } - MethodHandle body = - MethodHandles.guardWithTest(MethodHandles.dropArguments(NULL_CHECK, 0, int.class), - MethodHandles.dropArguments(MethodHandles.constant(int.class, -1), 0, int.class, Object.class), - mainTest); - MethodHandle switchImpl = - MethodHandles.permuteArguments(body, MethodType.methodType(int.class, Object.class, int.class), 1, 0); - return withIndexCheck(switchImpl, labels.length); - } - - private static boolean integerEqCheck(Object value, Integer constant) { - if (value instanceof Number input && constant.intValue() == input.intValue()) { - return true; - } else if (value instanceof Character input && constant.intValue() == input.charValue()) { - return true; - } - - return false; - } - private static boolean isZero(int value) { return value == 0; } @@ -330,16 +261,16 @@ public static CallSite enumSwitch(MethodHandles.Lookup lookup, //If all labels are enum constants, construct an optimized handle for repeat index 0: //if (selector == null) return -1 //else if (idx == 0) return mappingArray[selector.ordinal()]; //mapping array created lazily - //else return "createRepeatIndexSwitch(labels)" + //else return "typeSwitch(labels)" MethodHandle body = MethodHandles.guardWithTest(MethodHandles.dropArguments(NULL_CHECK, 0, int.class), MethodHandles.dropArguments(MethodHandles.constant(int.class, -1), 0, int.class, Object.class), MethodHandles.guardWithTest(MethodHandles.dropArguments(IS_ZERO, 1, Object.class), - createRepeatIndexSwitch(lookup, labels), + generateInnerClass(lookup, labels), MethodHandles.insertArguments(MAPPED_ENUM_LOOKUP, 1, lookup, enumClass, labels, new EnumMap()))); target = MethodHandles.permuteArguments(body, MethodType.methodType(int.class, Object.class, int.class), 1, 0); } else { - target = createMethodHandleSwitch(lookup, labels); + target = generateInnerClass(lookup, labels); } target = target.asType(invocationType); @@ -360,7 +291,7 @@ private static > Object convertEnumConstants(MethodHandles.Loo } return label; } else if (labelClass == String.class) { - return EnumDesc.of(enumClassTemplate.describeConstable().get(), (String) label); + return EnumDesc.of(enumClassTemplate.describeConstable().orElseThrow(), (String) label); } else { throw new IllegalArgumentException("label with illegal type found: " + labelClass + ", expected label of type either String or Class"); @@ -389,45 +320,225 @@ private static > int mappedEnumLookup(T value, MethodHandles.L return enumMap.map[value.ordinal()]; } - private static boolean enumEqCheck(Object value, EnumDesc label, MethodHandles.Lookup lookup, ResolvedEnumLabel resolvedEnum) { - if (resolvedEnum.resolvedEnum == null) { - Object resolved; - - try { - if (!(value instanceof Enum enumValue)) { - return false; - } + private static MethodHandle withIndexCheck(MethodHandle target, int labelsCount) { + MethodHandle checkIndex = MethodHandles.insertArguments(CHECK_INDEX, 1, labelsCount + 1); - Class clazz = label.constantType().resolveConstantDesc(lookup); + return MethodHandles.filterArguments(target, 1, checkIndex); + } - if (enumValue.getDeclaringClass() != clazz) { - return false; - } + private static final class ResolvedEnumLabels implements BiPredicate { - resolved = label.resolveConstantDesc(lookup); - } catch (IllegalArgumentException | ReflectiveOperationException ex) { - resolved = SENTINEL; - } + private final MethodHandles.Lookup lookup; + private final EnumDesc[] enumDescs; + @Stable + private Object[] resolvedEnum; - resolvedEnum.resolvedEnum = resolved; + public ResolvedEnumLabels(MethodHandles.Lookup lookup, EnumDesc[] enumDescs) { + this.lookup = lookup; + this.enumDescs = enumDescs; + this.resolvedEnum = new Object[enumDescs.length]; } - return value == resolvedEnum.resolvedEnum; - } + @Override + public boolean test(Integer labelIndex, Object value) { + Object result = resolvedEnum[labelIndex]; - private static MethodHandle withIndexCheck(MethodHandle target, int labelsCount) { - MethodHandle checkIndex = MethodHandles.insertArguments(CHECK_INDEX, 1, labelsCount + 1); + if (result == null) { + try { + if (!(value instanceof Enum enumValue)) { + return false; + } - return MethodHandles.filterArguments(target, 1, checkIndex); - } + EnumDesc label = enumDescs[labelIndex]; + Class clazz = label.constantType().resolveConstantDesc(lookup); - private static final class ResolvedEnumLabel { - @Stable - public Object resolvedEnum; + if (enumValue.getDeclaringClass() != clazz) { + return false; + } + + result = label.resolveConstantDesc(lookup); + } catch (IllegalArgumentException | ReflectiveOperationException ex) { + result = SENTINEL; + } + + resolvedEnum[labelIndex] = result; + } + + return result == value; + } } private static final class EnumMap { @Stable public int[] map; } + + /* + * Construct test chains for labels inside switch, to handle switch repeats: + * switch (idx) { + * case 0 -> if (selector matches label[0]) return 0; + * case 1 -> if (selector matches label[1]) return 1; + * ... + * } + */ + @SuppressWarnings("removal") + private static MethodHandle generateInnerClass(MethodHandles.Lookup caller, Object[] labels) { + List> enumDescs = new ArrayList<>(); + List> extraClassLabels = new ArrayList<>(); + + byte[] classBytes = Classfile.of().build(ClassDesc.of(typeSwitchClassName(caller.lookupClass())), clb -> { + clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC) + .withMethodBody("typeSwitch", + TYPES_SWITCH_DESCRIPTOR, + Classfile.ACC_FINAL | Classfile.ACC_PUBLIC | Classfile.ACC_STATIC, + cb -> { + cb.aload(0); + Label nonNullLabel = cb.newLabel(); + cb.if_nonnull(nonNullLabel); + cb.iconst_m1(); + cb.ireturn(); + cb.labelBinding(nonNullLabel); + if (labels.length == 0) { + cb.constantInstruction(0) + .ireturn(); + return ; + } + cb.iload(1); + Label dflt = cb.newLabel(); + record Element(Label target, Label next, Object caseLabel) {} + List cases = new ArrayList<>(); + List switchCases = new ArrayList<>(); + Object lastLabel = null; + for (int idx = labels.length - 1; idx >= 0; idx--) { + Object currentLabel = labels[idx]; + Label target = cb.newLabel(); + Label next; + if (lastLabel == null) { + next = dflt; + } else if (lastLabel.equals(currentLabel)) { + next = cases.getLast().next(); + } else { + next = cases.getLast().target(); + } + lastLabel = currentLabel; + cases.add(new Element(target, next, currentLabel)); + switchCases.add(SwitchCase.of(idx, target)); + } + cases = cases.reversed(); + switchCases = switchCases.reversed(); + cb.tableswitch(0, labels.length - 1, dflt, switchCases); + for (int idx = 0; idx < cases.size(); idx++) { + Element element = cases.get(idx); + Label next = element.next(); + cb.labelBinding(element.target()); + if (element.caseLabel() instanceof Class classLabel) { + Optional classLabelConstableOpt = classLabel.describeConstable(); + if (classLabelConstableOpt.isPresent()) { + cb.aload(0); + cb.instanceof_(classLabelConstableOpt.orElseThrow()); + cb.ifeq(next); + } else { + cb.aload(3); + cb.constantInstruction(extraClassLabels.size()); + cb.invokeinterface(ConstantDescs.CD_List, + "get", + MethodTypeDesc.of(ConstantDescs.CD_Object, + ConstantDescs.CD_int)); + cb.checkcast(ConstantDescs.CD_Class); + cb.aload(0); + cb.invokevirtual(ConstantDescs.CD_Class, + "isInstance", + MethodTypeDesc.of(ConstantDescs.CD_boolean, + ConstantDescs.CD_Object)); + cb.ifeq(next); + extraClassLabels.add(classLabel); + } + } else if (element.caseLabel() instanceof EnumDesc enumLabel) { + int enumIdx = enumDescs.size(); + enumDescs.add(enumLabel); + cb.aload(2); + cb.constantInstruction(enumIdx); + cb.invokestatic(ConstantDescs.CD_Integer, + "valueOf", + MethodTypeDesc.of(ConstantDescs.CD_Integer, + ConstantDescs.CD_int)); + cb.aload(0); + cb.invokeinterface(BiPredicate.class.describeConstable().orElseThrow(), + "test", + MethodTypeDesc.of(ConstantDescs.CD_boolean, + ConstantDescs.CD_Object, + ConstantDescs.CD_Object)); + cb.ifeq(next); + } else if (element.caseLabel() instanceof String stringLabel) { + cb.ldc(stringLabel); + cb.aload(0); + cb.invokevirtual(ConstantDescs.CD_Object, + "equals", + MethodTypeDesc.of(ConstantDescs.CD_boolean, + ConstantDescs.CD_Object)); + cb.ifeq(next); + } else if (element.caseLabel() instanceof Integer integerLabel) { + Label compare = cb.newLabel(); + Label notNumber = cb.newLabel(); + cb.aload(0); + cb.instanceof_(ConstantDescs.CD_Number); + cb.ifeq(notNumber); + cb.aload(0); + cb.checkcast(ConstantDescs.CD_Number); + cb.invokevirtual(ConstantDescs.CD_Number, + "intValue", + MethodTypeDesc.of(ConstantDescs.CD_int)); + cb.goto_(compare); + cb.labelBinding(notNumber); + cb.aload(0); + cb.instanceof_(ConstantDescs.CD_Character); + cb.ifeq(next); + cb.aload(0); + cb.checkcast(ConstantDescs.CD_Character); + cb.invokevirtual(ConstantDescs.CD_Character, + "charValue", + MethodTypeDesc.of(ConstantDescs.CD_char)); + cb.labelBinding(compare); + cb.ldc(integerLabel); + cb.if_icmpne(next); + } else { + throw new InternalError("Unsupported label type: " + + element.caseLabel().getClass()); + } + cb.constantInstruction(idx); + cb.ireturn(); + } + cb.labelBinding(dflt); + cb.constantInstruction(cases.size()); + cb.ireturn(); + }); + }); + + try { + // this class is linked at the indy callsite; so define a hidden nestmate + MethodHandles.Lookup lookup; + lookup = caller.defineHiddenClass(classBytes, true, NESTMATE, STRONG); + MethodHandle typeSwitch = lookup.findStatic(lookup.lookupClass(), + "typeSwitch", + MethodType.methodType(int.class, + Object.class, + int.class, + BiPredicate.class, + List.class)); + return MethodHandles.insertArguments(typeSwitch, 2, new ResolvedEnumLabels(caller, enumDescs.toArray(EnumDesc[]::new)), + List.copyOf(extraClassLabels)); + } catch (Throwable t) { + throw new IllegalArgumentException(t); + } + } + + //based on src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java: + private static String typeSwitchClassName(Class targetClass) { + String name = targetClass.getName(); + if (targetClass.isHidden()) { + // use the original class name + name = name.replace('/', '_'); + } + return name + "$$TypeSwitch"; + } } diff --git a/src/java.base/share/classes/java/net/DatagramSocket.java b/src/java.base/share/classes/java/net/DatagramSocket.java index 26220feac2c1a..9e94d1d8b6209 100644 --- a/src/java.base/share/classes/java/net/DatagramSocket.java +++ b/src/java.base/share/classes/java/net/DatagramSocket.java @@ -604,6 +604,12 @@ public SocketAddress getRemoteSocketAddress() { /** * Returns the address of the endpoint this socket is bound to. + *

If the socket was initially bound to the wildcard address and + * is now {@link #isConnected connected}, then the address returned + * may be the local address selected as the source address for + * datagrams sent on this socket instead of the wildcard address. + * When {@link #disconnect()} is called, the bound address reverts + * to the wildcard address. * * @return a {@code SocketAddress} representing the local endpoint of this * socket, or {@code null} if it is closed or not bound yet. @@ -714,6 +720,12 @@ public void receive(DatagramPacket p) throws IOException { /** * Gets the local address to which the socket is bound. + *

If the socket was initially bound to the wildcard address and + * is now {@link #isConnected connected}, then the address returned + * may be the local address selected as the source address for + * datagrams sent on the socket instead of the wildcard address. + * When {@link #disconnect()} is called, the bound address reverts + * to the wildcard address. * *

If there is a security manager, its * {@code checkConnect} method is first called diff --git a/src/java.base/share/classes/java/net/URLDecoder.java b/src/java.base/share/classes/java/net/URLDecoder.java index 84d74a7be305b..e2070458e2b17 100644 --- a/src/java.base/share/classes/java/net/URLDecoder.java +++ b/src/java.base/share/classes/java/net/URLDecoder.java @@ -98,6 +98,8 @@ private URLDecoder() {} * default charset. Instead, use the decode(String,String) method * to specify the encoding. * @return the newly decoded {@code String} + * @throws IllegalArgumentException if the implementation encounters malformed + * escape sequences */ @Deprecated public static String decode(String s) { @@ -113,9 +115,6 @@ public static String decode(String s) { * except that it will {@linkplain Charset#forName look up the charset} * using the given encoding name. * - * @implNote This implementation will throw an {@link java.lang.IllegalArgumentException} - * when illegal strings are encountered. - * * @param s the {@code String} to decode * @param enc The name of a supported * character @@ -124,6 +123,8 @@ public static String decode(String s) { * @throws UnsupportedEncodingException * If character encoding needs to be consulted, but * named character encoding is not supported + * @throws IllegalArgumentException if the implementation encounters malformed + * escape sequences * @see URLEncoder#encode(java.lang.String, java.lang.String) * @since 1.4 */ @@ -144,8 +145,10 @@ public static String decode(String s, String enc) throws UnsupportedEncodingExce * Decodes an {@code application/x-www-form-urlencoded} string using * a specific {@linkplain Charset Charset}. * The supplied charset is used to determine - * what characters are represented by any consecutive sequences of the - * form "{@code %xy}". + * what characters are represented by any consecutive escape sequences of + * the form "{@code %xy}". Erroneous bytes are replaced with the + * supplied {@code Charset}'s {@linkplain java.nio.charset.CharsetDecoder##cae + * replacement value}. *

* Note: The @@ -153,15 +156,12 @@ public static String decode(String s, String enc) throws UnsupportedEncodingExce * UTF-8 should be used. Not doing so may introduce * incompatibilities. * - * @implNote This implementation will throw an {@link java.lang.IllegalArgumentException} - * when illegal strings are encountered. - * * @param s the {@code String} to decode * @param charset the given charset * @return the newly decoded {@code String} * @throws NullPointerException if {@code s} or {@code charset} is {@code null} - * @throws IllegalArgumentException if the implementation encounters illegal - * characters + * @throws IllegalArgumentException if the implementation encounters malformed + * escape sequences * * @spec https://www.w3.org/TR/html4 HTML 4.01 Specification * @see URLEncoder#encode(java.lang.String, Charset) diff --git a/src/java.base/share/classes/java/net/URLEncoder.java b/src/java.base/share/classes/java/net/URLEncoder.java index f8fd79e6b5e3f..3c02dd50e6e9a 100644 --- a/src/java.base/share/classes/java/net/URLEncoder.java +++ b/src/java.base/share/classes/java/net/URLEncoder.java @@ -200,11 +200,15 @@ public static String encode(String s, String enc) * This method uses the supplied charset to obtain the bytes for unsafe * characters. *

- * Note: The * World Wide Web Consortium Recommendation states that - * UTF-8 should be used. Not doing so may introduce incompatibilities. - * + * UTF-8 should be used. Not doing so may introduce incompatibilities. * @param s {@code String} to be translated. * @param charset the given charset * @return the translated {@code String}. diff --git a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java index 1d0a838f65d5c..1b0e7c0f35a50 100644 --- a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java +++ b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java @@ -645,6 +645,13 @@ public final long write(ByteBuffer[] srcs) throws IOException { /** * {@inheritDoc} *

+ * If the channel's socket was initially bound to the wildcard address and + * is now {@link #isConnected connected}, then the address returned + * may be the local address selected as the source address for + * datagrams sent via this channel instead of the wildcard address. + * When {@link #disconnect} is called, the bound address reverts + * to the wildcard address. + *

* If there is a security manager set, its {@code checkConnect} method is * called with the local address and {@code -1} as its arguments to see * if the operation is allowed. If the operation is not allowed, diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java index 926b773827ca8..e446df8fd3e0c 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java @@ -55,20 +55,6 @@ public class AtomicLong extends Number implements java.io.Serializable { private static final long serialVersionUID = 1927816293512124184L; - /** - * Records whether the underlying JVM supports lockless - * compareAndSet for longs. While the intrinsic compareAndSetLong - * method works in either case, some constructions should be - * handled at Java level to avoid locking user-visible locks. - */ - static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); - - /** - * Returns whether underlying JVM supports lockless CompareAndSet - * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS. - */ - private static native boolean VMSupportsCS8(); - /* * This class intended to be implemented using VarHandles, but there * are unresolved cyclic startup dependencies. 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 ae5440ef5f2a0..0e496dbd6a55d 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 @@ -90,10 +90,7 @@ public abstract class AtomicLongFieldUpdater { public static AtomicLongFieldUpdater newUpdater(Class tclass, String fieldName) { Class caller = Reflection.getCallerClass(); - if (AtomicLong.VM_SUPPORTS_LONG_CAS) - return new CASUpdater(tclass, fieldName, caller); - else - return new LockedUpdater(tclass, fieldName, caller); + return new CASUpdater(tclass, fieldName, caller); } /** @@ -515,126 +512,6 @@ public final long addAndGet(T obj, long delta) { } } - private static final class LockedUpdater extends AtomicLongFieldUpdater { - private static final Unsafe U = Unsafe.getUnsafe(); - private final long offset; - /** - * if field is protected, the subclass constructing updater, else - * the same as tclass - */ - private final Class cclass; - /** class holding the field */ - private final Class tclass; - - @SuppressWarnings("removal") - LockedUpdater(final Class tclass, final String fieldName, - final Class caller) { - final Field field; - final int modifiers; - try { - field = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public Field run() throws NoSuchFieldException { - return tclass.getDeclaredField(fieldName); - } - }); - modifiers = field.getModifiers(); - sun.reflect.misc.ReflectUtil.ensureMemberAccess( - caller, tclass, null, modifiers); - ClassLoader cl = tclass.getClassLoader(); - ClassLoader ccl = caller.getClassLoader(); - if ((ccl != null) && (ccl != cl) && - ((cl == null) || !isAncestor(cl, ccl))) { - sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); - } - } catch (PrivilegedActionException pae) { - throw new RuntimeException(pae.getException()); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - - if (field.getType() != long.class) - throw new IllegalArgumentException("Must be long type"); - - if (!Modifier.isVolatile(modifiers)) - throw new IllegalArgumentException("Must be volatile type"); - - // 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) - // of the protected member's defining class. - // If the updater refers to a protected field of a declaring class - // outside the current package, the receiver argument will be - // narrowed to the type of the accessing class. - this.cclass = (Modifier.isProtected(modifiers) && - tclass.isAssignableFrom(caller) && - !isSamePackage(tclass, caller)) - ? caller : tclass; - this.tclass = tclass; - this.offset = U.objectFieldOffset(field); - } - - /** - * Checks that target argument is instance of cclass. On - * failure, throws cause. - */ - private final void accessCheck(T obj) { - if (!cclass.isInstance(obj)) - throw accessCheckException(obj); - } - - /** - * Returns access exception if accessCheck failed due to - * protected access, else ClassCastException. - */ - private final RuntimeException accessCheckException(T obj) { - if (cclass == tclass) - return new ClassCastException(); - else - return new RuntimeException( - new IllegalAccessException( - "Class " + - cclass.getName() + - " can not access a protected member of class " + - tclass.getName() + - " using an instance of " + - obj.getClass().getName())); - } - - public final boolean compareAndSet(T obj, long expect, long update) { - accessCheck(obj); - synchronized (this) { - long v = U.getLong(obj, offset); - if (v != expect) - return false; - U.putLong(obj, offset, update); - return true; - } - } - - public final boolean weakCompareAndSet(T obj, long expect, long update) { - return compareAndSet(obj, expect, update); - } - - public final void set(T obj, long newValue) { - accessCheck(obj); - synchronized (this) { - U.putLong(obj, offset, newValue); - } - } - - public final void lazySet(T obj, long newValue) { - set(obj, newValue); - } - - public final long get(T obj) { - accessCheck(obj); - synchronized (this) { - return U.getLong(obj, offset); - } - } - } - /** * Returns true if the second classloader can be found in the first * classloader's delegation chain. diff --git a/src/java.base/share/classes/java/util/zip/ZipInputStream.java b/src/java.base/share/classes/java/util/zip/ZipInputStream.java index 9e265fd668edc..e583bc289347f 100644 --- a/src/java.base/share/classes/java/util/zip/ZipInputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipInputStream.java @@ -51,12 +51,12 @@ * {@code ZipInputStream} read methods such * as {@link #read(byte[], int, int) read} or {@link #readAllBytes() readAllBytes()}. * For example: - * {@snippet : + * {@snippet lang="java" : * Path jar = Path.of("foo.jar"); * try (InputStream is = Files.newInputStream(jar); * ZipInputStream zis = new ZipInputStream(is)) { * ZipEntry ze; - * while((ze= zis.getNextEntry()) != null) { + * while ((ze = zis.getNextEntry()) != null) { * var bytes = zis.readAllBytes(); * System.out.printf("Entry: %s, bytes read: %s%n", ze.getName(), * bytes.length); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 27e68b9ecf9b4..3ae4b25923255 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -1394,7 +1394,7 @@ Type toArray() { } Type getComponent() { - if (sym.isArray()) { + if (isArray()) { var comp = sym.componentType(); if (comp.isPrimitive()) { return switch (comp.descriptorString().charAt(0)) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java index 7ac94ef6b93e2..aef62be815160 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/MemorySessionImpl.java @@ -55,8 +55,7 @@ public abstract sealed class MemorySessionImpl implements Scope permits ConfinedSession, GlobalSession, SharedSession { static final int OPEN = 0; - static final int CLOSING = -1; - static final int CLOSED = -2; + static final int CLOSED = -1; static final VarHandle STATE; static final int MAX_FORKS = Integer.MAX_VALUE; diff --git a/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java b/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java index 8d54a1e2d8af4..1569589ef9b81 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SharedSession.java @@ -77,17 +77,13 @@ public void release0() { } void justClose() { - int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSING); + int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSED); if (prevState < 0) { throw alreadyClosed(); } else if (prevState != OPEN) { throw alreadyAcquired(prevState); } - boolean success = SCOPED_MEMORY_ACCESS.closeScope(this); - STATE.setVolatile(this, success ? CLOSED : OPEN); - if (!success) { - throw alreadyAcquired(1); - } + SCOPED_MEMORY_ACCESS.closeScope(this, ALREADY_CLOSED); } /** diff --git a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template index 2c7f52352aaee..0147b3bacd86e 100644 --- a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template +++ b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template @@ -83,11 +83,11 @@ public class ScopedMemoryAccess { registerNatives(); } - public boolean closeScope(MemorySessionImpl session) { - return closeScope0(session); + public void closeScope(MemorySessionImpl session, ScopedAccessError error) { + closeScope0(session, error); } - native boolean closeScope0(MemorySessionImpl session); + native void closeScope0(MemorySessionImpl session, ScopedAccessError error); private ScopedMemoryAccess() {} diff --git a/src/java.base/share/classes/sun/net/www/MessageHeader.java b/src/java.base/share/classes/sun/net/www/MessageHeader.java index 5542193b9a990..6c6d18453ca7c 100644 --- a/src/java.base/share/classes/sun/net/www/MessageHeader.java +++ b/src/java.base/share/classes/sun/net/www/MessageHeader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -554,7 +554,7 @@ public void mergeHeader(InputStream is) throws java.io.IOException { } public synchronized String toString() { - String result = super.toString() + nkeys + " pairs: "; + String result = super.toString() + " " + nkeys + " pairs: "; for (int i = 0; i < keys.length && i < nkeys; i++) { result += "{"+keys[i]+": "+values[i]+"}"; } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java index 1021503318ec6..7881a1480c990 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java @@ -152,7 +152,7 @@ private static HashMap getCache() { @Override protected boolean useAuthCache() { - return super.useAuthCache() && cacheSPNEGO; + return false; } /** diff --git a/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java index 943af4d2333aa..4b615ca9af111 100644 --- a/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java +++ b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java @@ -23,7 +23,6 @@ * questions. */ - package sun.security.pkcs10; import java.io.PrintStream; @@ -174,7 +173,7 @@ public PKCS10(byte[] data) throw new SignatureException("Invalid PKCS #10 signature"); } } catch (InvalidKeyException e) { - throw new SignatureException("Invalid key"); + throw new SignatureException("Invalid key", e); } catch (InvalidAlgorithmParameterException e) { throw new SignatureException("Invalid signature parameters", e); } catch (ProviderException e) { diff --git a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c index 5c7531dcd75ba..39d869adb639e 100644 --- a/src/java.base/share/native/libfallbackLinker/fallbackLinker.c +++ b/src/java.base/share/native/libfallbackLinker/fallbackLinker.c @@ -28,8 +28,8 @@ #include #include -#include #include +#include #include #ifdef _WIN64 #include diff --git a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java index fd905da80a51c..ee041e215a32a 100644 --- a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java +++ b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java @@ -74,7 +74,8 @@ public enum SourceVersion { * 21: pattern matching for switch and record patterns (string * templates in preview, unnamed patterns and variables in * preview, unnamed classes and instance main methods in preview) - * 22: tbd + * 22: Unnamed Variables & Patterns (Statements before super(...) + * in Preview) */ /** @@ -392,9 +393,9 @@ public enum SourceVersion { * href="https://docs.oracle.com/javase/specs/jls/se21/html/index.html"> * The Java Language Specification, Java SE 21 Edition * @see - * Record Patterns + * JEP 440: Record Patterns * @see - * Pattern Matching for switch + * JEP 441: Pattern Matching for switch */ RELEASE_21, @@ -402,11 +403,16 @@ public enum SourceVersion { * The version introduced by the Java Platform, Standard Edition * 22. * + * Additions in this release include unnamed variables and unnamed + * patterns. + * * @since 22 * * @see * The Java Language Specification, Java SE 22 Edition + * @see + * JEP 456: Unnamed Variables & Patterns */ RELEASE_22, ; // Reduce code churn when appending new constants diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java index 0adf45df1ff0a..ee50976bbf4f9 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java @@ -215,9 +215,12 @@ public void update(Graphics g, JComponent c) { SynthContext context = getContext(c); SynthLookAndFeel.update(context, g); - context.getPainter().paintProgressBarBackground(context, - g, 0, 0, c.getWidth(), c.getHeight(), - progressBar.getOrientation()); + + if (((JProgressBar) c).isBorderPainted()) { + context.getPainter().paintProgressBarBackground(context, + g, 0, 0, c.getWidth(), c.getHeight(), + progressBar.getOrientation()); + } paint(context, g); } diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index bb60b38265465..15247608e1b4b 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -2965,6 +2965,17 @@ ImageIcon getImage(URL base) { } return image; } + + @Override + public int hashCode() { + return Objects.hashCode(svalue); + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.BackgroundImage img + && Objects.equals(svalue, img.svalue); + } } /** diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java index 91526938bff0a..d6d03a9aa8af9 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java @@ -101,7 +101,7 @@ class MultiExchange implements Cancelable { ); private final List filters; - ResponseTimerEvent responseTimerEvent; + volatile ResponseTimerEvent responseTimerEvent; volatile boolean cancelled; AtomicReference interrupted = new AtomicReference<>(); final PushGroup pushGroup; @@ -231,6 +231,7 @@ public Optional remainingConnectTimeout() { private void cancelTimer() { if (responseTimerEvent != null) { client.cancelTimer(responseTimerEvent); + responseTimerEvent = null; } } @@ -457,6 +458,7 @@ private CompletableFuture responseAsyncImpl() { } return completedFuture(response); } else { + cancelTimer(); this.response = new HttpResponseImpl<>(currentreq, response, this.response, null, exch); Exchange oldExch = exch; diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java index 92b81685a94e2..da21fb4a511b6 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/PropertyManager.java @@ -46,7 +46,7 @@ * @author K Venugopal * @author Sunitha Reddy * - * @LastModified: July 2023 + * @LastModified: Nov 2023 */ public class PropertyManager { @@ -150,10 +150,7 @@ private void initConfigurableReaderProperties() { // Initialize Catalog features supportedProps.put(XMLConstants.USE_CATALOG, JdkXmlUtils.USE_CATALOG_DEFAULT); - for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) { - supportedProps.put(f.getPropertyName(), null); - } - + JdkXmlUtils.initCatalogFeatures(supportedProps); supportedProps.put(JdkConstants.CDATA_CHUNK_SIZE, JdkConstants.CDATA_CHUNK_SIZE_DEFAULT); } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java index 8f55114af337a..b77108b1e0eba 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java @@ -74,7 +74,7 @@ * @author Eric Ye, IBM * @author Sunitha Reddy, SUN Microsystems * - * @LastModified: July 2023 + * @LastModified: Nov 2023 */ public class XMLDocumentFragmentScannerImpl extends XMLScanner @@ -343,6 +343,13 @@ public class XMLDocumentFragmentScannerImpl */ protected String fAccessExternalDTD = EXTERNAL_ACCESS_DEFAULT; + /** + * Properties to determine whether to use a user-specified Catalog: + * Feature USE_CATALOG, Resolve and Catalog File + */ + protected boolean fUseCatalog = true; + protected String fCatalogFile; + /** * standard uri conformant (strict uri). * http://apache.org/xml/features/standard-uri-conformant diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java index f859a499d29b7..52bfcd7aeec90 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java @@ -43,9 +43,11 @@ import java.io.CharConversionException; import java.io.EOFException; import java.io.IOException; +import javax.xml.XMLConstants; import javax.xml.stream.events.XMLEvent; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkProperty.State; +import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.SecuritySupport; import jdk.xml.internal.XMLSecurityManager.Limit; @@ -69,7 +71,7 @@ * Refer to the table in unit-test javax.xml.stream.XMLStreamReaderTest.SupportDTD for changes * related to property SupportDTD. * @author Joe Wang, Sun Microsystems - * @LastModified: July 2023 + * @LastModified: Nov 2023 */ public class XMLDocumentScannerImpl extends XMLDocumentFragmentScannerImpl{ @@ -281,6 +283,9 @@ public void reset(PropertyManager propertyManager) { fLoadExternalDTD = !((Boolean)propertyManager.getProperty( Constants.ZEPHYR_PROPERTY_PREFIX + Constants.IGNORE_EXTERNAL_DTD)); + fUseCatalog = (Boolean)propertyManager.getProperty(XMLConstants.USE_CATALOG); + fCatalogFile = (String)propertyManager.getProperty(JdkXmlUtils.CATALOG_FILES); + setScannerState(XMLEvent.START_DOCUMENT); setDriver(fXMLDeclDriver); fSeenInternalSubset = false; @@ -327,6 +332,8 @@ public void reset(XMLComponentManager componentManager) // xerces features fLoadExternalDTD = componentManager.getFeature(LOAD_EXTERNAL_DTD, true); + fUseCatalog = componentManager.getFeature(XMLConstants.USE_CATALOG, true); + fCatalogFile = (String)componentManager.getProperty(JdkXmlUtils.CATALOG_FILES); fNamespaces = componentManager.getFeature(NAMESPACES, true); fSeenInternalSubset = false; diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java index d127f7c8ec1bb..22de24ea8f058 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java @@ -56,6 +56,7 @@ import javax.xml.catalog.CatalogResolver; import javax.xml.stream.XMLInputFactory; import javax.xml.transform.Source; +import jdk.xml.internal.JdkCatalog; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkProperty; import jdk.xml.internal.JdkXmlUtils; @@ -93,7 +94,7 @@ * @author K.Venugopal SUN Microsystems * @author Neeraj Bajaj SUN Microsystems * @author Sunitha Reddy SUN Microsystems - * @LastModified: July 2023 + * @LastModified: Nov 2023 */ public class XMLEntityManager implements XMLComponent, XMLEntityResolver { @@ -265,9 +266,6 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver { /** Debug switching readers for encodings. */ private static final boolean DEBUG_ENCODINGS = false; - // should be diplayed trace resolving messages - private static final boolean DEBUG_RESOLVER = false ; - // // Data // @@ -355,6 +353,7 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver { /** Security Manager */ protected XMLSecurityManager fSecurityManager = null; + XMLSecurityPropertyManager fSecurityPropertyMgr; protected XMLLimitAnalyzer fLimitAnalyzer = null; @@ -418,8 +417,11 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver { /** indicate whether Catalog should be used for resolving external resources */ private boolean fUseCatalog = true; + // user-specified Catalog Resolver CatalogFeatures fCatalogFeatures; CatalogResolver fCatalogResolver; + // the default JDK Catalog Resolver + CatalogResolver fDefCR; private String fCatalogFile; private String fDefer; @@ -434,11 +436,16 @@ public class XMLEntityManager implements XMLComponent, XMLEntityResolver { * If this constructor is used to create the object, reset() should be invoked on this object */ public XMLEntityManager() { + this(null, new XMLSecurityManager(true)); + } + + public XMLEntityManager(XMLSecurityPropertyManager securityPropertyMgr, XMLSecurityManager securityManager) { //for entity managers not created by parsers - fSecurityManager = new XMLSecurityManager(true); + fSecurityManager = securityManager; + fSecurityPropertyMgr = securityPropertyMgr; fEntityStorage = new XMLEntityStorage(this) ; setScannerVersion(Constants.XML_VERSION_1_0); - } // () + } /** Default constructor. */ public XMLEntityManager(PropertyManager propertyManager) { @@ -653,7 +660,11 @@ public String setupCurrentEntity(boolean reference, String name, XMLInputSource URL location = new URL(expandedSystemId); URLConnection connect = location.openConnection(); if (!(connect instanceof HttpURLConnection)) { - stream = connect.getInputStream(); + if (expandedSystemId.startsWith("jrt:/java.xml")) { + stream = SecuritySupport.getInputStream(connect); + } else { + stream = connect.getInputStream(); + } } else { boolean followRedirects = true; @@ -1012,73 +1023,126 @@ public StaxXMLInputSource resolveEntityAsPerStax(XMLResourceIdentifier resourceI ri = fResourceIdentifier; } ri.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId); - if(DEBUG_RESOLVER){ - System.out.println("BEFORE Calling resolveEntity") ; - } fISCreatedByResolver = false; - //either of Stax or Xerces would be null - if(fStaxEntityResolver != null){ + // Step 1: custom resolver, either StAX or Entity + if (fStaxEntityResolver != null) { staxInputSource = fStaxEntityResolver.resolveEntity(ri); - if(staxInputSource != null) { + } else if (fEntityResolver != null) { + xmlInputSource = fEntityResolver.resolveEntity(ri); + if (xmlInputSource != null) { + //wrap it in StaxXMLInputSource fISCreatedByResolver = true; + staxInputSource = new StaxXMLInputSource(xmlInputSource, fISCreatedByResolver); } } - if(fEntityResolver != null){ - xmlInputSource = fEntityResolver.resolveEntity(ri); - if(xmlInputSource != null) { - fISCreatedByResolver = true; + // Step 2: custom catalog if specified + if (staxInputSource == null && (fUseCatalog && fCatalogFile != null)) { + if (fCatalogResolver == null) { + fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve); + fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); } - } - if(xmlInputSource != null){ - //wrap this XMLInputSource to StaxInputSource - staxInputSource = new StaxXMLInputSource(xmlInputSource, fISCreatedByResolver); + staxInputSource = resolveWithCatalogStAX(fCatalogResolver, fCatalogFile, publicId, literalSystemId); } - if (staxInputSource == null && fUseCatalog) { - if (fCatalogFeatures == null) { - fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve); - } - fCatalogFile = fCatalogFeatures.get(Feature.FILES); - if (fCatalogFile != null) { - try { - if (fCatalogResolver == null) { - fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); - } - InputSource is = fCatalogResolver.resolveEntity(publicId, literalSystemId); - if (is != null && !is.isEmpty()) { - staxInputSource = new StaxXMLInputSource(new XMLInputSource(is, true), true); - } - } catch (CatalogException e) { - fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"CatalogException", - new Object[]{SecuritySupport.sanitizePath(fCatalogFile)}, - XMLErrorReporter.SEVERITY_FATAL_ERROR, e ); - } - } + // Step 3: use the default JDK Catalog Resolver if Step 2's resolve is continue + if (staxInputSource == null && JdkXmlUtils.isResolveContinue(fCatalogFeatures)) { + initJdkCatalogResolver(); + + staxInputSource = resolveWithCatalogStAX(fDefCR, JdkCatalog.JDKCATALOG, publicId, literalSystemId); } - // do default resolution - //this works for both stax & Xerces, if staxInputSource is null, - //it means parser need to revert to default resolution - if (staxInputSource == null) { - // REVISIT: when systemId is null, I think we should return null. - // is this the right solution? -SG - //if (systemId != null) + // Step 4: default resolution if not resolved by a resolver and the RESOLVE + // feature is set to 'continue' + if (staxInputSource != null) { + fISCreatedByResolver = true; + } else if (JdkXmlUtils.isResolveContinue(fCatalogFeatures) && + fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE)) { staxInputSource = new StaxXMLInputSource( new XMLInputSource(publicId, literalSystemId, baseSystemId, true), false); - }else if(staxInputSource.hasXMLStreamOrXMLEventReader()){ - //Waiting for the clarification from EG. - nb } - if (DEBUG_RESOLVER) { - System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")"); - System.err.println(" = " + xmlInputSource); + return staxInputSource; + + } + + private void initJdkCatalogResolver() { + if (fDefCR == null) { + fDefCR = fSecurityManager.getJDKCatalogResolver(); } + } - return staxInputSource; + /** + * Resolves the external resource using the Catalog specified and returns + * a StaxXMLInputSource. + */ + private StaxXMLInputSource resolveWithCatalogStAX(CatalogResolver cr, String cFile, + String publicId, String systemId) { + InputSource is = resolveWithCatalog(cr, cFile, publicId, systemId); + // note that empty source isn't considered resolved + if (is != null) { + return new StaxXMLInputSource(new XMLInputSource(is, true), true); + } + return null; + } + /** + * Resolves the external resource using the Catalog specified and returns + * a InputSource. + */ + private InputSource resolveWithCatalog(CatalogResolver cr, String cFile, + String publicId, String systemId) { + if (cr != null) { + try { + return cr.resolveEntity(publicId, systemId); + } catch (CatalogException e) { + fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"CatalogException", + new Object[]{SecuritySupport.sanitizePath(cFile)}, + XMLErrorReporter.SEVERITY_FATAL_ERROR, e ); + } + } + return null; + } + + /** + * Resolves the external resource using the Catalog specified and returns + * a XMLInputSource. Since the Resolve method can be called from various processors, + * this method attempts to resolve the resource as an EntityResolver first + * and then URIResolver if no match is found. + */ + private XMLInputSource resolveEntityOrURI(CatalogResolver cr, String publicId, String systemId, String base) { + XMLInputSource xis = resolveEntity(cr, publicId, systemId, base); + + if (xis != null) { + return xis; + } else if (systemId != null) { + Source source = null; + try { + source = cr.resolve(systemId, base); + } catch (CatalogException e) { + throw new XNIException(e); + } + if (source != null && !source.isEmpty()) { + return new XMLInputSource(publicId, source.getSystemId(), base, true); + } + } + return null; + } + + private XMLInputSource resolveEntity(CatalogResolver cr, String publicId, String systemId, String base) { + InputSource is = null; + try { + if (publicId != null || systemId != null) { + is = cr.resolveEntity(publicId, systemId); + } + } catch (CatalogException e) {} + + if (is != null && !is.isEmpty()) { + return new XMLInputSource(is, true); + } + return null; } /** @@ -1128,7 +1192,7 @@ public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) th if (needExpand) expandedSystemId = expandSystemId(literalSystemId, baseSystemId,false); - // give the entity resolver a chance + // Step 1: custom Entity resolver XMLInputSource xmlInputSource = null; if (fEntityResolver != null) { @@ -1137,63 +1201,30 @@ public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) th xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier); } - if (xmlInputSource == null && fUseCatalog) { - if (fCatalogFeatures == null) { + // Step 2: custom catalog if specified + if ((publicId != null || literalSystemId != null || resourceIdentifier.getNamespace() !=null) + && xmlInputSource == null && (fUseCatalog && fCatalogFile != null)) { + if (fCatalogResolver == null) { fCatalogFeatures = JdkXmlUtils.getCatalogFeatures(fDefer, fCatalogFile, fPrefer, fResolve); + fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); } - fCatalogFile = fCatalogFeatures.get(Feature.FILES); - if (fCatalogFile != null) { - /* - since the method can be called from various processors, both - EntityResolver and URIResolver are used to attempt to find - a match - */ - InputSource is = null; - try { - if (fCatalogResolver == null) { - fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); - } - String pid = (publicId != null? publicId : resourceIdentifier.getNamespace()); - if (pid != null || literalSystemId != null) { - is = fCatalogResolver.resolveEntity(pid, literalSystemId); - } - } catch (CatalogException e) {} - - if (is != null && !is.isEmpty()) { - xmlInputSource = new XMLInputSource(is, true); - } else if (literalSystemId != null) { - if (fCatalogResolver == null) { - fCatalogResolver = CatalogManager.catalogResolver(fCatalogFeatures); - } - - Source source = null; - try { - source = fCatalogResolver.resolve(literalSystemId, baseSystemId); - } catch (CatalogException e) { - throw new XNIException(e); - } - if (source != null && !source.isEmpty()) { - xmlInputSource = new XMLInputSource(publicId, source.getSystemId(), baseSystemId, true); - } - } - } + String pid = (publicId != null? publicId : resourceIdentifier.getNamespace()); + xmlInputSource = resolveEntityOrURI(fCatalogResolver, pid, literalSystemId, baseSystemId); } - // do default resolution - // REVISIT: what's the correct behavior if the user provided an entity - // resolver (fEntityResolver != null), but resolveEntity doesn't return - // an input source (xmlInputSource == null)? - // do we do default resolution, or do we just return null? -SG - if (xmlInputSource == null) { - // REVISIT: when systemId is null, I think we should return null. - // is this the right solution? -SG - //if (systemId != null) - xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false); + // Step 3: use the default JDK Catalog Resolver if Step 2's resolve is continue + if ((publicId != null || literalSystemId != null) + && xmlInputSource == null && JdkXmlUtils.isResolveContinue(fCatalogFeatures)) { + initJdkCatalogResolver(); + // unlike a custom catalog, the JDK Catalog only contains entity references + xmlInputSource = resolveEntity(fDefCR, publicId, literalSystemId, baseSystemId); } - if (DEBUG_RESOLVER) { - System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")"); - System.err.println(" = " + xmlInputSource); + // Step 4: default resolution if not resolved by a resolver and the RESOLVE + // feature is set to 'continue' + if ((xmlInputSource == null) && JdkXmlUtils.isResolveContinue(fCatalogFeatures) && + fSecurityManager.is(Limit.JDKCATALOG_RESOLVE, JdkConstants.CONTINUE)) { + xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId, false); } return xmlInputSource; @@ -1411,7 +1442,7 @@ public void startEntity(boolean isGE, String name, fSecurityManager.debugPrint(fLimitAnalyzer); fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,"EntityExpansionLimit", new Object[]{fSecurityManager.getLimitValueByIndex(entityExpansionIndex)}, - XMLErrorReporter.SEVERITY_FATAL_ERROR ); + XMLErrorReporter.SEVERITY_FATAL_ERROR ); // is there anything better to do than reset the counter? // at least one can envision debugging applications where this might // be useful... diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java index f81b7cf41ee33..85d4ac0a50c9e 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/SchemaDVFactory.java @@ -53,7 +53,7 @@ public abstract class SchemaDVFactory { * @exception DVFactoryException cannot create an instance of the specified * class name or the default class name */ - public static synchronized final SchemaDVFactory getInstance() throws DVFactoryException { + public static final SchemaDVFactory getInstance() throws DVFactoryException { return getInstance(DEFAULT_FACTORY_CLASS); } //getInstance(): SchemaDVFactory @@ -66,7 +66,7 @@ public static synchronized final SchemaDVFactory getInstance() throws DVFactoryE * @exception DVFactoryException cannot create an instance of the specified * class name or the default class name */ - public static synchronized final SchemaDVFactory getInstance(String factoryClass) throws DVFactoryException { + public static final SchemaDVFactory getInstance(String factoryClass) throws DVFactoryException { try { // if the class name is not specified, use the default one @@ -78,7 +78,7 @@ public static synchronized final SchemaDVFactory getInstance(String factoryClass } // can't create a new object of this class - protected SchemaDVFactory(){} + protected SchemaDVFactory() {} /** * Get a built-in simple type of the given name diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java index c0ae87c08a64d..e5dd8a3c25e4c 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java @@ -63,7 +63,7 @@ * @author Rajiv Mordani * @author Edwin Goei * - * @LastModified: July 2023 + * @LastModified: Nov 2023 */ @SuppressWarnings("deprecation") public class SAXParserImpl extends javax.xml.parsers.SAXParser @@ -402,7 +402,7 @@ public JAXPSAXParser() { JAXPSAXParser(SAXParserImpl saxParser, XMLSecurityPropertyManager securityPropertyMgr, XMLSecurityManager securityManager) { - super(); + super(null, null, securityPropertyMgr, securityManager); fSAXParser = saxParser; fSecurityManager = securityManager; fSecurityPropertyMgr = securityPropertyMgr; diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java index bb1eb23284d81..f36d2bd14bbbd 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java @@ -162,9 +162,6 @@ public XMLSchemaFactory() { // use catalog fXMLSchemaLoader.setFeature(XMLConstants.USE_CATALOG, JdkXmlUtils.USE_CATALOG_DEFAULT); - for (Feature f : Feature.values()) { - fXMLSchemaLoader.setProperty(f.getPropertyName(), null); - } fXMLSchemaLoader.setProperty(JdkConstants.CDATA_CHUNK_SIZE, JdkConstants.CDATA_CHUNK_SIZE_DEFAULT); fXmlFeatures = new JdkXmlFeatures(fSecurityManager.isSecureProcessing()); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java index e8fd5424265d0..a71c8cc56d12d 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java @@ -41,7 +41,7 @@ * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM * - * @LastModified: Sep 2023 + * @LastModified: Nov 2023 */ public class SAXParser extends AbstractSAXParser { @@ -91,21 +91,21 @@ public class SAXParser */ public SAXParser(XMLParserConfiguration config) { super(config); - initSecurityManager(); + initSecurityManager(null, null); } // (XMLParserConfiguration) /** * Constructs a SAX parser using the dtd/xml schema parser configuration. */ public SAXParser() { - this(null, null); + this(null, null, null, null); } // () /** * Constructs a SAX parser using the specified symbol table. */ public SAXParser(SymbolTable symbolTable) { - this(symbolTable, null); + this(symbolTable, null, null, null); } // (SymbolTable) /** @@ -113,6 +113,11 @@ public SAXParser(SymbolTable symbolTable) { * grammar pool. */ public SAXParser(SymbolTable symbolTable, XMLGrammarPool grammarPool) { + this(symbolTable, grammarPool, null, null); + } + + public SAXParser(SymbolTable symbolTable, XMLGrammarPool grammarPool, + XMLSecurityPropertyManager securityPropertyMgr, XMLSecurityManager securityManager) { super(new XIncludeAwareParserConfiguration()); // set features @@ -128,7 +133,7 @@ public SAXParser(SymbolTable symbolTable, XMLGrammarPool grammarPool) { fConfiguration.setProperty(XMLGRAMMAR_POOL, grammarPool); } - initSecurityManager(); + initSecurityManager(securityPropertyMgr, securityManager); } // (SymbolTable,XMLGrammarPool) /** @@ -172,25 +177,4 @@ public void setProperty(String name, Object value) } } } - - /** - * Initiates the SecurityManager. This becomes necessary when the SAXParser - * is constructed directly by, for example, XMLReaderFactory rather than - * through SAXParserFactory. - */ - private void initSecurityManager() { - try { - if (securityManager == null) { - securityManager = new XMLSecurityManager(true); - super.setProperty(Constants.SECURITY_MANAGER, securityManager); - } - - if (securityPropertyManager == null) { - securityPropertyManager = new XMLSecurityPropertyManager(); - super.setProperty(JdkConstants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager); - } - } catch (SAXException e) { - Utils.dPrint(() -> e.getMessage()); - } - } } // class SAXParser diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java index 07a5498a7120d..cbe555adcd120 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XIncludeAwareParserConfiguration.java @@ -1,6 +1,5 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,6 +24,7 @@ import com.sun.org.apache.xerces.internal.util.FeatureState; import com.sun.org.apache.xerces.internal.util.NamespaceSupport; import com.sun.org.apache.xerces.internal.util.SymbolTable; +import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager; import com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler; import com.sun.org.apache.xerces.internal.xinclude.XIncludeNamespaceSupport; import com.sun.org.apache.xerces.internal.xni.NamespaceContext; @@ -33,6 +33,7 @@ import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; +import jdk.xml.internal.XMLSecurityManager; /** * This class is the configuration used to parse XML 1.0 and XML 1.1 documents @@ -40,6 +41,7 @@ * * @author Michael Glavassevich, IBM * + * @LastModified: Nov 2023 */ public class XIncludeAwareParserConfiguration extends XML11Configuration { @@ -88,7 +90,7 @@ public class XIncludeAwareParserConfiguration extends XML11Configuration { /** Default constructor. */ public XIncludeAwareParserConfiguration() { - this(null, null, null); + this(null, null, null, null, null); } // () /** @@ -97,7 +99,7 @@ public XIncludeAwareParserConfiguration() { * @param symbolTable The symbol table to use. */ public XIncludeAwareParserConfiguration(SymbolTable symbolTable) { - this(symbolTable, null, null); + this(symbolTable, null, null, null, null); } // (SymbolTable) /** @@ -111,7 +113,7 @@ public XIncludeAwareParserConfiguration(SymbolTable symbolTable) { public XIncludeAwareParserConfiguration( SymbolTable symbolTable, XMLGrammarPool grammarPool) { - this(symbolTable, grammarPool, null); + this(symbolTable, grammarPool, null, null, null); } // (SymbolTable,XMLGrammarPool) /** @@ -123,11 +125,15 @@ public XIncludeAwareParserConfiguration( * @param grammarPool The grammar pool to use. * @param parentSettings The parent settings. */ - public XIncludeAwareParserConfiguration( - SymbolTable symbolTable, - XMLGrammarPool grammarPool, + public XIncludeAwareParserConfiguration(SymbolTable symbolTable, XMLGrammarPool grammarPool, XMLComponentManager parentSettings) { - super(symbolTable, grammarPool, parentSettings); + this(symbolTable, grammarPool, parentSettings, null, null); + } + + public XIncludeAwareParserConfiguration(SymbolTable symbolTable, XMLGrammarPool grammarPool, + XMLComponentManager parentSettings, XMLSecurityPropertyManager securityPropertyMgr, + XMLSecurityManager securityManager) { + super(symbolTable, grammarPool, parentSettings, securityPropertyMgr, securityManager); final String[] recognizedFeatures = { ALLOW_UE_AND_NOTATION_EVENTS, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java index b85e0f319d0c7..d4db3be16cf9f 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -46,6 +46,7 @@ import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings; import com.sun.org.apache.xerces.internal.util.PropertyState; import com.sun.org.apache.xerces.internal.util.SymbolTable; +import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager; import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler; import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler; import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; @@ -70,6 +71,7 @@ import javax.xml.catalog.CatalogFeatures; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkXmlUtils; +import jdk.xml.internal.XMLSecurityManager; /** * This class is the configuration used to parse XML 1.0 and XML 1.1 documents. @@ -78,7 +80,7 @@ * @author Neil Graham, IBM * @author Michael Glavassevich, IBM * - * @LastModified: May 2021 + * @LastModified: Nov 2023 */ public class XML11Configuration extends ParserConfigurationSettings implements XMLPullParserConfiguration, XML11Configurable { @@ -432,7 +434,7 @@ public class XML11Configuration extends ParserConfigurationSettings /** Default constructor. */ public XML11Configuration() { - this(null, null, null); + this(null, null, null, null, null); } // () /** @@ -441,7 +443,7 @@ public XML11Configuration() { * @param symbolTable The symbol table to use. */ public XML11Configuration(SymbolTable symbolTable) { - this(symbolTable, null, null); + this(symbolTable, null, null, null, null); } // (SymbolTable) /** @@ -456,7 +458,7 @@ public XML11Configuration(SymbolTable symbolTable) { * @param grammarPool The grammar pool to use. */ public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool) { - this(symbolTable, grammarPool, null); + this(symbolTable, grammarPool, null, null, null); } // (SymbolTable,XMLGrammarPool) /** @@ -471,10 +473,14 @@ public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool) { * @param grammarPool The grammar pool to use. * @param parentSettings The parent settings. */ - public XML11Configuration( - SymbolTable symbolTable, - XMLGrammarPool grammarPool, - XMLComponentManager parentSettings) { + public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool, + XMLComponentManager parentSettings) { + this(symbolTable, grammarPool, parentSettings, null, null); + } + + public XML11Configuration(SymbolTable symbolTable, XMLGrammarPool grammarPool, + XMLComponentManager parentSettings, XMLSecurityPropertyManager securityPropertyMgr, + XMLSecurityManager securityManager) { super(parentSettings); @@ -592,7 +598,7 @@ public XML11Configuration( fProperties.put(XMLGRAMMAR_POOL, fGrammarPool); } - fEntityManager = new XMLEntityManager(); + fEntityManager = new XMLEntityManager(securityPropertyMgr, securityManager); fProperties.put(ENTITY_MANAGER, fEntityManager); addCommonComponent(fEntityManager); @@ -640,11 +646,6 @@ public XML11Configuration( // REVISIT: What is the right thing to do? -Ac } - // Initialize Catalog features - for( CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) { - fProperties.put(f.getPropertyName(), null); - } - setProperty(JdkConstants.CDATA_CHUNK_SIZE, JdkConstants.CDATA_CHUNK_SIZE_DEFAULT); fConfigUpdated = false; diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java index 0bd3dab5678d0..01d1c904b5e94 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XMLParser.java @@ -28,7 +28,9 @@ import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration; import jdk.xml.internal.JdkConstants; +import jdk.xml.internal.Utils; import jdk.xml.internal.XMLSecurityManager; +import org.xml.sax.SAXException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXNotRecognizedException; @@ -48,7 +50,7 @@ * * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM - * @LastModified: July 2023 + * @LastModified: Nov 2023 */ public abstract class XMLParser { @@ -127,20 +129,29 @@ protected XMLParser(XMLParserConfiguration config) { public void parse(XMLInputSource inputSource) throws XNIException, IOException { // null indicates that the parser is called directly, initialize them - if (securityManager == null) { - securityManager = new XMLSecurityManager(true); - fConfiguration.setProperty(Constants.SECURITY_MANAGER, securityManager); - } - if (securityPropertyManager == null) { - securityPropertyManager = new XMLSecurityPropertyManager(); - fConfiguration.setProperty(JdkConstants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager); - } - + initSecurityManager(null, null); reset(); fConfiguration.parse(inputSource); } // parse(XMLInputSource) + /** + * Initiates the SecurityManager. This becomes necessary when the Parser + * is constructed directly by, for example, XMLReaderFactory rather than + * through SAXParserFactory. + */ + void initSecurityManager(XMLSecurityPropertyManager spm, XMLSecurityManager sm) { + if (securityManager == null) { + securityManager = sm != null ? sm : new XMLSecurityManager(true); + } + fConfiguration.setProperty(Constants.SECURITY_MANAGER, securityManager); + + if (securityPropertyManager == null) { + securityPropertyManager = spm != null ? spm : new XMLSecurityPropertyManager(); + } + fConfiguration.setProperty(JdkConstants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager); + } + // // Protected methods // diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java index 377bb108750be..caa4ade1df84c 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/util/ParserConfigurationSettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import jdk.xml.internal.JdkXmlUtils; /** * This class implements the basic operations for managing parser @@ -42,7 +43,7 @@ * * @author Andy Clark, IBM * - * @LastModified: Apr 2019 + * @LastModified: Nov 2023 */ public class ParserConfigurationSettings implements XMLComponentManager { @@ -97,6 +98,8 @@ public ParserConfigurationSettings(XMLComponentManager parent) { // save parent fParentSettings = parent; + // Initialize Catalog features + JdkXmlUtils.initCatalogFeatures(fProperties); } // (XMLComponentManager) // diff --git a/src/java.xml/share/classes/javax/xml/catalog/Util.java b/src/java.xml/share/classes/javax/xml/catalog/Util.java index 3bf6e31f98bba..496626d64fa20 100644 --- a/src/java.xml/share/classes/javax/xml/catalog/Util.java +++ b/src/java.xml/share/classes/javax/xml/catalog/Util.java @@ -165,7 +165,7 @@ static boolean isFileUriExist(URI uri, boolean openJarFile) { case SCHEME_FILE: String path = uri.getPath(); File f1 = new File(path); - if (f1.isFile()) { + if (SecuritySupport.isFile(f1)) { return true; } break; diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkCatalog.java b/src/java.xml/share/classes/jdk/xml/internal/JdkCatalog.java new file mode 100644 index 0000000000000..518903073998d --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkCatalog.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.xml.internal; + +import java.net.URI; +import javax.xml.catalog.Catalog; +import javax.xml.catalog.CatalogFeatures; +import javax.xml.catalog.CatalogManager; + +/** + * Represents the built-in Catalog that hosts the DTDs for the Java platform. + */ +public class JdkCatalog { + public static final String JDKCATALOG = "/jdk/xml/internal/jdkcatalog/JDKCatalog.xml"; + private static final String JDKCATALOG_URL = SecuritySupport.getResource(JDKCATALOG).toExternalForm(); + public static Catalog catalog; + + public static void init(String resolve) { + if (catalog == null) { + CatalogFeatures cf = JdkXmlUtils.getCatalogFeatures(null, JDKCATALOG_URL, null, resolve); + catalog = CatalogManager.catalog(cf, URI.create(JDKCATALOG_URL)); + } + } +} diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java index da373c3813f78..412eddb5ab0a2 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java @@ -291,6 +291,7 @@ public final class JdkConstants { /** * System Property for the DTD property + * @since 22 */ public static final String DTD_PROPNAME = "jdk.xml.dtd.support"; @@ -299,6 +300,17 @@ public final class JdkConstants { public static final int IGNORE = 1; public static final int DENY = 2; + /** + * System Property for the JDKCatalog' RESOLVE property + * @since 22 + */ + public static final String JDKCATALOG_RESOLVE = "jdk.xml.jdkcatalog.resolve"; + + // Catalog Resolve Integer mappings for String values + public static final int CONTINUE = 0; + //public static final int IGNORE = 1; // same as that of DTD + public static final int STRICT = 2; + /** * Values for a feature */ diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java index 5acfdf6cbe82d..7ee98622317f1 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java @@ -31,6 +31,7 @@ import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings; import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; +import java.util.Map; import javax.xml.XMLConstants; import javax.xml.catalog.CatalogFeatures; import javax.xml.catalog.CatalogFeatures.Feature; @@ -156,6 +157,31 @@ public static String getCatalogFeature(CatalogFeatures features, String name) { return null; } + /** + * Initialize catalog features, including setting the default values and reading + * from the JAXP configuration file and System Properties. + * + * @param properties the Map object that holds the properties + */ + public static void initCatalogFeatures(Map properties) { + CatalogFeatures cf = getCatalogFeatures(); + for( CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) { + properties.put(f.getPropertyName(), cf.get(f)); + } + } + + /** + * Creates an instance of a CatalogFeatures with default settings. + * Note: the CatalogFeatures is initialized with settings in the following + * order: + * Default values -> values in the config -> values set with System Properties + * + * @return an instance of a CatalogFeatures + */ + public static CatalogFeatures getCatalogFeatures() { + return CatalogFeatures.builder().build(); + } + /** * Creates an instance of a CatalogFeatures. * diff --git a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java index 0a4f10cea4f6d..cfb2a264f7b36 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java +++ b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.net.URLConnection; import java.nio.file.Paths; import java.security.AccessController; import java.security.CodeSource; @@ -266,6 +267,18 @@ public static boolean isFileExists(final File f) { -> f.exists())); } + /** + * Tests whether the input is file. + * + * @param f the file to be tested + * @return true if the input is file, false otherwise + */ + @SuppressWarnings("removal") + public static boolean isFile(final File f) { + return (AccessController.doPrivileged((PrivilegedAction) () + -> f.isFile())); + } + /** * Creates and returns a new FileInputStream from a file. * @param file the specified file @@ -283,6 +296,23 @@ public static FileInputStream getFileInputStream(final File file) } } + /** + * Returns an InputStream from a URLConnection. + * @param uc the URLConnection + * @return the InputStream + * @throws IOException if an I/O error occurs while creating the input stream + */ + @SuppressWarnings("removal") + public static InputStream getInputStream(final URLConnection uc) + throws IOException { + try { + return AccessController.doPrivileged((PrivilegedExceptionAction) () + -> uc.getInputStream()); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } + /** * Returns the resource as a stream. * @param name the resource name @@ -294,6 +324,17 @@ public static InputStream getResourceAsStream(final String name) { SecuritySupport.class.getResourceAsStream("/"+name)); } + /** + * Returns the resource by the name. + * @param name the resource name + * @return the resource + */ + @SuppressWarnings("removal") + public static URL getResource(final String name) { + return AccessController.doPrivileged((PrivilegedAction) () -> + SecuritySupport.class.getResource(name)); + } + /** * Gets a resource bundle using the specified base name, the default locale, and the caller's class loader. * @param bundle the base name of the resource bundle, a fully qualified class name diff --git a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java index 07493c1b32569..d343b99cf14ac 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java +++ b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java @@ -32,6 +32,9 @@ import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; +import javax.xml.catalog.CatalogManager; +import javax.xml.catalog.CatalogResolver; +import javax.xml.catalog.CatalogResolver.NotFoundAction; import javax.xml.stream.XMLInputFactory; import jdk.xml.internal.JdkProperty.State; import jdk.xml.internal.JdkProperty.ImplPropMap; @@ -67,15 +70,30 @@ public final class XMLSecurityManager { DTD_MAP = Collections.unmodifiableMap(map); } + // Valid values for Catalog Resolve, and mappings between the string and + // interger values + static final Map CR_MAP; + // Source Level JDK 8 + static { + Map map = new HashMap<>(); + map.put("continue", 0); + map.put("ignore", 1); + map.put("strict", 2); + CR_MAP = Collections.unmodifiableMap(map); + } + // Value converter for properties of type Boolean private static final BooleanMapper BOOLMAPPER = new BooleanMapper(); // Value converter for properties of type Integer private static final IntegerMapper INTMAPPER = new IntegerMapper(); - // DTD value map + // DTD value mapper private static final StringMapper DTDMAPPER = new StringMapper(DTD_MAP); + // Catalog Resolve value mapper + private static final StringMapper CRMAPPER = new StringMapper(CR_MAP); + /** * Limits managed by the security manager */ @@ -109,6 +127,8 @@ public static enum Limit { JdkConstants.ALLOW, JdkConstants.ALLOW, Processor.PARSER, DTDMAPPER), XERCES_DISALLOW_DTD("disallowDTD", DISALLOW_DTD, null, null, 0, 0, Processor.PARSER, BOOLMAPPER), STAX_SUPPORT_DTD("supportDTD", XMLInputFactory.SUPPORT_DTD, null, null, 1, 1, Processor.PARSER, BOOLMAPPER), + JDKCATALOG_RESOLVE("JDKCatalogResolve", JdkConstants.JDKCATALOG_RESOLVE, JdkConstants.JDKCATALOG_RESOLVE, null, + JdkConstants.CONTINUE, JdkConstants.CONTINUE, Processor.PARSER, CRMAPPER), ; final String key; @@ -266,6 +286,48 @@ public XMLSecurityManager(boolean secureProcessing) { //read system properties or the config file (jaxp.properties by default) readSystemProperties(); + // prepare the JDK Catalog + prepareCatalog(); + } + + /** + * Flag indicating whether the JDK Catalog has been initialized + */ + static volatile boolean jdkcatalogInitialized = false; + private final Object lock = new Object(); + + private void prepareCatalog() { + if (!jdkcatalogInitialized) { + synchronized (lock) { + if (!jdkcatalogInitialized) { + jdkcatalogInitialized = true; + String resolve = getLimitValueAsString(Limit.JDKCATALOG_RESOLVE); + JdkCatalog.init(resolve); + } + } + } + } + + /** + * Returns the JDKCatalogResolver with the current setting of the RESOLVE + * property. + * + * @return the JDKCatalogResolver + */ + public CatalogResolver getJDKCatalogResolver() { + String resolve = getLimitValueAsString(Limit.JDKCATALOG_RESOLVE); + return CatalogManager.catalogResolver(JdkCatalog.catalog, toActionType(resolve)); + } + + // convert the string value of the RESOLVE property to the corresponding + // action type + private NotFoundAction toActionType(String resolve) { + for (NotFoundAction type : NotFoundAction.values()) { + if (type.toString().equals(resolve)) { + return type; + } + } + return null; } /** diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml new file mode 100644 index 0000000000000..3919dd4981d75 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/JDKCatalog.xml @@ -0,0 +1,41 @@ + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/preferences.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/preferences.dtd new file mode 100644 index 0000000000000..27166e805c699 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/preferences.dtd @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/properties.dtd b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/properties.dtd new file mode 100644 index 0000000000000..4a22bd53d8569 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/jdkcatalog/java/dtd/properties.dtd @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/src/java.xml/share/classes/module-info.java b/src/java.xml/share/classes/module-info.java index bb15ee81b513c..0ed2ebfbe2641 100644 --- a/src/java.xml/share/classes/module-info.java +++ b/src/java.xml/share/classes/module-info.java @@ -402,6 +402,11 @@ * @implNote * *

    + *
  • JDK built-in Catalog + * + *
  • *
  • Implementation Specific Properties * * + *

    JDK built-in Catalog

    + * The JDK has a built-in catalog that hosts the following DTDs defined by the Java Platform: + *
      + *
    • DTD for {@link java.util.prefs.Preferences java.util.prefs.Preferences}, preferences.dtd
    • + *
    • DTD for {@link java.util.Properties java.util.Properties}, properties.dtd
    • + *
    + *

    + * The catalog is loaded once when the first JAXP processor factory is created. + * + *

    External Resource Resolution Process with the built-in Catalog

    + * The JDK creates a {@link javax.xml.catalog.CatalogResolver CatalogResolver} + * with the built-in catalog when needed. This CatalogResolver is used as the + * default external resource resolver. + *

    + * XML processors may use resolvers (such as {@link org.xml.sax.EntityResolver EntityResolver}, + * {@link javax.xml.stream.XMLResolver XMLResolver}, and {@link javax.xml.catalog.CatalogResolver CatalogResolver}) + * to handle external references. In the absence of the user-defined resolvers, + * the JDK XML processors fall back to the default CatalogResolver to attempt to + * find a resolution before making a connection to fetch the resources. The fall-back + * also takes place if a user-defined resolver exists but allows the process to + * continue when unable to resolve the resource. + *

    + * If the default CatalogResolver is unable to locate a resource, it may signal + * the XML processors to continue processing, or skip the resource, or + * throw a CatalogException. The behavior is configured with the + * {@code jdk.xml.jdkcatalog.resolve} property. + * *

    Implementation Specific Properties

    * In addition to the standard JAXP Properties, * the JDK implementation supports a number of implementation specific properties @@ -752,7 +784,7 @@ * {@systemProperty jdk.xml.enableExtensionFunctions} * Determines if XSLT and XPath extension functions are to be allowed. * - * yes + * yes * Boolean * * true or false. True indicates that extension functions are allowed; False otherwise. @@ -842,6 +874,40 @@ * Method 1 * 22 * + * + * {@systemProperty jdk.xml.jdkcatalog.resolve} + * Instructs the JDK default CatalogResolver to act in accordance with the setting + * of this property when unable to resolve an external reference with the built-in Catalog. + * The options are: + *
      + *
    • + * {@code continue} -- Indicates that the processing should continue + *

    • + *
    • + * {@code ignore} -- Indicates that the reference is skipped + *

    • + *
    • + * {@code strict} -- Indicates that the resolver should throw a CatalogException + *

    • + *
    + * + * String + * + * {@code continue, ignore, and strict}. Values are case-insensitive. + * + * continue + * No + * Yes + * + * DOM
    + * SAX
    + * StAX
    + * Validation
    + * Transform + * + * Method 1 + * 22 + * * * *

    diff --git a/src/java.xml/share/conf/jaxp.properties b/src/java.xml/share/conf/jaxp.properties index b43b5a3e8d178..53074816cb977 100644 --- a/src/java.xml/share/conf/jaxp.properties +++ b/src/java.xml/share/conf/jaxp.properties @@ -47,11 +47,11 @@ # the default property values. The format is: # system-property-name=value # -# For example, the FILES property in CatalogFeatures has an associated system -# property called javax.xml.catalog.files. An entry for the FILES property in the -# configuration file would therefore use javax.xml.catalog.files as the key, that +# For example, the RESOLVE property in CatalogFeatures has an associated system +# property called javax.xml.catalog.resolve. An entry for the RESOLVE property in the +# configuration file would therefore use javax.xml.catalog.resolve as the key, that # is: -# javax.xml.catalog.files=strict +# javax.xml.catalog.resolve=strict # # # Extension Functions: @@ -128,6 +128,24 @@ jdk.xml.overrideDefaultParser=false # # javax.xml.useCatalog=true # +# Implementation Specific Properties - jdkcatalog.resolve +# +# This property instructs the JDK default CatalogResolver to act in accordance with +# the setting when unable to resolve an external reference with the built-in Catalog. +# The options are: +# continue -- indicates that the processing should continue +# ignore -- indicates that the reference is skipped +# strict -- indicates that the resolver should throw a CatalogException +# +# The following setting would cause the resolve to throw a CatalogException when +# unable to resolve an external reference: +# jdk.xml.jdkcatalog.resolve=strict +# +# Implementation Specific Properties - DTD +# +# This property instructs the parsers to: deny, ignore or allow DTD processing. +# The following setting would cause the parser to reject DTD by throwing an exception. +# jdk.xml.dtd.support=deny # # Implementation Specific Properties - Limits # diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index de37229f68f29..92b2a773ffa98 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -349,12 +349,7 @@ public enum LintCategory { RESTRICTED("restricted"); LintCategory(String option) { - this(option, false); - } - - LintCategory(String option, boolean hidden) { this.option = option; - this.hidden = hidden; map.put(option, this); } @@ -363,7 +358,6 @@ static LintCategory get(String option) { } public final String option; - public final boolean hidden; } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index cdbd57047ff8f..ad859bb45077f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -211,6 +211,7 @@ public boolean isPreview(Feature feature) { return switch (feature) { case STRING_TEMPLATES -> true; case UNNAMED_CLASSES -> true; + case SUPER_INIT -> true; //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). //When real preview features will be added, this method can be implemented to return 'true' //for those selected features, and 'false' for all the others. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java index 017400adcfca9..0290f8bc75214 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -248,6 +248,7 @@ public enum Feature { UNNAMED_CLASSES(JDK21, Fragments.FeatureUnnamedClasses, DiagKind.PLURAL), WARN_ON_ILLEGAL_UTF8(MIN, JDK21), UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL), + SUPER_INIT(JDK22, Fragments.FeatureSuperInit, DiagKind.NORMAL), ; enum DiagKind { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 33a751aac7e44..b69c2156e1d86 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -937,6 +937,8 @@ public void visitClassDef(JCClassDecl tree) { Optional localCacheContext = Optional.ofNullable(env.info.attributionMode.isSpeculative ? argumentAttr.withLocalCacheContext() : null); + boolean ctorProloguePrev = env.info.ctorPrologue; + env.info.ctorPrologue = false; try { // Local and anonymous classes have not been entered yet, so we need to // do it now. @@ -959,12 +961,10 @@ public void visitClassDef(JCClassDecl tree) { // make sure class has been completed: c.complete(); - // If this class appears as an anonymous class - // in a superclass constructor call - // disable implicit outer instance from being passed. + // If this class appears as an anonymous class in a constructor + // prologue, disable implicit outer instance from being passed. // (This would be an illegal access to "this before super"). - if (env.info.isSelfCall && - env.tree.hasTag(NEWCLASS)) { + if (ctorProloguePrev && env.tree.hasTag(NEWCLASS)) { c.flags_field |= NOOUTERTHIS; } attribClass(tree.pos(), c); @@ -972,6 +972,7 @@ public void visitClassDef(JCClassDecl tree) { } } finally { localCacheContext.ifPresent(LocalCacheContext::leave); + env.info.ctorPrologue = ctorProloguePrev; } } @@ -981,6 +982,8 @@ public void visitMethodDef(JCMethodDecl tree) { Lint lint = env.info.lint.augment(m); Lint prevLint = chk.setLint(lint); + boolean ctorProloguePrev = env.info.ctorPrologue; + env.info.ctorPrologue = false; MethodSymbol prevMethod = chk.setMethod(m); try { deferredLintHandler.flush(tree.pos()); @@ -1044,6 +1047,9 @@ public void visitMethodDef(JCMethodDecl tree) { chk.validate(tree.recvparam, newEnv); } + // Is this method a constructor? + boolean isConstructor = TreeInfo.isConstructor(tree); + if (env.enclClass.sym.isRecord() && tree.sym.owner.kind == TYP) { // lets find if this method is an accessor Optional recordComponent = env.enclClass.sym.getRecordComponents().stream() @@ -1071,14 +1077,11 @@ public void visitMethodDef(JCMethodDecl tree) { } } - if (tree.name == names.init) { + if (isConstructor) { // if this a constructor other than the canonical one if ((tree.sym.flags_field & RECORD) == 0) { - JCMethodInvocation app = TreeInfo.firstConstructorCall(tree); - if (app == null || - TreeInfo.name(app.meth) != names._this || - !checkFirstConstructorStat(app, tree, false)) { - log.error(tree, Errors.FirstStatementMustBeCallToAnotherConstructor(env.enclClass.sym)); + if (!TreeInfo.hasConstructorCall(tree, names._this)) { + log.error(tree, Errors.NonCanonicalConstructorInvokeAnotherConstructor(env.enclClass.sym)); } } else { // but if it is the canonical: @@ -1104,11 +1107,7 @@ public void visitMethodDef(JCMethodDecl tree) { ); } - JCMethodInvocation app = TreeInfo.firstConstructorCall(tree); - if (app != null && - (TreeInfo.name(app.meth) == names._this || - TreeInfo.name(app.meth) == names._super) && - checkFirstConstructorStat(app, tree, false)) { + if (TreeInfo.hasAnyConstructorCall(tree)) { log.error(tree, Errors.InvalidCanonicalConstructorInRecord( Fragments.Canonical, env.enclClass.sym.name, Fragments.CanonicalMustNotContainExplicitConstructorInvocation)); @@ -1186,16 +1185,14 @@ public void visitMethodDef(JCMethodDecl tree) { // Add an implicit super() call unless an explicit call to // super(...) or this(...) is given // or we are compiling class java.lang.Object. - if (tree.name == names.init && owner.type != syms.objectType) { - JCBlock body = tree.body; - if (body.stats.isEmpty() || - TreeInfo.getConstructorInvocationName(body.stats, names) == names.empty) { - JCStatement supCall = make.at(body.pos).Exec(make.Apply(List.nil(), + if (isConstructor && owner.type != syms.objectType) { + if (!TreeInfo.hasAnyConstructorCall(tree)) { + JCStatement supCall = make.at(tree.body.pos).Exec(make.Apply(List.nil(), make.Ident(names._super), make.Idents(List.nil()))); - body.stats = body.stats.prepend(supCall); + tree.body.stats = tree.body.stats.prepend(supCall); } else if ((env.enclClass.sym.flags() & ENUM) != 0 && (tree.mods.flags & GENERATEDCONSTR) == 0 && - TreeInfo.isSuperCall(body.stats.head)) { + TreeInfo.hasConstructorCall(tree, names._super)) { // enum constructors are not allowed to call super // directly, so make sure there aren't any super calls // in enum constructors, except in the compiler @@ -1225,6 +1222,9 @@ public void visitMethodDef(JCMethodDecl tree) { annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null); annotate.flush(); + // Start of constructor prologue + localEnv.info.ctorPrologue = isConstructor; + // Attribute method body. attribStat(tree.body, localEnv); } @@ -1234,6 +1234,7 @@ public void visitMethodDef(JCMethodDecl tree) { } finally { chk.setLint(prevLint); chk.setMethod(prevMethod); + env.info.ctorPrologue = ctorProloguePrev; } } @@ -2518,21 +2519,15 @@ public void visitApply(JCMethodInvocation tree) { ListBuffer argtypesBuf = new ListBuffer<>(); if (isConstructorCall) { - // We are seeing a ...this(...) or ...super(...) call. - // Check that this is the first statement in a constructor. - checkFirstConstructorStat(tree, env.enclMethod, true); - - // Record the fact - // that this is a constructor call (using isSelfCall). - localEnv.info.isSelfCall = true; // Attribute arguments, yielding list of argument types. - localEnv.info.constructorArgs = true; KindSelector kind = attribArgs(KindSelector.MTH, tree.args, localEnv, argtypesBuf); - localEnv.info.constructorArgs = false; argtypes = argtypesBuf.toList(); typeargtypes = attribTypes(tree.typeargs, localEnv); + // Done with this()/super() parameters. End of constructor prologue. + env.info.ctorPrologue = false; + // Variable `site' points to the class in which the called // constructor is defined. Type site = env.enclClass.sym.type; @@ -2661,26 +2656,6 @@ Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, Li } } - /** Check that given application node appears as first statement - * in a constructor call. - * @param tree The application node - * @param enclMethod The enclosing method of the application. - * @param error Should an error be issued? - */ - boolean checkFirstConstructorStat(JCMethodInvocation tree, JCMethodDecl enclMethod, boolean error) { - if (enclMethod != null && enclMethod.name == names.init) { - JCBlock body = enclMethod.body; - if (body.stats.head.hasTag(EXEC) && - ((JCExpressionStatement) body.stats.head).expr == tree) - return true; - } - if (error) { - log.error(tree.pos(), - Errors.CallMustBeFirstStmtInCtor(TreeInfo.name(tree.meth))); - } - return false; - } - /** Obtain a method type with given argument types. */ Type newMethodTemplate(Type restype, List argtypes, List typeargtypes) { @@ -4353,16 +4328,6 @@ public void visitIdent(JCIdent tree) { checkAssignable(tree.pos(), v, null, env); } - // In a constructor body, - // if symbol is a field or instance method, check that it is - // not accessed before the supertype constructor is called. - if (symEnv.info.isSelfCall && - sym.kind.matches(KindSelector.VAL_MTH) && - sym.owner.kind == TYP && - (sym.flags() & STATIC) == 0) { - chk.earlyRefError(tree.pos(), sym.kind == VAR ? - sym : thisSym(tree.pos(), env)); - } Env env1 = env; if (sym.kind != ERR && sym.kind != TYP && sym.owner != null && sym.owner != env1.enclClass.sym) { @@ -4474,18 +4439,7 @@ public void visitSelect(JCFieldAccess tree) { } if (isType(sitesym)) { - if (sym.name == names._this || sym.name == names._super) { - // If `C' is the currently compiled class, check that - // `C.this' does not appear in an explicit call to a constructor - // also make sure that `super` is not used in constructor invocations - if (env.info.isSelfCall && - ((sym.name == names._this && - site.tsym == env.enclClass.sym) || - sym.name == names._super && env.info.constructorArgs && - (sitesym.isInterface() || site.tsym == env.enclClass.sym))) { - chk.earlyRefError(tree.pos(), sym); - } - } else { + if (sym.name != names._this && sym.name != names._super) { // Check if type-qualified fields or methods are static (JLS) if ((sym.flags() & STATIC) == 0 && sym.name != names._super && @@ -5674,6 +5628,9 @@ private void attribClassBody(Env env, ClassSymbol c) { } } + // Check for proper placement of super()/this() calls. + chk.checkSuperInitCalls(tree); + // Check for cycles among non-initial constructors. chk.checkCyclicConstructors(tree); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java index 2a96b11316c01..b8afce009a591 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java @@ -49,13 +49,9 @@ public class AttrContext { */ int staticLevel = 0; - /** Is this an environment for a this(...) or super(...) call? + /** Are we in the 'prologue' part of a constructor, prior to an explicit this()/super()? */ - boolean isSelfCall = false; - - /** are we analyzing the arguments for a constructor invocation? - */ - boolean constructorArgs = false; + boolean ctorPrologue = false; /** Are we evaluating the selector of a `super' or type name? */ @@ -136,8 +132,7 @@ AttrContext dup(WriteableScope scope) { AttrContext info = new AttrContext(); info.scope = scope; info.staticLevel = staticLevel; - info.isSelfCall = isSelfCall; - info.constructorArgs = constructorArgs; + info.ctorPrologue = ctorPrologue; info.selectSuper = selectSuper; info.pendingResolutionPhase = pendingResolutionPhase; info.lint = lint; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index d05d5e2f51095..ead1ecf3c633f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -359,15 +359,6 @@ Type typeTagError(DiagnosticPosition pos, JCDiagnostic required, Object found) { return types.createErrorType(found instanceof Type type ? type : syms.errType); } - /** Report an error that symbol cannot be referenced before super - * has been called. - * @param pos Position to be used for error reporting. - * @param sym The referenced symbol. - */ - void earlyRefError(DiagnosticPosition pos, Symbol sym) { - log.error(pos, Errors.CantRefBeforeCtorCalled(sym)); - } - /** Report duplicate declaration error. */ void duplicateError(DiagnosticPosition pos, Symbol sym) { @@ -3934,10 +3925,11 @@ void checkCyclicConstructors(JCClassDecl tree) { // enter each constructor this-call into the map for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - JCMethodInvocation app = TreeInfo.firstConstructorCall(l.head); - if (app == null) continue; - JCMethodDecl meth = (JCMethodDecl) l.head; - if (TreeInfo.name(app.meth) == names._this) { + if (!TreeInfo.isConstructor(l.head)) + continue; + JCMethodDecl meth = (JCMethodDecl)l.head; + JCMethodInvocation app = TreeInfo.findConstructorCall(meth); + if (app != null && TreeInfo.name(app.meth) == names._this) { callMap.put(meth.sym, TreeInfo.symbol(app.meth)); } else { meth.sym.flags_field |= ACYCLIC; @@ -3970,6 +3962,128 @@ private void checkCyclicConstructor(JCClassDecl tree, Symbol ctor, } } +/* ************************************************************************* + * Verify the proper placement of super()/this() calls. + * + * - super()/this() may only appear in constructors + * - There must be at most one super()/this() call per constructor + * - The super()/this() call, if any, must be a top-level statement in the + * constructor, i.e., not nested inside any other statement or block + * - There must be no return statements prior to the super()/this() call + **************************************************************************/ + + void checkSuperInitCalls(JCClassDecl tree) { + new SuperThisChecker().check(tree); + } + + private class SuperThisChecker extends TreeScanner { + + // Match this scan stack: 1=JCMethodDecl, 2=JCExpressionStatement, 3=JCMethodInvocation + private static final int MATCH_SCAN_DEPTH = 3; + + private boolean constructor; // is this method a constructor? + private boolean firstStatement; // at the first statement in method? + private JCReturn earlyReturn; // first return prior to the super()/init(), if any + private Name initCall; // whichever of "super" or "init" we've seen already + private int scanDepth; // current scan recursion depth in method body + + public void check(JCClassDecl classDef) { + scan(classDef.defs); + } + + @Override + public void visitMethodDef(JCMethodDecl tree) { + Assert.check(!constructor); + Assert.check(earlyReturn == null); + Assert.check(initCall == null); + Assert.check(scanDepth == 1); + + // Initialize state for this method + constructor = TreeInfo.isConstructor(tree); + try { + + // Scan method body + if (tree.body != null) { + firstStatement = true; + for (List l = tree.body.stats; l.nonEmpty(); l = l.tail) { + scan(l.head); + firstStatement = false; + } + } + + // Verify no 'return' seen prior to an explicit super()/this() call + if (constructor && earlyReturn != null && initCall != null) + log.error(earlyReturn.pos(), Errors.ReturnBeforeSuperclassInitialized); + } finally { + firstStatement = false; + constructor = false; + earlyReturn = null; + initCall = null; + } + } + + @Override + public void scan(JCTree tree) { + scanDepth++; + try { + super.scan(tree); + } finally { + scanDepth--; + } + } + + @Override + public void visitApply(JCMethodInvocation apply) { + do { + + // Is this a super() or this() call? + Name methodName = TreeInfo.name(apply.meth); + if (methodName != names._super && methodName != names._this) + break; + + // super()/this() calls must only appear in a constructor + if (!constructor) { + log.error(apply.pos(), Errors.CallMustOnlyAppearInCtor); + break; + } + + // super()/this() calls must be a top level statement + if (scanDepth != MATCH_SCAN_DEPTH) { + log.error(apply.pos(), Errors.CtorCallsNotAllowedHere); + break; + } + + // super()/this() calls must not appear more than once + if (initCall != null) { + log.error(apply.pos(), Errors.RedundantSuperclassInit); + break; + } + + // If super()/this() isn't first, require "statements before super()" feature + if (!firstStatement) + preview.checkSourceLevel(apply.pos(), Feature.SUPER_INIT); + + // We found a legitimate super()/this() call; remember it + initCall = methodName; + } while (false); + + // Proceed + super.visitApply(apply); + } + + @Override + public void visitReturn(JCReturn tree) { + if (constructor && initCall == null && earlyReturn == null) + earlyReturn = tree; // we have seen a return but not (yet) a super()/this() + super.visitReturn(tree); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + // don't descend any further + } + } + /* ************************************************************************* * Miscellaneous **************************************************************************/ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java index e09ed62d62bb0..90d482438417f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java @@ -206,7 +206,6 @@ public Env classEnv(JCClassDecl tree, Env env) { env.dup(tree, env.info.dup(WriteableScope.create(tree.sym))); localEnv.enclClass = tree; localEnv.outer = env; - localEnv.info.isSelfCall = false; localEnv.info.lint = null; // leave this to be filled in by Attr, // when annotations have been processed localEnv.info.isAnonymousDiamond = TreeInfo.isDiamond(env.tree); @@ -259,7 +258,6 @@ public Env moduleEnv(JCModuleDecl tree, Env env) { env.dup(tree, env.info.dup(WriteableScope.create(tree.sym))); localEnv.enclClass = predefClassDef; localEnv.outer = env; - localEnv.info.isSelfCall = false; localEnv.info.lint = null; // leave this to be filled in by Attr, // when annotations have been processed return localEnv; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 4a23bda0ea875..d07b3a41ccb7c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import java.util.function.Consumer; import com.sun.source.tree.CaseTree; import com.sun.source.tree.LambdaExpressionTree.BodyKind; @@ -386,6 +387,13 @@ private JumpKind(Tag treeTag) { */ ListBuffer pendingExits; + /** A class whose initializers we are scanning. Because initializer + * scans can be triggered out of sequence when visiting certain nodes + * (e.g., super()), we protect against infinite loops that could be + * triggered by incorrect code (e.g., super() inside initializer). + */ + JCClassDecl initScanClass; + /** A pending exit. These are the statements return, break, and * continue. In addition, exception-throwing expressions or * statements are put here when not known to be caught. This @@ -471,6 +479,24 @@ protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) { scan(brk); } } + + // Do something with all static or non-static field initializers and initialization blocks. + // Note: This method also sends nested class definitions to the handler. + protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consumer handler) { + if (classDef == initScanClass) // avoid infinite loops + return; + JCClassDecl initScanClassPrev = initScanClass; + initScanClass = classDef; + try { + for (List defs = classDef.defs; defs.nonEmpty(); defs = defs.tail) { + JCTree def = defs.head; + if (!def.hasTag(METHODDEF) && ((TreeInfo.flags(def) & STATIC) != 0) == isStatic) + handler.accept(def); + } + } finally { + initScanClass = initScanClassPrev; + } + } } /** @@ -536,22 +562,16 @@ public void visitClassDef(JCClassDecl tree) { try { // process all the static initializers - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (!l.head.hasTag(METHODDEF) && - (TreeInfo.flags(l.head) & STATIC) != 0) { - scanDef(l.head); - clearPendingExits(false); - } - } + forEachInitializer(tree, true, def -> { + scanDef(def); + clearPendingExits(false); + }); // process all the instance initializers - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (!l.head.hasTag(METHODDEF) && - (TreeInfo.flags(l.head) & STATIC) == 0) { - scanDef(l.head); - clearPendingExits(false); - } - } + forEachInitializer(tree, false, def -> { + scanDef(def); + clearPendingExits(false); + }); // process all the methods for (List l = tree.defs; l.nonEmpty(); l = l.tail) { @@ -1362,40 +1382,10 @@ public void visitClassDef(JCClassDecl tree) { try { // process all the static initializers - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (!l.head.hasTag(METHODDEF) && - (TreeInfo.flags(l.head) & STATIC) != 0) { - scan(l.head); - errorUncaught(); - } - } - - // add intersection of all throws clauses of initial constructors - // to set of caught exceptions, unless class is anonymous. - if (!anonymousClass) { - boolean firstConstructor = true; - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (TreeInfo.isInitialConstructor(l.head)) { - List mthrown = - ((JCMethodDecl) l.head).sym.type.getThrownTypes(); - if (firstConstructor) { - caught = mthrown; - firstConstructor = false; - } else { - caught = chk.intersect(mthrown, caught); - } - } - } - } - - // process all the instance initializers - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (!l.head.hasTag(METHODDEF) && - (TreeInfo.flags(l.head) & STATIC) == 0) { - scan(l.head); - errorUncaught(); - } - } + forEachInitializer(tree, true, def -> { + scan(def); + errorUncaught(); + }); // in an anonymous class, add the set of thrown exceptions to // the throws clause of the synthetic constructor and propagate @@ -1450,7 +1440,7 @@ public void visitMethodDef(JCMethodDecl tree) { JCVariableDecl def = l.head; scan(def); } - if (TreeInfo.isInitialConstructor(tree)) + if (TreeInfo.hasConstructorCall(tree, names._super)) caught = chk.union(caught, mthrown); else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) caught = mthrown; @@ -1751,8 +1741,18 @@ public void visitThrow(JCThrow tree) { public void visitApply(JCMethodInvocation tree) { scan(tree.meth); scan(tree.args); + + // Mark as thrown the exceptions thrown by the method being invoked for (List l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) markThrown(tree, l.head); + + // After super(), scan initializers to uncover any exceptions they throw + if (TreeInfo.name(tree.meth) == names._super) { + forEachInitializer(classDef, false, def -> { + scan(def); + errorUncaught(); + }); + } } public void visitNewClass(JCNewClass tree) { @@ -2095,11 +2095,11 @@ public AssignAnalyzer() { uninitsWhenFalse = new Bits(true); } - private boolean isInitialConstructor = false; + private boolean isConstructor; @Override protected void markDead() { - if (!isInitialConstructor) { + if (!isConstructor) { inits.inclRange(returnadr, nextadr); } else { for (int address = returnadr; address < nextadr; address++) { @@ -2346,13 +2346,10 @@ public void visitClassDef(JCClassDecl tree) { } // process all the static initializers - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (!l.head.hasTag(METHODDEF) && - (TreeInfo.flags(l.head) & STATIC) != 0) { - scan(l.head); - clearPendingExits(false); - } - } + forEachInitializer(tree, true, def -> { + scan(def); + clearPendingExits(false); + }); // verify all static final fields got initailized for (int i = firstadr; i < nextadr; i++) { @@ -2376,15 +2373,6 @@ public void visitClassDef(JCClassDecl tree) { } } - // process all the instance initializers - for (List l = tree.defs; l.nonEmpty(); l = l.tail) { - if (!l.head.hasTag(METHODDEF) && - (TreeInfo.flags(l.head) & STATIC) == 0) { - scan(l.head); - clearPendingExits(false); - } - } - // process all the methods for (List l = tree.defs; l.nonEmpty(); l = l.tail) { if (l.head.hasTag(METHODDEF)) { @@ -2423,13 +2411,16 @@ public void visitMethodDef(JCMethodDecl tree) { int returnadrPrev = returnadr; Assert.check(pendingExits.isEmpty()); - boolean lastInitialConstructor = isInitialConstructor; + boolean isConstructorPrev = isConstructor; try { - isInitialConstructor = TreeInfo.isInitialConstructor(tree); + isConstructor = TreeInfo.isConstructor(tree); - if (!isInitialConstructor) { + // We only track field initialization inside constructors + if (!isConstructor) { firstadr = nextadr; } + + // Mark all method parameters as DA for (List l = tree.params; l.nonEmpty(); l = l.tail) { JCVariableDecl def = l.head; scan(def); @@ -2445,7 +2436,7 @@ public void visitMethodDef(JCMethodDecl tree) { boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 || (tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD); - if (isInitialConstructor) { + if (isConstructor) { boolean isSynthesized = (tree.sym.flags() & GENERATEDCONSTR) != 0; for (int i = firstadr; i < nextadr; i++) { @@ -2487,7 +2478,7 @@ public void visitMethodDef(JCMethodDecl tree) { nextadr = nextadrPrev; firstadr = firstadrPrev; returnadr = returnadrPrev; - isInitialConstructor = lastInitialConstructor; + isConstructor = isConstructorPrev; } } finally { lint = lintPrev; @@ -2503,7 +2494,7 @@ private void clearPendingExits(boolean inMethod) { Assert.check((inMethod && exit.tree.hasTag(RETURN)) || log.hasErrorOn(exit.tree.pos()), exit.tree); - if (inMethod && isInitialConstructor) { + if (inMethod && isConstructor) { Assert.check(exit instanceof AssignPendingExit); inits.assign(((AssignPendingExit) exit).exit_inits); for (int i = firstadr; i < nextadr; i++) { @@ -2959,6 +2950,28 @@ public void visitThrow(JCThrow tree) { public void visitApply(JCMethodInvocation tree) { scanExpr(tree.meth); scanExprs(tree.args); + + // Handle superclass constructor invocations + if (isConstructor) { + + // If super(): at this point all initialization blocks will execute + Name name = TreeInfo.name(tree.meth); + if (name == names._super) { + forEachInitializer(classDef, false, def -> { + scan(def); + clearPendingExits(false); + }); + } + + // If this(): at this point all final uninitialized fields will get initialized + else if (name == names._this) { + for (int address = firstadr; address < nextadr; address++) { + VarSymbol sym = vardecls[address].sym; + if (isFinalUninitializedField(sym) && !sym.isStatic()) + letInit(tree.pos(), sym); + } + } + } } public void visitNewClass(JCNewClass tree) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index c51b1cb2a7e8b..39807aeaa5e18 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -1859,7 +1859,7 @@ JCExpression makeOuterThis(DiagnosticPosition pos, TypeSymbol c) { ot = ots.head; } while (ot.owner != otc); if (otc.owner.kind != PCK && !otc.hasOuterInstance()) { - chk.earlyRefError(pos, c); + log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c)); Assert.error(); // should have been caught in Attr return makeNull(); } @@ -1899,7 +1899,6 @@ JCExpression makeOwnerThisN(DiagnosticPosition pos, Symbol sym, boolean preciseM List ots = outerThisStack; if (ots.isEmpty()) { log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c)); - Assert.error(); return makeNull(); } VarSymbol ot = ots.head; @@ -1911,7 +1910,6 @@ JCExpression makeOwnerThisN(DiagnosticPosition pos, Symbol sym, boolean preciseM ots = ots.tail; if (ots.isEmpty()) { log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c)); - Assert.error(); return tree; } ot = ots.head; @@ -2351,17 +2349,19 @@ public void visitClassDef(JCClassDecl tree) { tree.defs = tree.defs.prepend(l.head); enterSynthetic(tree.pos(), l.head.sym, currentClass.members()); } - // If this$n was accessed, add the field definition and - // update initial constructors to initialize it + // If this$n was accessed, add the field definition and prepend + // initializer code to any super() invocation to initialize it if (currentClass.hasOuterInstance() && shouldEmitOuterThis(currentClass)) { tree.defs = tree.defs.prepend(otdef); enterSynthetic(tree.pos(), otdef.sym, currentClass.members()); - for (JCTree def : tree.defs) { - if (TreeInfo.isInitialConstructor(def)) { - JCMethodDecl mdef = (JCMethodDecl) def; - mdef.body.stats = mdef.body.stats.prepend( - initOuterThis(mdef.body.pos, mdef.params.head.sym)); + for (JCTree def : tree.defs) { + if (TreeInfo.isConstructor(def)) { + JCMethodDecl mdef = (JCMethodDecl)def; + if (TreeInfo.hasConstructorCall(mdef, names._super)) { + List initializer = List.of(initOuterThis(mdef.body.pos, mdef.params.head.sym)); + TreeInfo.mapSuperCalls(mdef.body, supercall -> make.Block(0, initializer.append(supercall))); + } } } } @@ -2826,19 +2826,18 @@ private void visitMethodDefInternal(JCMethodDecl tree) { tree.params = tree.params.prepend(otdef); } - // If this is an initial constructor, i.e., it does not start with - // this(...), insert initializers for this$n and proxies - // before (pre-1.4, after) the call to superclass constructor. - JCStatement selfCall = translate(tree.body.stats.head); + // Determine whether this constructor has a super() invocation + boolean invokesSuper = TreeInfo.hasConstructorCall(tree, names._super); - List added = List.nil(); + // Create initializers for this$n and proxies + ListBuffer added = new ListBuffer<>(); if (fvs.nonEmpty()) { List addedargtypes = List.nil(); for (List l = fvs; l.nonEmpty(); l = l.tail) { m.capturedLocals = m.capturedLocals.prepend((VarSymbol) (proxies.get(l.head))); - if (TreeInfo.isInitialConstructor(tree)) { + if (invokesSuper) { added = added.prepend( initField(tree.body.pos, proxies.get(l.head), prevProxies.get(l.head))); } @@ -2852,13 +2851,18 @@ private void visitMethodDefInternal(JCMethodDecl tree) { syms.methodClass); } + // Recursively translate existing local statements + tree.body.stats = translate(tree.body.stats); + + // Prepend initializers in front of super() call + if (added.nonEmpty()) { + List initializers = added.toList(); + TreeInfo.mapSuperCalls(tree.body, supercall -> make.Block(0, initializers.append(supercall))); + } + // pop local variables from proxy stack proxies = prevProxies; - // recursively translate following local statements and - // combine with this- or super-call - List stats = translate(tree.body.stats.tail); - tree.body.stats = stats.prepend(selfCall).prependList(added); outerThisStack = prevOuterThisStack; } else { Map prevLambdaTranslationMap = diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index ab84d372bd72d..3980f03713d0a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -1503,13 +1503,15 @@ Symbol findVar(Env env, Name name) { sym = findField(env1, env1.enclClass.sym.type, name, env1.enclClass.sym); } if (sym.exists()) { - if (staticOnly && - sym.kind == VAR && + if (sym.kind == VAR && sym.owner.kind == TYP && - (sym.flags() & STATIC) == 0) - return new StaticError(sym); - else - return sym; + (sym.flags() & STATIC) == 0) { + if (staticOnly) + return new StaticError(sym); + if (env1.info.ctorPrologue && (sym.flags_field & SYNTHETIC) == 0) + return new RefBeforeCtorCalledError(sym); + } + return sym; } else { bestSoFar = bestOf(bestSoFar, sym); } @@ -2006,11 +2008,15 @@ Symbol findFun(Env env, Name name, env1, env1.enclClass.sym.type, name, argtypes, typeargtypes, allowBoxing, useVarargs); if (sym.exists()) { - if (staticOnly && - sym.kind == MTH && - sym.owner.kind == TYP && - (sym.flags() & STATIC) == 0) return new StaticError(sym); - else return sym; + if (sym.kind == MTH && + sym.owner.kind == TYP && + (sym.flags() & STATIC) == 0) { + if (staticOnly) + return new StaticError(sym); + if (env1.info.ctorPrologue && env1 == env) + return new RefBeforeCtorCalledError(sym); + } + return sym; } else { bestSoFar = bestOf(bestSoFar, sym); } @@ -3767,7 +3773,10 @@ Symbol resolveSelf(DiagnosticPosition pos, if (env1.enclClass.sym == c) { Symbol sym = env1.info.scope.findFirst(name); if (sym != null) { - if (staticOnly) sym = new StaticError(sym); + if (staticOnly) + sym = new StaticError(sym); + else if (env1.info.ctorPrologue) + sym = new RefBeforeCtorCalledError(sym); return accessBase(sym, pos, env.enclClass.sym.type, name, true); } @@ -3781,6 +3790,8 @@ Symbol resolveSelf(DiagnosticPosition pos, //this might be a default super call if one of the superinterfaces is 'c' for (Type t : pruneInterfaces(env.enclClass.type)) { if (t.tsym == c) { + if (env.info.ctorPrologue) + log.error(pos, Errors.CantRefBeforeCtorCalled(name)); env.info.defaultSuperCallSite = t; return new VarSymbol(0, names._super, types.asSuper(env.enclClass.type, c), env.enclClass.sym); @@ -3882,8 +3893,8 @@ Type resolveImplicitThis(DiagnosticPosition pos, Env env, Type t, b Type thisType = (t.tsym.owner.kind.matches(KindSelector.VAL_MTH) ? resolveSelf(pos, env, t.getEnclosingType().tsym, names._this) : resolveSelfContaining(pos, env, t.tsym, isSuperCall)).type; - if (env.info.isSelfCall && thisType.tsym == env.enclClass.sym) { - log.error(pos, Errors.CantRefBeforeCtorCalled("this")); + if (env.info.ctorPrologue && thisType.tsym == env.enclClass.sym) { + log.error(pos, Errors.CantRefBeforeCtorCalled(names._this)); } return thisType; } @@ -4588,7 +4599,11 @@ JCDiagnostic inaccessiblePackageReason(Env env, PackageSymbol sym) class StaticError extends InvalidSymbolError { StaticError(Symbol sym) { - super(STATICERR, sym, "static error"); + this(sym, "static error"); + } + + StaticError(Symbol sym, String debugName) { + super(STATICERR, sym, debugName); } @Override @@ -4607,6 +4622,32 @@ JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, } } + /** + * Specialization of {@link InvalidSymbolError} for illegal + * early accesses within a constructor prologue. + */ + class RefBeforeCtorCalledError extends StaticError { + + RefBeforeCtorCalledError(Symbol sym) { + super(sym, "prologue error"); + } + + @Override + JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, + DiagnosticPosition pos, + Symbol location, + Type site, + Name name, + List argtypes, + List typeargtypes) { + Symbol errSym = ((sym.kind == TYP && sym.type.hasTag(CLASS)) + ? types.erasure(sym.type).tsym + : sym); + return diags.create(dkind, log.currentSource(), pos, + "cant.ref.before.ctor.called", errSym); + } + } + /** * InvalidSymbolError error class indicating that a pair of symbols * (either methods, constructors or operands) are ambiguous @@ -4720,7 +4761,7 @@ class BadMethodReferenceError extends StaticError { boolean unboundLookup; public BadMethodReferenceError(Symbol sym, boolean unboundLookup) { - super(sym); + super(sym, "bad method ref error"); this.unboundLookup = unboundLookup; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index 313ee5c16d144..ef74ccc9877ca 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -570,7 +570,6 @@ protected Env baseEnv(JCClassDecl tree, Env env) { Env localEnv = outer.dup(tree, outer.info.dup(baseScope)); localEnv.baseClause = true; localEnv.outer = outer; - localEnv.info.isSelfCall = false; return localEnv; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 232beb83b255d..51486b997d3c3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -2332,7 +2332,12 @@ public Void visitMethodSymbol(Symbol.MethodSymbol s, Void unused) { thrown.add(addTypeAnnotations(thrownType, thrownType(i++))); } mt.thrown = thrown.toList(); - mt.restype = addTypeAnnotations(mt.restype, TargetType.METHOD_RETURN); + /* possible information loss if the type of the method is void then we can't add type + * annotations to it + */ + if (!mt.restype.hasTag(TypeTag.VOID)) { + mt.restype = addTypeAnnotations(mt.restype, TargetType.METHOD_RETURN); + } Type recvtype = mt.recvtype != null ? mt.recvtype : s.implicitReceiverType(); if (recvtype != null) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index caa5249bb8324..4e5c4e49436c2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -541,44 +541,18 @@ private void checkStringConstant(DiagnosticPosition pos, Object constValue) { nerrs++; } - /** Insert instance initializer code into initial constructor. + /** Insert instance initializer code into constructors prior to the super() call. * @param md The tree potentially representing a * constructor's definition. * @param initCode The list of instance initializer statements. * @param initTAs Type annotations from the initializer expression. */ void normalizeMethod(JCMethodDecl md, List initCode, List initTAs) { - if (md.name == names.init && TreeInfo.isInitialConstructor(md)) { - // We are seeing a constructor that does not call another - // constructor of the same class. - List stats = md.body.stats; - ListBuffer newstats = new ListBuffer<>(); - - if (stats.nonEmpty()) { - // Copy initializers of synthetic variables generated in - // the translation of inner classes. - while (TreeInfo.isSyntheticInit(stats.head)) { - newstats.append(stats.head); - stats = stats.tail; - } - // Copy superclass constructor call - newstats.append(stats.head); - stats = stats.tail; - // Copy remaining synthetic initializers. - while (stats.nonEmpty() && - TreeInfo.isSyntheticInit(stats.head)) { - newstats.append(stats.head); - stats = stats.tail; - } - // Now insert the initializer code. - newstats.appendList(initCode); - // And copy all remaining statements. - while (stats.nonEmpty()) { - newstats.append(stats.head); - stats = stats.tail; - } - } - md.body.stats = newstats.toList(); + if (TreeInfo.isConstructor(md) && TreeInfo.hasConstructorCall(md, names._super)) { + // We are seeing a constructor that has a super() call. + // Find the super() invocation and append the given initializer code. + TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.prepend(supercall))); + if (md.body.endpos == Position.NOPOS) md.body.endpos = TreeInfo.endPos(md.body.stats.last()); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 240e6ad2a54c0..2af50832f96fc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -62,10 +62,12 @@ import com.sun.tools.javac.platform.PlatformDescription; import com.sun.tools.javac.platform.PlatformUtils; import com.sun.tools.javac.resources.CompilerProperties.Errors; +import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticInfo; +import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; @@ -521,9 +523,9 @@ public boolean validate() { if (target.compareTo(source.requiredTarget()) < 0) { if (targetString != null) { if (sourceString == null) { - reportDiag(Warnings.TargetDefaultSourceConflict(targetString, source.requiredTarget())); + reportDiag(Errors.TargetDefaultSourceConflict(source.name, targetString)); } else { - reportDiag(Warnings.SourceTargetConflict(sourceString, source.requiredTarget())); + reportDiag(Errors.SourceTargetConflict(sourceString, targetString)); } return false; } else { @@ -569,10 +571,10 @@ public boolean validate() { if (fm instanceof BaseFileManager baseFileManager) { if (source.compareTo(Source.JDK8) <= 0) { if (baseFileManager.isDefaultBootClassPath()) - log.warning(LintCategory.OPTIONS, Warnings.SourceNoBootclasspath(source.name)); + log.warning(LintCategory.OPTIONS, Warnings.SourceNoBootclasspath(source.name, releaseNote(source, targetString))); } else { if (baseFileManager.isDefaultSystemModulesPath()) - log.warning(LintCategory.OPTIONS, Warnings.SourceNoSystemModulesPath(source.name)); + log.warning(LintCategory.OPTIONS, Warnings.SourceNoSystemModulesPath(source.name, releaseNote(source, targetString))); } } } @@ -640,6 +642,22 @@ public boolean validate() { return !errors && (log.nerrors == 0); } + private Fragment releaseNote(Source source, String targetString) { + if (source.compareTo(Source.JDK8) <= 0) { + if (targetString != null) { + return Fragments.SourceNoBootclasspathWithTarget(source.name, targetString); + } else { + return Fragments.SourceNoBootclasspath(source.name); + } + } else { + if (targetString != null) { + return Fragments.SourceNoSystemModulesPathWithTarget(source.name, targetString); + } else { + return Fragments.SourceNoSystemModulesPath(source.name); + } + } + } + private void validateAddExports(SourceVersion sv) { String addExports = options.get(Option.ADD_EXPORTS); if (addExports != null) { 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 63d2a1a96bded..487fe969f9774 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 @@ -27,7 +27,6 @@ import java.io.IOException; import java.io.PrintWriter; -import java.net.URI; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -48,8 +47,6 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.processing.Processor; import javax.tools.ForwardingJavaFileObject; @@ -71,8 +68,6 @@ import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.StringUtils; -import static java.nio.charset.StandardCharsets.UTF_8; - /** PlatformProvider for JDK N. * *

    This is NOT part of any supported API. @@ -173,17 +168,8 @@ public JavaFileObject getJavaFileForInput(Location location, String className, "", fileName + ".sig"); - if (result == null) { - //in jrt://, the classfile may have the .class extension: - result = (JavaFileObject) getFileForInput(location, - "", - fileName + ".class"); - } - if (result != null) { return new SigJavaFileObject(result); - } else { - return null; } } @@ -262,7 +248,6 @@ public String inferBinaryName(Location location, JavaFileObject file) { Path root = fs.getRootDirectories().iterator().next(); boolean hasModules = Feature.MODULES.allowedInSource(Source.lookup(sourceVersion)); - Path systemModules = root.resolve(ctSymVersion).resolve("system-modules"); if (!hasModules) { List paths = new ArrayList<>(); @@ -281,18 +266,6 @@ public String inferBinaryName(Location location, JavaFileObject file) { } fm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, paths); - } else if (Files.isRegularFile(systemModules)) { - fm.handleOption("--system", Arrays.asList("none").iterator()); - - Path jrtModules = - FileSystems.getFileSystem(URI.create("jrt:/")) - .getPath("modules"); - try (Stream lines = - Files.lines(systemModules, UTF_8)) { - lines.map(line -> jrtModules.resolve(line)) - .filter(mod -> Files.exists(mod)) - .forEach(mod -> setModule(fm, mod)); - } } else { Map> module2Paths = new HashMap<>(); @@ -327,16 +300,6 @@ public String inferBinaryName(Location location, JavaFileObject file) { } } - private static void setModule(StandardJavaFileManager fm, Path mod) { - try { - fm.setLocationForModule(StandardLocation.SYSTEM_MODULES, - mod.getFileName().toString(), - Collections.singleton(mod)); - } catch (IOException ex) { - throw new IllegalStateException(ex); - } - } - private static class SigJavaFileObject extends ForwardingJavaFileObject { public SigJavaFileObject(JavaFileObject fileObject) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index b485370da27ef..30756ef6fa6d7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -233,9 +233,17 @@ compiler.err.switch.expression.empty=\ compiler.err.switch.expression.no.result.expressions=\ switch expression does not have any result expressions -# 0: name -compiler.err.call.must.be.first.stmt.in.ctor=\ - call to {0} must be first statement in constructor +compiler.err.call.must.only.appear.in.ctor=\ + explicit constructor invocation may only appear within a constructor body + +compiler.err.redundant.superclass.init=\ + redundant explicit constructor invocation + +compiler.err.ctor.calls.not.allowed.here=\ + explicit constructor invocation not allowed here + +compiler.err.return.before.superclass.initialized=\ + ''return'' not allowed before explicit constructor invocation # 0: symbol kind, 1: name, 2: symbol kind, 3: type, 4: message segment compiler.err.cant.apply.symbol.noargs=\ @@ -387,7 +395,7 @@ compiler.err.annotation.decl.not.allowed.here=\ compiler.err.cant.inherit.from.final=\ cannot inherit from final {0} -# 0: symbol or string +# 0: symbol or name compiler.err.cant.ref.before.ctor.called=\ cannot reference {0} before supertype constructor has been called @@ -2111,13 +2119,33 @@ compiler.warn.static.not.qualified.by.type=\ compiler.warn.static.not.qualified.by.type2=\ static {0} should not be used as a member of an anonymous class -# 0: string +# 0: string, 1: fragment compiler.warn.source.no.bootclasspath=\ - bootstrap class path not set in conjunction with -source {0} + bootstrap class path is not set in conjunction with -source {0}\n{1} -# 0: string +# 0: string, 1: fragment compiler.warn.source.no.system.modules.path=\ - system modules path not set in conjunction with -source {0} + location of system modules is not set in conjunction with -source {0}\n{1} + +# 0: string +compiler.misc.source.no.bootclasspath=\ + not setting the bootstrap class path may lead to class files that cannot run on JDK {0}\n\ + --release {0} is recommended instead of -source {0} because it sets the bootstrap class path automatically + +# 0: string +compiler.misc.source.no.system.modules.path=\ + not setting the location of system modules may lead to class files that cannot run on JDK {0}\n\ + --release {0} is recommended instead of -source {0} because it sets the location of system modules automatically + +# 0: string, 1: string +compiler.misc.source.no.bootclasspath.with.target=\ + not setting the bootstrap class path may lead to class files that cannot run on JDK 8\n\ + --release {0} is recommended instead of -source {0} -target {1} because it sets the bootstrap class path automatically + +# 0: string, 1: string +compiler.misc.source.no.system.modules.path.with.target=\ + not setting the location of system modules may lead to class files that cannot run on JDK {0}\n\ + --release {0} is recommended instead of -source {0} -target {1} because it sets the location of system modules automatically # 0: string compiler.warn.option.obsolete.source=\ @@ -3193,6 +3221,9 @@ compiler.misc.feature.unconditional.patterns.in.instanceof=\ compiler.misc.feature.unnamed.classes=\ unnamed classes +compiler.misc.feature.super.init=\ + statements before super() + compiler.warn.underscore.as.identifier=\ as of release 9, ''_'' is a keyword, and may not be used as an identifier @@ -3895,8 +3926,8 @@ compiler.err.invalid.supertype.record=\ classes cannot directly extend {0} # 0: symbol -compiler.err.first.statement.must.be.call.to.another.constructor=\ - constructor is not canonical, so its first statement must invoke another constructor of class {0} +compiler.err.non.canonical.constructor.invoke.another.constructor=\ + constructor is not canonical, so it must invoke another constructor of class {0} compiler.err.instance.initializer.not.allowed.in.records=\ instance initializers not allowed in records @@ -3961,13 +3992,15 @@ compiler.err.error.writing.file=\ compiler.err.sourcepath.modulesourcepath.conflict=\ cannot specify both --source-path and --module-source-path -# 0: string, 1: target -compiler.warn.source.target.conflict=\ - source release {0} requires target release {1} +# 0: string, 1: string +compiler.err.source.target.conflict=\ + specified target release {1} is too old for the specified source release {0}\n\ + --release {1} is recommended when compiling code to run on JDK {1} -# 0: string, 1: target -compiler.warn.target.default.source.conflict=\ - target release {0} conflicts with default source release {1} +# 0: string, 1: string +compiler.err.target.default.source.conflict=\ + specified target release {1} is too old for the default source release {0}\n\ + --release {1} is recommended when compiling code to run on JDK {1} # 0: profile, 1: target compiler.warn.profile.target.conflict=\ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 1393256043447..ee4477135c678 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -50,6 +50,7 @@ import javax.lang.model.element.ElementKind; import javax.tools.JavaFileObject; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.ToIntFunction; @@ -113,25 +114,6 @@ public static boolean hasConstructors(List trees) { return false; } - /** Is there a constructor invocation in the given list of trees? - */ - public static Name getConstructorInvocationName(List trees, Names names) { - for (JCTree tree : trees) { - if (tree.hasTag(EXEC)) { - JCExpressionStatement stat = (JCExpressionStatement)tree; - if (stat.expr.hasTag(APPLY)) { - JCMethodInvocation apply = (JCMethodInvocation)stat.expr; - Name methName = TreeInfo.name(apply.meth); - if (methName == names._this || - methName == names._super) { - return methName; - } - } - } - } - return names.empty; - } - public static boolean isMultiCatch(JCCatch catchClause) { return catchClause.param.vartype.hasTag(TYPEUNION); } @@ -170,18 +152,6 @@ public static Name calledMethodName(JCTree tree) { return null; } - /** Is this a call to this or super? - */ - public static boolean isSelfCall(JCTree tree) { - Name name = calledMethodName(tree); - if (name != null) { - Names names = name.table.names; - return name==names._this || name==names._super; - } else { - return false; - } - } - /** Is this tree a 'this' identifier? */ public static boolean isThisQualifier(JCTree tree) { @@ -238,32 +208,103 @@ public static List recordFieldTypes(JCClassDecl tree) { .collect(List.collector()); } - /** Is this a constructor whose first (non-synthetic) statement is not - * of the form this(...)? + /** Is the given method a constructor containing a super() or this() call? + */ + public static boolean hasAnyConstructorCall(JCMethodDecl tree) { + return hasConstructorCall(tree, null); + } + + /** Is the given method a constructor containing a super() and/or this() call? + * The "target" is either names._this, names._super, or null for either/both. + */ + public static boolean hasConstructorCall(JCMethodDecl tree, Name target) { + JCMethodInvocation app = findConstructorCall(tree); + return app != null && (target == null || target == name(app.meth)); + } + + /** Find the first super() or init() call in the given constructor. + */ + public static JCMethodInvocation findConstructorCall(JCMethodDecl md) { + if (!TreeInfo.isConstructor(md) || md.body == null) + return null; + return new ConstructorCallFinder(md.name.table.names).find(md).head; + } + + /** Finds all calls to this() and/or super() in a given constructor. + * We can't assume they will be "top level" statements, because + * some synthetic calls to super() are added inside { } blocks. + * So we must recurse through the method's entire syntax tree. + */ + private static class ConstructorCallFinder extends TreeScanner { + + final ListBuffer calls = new ListBuffer<>(); + final Names names; + + ConstructorCallFinder(Names names) { + this.names = names; + } + + List find(JCMethodDecl meth) { + scan(meth); + return calls.toList(); + } + + @Override + public void visitApply(JCMethodInvocation invoke) { + Name name = TreeInfo.name(invoke.meth); + if ((name == names._this || name == names._super)) + calls.append(invoke); + super.visitApply(invoke); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + // don't descend any further + } + + @Override + public void visitLambda(JCLambda tree) { + // don't descend any further + } + } + + /** Finds super() invocations and translates them using the given mapping. + */ + public static void mapSuperCalls(JCBlock block, Function mapper) { + block.stats = block.stats.map(new TreeInfo.SuperCallTranslator(mapper)::translate); + } + + /** Finds all super() invocations and translates them somehow. */ - public static boolean isInitialConstructor(JCTree tree) { - JCMethodInvocation app = firstConstructorCall(tree); - if (app == null) return false; - Name meth = name(app.meth); - return meth == null || meth != meth.table.names._this; - } - - /** Return the first call in a constructor definition. */ - public static JCMethodInvocation firstConstructorCall(JCTree tree) { - if (!tree.hasTag(METHODDEF)) return null; - JCMethodDecl md = (JCMethodDecl) tree; - Names names = md.name.table.names; - if (md.name != names.init) return null; - if (md.body == null) return null; - List stats = md.body.stats; - // Synthetic initializations can appear before the super call. - while (stats.nonEmpty() && isSyntheticInit(stats.head)) - stats = stats.tail; - if (stats.isEmpty()) return null; - if (!stats.head.hasTag(EXEC)) return null; - JCExpressionStatement exec = (JCExpressionStatement) stats.head; - if (!exec.expr.hasTag(APPLY)) return null; - return (JCMethodInvocation)exec.expr; + private static class SuperCallTranslator extends TreeTranslator { + + final Function translator; + + /** Constructor. + * + * @param translator translates super() invocations, returning replacement statement or null for no change + */ + SuperCallTranslator(Function translator) { + this.translator = translator; + } + + // Because it returns void, anywhere super() can legally appear must be a location where a JCStatement + // could also appear, so it's OK that we are replacing a JCExpressionStatement with a JCStatement here. + @Override + public void visitExec(JCExpressionStatement stat) { + if (!TreeInfo.isSuperCall(stat) || (result = this.translator.apply(stat)) == null) + super.visitExec(stat); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + // don't descend any further + } + + @Override + public void visitLambda(JCLambda tree) { + // don't descend any further + } } /** Return true if a tree represents a diamond new expr. */ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java index dd813197bc05f..d54cc7fb56e27 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HSDB.java @@ -1111,6 +1111,9 @@ public void addAnnotation(Address addr, OopHandle handle) { anno = "Old "; bad = false; } + if (!bad && region.isPinned()) { + anno += "Pinned "; + } } else if (collHeap instanceof ParallelScavengeHeap) { ParallelScavengeHeap heap = (ParallelScavengeHeap) collHeap; if (heap.youngGen().isIn(handle)) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegion.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegion.java index b681330e5a7f2..580a4b1c3978f 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegion.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/HeapRegion.java @@ -50,6 +50,8 @@ public class HeapRegion extends ContiguousSpace implements LiveRegionsProvider { private static AddressField endField; private static CIntegerField grainBytesField; + private static CIntegerField pinnedCountField; + private static long typeFieldOffset; private static long pointerSize; @@ -71,6 +73,8 @@ private static synchronized void initialize(TypeDataBase db) { endField = type.getAddressField("_end"); grainBytesField = type.getCIntegerField("GrainBytes"); + pinnedCountField = type.getCIntegerField("_pinned_object_count"); + typeFieldOffset = type.getField("_type").getOffset(); pointerSize = db.lookupType("HeapRegion*").getSize(); @@ -124,6 +128,10 @@ public boolean isHumongous() { return type.isHumongous(); } + public boolean isPinned() { + return pinnedCountField.getValue(addr) != 0; + } + public boolean isOld() { return type.isOld(); } @@ -134,6 +142,6 @@ public static long getPointerSize() { public void printOn(PrintStream tty) { tty.print("Region: " + bottom() + "," + top() + "," + end()); - tty.println(":" + type.typeAnnotation()); + tty.println(":" + type.typeAnnotation() + (isPinned() ? " Pinned" : "")); } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenCollectedHeap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenCollectedHeap.java index 9241d847c73d8..3442a0629e2f3 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenCollectedHeap.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenCollectedHeap.java @@ -39,9 +39,6 @@ public abstract class GenCollectedHeap extends CollectedHeap { private static AddressField youngGenField; private static AddressField oldGenField; - private static AddressField youngGenSpecField; - private static AddressField oldGenSpecField; - private static GenerationFactory genFactory; static { @@ -57,8 +54,6 @@ private static synchronized void initialize(TypeDataBase db) { youngGenField = type.getAddressField("_young_gen"); oldGenField = type.getAddressField("_old_gen"); - youngGenSpecField = type.getAddressField("_young_gen_spec"); - oldGenSpecField = type.getAddressField("_old_gen_spec"); genFactory = new GenerationFactory(); } @@ -115,26 +110,6 @@ public long used() { return used; } - /** Package-private access to GenerationSpecs */ - GenerationSpec spec(int level) { - if (Assert.ASSERTS_ENABLED) { - Assert.that((level == 0) || (level == 1), "Index " + level + - " out of range (should be 0 or 1)"); - } - - if ((level != 0) && (level != 1)) { - return null; - } - - if (level == 0) { - return VMObjectFactory.newObject(GenerationSpec.class, - youngGenSpecField.getAddress()); - } else { - return VMObjectFactory.newObject(GenerationSpec.class, - oldGenSpecField.getAddress()); - } - } - public void liveRegionsIterate(LiveRegionsClosure closure) { // Run through all generations, obtaining bottom-top pairs. for (int i = 0; i < nGens(); i++) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenerationSpec.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenerationSpec.java deleted file mode 100644 index 02e85b192c62e..0000000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/GenerationSpec.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -package sun.jvm.hotspot.gc.shared; - -import java.util.*; - -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.runtime.*; -import sun.jvm.hotspot.types.*; -import sun.jvm.hotspot.utilities.Observable; -import sun.jvm.hotspot.utilities.Observer; - -public class GenerationSpec extends VMObject { - private static CIntegerField nameField; - private static CIntegerField initSizeField; - private static CIntegerField maxSizeField; - - static { - VM.registerVMInitializedObserver(new Observer() { - public void update(Observable o, Object data) { - initialize(VM.getVM().getTypeDataBase()); - } - }); - } - - private static synchronized void initialize(TypeDataBase db) { - Type type = db.lookupType("GenerationSpec"); - - nameField = type.getCIntegerField("_name"); - initSizeField = type.getCIntegerField("_init_size"); - maxSizeField = type.getCIntegerField("_max_size"); - } - - public GenerationSpec(Address addr) { - super(addr); - } - - public Generation.Name name() { - return Generation.nameForEnum((int)nameField.getValue(addr)); - } - - public long initSize() { - return initSizeField.getValue(addr); - } - - public long maxSize() { - return maxSizeField.getValue(addr); - } -} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java index 06bfd1d59e834..1b02c9ea05e07 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/LinkTaglet.java @@ -117,7 +117,7 @@ public Content getInlineTagOutput(Element element, DocTree tag, TagletWriter tag * @param refTree the tree node containing the information, or {@code null} if not available * @param refSignature the normalized signature of the target of the reference * @param ref the target of the reference - * @param isLinkPlain {@code true} if the link should be presented in "plain" font, + * @param isPlain {@code true} if the link should be presented in "plain" font, * or {@code false} for "code" font * @param label the label for the link, * or an empty item to use a default label derived from the signature @@ -130,17 +130,17 @@ Content linkSeeReferenceOutput(Element holder, DocTree refTree, String refSignature, Element ref, - boolean isLinkPlain, + boolean isPlain, Content label, BiConsumer reportWarning, TagletWriter tagletWriter) { var config = tagletWriter.configuration; var htmlWriter = tagletWriter.htmlWriter; - Content labelContent = plainOrCode(isLinkPlain, label); + Content labelContent = plainOrCode(isPlain, label); // The signature from the @see tag. We will output this text when a label is not specified. - Content text = plainOrCode(isLinkPlain, + Content text = plainOrCode(isPlain, Text.of(Objects.requireNonNullElse(refSignature, ""))); CommentHelper ch = utils.getCommentHelper(holder); @@ -170,7 +170,7 @@ Content linkSeeReferenceOutput(Element holder, if (refPackage != null && utils.isIncluded(refPackage)) { //@see is referencing an included package if (labelContent.isEmpty()) { - labelContent = plainOrCode(isLinkPlain, + labelContent = plainOrCode(isPlain, Text.of(refPackage.getQualifiedName())); } return htmlWriter.getPackageLink(refPackage, labelContent, refFragment); @@ -202,10 +202,10 @@ Content linkSeeReferenceOutput(Element holder, TypeMirror referencedType = ch.getReferencedType(refTree); if (utils.isGenericType(referencedType)) { // This is a generic type link, use the TypeMirror representation. - return plainOrCode(isLinkPlain, htmlWriter.getLink( + return plainOrCode(isPlain, htmlWriter.getLink( new HtmlLinkInfo(config, HtmlLinkInfo.Kind.LINK_TYPE_PARAMS_AND_BOUNDS, referencedType))); } - labelContent = plainOrCode(isLinkPlain, Text.of(utils.getSimpleName(refClass))); + labelContent = plainOrCode(isPlain, Text.of(utils.getSimpleName(refClass))); } return htmlWriter.getLink(new HtmlLinkInfo(config, HtmlLinkInfo.Kind.PLAIN, refClass) .label(labelContent)); @@ -267,7 +267,7 @@ Content linkSeeReferenceOutput(Element holder, return htmlWriter.getDocLink(HtmlLinkInfo.Kind.SHOW_PREVIEW, containing, refMem, (labelContent.isEmpty() - ? plainOrCode(isLinkPlain, Text.of(refMemName)) + ? plainOrCode(isPlain, Text.of(refMemName)) : labelContent), null, false); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java index 2b6ad36896ab4..8e029c5fbb525 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SeeTaglet.java @@ -37,6 +37,7 @@ import com.sun.source.doctree.DocTree; import com.sun.source.doctree.SeeTree; +import com.sun.source.doctree.TextTree; import jdk.javadoc.doclet.Taglet; import jdk.javadoc.internal.doclets.formats.html.ClassWriter; @@ -174,7 +175,7 @@ private Content seeTagOutput(Element element, SeeTree seeTag) { seeTag, refSignature, ch.getReferencedElement(seeTag), - false, + isPlain(refSignature, label), htmlWriter.commentTagsToContent(element, label, tagletWriter.getContext().within(seeTag)), (key, args) -> messages.warning(ch.getDocTreePath(seeTag), key, args), tagletWriter @@ -189,7 +190,124 @@ private Content seeTagOutput(Element element, SeeTree seeTag) { default -> throw new IllegalStateException(ref0.getKind().toString()); } + } + + /** + * {@return {@code true} if the label should be rendered in plain font} + * + * The method uses a heuristic, to see if the string form of the label + * is a substring of the reference. Thus, for example: + * + *

      + *
    • {@code @see MyClass.MY_CONSTANT MY_CONSTANT} returns {@code true} + *
    • {@code @see MyClass.MY_CONSTANT a constant} returns {@code false} + *
    + * + * The result will be {@code true} (meaning, displayed in plain font) if + * any of the following are true about the label: + * + *
      + *
    • There is more than a single item in the list of nodes, + * suggesting there may be formatting nodes. + *
    • There is whitespace outside any parentheses, + * suggesting the label is a phrase + *
    • There are nested parentheses, or content after the parentheses, + * which cannot occur in a standalone signature + *
    • The simple name inferred from the reference does not match + * any simple name inferred from the label + *
    + * + * @param refSignature the signature of the target of the reference + * @param label the label + */ + private boolean isPlain(String refSignature, List label) { + if (label.isEmpty()) { + return false; + } else if (label.size() > 1) { + return true; + } + + var l0 = label.get(0); + String s; + if (l0 instanceof TextTree t) { + s = t.getBody().trim(); + } else { + return true; + } + + // look for whitespace outside any parens, nested parens, or characters after parens: + // all of which will not be found in a simple signature + var inParens = false; + var ids = new ArrayList(); + var sb = new StringBuilder(); + for (var i = 0; i < s.length(); i++) { + var ch = s.charAt(i); + if (!sb.isEmpty() && !Character.isJavaIdentifierPart(ch)) { + ids.add(sb.toString()); + sb.setLength(0); + } + switch (ch) { + case '(' -> { + if (inParens) { + return true; + } else { + inParens = true; + } + } + case ')' -> { + if (inParens && i < s.length() - 1) { + return true; + } else { + inParens = false; + } + } + default -> { + if (!inParens) { + if (Character.isJavaIdentifierStart(ch) + || (!sb.isEmpty() && Character.isJavaIdentifierPart(ch))) { + sb.append(ch); + } else if (Character.isWhitespace(ch)) { + return true; + } + } + } + } + } + + if (!sb.isEmpty()) { + ids.add(sb.toString()); + } + + if (ids.isEmpty()) { + return true; + } + + // final check: does the simple name inferred from the label + // match the simple name inferred from the reference + var labelSimpleName = ids.get(ids.size() - 1); + var refSimpleName = getSimpleName(refSignature); + return !labelSimpleName.equals((refSimpleName)); + } + + /** + * {@return the simple name from a signature} + * + * If there is a member part in the signature, the simple name is the + * identifier after the {@code #} character. + * Otherwise, the simple name is the last identifier in the signature. + * + * @param sig the signature + */ + private String getSimpleName(String sig) { + int hash = sig.indexOf('#'); + if (hash == -1 ) { + int lastDot = sig.lastIndexOf("."); + return lastDot == -1 ? sig : sig.substring(lastDot + 1); + } else { + int parens = sig.indexOf("(", hash); + return parens == -1 ? sig.substring(hash + 1) : sig.substring(hash + 1, parens); + } } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java index b097866a2d85a..871acd26ec3ab 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclint/Checker.java @@ -1248,14 +1248,8 @@ private boolean isAnonymous() { } private boolean isDefaultConstructor() { - if (env.currElement.getKind() == ElementKind.CONSTRUCTOR) { - // A synthetic default constructor has the same pos as the - // enclosing class - TreePath p = env.currPath; - return env.getPos(p) == env.getPos(p.getParentPath()); - } else { - return false; - } + return env.currElement.getKind() == ElementKind.CONSTRUCTOR + && env.elements.getOrigin(env.currElement) == Elements.Origin.MANDATED; } private boolean isDeclaredType() { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/ProcessStartEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/ProcessStartEvent.java index b936099aa743a..5ad2dc82fc1de 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/ProcessStartEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ProcessStartEvent.java @@ -38,6 +38,7 @@ @Description("Operating system process started") @MirrorEvent(className = "jdk.internal.event.ProcessStartEvent") @RemoveFields("duration") +@StackFilter({"java.lang.ProcessBuilder", "java.lang.Runtime::exec"}) public final class ProcessStartEvent extends AbstractJDKEvent { @Label("Process Id") public long pid; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityPropertyModificationEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityPropertyModificationEvent.java index df2e9c241de06..7f4e5045c9d23 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityPropertyModificationEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityPropertyModificationEvent.java @@ -35,6 +35,7 @@ @Description("Modification of Security property") @MirrorEvent(className = "jdk.internal.event.SecurityPropertyModificationEvent") @RemoveFields("duration") +@StackFilter({"java.security.Security::setProperty"}) public final class SecurityPropertyModificationEvent extends AbstractJDKEvent { @Label("Key") public String key; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityProviderServiceEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityProviderServiceEvent.java index 193bfa54ae255..8bbbf09d87df5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityProviderServiceEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SecurityProviderServiceEvent.java @@ -38,6 +38,7 @@ @Description("Details of Provider.getInstance(String type, String algorithm) calls") @MirrorEvent(className = "jdk.internal.event.SecurityProviderServiceEvent") @RemoveFields("duration") +@StackFilter({"java.security.Provider::getService"}) public final class SecurityProviderServiceEvent extends AbstractJDKEvent { @Label("Type of Service") public String type; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/StackFilter.java b/src/jdk.jfr/share/classes/jdk/jfr/events/StackFilter.java new file mode 100644 index 0000000000000..f0c6a45bf836f --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/StackFilter.java @@ -0,0 +1,65 @@ +package jdk.jfr.events; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import jdk.jfr.MetadataDefinition; + +/** +* Event annotation, specifies method names or classes to exclude in a stack +* trace. +*

    +* The following example illustrates how the {@code StackFilter} annotation can +* be used to remove the {@code Logger::log} method in a stack trace: +* +* {@snippet : +* package com.example; +* +* @Name("example.LogMessage") +* @Label("Log Message") +* @StackFilter("com.example.Logger::log") +* class LogMessage extends Event { +* @Label("Message") +* String message; +* } +* +* public class Logger { +* +* public static void log(String message) { +* System.out.print(Instant.now() + " : " + message); +* LogMessage event = new LogMessage(); +* event.message = message; +* event.commit(); +* } +* } +* } +* +* @since 22 +*/ +@Target({ ElementType.TYPE }) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@MetadataDefinition +public @interface StackFilter { + /** + * The methods or classes that should not be part of an event stack trace. + *

    + * A filter is formed by using the fully qualified class name concatenated with + * the method name using {@code "::"} as separator, for example + * {@code "java.lang.String::toString"} + *

    + * If only the name of a class is specified, for example {@code + * "java.lang.String"}, all methods in that class are filtered out. + *

    + * Methods can't be qualified using method parameters or return types. + *

    + * Instance methods belonging to an interface can't be filtered out. + *

    + * Wilcards are not permitted. + * + * @return the method names, not {@code null} + */ + String[] value(); +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java index ce5e8c8576804..df2f4cfc2ed55 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/TLSHandshakeEvent.java @@ -39,6 +39,7 @@ @Description("Parameters used in TLS Handshake") @MirrorEvent(className = "jdk.internal.event.TLSHandshakeEvent") @RemoveFields("duration") +@StackFilter("sun.security.ssl.Finished::recordEvent") public final class TLSHandshakeEvent extends AbstractJDKEvent { @Label("Peer Host") public String peerHost; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/ThreadSleepEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/ThreadSleepEvent.java index ab606d8d6de4d..216ad370acafd 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/ThreadSleepEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ThreadSleepEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,9 @@ @Label("Java Thread Sleep") @Name("jdk.ThreadSleep") @MirrorEvent(className = "jdk.internal.event.ThreadSleepEvent") +@StackFilter({"java.lang.Thread::afterSleep", + "java.lang.Thread::sleepNanos", + "java.lang.Thread::sleep"}) public final class ThreadSleepEvent extends AbstractJDKEvent { @Label("Sleep Time") @Timespan(Timespan.NANOSECONDS) 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 d2327a189b54e..a83d244f1d773 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.List; +import jdk.internal.module.Checks; import jdk.internal.module.Modules; import jdk.jfr.AnnotationElement; import jdk.jfr.Enabled; @@ -44,6 +45,8 @@ import jdk.jfr.StackTrace; import jdk.jfr.Threshold; import jdk.jfr.events.ActiveSettingEvent; +import jdk.jfr.events.StackFilter; +import jdk.jfr.internal.JVM; import jdk.jfr.internal.settings.CutoffSetting; import jdk.jfr.internal.settings.EnabledSetting; import jdk.jfr.internal.settings.PeriodSetting; @@ -65,6 +68,7 @@ record NamedControl(String name, Control control) { private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class); private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class); private static final Type TYPE_THROTTLE = TypeLibrary.createType(ThrottleSetting.class); + private static final long STACK_FILTER_ID = Type.getTypeId(StackFilter.class); private final ArrayList settingControls = new ArrayList<>(); private final ArrayList namedControls = new ArrayList<>(5); @@ -89,6 +93,7 @@ record NamedControl(String name, Control control) { } addControl(Enabled.NAME, defineEnabled(eventType)); + addStackFilters(eventType); List aes = new ArrayList<>(eventType.getAnnotationElements()); remove(eventType, aes, Threshold.class); remove(eventType, aes, Period.class); @@ -96,11 +101,84 @@ record NamedControl(String name, Control control) { remove(eventType, aes, StackTrace.class); remove(eventType, aes, Cutoff.class); remove(eventType, aes, Throttle.class); + remove(eventType, aes, StackFilter.class); eventType.setAnnotations(aes); this.type = eventType; this.idName = String.valueOf(eventType.getId()); } + private void addStackFilters(PlatformEventType eventType) { + String[] filter = getStackFilter(eventType); + if (filter != null) { + int size = filter.length; + List types = new ArrayList<>(size); + List methods = new ArrayList<>(size); + for (String frame : filter) { + int index = frame.indexOf("::"); + String clazz = null; + String method = null; + boolean valid = false; + if (index != -1) { + clazz = frame.substring(0, index); + method = frame.substring(index + 2); + if (clazz.isEmpty()) { + clazz = null; + valid = isValidMethod(method); + } else { + valid = isValidType(clazz) && isValidMethod(method); + } + } else { + clazz = frame; + valid = isValidType(frame); + } + if (valid) { + if (clazz == null) { + types.add(null); + } else { + types.add(clazz.replace(".", "/")); + } + // If unqualified class name equals method name, it's a constructor + String className = clazz.substring(clazz.lastIndexOf(".") + 1); + if (className.equals(method)) { + method = ""; + } + methods.add(method); + } else { + Logger.log(LogTag.JFR, LogLevel.WARN, "@StackFrameFilter element ignored, not a valid Java identifier."); + } + } + if (!types.isEmpty()) { + String[] typeArray = types.toArray(new String[0]); + String[] methodArray = methods.toArray(new String[0]); + long id = MetadataRepository.getInstance().registerStackFilter(typeArray, methodArray); + eventType.setStackFilterId(id); + } + } + } + + private String[] getStackFilter(PlatformEventType eventType) { + for (var a : eventType.getAnnotationElements()) { + if (a.getTypeId() == STACK_FILTER_ID) { + return (String[])a.getValue("value"); + } + } + return null; + } + + private boolean isValidType(String className) { + if (className.length() < 1 || className.length() > 65535) { + return false; + } + return Checks.isClassName(className); + } + + private boolean isValidMethod(String method) { + if (method.length() < 1 || method.length() > 65535) { + return false; + } + return Checks.isJavaIdentifier(method); + } + private boolean hasControl(String name) { for (NamedControl nc : namedControls) { if (name.equals(nc.name)) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java index 986b6f2450bac..3f37924c9a72f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -149,10 +149,15 @@ private static class ChunkRotationMonitor {} * * Requires that JFR has been started with {@link #createNativeJFR()} * - * @param skipCount number of frames to skip + * @param skipCount number of frames to skip, or 0 if no frames should be + * skipped + * + * @param ID ID of the filter that should be used, or -1 if no filter should + * be used + * * @return a unique stack trace identifier */ - public static native long getStackTraceId(int skipCount); + public static native long getStackTraceId(int skipCount, long stackFilerId); /** * Return identifier for thread @@ -628,4 +633,30 @@ private static class ChunkRotationMonitor {} * @param bytes number of bytes that were lost */ public static native void emitDataLoss(long bytes); + + /** + * Registers stack filters that should be used with getStackTrace(int, long) + *

    + * Method name at an array index is for class at the same array index. + *

    + * This method should be called holding the MetadataRepository lock and before + * bytecode for the associated event class has been added. + * + * @param classes, name of classes, for example {"java/lang/String"}, not + * {@code null} + * @param methods, name of method, for example {"toString"}, not {@code null} + * + * @return an ID that can be used to unregister the start frames, or -1 if it could not be registered + */ + public static native long registerStackFilter(String[] classes, String[] methods); + + /** + * Unregisters a set of stack filters. + *

    + * This method should be called holding the MetadataRepository lock and after + * the associated event class has been unloaded. + * + * @param stackFilterId the stack filter ID to unregister + */ + public static native void unregisterStackFilter(long stackFilterId); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java index 417238c06f8af..c83e9de7893cf 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataRepository.java @@ -321,6 +321,9 @@ private void unregisterUnloaded() { if (!knownIds.contains(pe.getId())) { if (!pe.isJVM()) { pe.setRegistered(false); + if (pe.hasStackFilters()) { + JVM.unregisterStackFilter(pe.getStackFilterId()); + } } } } @@ -355,4 +358,8 @@ static void unhideInternalTypes() { public synchronized List getVisibleTypes() { return TypeLibrary.getVisibleTypes(); } + + public synchronized long registerStackFilter(String[] typeArray, String[] methodArray) { + return JVM.registerStackFilter(typeArray, methodArray); + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java index e600a0c70fe0c..e8621c56794d9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java @@ -46,6 +46,7 @@ public final class PlatformEventType extends Type { private final List settings = new ArrayList<>(5); private final boolean dynamicSettings; private final int stackTraceOffset; + private long startFilterId = -1; // default values private boolean largeSize = false; @@ -339,4 +340,16 @@ public void setLargeSize() { public boolean isMethodSampling() { return isMethodSampling; } + + public void setStackFilterId(long id) { + startFilterId = id; + } + + public boolean hasStackFilters() { + return startFilterId >= 0; + } + + public long getStackFilterId() { + return startFilterId; + } } 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 0a1f1c05dee57..f461212659d37 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/TypeLibrary.java @@ -60,6 +60,7 @@ import jdk.jfr.ValueDescriptor; import jdk.jfr.internal.util.Utils; import jdk.jfr.internal.util.ImplicitFields; +import jdk.internal.module.Modules; public final class TypeLibrary { private static boolean implicitFieldTypes; @@ -165,7 +166,6 @@ public static synchronized AnnotationElement createAnnotation(Annotation annotat for (ValueDescriptor v : type.getFields()) { values.add(invokeAnnotation(annotation, v.getName())); } - return PrivateAccess.getInstance().newAnnotation(type, values, annotation.annotationType().getClassLoader() == null); } return null; @@ -178,6 +178,15 @@ private static Object invokeAnnotation(Annotation annotation, String methodName) } catch (NoSuchMethodException e1) { throw (Error) new InternalError("Could not locate method " + methodName + " in annotation " + annotation.getClass().getName()); } + // Add export from JDK proxy module + if (annotation.getClass().getClassLoader() == null) { + if (annotation.getClass().getName().contains("Proxy")) { + Module proxyModule = annotation.getClass().getModule(); + String proxyPackage = annotation.getClass().getPackageName(); + Module jfrModule = TypeLibrary.class.getModule(); + Modules.addExports(proxyModule, proxyPackage, jfrModule); + } + } SecuritySupport.setAccessible(m); try { return m.invoke(annotation, new Object[0]); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java index 43fc613049558..833fb087e2420 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/event/EventWriter.java @@ -188,7 +188,7 @@ public void putClass(Class aClass) { public void putStackTrace() { if (eventType.getStackTraceEnabled()) { - putLong(JVM.getStackTraceId(eventType.getStackTraceOffset())); + putLong(JVM.getStackTraceId(eventType.getStackTraceOffset(), eventType.getStackFilterId())); } else { putLong(0L); } 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 0e027ebbf417f..6a5b200f8f304 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,9 +63,10 @@ final class DesktopIntegration extends ShellCustomAction { private static final String COMMANDS_INSTALL = "DESKTOP_COMMANDS_INSTALL"; private static final String COMMANDS_UNINSTALL = "DESKTOP_COMMANDS_UNINSTALL"; private static final String SCRIPTS = "DESKTOP_SCRIPTS"; + private static final String COMMON_SCRIPTS = "COMMON_SCRIPTS"; private static final List REPLACEMENT_STRING_IDS = List.of( - COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS); + COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS, COMMON_SCRIPTS); private DesktopIntegration(PlatformPackage thePackage, Map params, @@ -229,8 +230,6 @@ protected Map createImpl() throws IOException { shellCommands.applyTo(data); } - boolean needCleanupScripts = !associations.isEmpty(); - // Take care of additional launchers if there are any. // Process every additional launcher as the main application launcher. // Collect shell commands to install/uninstall integration with desktop @@ -241,10 +240,6 @@ protected Map createImpl() throws IOException { List uninstallShellCmds = new ArrayList<>(Arrays.asList( data.get(COMMANDS_UNINSTALL))); for (var integration: nestedIntegrations) { - if (!integration.associations.isEmpty()) { - needCleanupScripts = true; - } - Map launcherData = integration.create(); installShellCmds.add(launcherData.get(COMMANDS_INSTALL)); @@ -254,10 +249,8 @@ protected Map createImpl() throws IOException { data.put(COMMANDS_INSTALL, stringifyShellCommands(installShellCmds)); data.put(COMMANDS_UNINSTALL, stringifyShellCommands(uninstallShellCmds)); - if (needCleanupScripts) { - // Pull in desktop_utils.sh scrips library. - data.put(SCRIPTS, stringifyTextFile("desktop_utils.sh")); - } + data.put(COMMON_SCRIPTS, stringifyTextFile("common_utils.sh")); + data.put(SCRIPTS, stringifyTextFile("desktop_utils.sh")); return data; } @@ -295,7 +288,9 @@ private class ShellCommands { registerDesktopFileCmd = String.join(" ", "xdg-desktop-menu", "install", desktopFile.installPath().toString()); - unregisterDesktopFileCmd = String.join(" ", "xdg-desktop-menu", + unregisterDesktopFileCmd = String.join(" ", + "do_if_file_belongs_to_single_package", desktopFile. + installPath().toString(), "xdg-desktop-menu", "uninstall", desktopFile.installPath().toString()); } @@ -303,8 +298,10 @@ void setFileAssociations() { registerFileAssociationsCmd = String.join(" ", "xdg-mime", "install", mimeInfoFile.installPath().toString()); - unregisterFileAssociationsCmd = String.join(" ", "xdg-mime", - "uninstall", mimeInfoFile.installPath().toString()); + unregisterFileAssociationsCmd = String.join(" ", + "do_if_file_belongs_to_single_package", mimeInfoFile. + installPath().toString(), "xdg-mime", "uninstall", + mimeInfoFile.installPath().toString()); // // Add manual cleanup of system files to get rid of @@ -320,26 +317,26 @@ void setFileAssociations() { // of non-existing desktop file. // String cleanUpCommand = String.join(" ", - "uninstall_default_mime_handler", - desktopFile.installPath().getFileName().toString(), - String.join(" ", getMimeTypeNamesFromFileAssociations())); + "do_if_file_belongs_to_single_package", desktopFile. + installPath().toString(), + "desktop_uninstall_default_mime_handler", desktopFile. + installPath().getFileName().toString(), String.join( + " ", getMimeTypeNamesFromFileAssociations())); unregisterFileAssociationsCmd = stringifyShellCommands( unregisterFileAssociationsCmd, cleanUpCommand); } - void addIcon(String mimeType, Path iconFile) { - addIcon(mimeType, iconFile, getSquareSizeOfImage(iconFile.toFile())); - } - void addIcon(String mimeType, Path iconFile, int imgSize) { imgSize = normalizeIconSize(imgSize); final String dashMime = mimeType.replace('/', '-'); registerIconCmds.add(String.join(" ", "xdg-icon-resource", "install", "--context", "mimetypes", "--size", Integer.toString(imgSize), iconFile.toString(), dashMime)); - unregisterIconCmds.add(String.join(" ", "xdg-icon-resource", - "uninstall", dashMime, "--size", Integer.toString(imgSize))); + unregisterIconCmds.add(String.join(" ", + "do_if_file_belongs_to_single_package", iconFile.toString(), + "xdg-icon-resource", "uninstall", dashMime, "--size", + Integer.toString(imgSize))); } void applyTo(Map data) { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java index 10a655538507e..9c5b272b4312e 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import static jdk.jpackage.internal.OverridableResource.createResource; @@ -42,10 +44,24 @@ private LinuxLaunchersAsServices(PlatformPackage thePackage, }); } + @Override + protected List replacementStringIds() { + return LINUX_REPLACEMENT_STRING_IDS; + } + + @Override + protected Map createImpl() throws IOException { + var data = super.createImpl(); + if (!data.isEmpty()) { + data.put(COMMON_SCRIPTS, stringifyTextFile("common_utils.sh")); + } + return data; + } + static ShellCustomAction create(PlatformPackage thePackage, Map params) throws IOException { if (StandardBundlerParam.isRuntimeInstaller(params)) { - return ShellCustomAction.nop(REPLACEMENT_STRING_IDS); + return ShellCustomAction.nop(LINUX_REPLACEMENT_STRING_IDS); } return new LinuxLaunchersAsServices(thePackage, params); } @@ -84,4 +100,16 @@ Path descriptorFilePath(Path root) { private final static List REQUIRED_PACKAGES = List.of("systemd", "coreutils" /* /usr/bin/wc */, "grep"); + + private static final String COMMON_SCRIPTS = "COMMON_SCRIPTS"; + + private static final List LINUX_REPLACEMENT_STRING_IDS; + + static { + ArrayList buf = new ArrayList<>(); + buf.addAll(UnixLaunchersAsServices.REPLACEMENT_STRING_IDS); + buf.add(COMMON_SCRIPTS); + + LINUX_REPLACEMENT_STRING_IDS = Collections.unmodifiableList(buf); + } } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java index 374f88878e82d..b510ed414118b 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -155,7 +155,8 @@ public final Path execute(Map params, Map data = createDefaultReplacementData(params); for (var ca : customActions) { - data.putAll(ca.instance.create()); + ShellCustomAction.mergeReplacementData(data, ca.instance. + create()); } data.putAll(createReplacementData(params)); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/common_utils.sh b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/common_utils.sh new file mode 100644 index 0000000000000..31a81bb5fb0bf --- /dev/null +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/common_utils.sh @@ -0,0 +1,23 @@ +file_belongs_to_single_package () +{ + if [ ! -e "$1" ]; then + false + elif [ "$package_type" = rpm ]; then + test `rpm -q --whatprovides "$1" | wc -l` = 1 + elif [ "$package_type" = deb ]; then + test `dpkg -S "$1" | wc -l` = 1 + else + exit 1 + fi +} + + +do_if_file_belongs_to_single_package () +{ + local file="$1" + shift + + if file_belongs_to_single_package "$file"; then + "$@" + fi +} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh index 1ed96076282c4..7c243b2b5c5bc 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh @@ -2,7 +2,7 @@ # Remove $1 desktop file from the list of default handlers for $2 mime type # in $3 file dumping output to stdout. # -_filter_out_default_mime_handler () +desktop_filter_out_default_mime_handler () { local defaults_list="$3" @@ -50,7 +50,7 @@ EOF # in $1 file. # Result is saved in $1 file. # -_uninstall_default_mime_handler () +desktop_uninstall_default_mime_handler_0 () { local defaults_list=$1 shift @@ -66,20 +66,20 @@ _uninstall_default_mime_handler () local v local update= for mime in "$@"; do - _filter_out_default_mime_handler "$desktop_file" "$mime" "$tmpfile1" > "$tmpfile2" + desktop_filter_out_default_mime_handler "$desktop_file" "$mime" "$tmpfile1" > "$tmpfile2" v="$tmpfile2" tmpfile2="$tmpfile1" tmpfile1="$v" if ! diff -q "$tmpfile1" "$tmpfile2" > /dev/null; then update=yes - trace Remove $desktop_file default handler for $mime mime type from $defaults_list file + desktop_trace Remove $desktop_file default handler for $mime mime type from $defaults_list file fi done if [ -n "$update" ]; then cat "$tmpfile1" > "$defaults_list" - trace "$defaults_list" file updated + desktop_trace "$defaults_list" file updated fi rm -f "$tmpfile1" "$tmpfile2" @@ -90,15 +90,15 @@ _uninstall_default_mime_handler () # Remove $1 desktop file from the list of default handlers for $@ mime types # in all known system defaults lists. # -uninstall_default_mime_handler () +desktop_uninstall_default_mime_handler () { for f in /usr/share/applications/defaults.list /usr/local/share/applications/defaults.list; do - _uninstall_default_mime_handler "$f" "$@" + desktop_uninstall_default_mime_handler_0 "$f" "$@" done } -trace () +desktop_trace () { echo "$@" } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh index 39ea958d1a427..730137104cf4f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh @@ -23,16 +23,3 @@ unregister_services () fi done } - -file_belongs_to_single_package () -{ - if [ ! -e "$1" ]; then - false - elif [ "$package_type" = rpm ]; then - test `rpm -q --whatprovides "$1" | wc -l` = 1 - elif [ "$package_type" = deb ]; then - test `dpkg -S "$1" | wc -l` = 1 - else - exit 1 - fi -} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst index 16288e4f34894..489c161377297 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst @@ -14,12 +14,13 @@ set -e # the debian-policy package package_type=deb +COMMON_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS case "$1" in install|upgrade) if [ -n "$2" ]; then - true; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL + :; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL fi ;; diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm index a772e309aceae..b5ed447ad69e2 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm @@ -18,6 +18,7 @@ set -e package_type=deb +COMMON_SCRIPTS DESKTOP_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec index 45e5e86126bc6..7918ce6bd884f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec @@ -83,13 +83,15 @@ LAUNCHER_AS_SERVICE_COMMANDS_INSTALL %pre package_type=rpm +COMMON_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS -if [ "$1" = 2 ]; then - true; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL +if [ "$1" -gt 1 ]; then + :; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL fi %preun package_type=rpm +COMMON_SCRIPTS DESKTOP_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS DESKTOP_COMMANDS_UNINSTALL diff --git a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java index 8b37377058c10..64afc95516da8 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -74,6 +75,26 @@ protected Map createImpl() throws IOException { }; } + static void mergeReplacementData(Map target, + Map newValues) { + Objects.requireNonNull(target); + Objects.requireNonNull(newValues); + + for (var kvp : newValues.entrySet()) { + String newValue = kvp.getValue(); + String existingValue = target.putIfAbsent(kvp.getKey(), newValue); + if (existingValue != null) { + if (existingValue.isEmpty()) { + target.replace(kvp.getKey(), newValue); + } else if (!newValue.isEmpty() && !newValue. + equals(existingValue)) { + throw new IllegalArgumentException(String.format( + "Key [%s] value mismatch", kvp.getKey())); + } + } + } + } + protected static String stringifyShellCommands(String... commands) { return stringifyShellCommands(Arrays.asList(commands)); } diff --git a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java index 59f4dacca57b5..1e90c8371b10c 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,12 +65,12 @@ final List requiredPackages() { } @Override - final protected List replacementStringIds() { - return List.of(COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS); + protected List replacementStringIds() { + return REPLACEMENT_STRING_IDS; } @Override - final protected Map createImpl() throws IOException { + protected Map createImpl() throws IOException { Map data = new HashMap<>(); if (launchers.isEmpty()) { @@ -87,11 +87,7 @@ final protected Map createImpl() throws IOException { Collectors.joining(" "))); }; - try { - data.put(SCRIPTS, stringifyTextFile("services_utils.sh")); - } catch (IOException ex) { - throw new RuntimeException(ex); - } + data.put(SCRIPTS, stringifyTextFile("services_utils.sh")); data.put(COMMANDS_INSTALL, strigifier.apply("register_services")); data.put(COMMANDS_UNINSTALL, strigifier.apply("unregister_services")); diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.java index cc7d7b019f553..060c94b12cf8a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/ExecutableRebrander.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -131,18 +131,26 @@ private void rebrandExecutable(Map params, I18N.getString("error.lock-resource"), target)); } + final boolean resourceUnlockedSuccess; try { action.editResource(resourceLock); if (extraActions != null) { - for (UpdateResourceAction extraAction: extraActions) { + for (UpdateResourceAction extraAction : extraActions) { extraAction.editResource(resourceLock); } } } finally { - if (resourceLock != 0) { - unlockResource(resourceLock); + if (resourceLock == 0) { + resourceUnlockedSuccess = true; + } else { + resourceUnlockedSuccess = unlockResource(resourceLock); } } + + if (!resourceUnlockedSuccess) { + throw new RuntimeException(MessageFormat.format(I18N.getString( + "error.unlock-resource"), target)); + } } finally { target.toFile().setReadOnly(); } @@ -250,7 +258,7 @@ private static void versionSwapWrapper(long resourceLock, private static native long lockResource(String executable); - private static native void unlockResource(long resourceLock); + private static native boolean unlockResource(long resourceLock); private static native int iconSwap(long resourceLock, String iconTarget); diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties index 5d2a6d9754124..cdbf04e746460 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties @@ -53,6 +53,7 @@ error.version-swap=Failed to update version information for {0} error.icon-swap=Failed to update icon for {0} error.invalid-envvar=Invalid value of {0} environment variable error.lock-resource=Failed to lock: {0} +error.unlock-resource=Failed to unlock: {0} error.read-wix-l10n-file=Failed to parse {0} file error.extract-culture-from-wix-l10n-file=Failed to read value of culture from {0} file diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties index abae3d6b4d0dc..473dc5f0a9878 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_de.properties @@ -53,6 +53,7 @@ error.version-swap=Versionsinformationen für {0} konnten nicht aktualisiert wer error.icon-swap=Symbol für {0} konnte nicht aktualisiert werden error.invalid-envvar=Ungültiger Wert der {0}-Umgebungsvariable error.lock-resource=Sperren nicht erfolgreich: {0} +error.unlock-resource=Failed to unlock: {0} error.read-wix-l10n-file=Datei {0} konnte nicht geparst werden error.extract-culture-from-wix-l10n-file=Kulturwert konnte nicht aus Datei {0} gelesen werden diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties index 89579e50c02f2..7a6bcf40ef405 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties @@ -53,6 +53,7 @@ error.version-swap={0}のバージョン情報の更新に失敗しました error.icon-swap={0}のアイコンの更新に失敗しました error.invalid-envvar={0}環境変数の値が無効です error.lock-resource=ロックに失敗しました: {0} +error.unlock-resource=Failed to unlock: {0} error.read-wix-l10n-file={0}ファイルの解析に失敗しました error.extract-culture-from-wix-l10n-file={0}ファイルからのカルチャの値の読取りに失敗しました diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties index 3e08edf2e6461..9b0809e09844e 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties @@ -53,6 +53,7 @@ error.version-swap=无法更新 {0} 的版本信息 error.icon-swap=无法更新 {0} 的图标 error.invalid-envvar={0} 环境变量的值无效 error.lock-resource=无法锁定:{0} +error.unlock-resource=Failed to unlock: {0} error.read-wix-l10n-file=无法解析 {0} 文件 error.extract-culture-from-wix-l10n-file=无法从 {0} 文件读取文化值 diff --git a/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp index 288a1d2394c2c..69ed25264e872 100644 --- a/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp +++ b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,19 +39,26 @@ ResourceEditor::FileLock::FileLock(const std::wstring& binaryPath) { ownHandle(true); discard(false); + notifyUnlockFailed(); } ResourceEditor::FileLock::FileLock(HANDLE h): h(h) { ownHandle(false); discard(false); + notifyUnlockFailed(); } ResourceEditor::FileLock::~FileLock() { if (theOwnHandle && !EndUpdateResource(h, theDiscard)) { + if (unlockFailed) { + *unlockFailed = true; + } JP_NO_THROW(JP_THROW(SysError(tstrings::any() << "EndUpdateResource(" << h << ") failed.", EndUpdateResource))); + } else if (unlockFailed) { + *unlockFailed = false; } } diff --git a/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h index 6bf81134f5da6..a41de54d96048 100644 --- a/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h +++ b/src/jdk.jpackage/windows/native/libjpackage/ResourceEditor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,8 +43,14 @@ class ResourceEditor { return h; } - void discard(bool v = true) { + FileLock& discard(bool v = true) { theDiscard = v; + return *this; + } + + FileLock& notifyUnlockFailed(bool* v = 0) { + unlockFailed = v; + return *this; } FileLock& ownHandle(bool v) { @@ -59,6 +65,7 @@ class ResourceEditor { HANDLE h; bool theOwnHandle; bool theDiscard; + bool* unlockFailed; }; public: diff --git a/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp b/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp index b6dcb53b525dd..6317842787e52 100644 --- a/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp +++ b/src/jdk.jpackage/windows/native/libjpackage/jpackage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,16 +56,21 @@ extern "C" { /* * Class: jdk_jpackage_internal_ExecutableRebrander * Method: unlockResource - * Signature: (J;)V + * Signature: (J;)Z */ - JNIEXPORT void JNICALL + JNIEXPORT jboolean JNICALL Java_jdk_jpackage_internal_ExecutableRebrander_unlockResource( JNIEnv *pEnv, jclass c, jlong jResourceLock) { + bool unlockFailed = false; JP_TRY; ResourceEditor::FileLock( - reinterpret_cast(jResourceLock)).ownHandle(true); + reinterpret_cast(jResourceLock)) + .ownHandle(true) + .notifyUnlockFailed(&unlockFailed); JP_CATCH_ALL; + + return unlockFailed ? JNI_FALSE : JNI_TRUE; } /* diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java index 414d58b589d94..e9eaccea32149 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java @@ -56,6 +56,7 @@ import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; @@ -428,7 +429,9 @@ private ExpressionInfo treeToInfo(TreePath tp) { MethodInvocationTree superCall = clazz.getMembers() .stream() - .map(TreeInfo::firstConstructorCall) + .filter(JCMethodDecl.class::isInstance) + .map(JCMethodDecl.class::cast) + .map(TreeInfo::findConstructorCall) .findAny() .get(); TreePath superCallPath diff --git a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java index 329e9445cead6..2d429691b6013 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiDefaultExecutionControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,9 +47,11 @@ import com.sun.jdi.ThreadReference; import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; +import java.io.PrintStream; import java.util.Optional; import java.util.stream.Stream; import jdk.jshell.JShellConsole; +import jdk.jshell.execution.JdiDefaultExecutionControl.JdiStarter.TargetDescription; import jdk.jshell.spi.ExecutionControl; import jdk.jshell.spi.ExecutionEnv; import static jdk.jshell.execution.Util.remoteInputOutput; @@ -94,8 +95,7 @@ public class JdiDefaultExecutionControl extends JdiExecutionControl { * @return the channel * @throws IOException if there are errors in set-up */ - static ExecutionControl create(ExecutionEnv env, String remoteAgent, - boolean isLaunch, String host, int timeout) throws IOException { + static ExecutionControl create(ExecutionEnv env, Map parameters, String remoteAgent, int timeout, JdiStarter starter) throws IOException { try (final ServerSocket listener = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) { // timeout on I/O-socket listener.setSoTimeout(timeout); @@ -107,13 +107,37 @@ static ExecutionControl create(ExecutionEnv env, String remoteAgent, //disable System.console(): List.of("-Djdk.console=" + consoleModule).stream()) .toList(); + ExecutionEnv augmentedEnv = new ExecutionEnv() { + @Override + public InputStream userIn() { + return env.userIn(); + } + + @Override + public PrintStream userOut() { + return env.userOut(); + } + + @Override + public PrintStream userErr() { + return env.userErr(); + } + + @Override + public List extraRemoteVMOptions() { + return augmentedremoteVMOptions; + } + + @Override + public void closeDown() { + env.closeDown(); + } + }; // Set-up the JDI connection - JdiInitiator jdii = new JdiInitiator(port, - augmentedremoteVMOptions, remoteAgent, isLaunch, host, - timeout, Collections.emptyMap()); - VirtualMachine vm = jdii.vm(); - Process process = jdii.process(); + TargetDescription target = starter.start(augmentedEnv, parameters, port); + VirtualMachine vm = target.vm(); + Process process = target.process(); List> deathListeners = new ArrayList<>(); Util.detectJdiExitEvent(vm, s -> { @@ -294,4 +318,31 @@ private static void debug(Throwable ex, String where) { // Reserved for future logging } + /** + * Start an external process where the user's snippets can be run. + * + * @since 22 + */ + public interface JdiStarter { + /** + * Start the external process based on the given parameters. The external + * process should connect to the given {@code port} to communicate with the + * driving instance of JShell. + * + * @param env the execution context + * @param parameters additional execution parameters + * @param port the port to which the remote process should connect + * @return a description of the started external process + * @throws RuntimeException if the process cannot be started + * @throws Error if the process cannot be started + */ + public TargetDescription start(ExecutionEnv env, Map parameters, int port); + + /** + * The description of a started external process. + * @param vm the JDI's {@code VirtualMachine} + * @param process the external {@code Process} + */ + public record TargetDescription(VirtualMachine vm, Process process) {} + } } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControlProvider.java b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControlProvider.java index 7d3db170ec4ce..120138dfbf4a7 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControlProvider.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControlProvider.java @@ -26,9 +26,11 @@ package jdk.jshell.execution; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import jdk.jshell.execution.JdiDefaultExecutionControl.JdiStarter; import jdk.jshell.spi.ExecutionControl; import jdk.jshell.spi.ExecutionControlProvider; import jdk.jshell.spi.ExecutionEnv; @@ -66,6 +68,8 @@ public class JdiExecutionControlProvider implements ExecutionControlProvider { */ private static final int DEFAULT_TIMEOUT = 5000; + private final JdiStarter starter; + /** * Create an instance. An instance can be used to * {@linkplain #generate generate} an {@link ExecutionControl} instance @@ -73,6 +77,37 @@ public class JdiExecutionControlProvider implements ExecutionControlProvider { * process. */ public JdiExecutionControlProvider() { + this(new JdiStarter() { + @Override + public TargetDescription start(ExecutionEnv env, Map parameters, int port) { + String remoteAgent = parameters.get(PARAM_REMOTE_AGENT); + int timeout = Integer.parseUnsignedInt( + parameters.get(PARAM_TIMEOUT)); + String host = parameters.get(PARAM_HOST_NAME); + String sIsLaunch = parameters.get(PARAM_LAUNCH) + .toLowerCase(Locale.ROOT); + boolean isLaunch = sIsLaunch.length() > 0 + && ("true".startsWith(sIsLaunch) || "yes".startsWith(sIsLaunch)); + + JdiInitiator jdii = new JdiInitiator(port, + env.extraRemoteVMOptions(), remoteAgent, isLaunch, host, + timeout, Collections.emptyMap()); + return new TargetDescription(jdii.vm(), jdii.process()); + } + }); + } + + /** + * Create an instance. An instance can be used to + * {@linkplain #generate generate} an {@link ExecutionControl} instance + * that uses the Java Debug Interface as part of the control of a remote + * process. The provided {@code start} will be used to start the remote process. + * + * @param starter starter that will create the remote process + * @since 22 + */ + public JdiExecutionControlProvider(JdiStarter starter) { + this.starter = starter; } /** @@ -142,14 +177,14 @@ public ExecutionControl generate(ExecutionEnv env, Map parameter if (parameters == null) { parameters = dp; } - String remoteAgent = parameters.getOrDefault(PARAM_REMOTE_AGENT, dp.get(PARAM_REMOTE_AGENT)); + parameters = new HashMap<>(parameters); + String remoteAgent = parameters.computeIfAbsent(PARAM_REMOTE_AGENT, x -> dp.get(PARAM_REMOTE_AGENT)); int timeout = Integer.parseUnsignedInt( - parameters.getOrDefault(PARAM_TIMEOUT, dp.get(PARAM_TIMEOUT))); - String host = parameters.getOrDefault(PARAM_HOST_NAME, dp.get(PARAM_HOST_NAME)); - String sIsLaunch = parameters.getOrDefault(PARAM_LAUNCH, dp.get(PARAM_LAUNCH)).toLowerCase(Locale.ROOT); - boolean isLaunch = sIsLaunch.length() > 0 - && ("true".startsWith(sIsLaunch) || "yes".startsWith(sIsLaunch)); - return JdiDefaultExecutionControl.create(env, remoteAgent, isLaunch, host, timeout); + parameters.computeIfAbsent(PARAM_TIMEOUT, x -> dp.get(PARAM_TIMEOUT))); + parameters.putIfAbsent(PARAM_HOST_NAME, dp.get(PARAM_HOST_NAME)); + parameters.putIfAbsent(PARAM_LAUNCH, dp.get(PARAM_LAUNCH)); + + return JdiDefaultExecutionControl.create(env, parameters, remoteAgent, timeout, starter); } } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java index 2dad37186edbf..fd908745d81d8 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/execution/JdiInitiator.java @@ -153,14 +153,55 @@ private VirtualMachine launchTarget() { */ private VirtualMachine listenTarget(int port, List remoteVMOptions) { ListeningConnector listener = (ListeningConnector) connector; + try { + String addr; + + try { + // Start listening, get the JDI connection address + addr = listener.startListening(connectorArgs); + debug("Listening at address: " + addr); + } catch (Throwable t) { + throw reportLaunchFail(t, "listen"); + } + + runListenProcess(addr, port, remoteVMOptions, process -> { + // Accept the connection from the remote agent + vm = timedVirtualMachineCreation(() -> listener.accept(connectorArgs), + () -> process.waitFor()); + try { + listener.stopListening(connectorArgs); + } catch (IOException | IllegalConnectorArgumentsException ex) { + // ignore + } + }); + return vm; + } catch (Throwable ex) { + try { + listener.stopListening(connectorArgs); + } catch (IOException | IllegalConnectorArgumentsException iex) { + // ignore + } + throw ex; + } + } + + /** + * Create a process that will attach to the given address. + * @param jdiAddress address on which a JDI server is waiting for a connection + * @param jshellControlPort the port which the remote agent should connect to + * @param remoteVMOptions VM options for the remote agent VM + * @param setupVM a callback that should be called then the remote agent process + * is created. The callback will setup the JDI's {@code VirtualMachine}. + * @since 22 + */ + protected void runListenProcess(String jdiAddress, + int jshellControlPort, + List remoteVMOptions, + ProcessStarted setupVM) { // Files to collection to output of a start-up failure File crashErrorFile = createTempFile("error"); File crashOutputFile = createTempFile("output"); try { - // Start listening, get the JDI connection address - String addr = listener.startListening(connectorArgs); - debug("Listening at address: " + addr); - // Launch the RemoteAgent requesting a connection on that address String javaHome = System.getProperty("java.home"); List args = new ArrayList<>(); @@ -168,35 +209,23 @@ private VirtualMachine listenTarget(int port, List remoteVMOptions) { ? "java" : javaHome + File.separator + "bin" + File.separator + "java"); args.add("-agentlib:jdwp=transport=" + connector.transport().name() + - ",address=" + addr); + ",address=" + jdiAddress); args.addAll(remoteVMOptions); args.add(remoteAgent); - args.add("" + port); + args.add("" + jshellControlPort); ProcessBuilder pb = new ProcessBuilder(args); pb.redirectError(crashErrorFile); pb.redirectOutput(crashOutputFile); process = pb.start(); - // Accept the connection from the remote agent - vm = timedVirtualMachineCreation(() -> listener.accept(connectorArgs), - () -> process.waitFor()); - try { - listener.stopListening(connectorArgs); - } catch (IOException | IllegalConnectorArgumentsException ex) { - // ignore - } + setupVM.processStarted(process); + crashErrorFile.delete(); crashOutputFile.delete(); - return vm; } catch (Throwable ex) { if (process != null) { process.destroyForcibly(); } - try { - listener.stopListening(connectorArgs); - } catch (IOException | IllegalConnectorArgumentsException iex) { - // ignore - } String text = readFile(crashErrorFile) + readFile(crashOutputFile); crashErrorFile.delete(); crashOutputFile.delete(); @@ -328,4 +357,19 @@ private void debug(Throwable ex, String where) { // Reserved for future logging } + /** + * Callback that should invoked when the remote process is invoked. + * + * @since 22 + */ + protected interface ProcessStarted { + /** + * Notify the process has been started. + * + * @param p the {@code Process} + * @throws Throwable thrown when anything goes wrong. + */ + public void processStarted(Process p) throws Throwable; + } + } diff --git a/src/utils/hsdis/capstone/hsdis-capstone.c b/src/utils/hsdis/capstone/hsdis-capstone.c index ef65a01efcaf0..410b88eb2db46 100644 --- a/src/utils/hsdis/capstone/hsdis-capstone.c +++ b/src/utils/hsdis/capstone/hsdis-capstone.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -52,6 +52,12 @@ #include #include +/* Undefine macro to avoid generating invalid C code. + Capstone refactored cs_detail for AArch64 architecture + from `cs_arm64 arm64` to `cs_aarch64 aarch64` + and that causes invalid macro expansion. +*/ +#undef aarch64 #include #include "hsdis.h" diff --git a/test/failure_handler/src/share/conf/windows.properties b/test/failure_handler/src/share/conf/windows.properties index a51e40bcd0df7..e22c1a523d8a3 100644 --- a/test/failure_handler/src/share/conf/windows.properties +++ b/test/failure_handler/src/share/conf/windows.properties @@ -57,8 +57,8 @@ native.stack.args=-c "~*kP n;qd" -p %p native.stack.params.repeat=6 native.core.app=cdb -native.core.args=-c ".dump /f core.%p;qd" -p %p -native.core.params.timeout=3600000 +native.core.args=-c ".dump /mA core.%p;qd" -p %p +native.core.params.timeout=600000 ################################################################################ # environment info to gather ################################################################################ diff --git a/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp b/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp index 38e7b15202428..e07e073974fce 100644 --- a/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp +++ b/test/hotspot/gtest/gc/shared/test_bufferNodeAllocator.cpp @@ -185,16 +185,21 @@ class BufferNode::TestSupport::ProcessorThread : public JavaTestThread { }; static void run_test(BufferNode::Allocator* allocator, CompletedList* cbl) { - const uint nthreads = 4; - const uint milliseconds_to_run = 1000; + + // deallocation is slower than allocation, so lets create + // more deallocation threads to prevent too large buildup of + // free nodes (footprint) + constexpr uint num_allocator_threads = 4; + constexpr uint num_processor_threads = 6; + constexpr uint milliseconds_to_run = 1000; Semaphore post; volatile size_t total_allocations = 0; volatile bool allocator_running = true; volatile bool processor_running = true; - ProcessorThread* proc_threads[nthreads] = {}; - for (uint i = 0; i < nthreads; ++i) { + ProcessorThread* proc_threads[num_processor_threads] = {}; + for (uint i = 0; i < num_processor_threads; ++i) { proc_threads[i] = new ProcessorThread(&post, allocator, cbl, @@ -202,8 +207,8 @@ static void run_test(BufferNode::Allocator* allocator, CompletedList* cbl) { proc_threads[i]->doit(); } - AllocatorThread* alloc_threads[nthreads] = {}; - for (uint i = 0; i < nthreads; ++i) { + AllocatorThread* alloc_threads[num_allocator_threads] = {}; + for (uint i = 0; i < num_allocator_threads; ++i) { alloc_threads[i] = new AllocatorThread(&post, allocator, cbl, @@ -219,12 +224,12 @@ static void run_test(BufferNode::Allocator* allocator, CompletedList* cbl) { this_thread->sleep(milliseconds_to_run); } Atomic::release_store(&allocator_running, false); - for (uint i = 0; i < nthreads; ++i) { + for (uint i = 0; i < num_allocator_threads; ++i) { ThreadInVMfromNative invm(this_thread); post.wait_with_safepoint_check(this_thread); } Atomic::release_store(&processor_running, false); - for (uint i = 0; i < nthreads; ++i) { + for (uint i = 0; i < num_processor_threads; ++i) { ThreadInVMfromNative invm(this_thread); post.wait_with_safepoint_check(this_thread); } @@ -234,7 +239,7 @@ static void run_test(BufferNode::Allocator* allocator, CompletedList* cbl) { } TEST_VM(BufferNodeAllocatorTest, stress_free_list_allocator) { - const size_t buffer_capacity = 1024; + const size_t buffer_capacity = DEFAULT_CACHE_LINE_SIZE / sizeof(void*); BufferNode::Allocator allocator("Test Allocator", buffer_capacity); CompletedList completed; run_test(&allocator, &completed); diff --git a/test/hotspot/gtest/logging/test_asynclog.cpp b/test/hotspot/gtest/logging/test_asynclog.cpp index 89a1d6fef8a94..8573d93cdf1ee 100644 --- a/test/hotspot/gtest/logging/test_asynclog.cpp +++ b/test/hotspot/gtest/logging/test_asynclog.cpp @@ -69,21 +69,22 @@ LOG_LEVEL_LIST log_debug(logging)("log_debug-test"); } + // Caveat: BufferUpdater is not MT-safe. We use it only for testing. + // We would observe missing loglines if we interleaved buffers. + // Emit all logs between constructor and destructor of BufferUpdater. void test_asynclog_drop_messages() { - auto writer = AsyncLogWriter::instance(); - if (writer != nullptr) { - const size_t sz = 2000; + const size_t sz = 2000; - // shrink async buffer. - AsyncLogWriter::BufferUpdater saver(1024); - LogMessage(logging) lm; + // shrink async buffer. + AsyncLogWriter::BufferUpdater saver(1024); + test_asynclog_ls(); // roughly 200 bytes. + LogMessage(logging) lm; - // write more messages than its capacity in burst - for (size_t i = 0; i < sz; ++i) { - lm.debug("a lot of log..."); - } - lm.flush(); + // write more messages than its capacity in burst + for (size_t i = 0; i < sz; ++i) { + lm.debug("a lot of log..."); } + lm.flush(); } // stdout/stderr support @@ -93,8 +94,7 @@ LOG_LEVEL_LIST if (f != NULL) { size_t sz = output.size(); size_t written = fwrite(output.c_str(), sizeof(char), output.size(), f); - // at least see "header" - return fclose(f) == 0 && sz == written && sz >= 6; + return fclose(f) == 0 && sz == written; } return false; @@ -244,67 +244,69 @@ TEST_VM_F(AsyncLogTest, logBuffer) { } TEST_VM_F(AsyncLogTest, droppingMessage) { + if (AsyncLogWriter::instance() == nullptr) { + return; + } + set_log_config(TestLogFileName, "logging=debug"); test_asynclog_drop_messages(); - - AsyncLogWriter::flush(); - if (AsyncLogWriter::instance() != nullptr) { - EXPECT_TRUE(file_contains_substring(TestLogFileName, "messages dropped due to async logging")); - } + EXPECT_TRUE(file_contains_substring(TestLogFileName, "messages dropped due to async logging")); } TEST_VM_F(AsyncLogTest, stdoutOutput) { testing::internal::CaptureStdout(); - fprintf(stdout, "header"); if (!set_log_config("stdout", "logging=debug")) { return; } - test_asynclog_ls(); - test_asynclog_drop_messages(); + bool async = AsyncLogWriter::instance() != nullptr; + if (async) { + test_asynclog_drop_messages(); + AsyncLogWriter::flush(); + } else { + test_asynclog_ls(); + } - AsyncLogWriter::flush(); fflush(nullptr); - if (!write_to_file(testing::internal::GetCapturedStdout())) { return; } - EXPECT_TRUE(file_contains_substring(TestLogFileName, "header")); EXPECT_TRUE(file_contains_substring(TestLogFileName, "LogStreamWithAsyncLogImpl")); EXPECT_TRUE(file_contains_substring(TestLogFileName, "logStream msg1-msg2-msg3")); EXPECT_TRUE(file_contains_substring(TestLogFileName, "logStream newline")); - if (AsyncLogWriter::instance() != nullptr) { + if (async) { EXPECT_TRUE(file_contains_substring(TestLogFileName, "messages dropped due to async logging")); } } TEST_VM_F(AsyncLogTest, stderrOutput) { testing::internal::CaptureStderr(); - fprintf(stderr, "header"); if (!set_log_config("stderr", "logging=debug")) { return; } - test_asynclog_ls(); - test_asynclog_drop_messages(); + bool async = AsyncLogWriter::instance() != nullptr; + if (async) { + test_asynclog_drop_messages(); + AsyncLogWriter::flush(); + } else { + test_asynclog_ls(); + } - AsyncLogWriter::flush(); fflush(nullptr); - if (!write_to_file(testing::internal::GetCapturedStderr())) { return; } - EXPECT_TRUE(file_contains_substring(TestLogFileName, "header")); EXPECT_TRUE(file_contains_substring(TestLogFileName, "LogStreamWithAsyncLogImpl")); EXPECT_TRUE(file_contains_substring(TestLogFileName, "logStream msg1-msg2-msg3")); EXPECT_TRUE(file_contains_substring(TestLogFileName, "logStream newline")); - if (AsyncLogWriter::instance() != nullptr) { + if (async) { EXPECT_TRUE(file_contains_substring(TestLogFileName, "messages dropped due to async logging")); } } diff --git a/test/hotspot/gtest/runtime/test_atomic.cpp b/test/hotspot/gtest/runtime/test_atomic.cpp index 744714c6f7fa5..b99ec0f984dde 100644 --- a/test/hotspot/gtest/runtime/test_atomic.cpp +++ b/test/hotspot/gtest/runtime/test_atomic.cpp @@ -60,9 +60,6 @@ TEST_VM(AtomicAddTest, int32) { } TEST_VM(AtomicAddTest, int64) { - // Check if 64-bit atomics are available on the machine. - if (!VM_Version::supports_cx8()) return; - using Support = AtomicAddTestSupport; Support().test_add(); Support().test_fetch_add(); @@ -109,9 +106,6 @@ TEST_VM(AtomicXchgTest, int32) { } TEST_VM(AtomicXchgTest, int64) { - // Check if 64-bit atomics are available on the machine. - if (!VM_Version::supports_cx8()) return; - using Support = AtomicXchgTestSupport; Support().test(); } @@ -349,15 +343,9 @@ TEST_VM(AtomicBitopsTest, uint32) { } TEST_VM(AtomicBitopsTest, int64) { - // Check if 64-bit atomics are available on the machine. - if (!VM_Version::supports_cx8()) return; - AtomicBitopsTestSupport()(); } TEST_VM(AtomicBitopsTest, uint64) { - // Check if 64-bit atomics are available on the machine. - if (!VM_Version::supports_cx8()) return; - AtomicBitopsTestSupport()(); } diff --git a/test/hotspot/gtest/utilities/test_growableArray.cpp b/test/hotspot/gtest/utilities/test_growableArray.cpp index cb70dfb57b393..cd269e092121e 100644 --- a/test/hotspot/gtest/utilities/test_growableArray.cpp +++ b/test/hotspot/gtest/utilities/test_growableArray.cpp @@ -601,3 +601,65 @@ TEST(GrowableArrayCHeap, sanity) { delete a; } } + +TEST(GrowableArrayCHeap, find_if) { + struct Element { + int value; + }; + GrowableArrayCHeap array; + array.push({1}); + array.push({2}); + array.push({3}); + + { + int index = array.find_if([&](const Element& elem) { + return elem.value == 1; + }); + ASSERT_EQ(index, 0); + } + + { + int index = array.find_if([&](const Element& elem) { + return elem.value > 1; + }); + ASSERT_EQ(index, 1); + } + + { + int index = array.find_if([&](const Element& elem) { + return elem.value == 4; + }); + ASSERT_EQ(index, -1); + } +} + +TEST(GrowableArrayCHeap, find_from_end_if) { + struct Element { + int value; + }; + GrowableArrayCHeap array; + array.push({1}); + array.push({2}); + array.push({3}); + + { + int index = array.find_from_end_if([&](const Element& elem) { + return elem.value == 1; + }); + ASSERT_EQ(index, 0); + } + + { + int index = array.find_from_end_if([&](const Element& elem) { + return elem.value > 1; + }); + ASSERT_EQ(index, 2); + } + + { + int index = array.find_from_end_if([&](const Element& elem) { + return elem.value == 4; + }); + ASSERT_EQ(index, -1); + } +} diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 01a47f51898d9..726c78528a3f7 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -67,6 +67,7 @@ compiler/c2/Test8004741.java 8235801 generic-all compiler/codecache/jmx/PoolsIndependenceTest.java 8264632 macosx-all +compiler/vectorapi/reshape/TestVectorReinterpret.java 8320897 aix-ppc64,linux-ppc64le compiler/vectorapi/VectorLogicalOpIdentityTest.java 8302459 linux-x64,windows-x64 compiler/jvmci/TestUncaughtErrorInCompileMethod.java 8309073 generic-all @@ -86,8 +87,8 @@ gc/g1/logging/TestG1LoggingFailure.java 8169634 generic-all gc/g1/humongousObjects/TestHeapCounters.java 8178918 generic-all gc/stress/gclocker/TestExcessGCLockerCollections.java 8229120 generic-all gc/stress/gclocker/TestGCLockerWithParallel.java 8180622 generic-all -gc/stress/gclocker/TestGCLockerWithG1.java 8180622 generic-all -gc/stress/TestJNIBlockFullGC/TestJNIBlockFullGC.java 8192647 generic-all +gc/stress/gclocker/TestGCLockerWithSerial.java 8180622 generic-all +gc/stress/gclocker/TestGCLockerWithShenandoah.java 8180622 generic-all gc/stress/TestStressG1Humongous.java 8286554 windows-x64 ############################################################################# @@ -134,7 +135,7 @@ serviceability/sa/ClhsdbPstack.java#core 8267433 macosx-x64 serviceability/sa/TestJmapCore.java 8267433 macosx-x64 serviceability/sa/TestJmapCoreMetaspace.java 8267433 macosx-x64 -serviceability/attach/ConcAttachTest.java 8290043 linux-all +serviceability/attach/ConcAttachTest.java 8290043,8318866 linux-all,macosx-all serviceability/jvmti/stress/StackTrace/NotSuspended/GetStackTraceNotSuspendedStressTest.java 8315980 linux-all,windows-x64 diff --git a/test/hotspot/jtreg/compiler/c2/TestExHandlerTrap.java b/test/hotspot/jtreg/compiler/c2/TestExHandlerTrap.java new file mode 100644 index 0000000000000..611f3da2bba9e --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestExHandlerTrap.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2; + +import java.io.PrintStream; +import java.util.List; +import java.util.ArrayList; + +/* + * @test id=default_config + * @bug 8267532 + * @summary Test whether trap in place of pruned exception handler block works + * + * @run main/othervm + * -Xbatch + * -Xlog:deoptimization=trace + * -XX:CompileCommand=PrintCompilation,compiler.c2.TestExHandlerTrap::payload + * -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::payload + * -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::maybeThrow + * compiler.c2.TestExHandlerTrap + */ + +/* + * @test id=no_profiling + * @bug 8267532 + * @summary basic smoke test for disabled ex. handler profiling + * + * @run main/othervm + * -Xbatch + * -Xlog:deoptimization=trace + * -XX:CompileCommand=PrintCompilation,compiler.c2.TestExHandlerTrap::payload + * -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::payload + * -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::maybeThrow + * -XX:-ProfileExceptionHandlers + * compiler.c2.TestExHandlerTrap + */ + +/* + * @test id=stress + * @bug 8267532 + * @summary basic smoke test for stressing ex. handler pruning + * @requires vm.debug + * + * @run main/othervm + * -Xbatch + * -Xlog:deoptimization=trace + * -XX:CompileCommand=PrintCompilation,compiler.c2.TestExHandlerTrap::payload + * -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::payload + * -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::maybeThrow + * -XX:+StressPrunedExceptionHandlers + * compiler.c2.TestExHandlerTrap + */ + +public class TestExHandlerTrap { + + private static final String EX_MESSAGE = "Testing trap"; + + public static void main(String[] args) throws Throwable { + // warmup, compile payload + for (int i = 0; i < 20_000; i++) { + payload(false); + } + + try { + // trigger uncommon trap in pruned catch block + payload(true); + } catch (IllegalStateException e) { + if (!e.getMessage().equals(EX_MESSAGE)) { + throw e; + } + } + + // continue for a bit, to see if anything breaks + for (int i = 0; i < 1_000; i++) { + payload(false); + } + } + + public static void payload(boolean shouldThrow) { + doIt(shouldThrow); // mix in some inlining + } + + public static void doIt(boolean shouldThrow) { + PrintStream err = System.err; + try (ConfinedScope r = new ConfinedScope()) { + r.addCloseAction(dummy); + maybeThrow(shouldThrow); // out of line to prevent 'payload' from being deoptimized by unstable if + } catch (IllegalArgumentException e) { + // some side effects to see whether deopt is successful + err.println("Exception message: " + e.getMessage()); + err.println("shouldThrow: " + shouldThrow); + } + } + + private static void maybeThrow(boolean shouldThrow) { + if (shouldThrow) { + throw new IllegalStateException(EX_MESSAGE); + } + } + + static final Runnable dummy = () -> {}; + + static class ConfinedScope implements AutoCloseable { + final Thread owner; + boolean closed; + final List resources = new ArrayList<>(); + + private void checkState() { + if (closed) { + throw new AssertionError("Closed"); + } else if (owner != Thread.currentThread()) { + throw new AssertionError("Wrong thread"); + } + } + + ConfinedScope() { + this.owner = Thread.currentThread(); + } + + void addCloseAction(Runnable runnable) { + checkState(); + resources.add(runnable); + } + + public void close() { + checkState(); + closed = true; + for (Runnable r : resources) { + r.run(); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/RShiftINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/RShiftINodeIdealizationTests.java new file mode 100644 index 0000000000000..e6501d3728a64 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/RShiftINodeIdealizationTests.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2.irTests; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; + +/* + * @test + * @bug 8320330 + * @summary Test that RShiftINode optimizations are being performed as expected. + * @library /test/lib / + * @run driver compiler.c2.irTests.RShiftINodeIdealizationTests + */ +public class RShiftINodeIdealizationTests { + public static void main(String[] args) { + TestFramework.run(); + } + + @Run(test = { "test1", "test2", "test3", "test4" }) + public void runMethod() { + int a = RunInfo.getRandom().nextInt(); + int b = RunInfo.getRandom().nextInt(); + int c = RunInfo.getRandom().nextInt(); + int d = RunInfo.getRandom().nextInt(); + + int min = Integer.MIN_VALUE; + int max = Integer.MAX_VALUE; + + assertResult(a, 0); + assertResult(a, b); + assertResult(b, a); + assertResult(c, d); + assertResult(d, c); + assertResult(min, max); + assertResult(max, min); + assertResult(min, min); + assertResult(max, max); + } + + @DontCompile + public void assertResult(int x, int y) { + Asserts.assertEQ((x >> y) >= 0 ? 0 : 1, test1(x, y)); + Asserts.assertEQ(((x & 127) >> y) >= 0 ? 0 : 1, test2(x, y)); + Asserts.assertEQ(((-(x & 127) - 1) >> y) >= 0 ? 0 : 1, test3(x, y)); + Asserts.assertEQ((x >> 30) > 4 ? 0 : 1, test4(x, y)); + } + + @Test + @IR(counts = { IRNode.RSHIFT, "1" }) + public int test1(int x, int y) { + return (x >> y) >= 0 ? 0 : 1; + } + + @Test + @IR(failOn = { IRNode.RSHIFT }) + public int test2(int x, int y) { + return ((x & 127) >> y) >= 0 ? 0 : 1; + } + + @Test + @IR(failOn = { IRNode.RSHIFT }) + public int test3(int x, int y) { + return ((-(x & 127) - 1) >> y) >= 0 ? 0 : 1; + } + + @Test + @IR(failOn = { IRNode.RSHIFT }) + public int test4(int x, int y) { + return (x >> 30) > 4 ? 0 : 1; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/RShiftLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/RShiftLNodeIdealizationTests.java new file mode 100644 index 0000000000000..0a8d2bde0b185 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/RShiftLNodeIdealizationTests.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2.irTests; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; + +/* + * @test + * @bug 8320330 + * @summary Test that RShiftLNode optimizations are being performed as expected. + * @library /test/lib / + * @run driver compiler.c2.irTests.RShiftLNodeIdealizationTests + */ +public class RShiftLNodeIdealizationTests { + public static void main(String[] args) { + TestFramework.run(); + } + + @Run(test = { "test1", "test2", "test3", "test4" }) + public void runMethod() { + long a = RunInfo.getRandom().nextLong(); + long b = RunInfo.getRandom().nextLong(); + long c = RunInfo.getRandom().nextLong(); + long d = RunInfo.getRandom().nextLong(); + + long min = Long.MIN_VALUE; + long max = Long.MAX_VALUE; + + assertResult(a, 0); + assertResult(a, b); + assertResult(b, a); + assertResult(c, d); + assertResult(d, c); + assertResult(min, max); + assertResult(max, min); + assertResult(min, min); + assertResult(max, max); + } + + @DontCompile + public void assertResult(long x, long y) { + Asserts.assertEQ((x >> y) >= 0 ? 0L : 1L, test1(x, y)); + Asserts.assertEQ(((x & 127) >> y) >= 0 ? 0L : 1L, test2(x, y)); + Asserts.assertEQ(((-(x & 127) - 1) >> y) >= 0 ? 0L : 1L, test3(x, y)); + Asserts.assertEQ((x >> 62) > 4 ? 0L : 1L, test4(x, y)); + } + + @Test + @IR(counts = { IRNode.RSHIFT, "1" }) + public long test1(long x, long y) { + return (x >> y) >= 0 ? 0 : 1; + } + + @Test + @IR(failOn = { IRNode.RSHIFT }) + public long test2(long x, long y) { + return ((x & 127) >> y) >= 0 ? 0L : 1L; + } + + @Test + @IR(failOn = { IRNode.RSHIFT }) + public long test3(long x, long y) { + return ((-(x & 127) - 1) >> y) >= 0 ? 0L : 1L; + } + + @Test + @IR(failOn = { IRNode.RSHIFT }) + public long test4(long x, long y) { + return (x >> 62) > 4 ? 0L : 1L; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java b/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java new file mode 100644 index 0000000000000..eacaa9fca8583 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.c2.irTests; + +import compiler.lib.ir_framework.*; + +/* + * @test + * @bug 8267532 + * @summary check that uncommon trap is generated for unhandled catch block + * @library /test/lib / + * @run driver compiler.c2.irTests.TestPrunedExHandler + */ + +public class TestPrunedExHandler { + public static void main(String[] args) { + TestFramework.runWithFlags( + "-XX:+TieredCompilation", // we only profile in tier 3 + "-XX:CompileCommand=inline,compiler.c2.irTests.TestPrunedExHandler::inlinee", + "-XX:CompileCommand=dontinline,compiler.c2.irTests.TestPrunedExHandler::outOfLine"); + } + + @Test + @IR(counts = {IRNode.UNREACHED_TRAP, "1"}) + public static void testTrap() { + try { + outOfLine(false); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + private static void outOfLine(boolean shouldThrow) { + if (shouldThrow) { + throw new IllegalStateException(); + } + } + + @Test + @IR(counts = {IRNode.UNREACHED_TRAP, "0"}) + public static void testNoTrap(boolean shouldThrow) { + try { + outOfLine(shouldThrow); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + @Run(test = "testNoTrap", mode = RunMode.STANDALONE) + public static void runNoTrap(RunInfo info) { + for (int i = 0; i < 2_000; i++) { // tier 3 + testNoTrap(false); + } + + TestFramework.assertCompiledAtLevel(info.getTest(), CompLevel.C1_FULL_PROFILE); + + testNoTrap(true); // mark ex handler as entered + + for (int i = 0; i < 20_000; i++) { // tier 4 + testNoTrap(false); // should have no trap + } + } + + @Test + @IR(counts = {IRNode.UNREACHED_TRAP, "0"}) + public static void testNoTrapAfterDeopt(boolean shouldThrow) { + try { + outOfLine(shouldThrow); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + @Run(test = "testNoTrapAfterDeopt", mode = RunMode.STANDALONE) + public static void runNoTrapAfterDeopt(RunInfo info) { + for (int i = 0; i < 20_000; i++) { // tier 4 + testNoTrapAfterDeopt(false); + } + + TestFramework.assertCompiledByC2(info.getTest()); + + testNoTrapAfterDeopt(true); // deopt + mark ex handler as entered + + TestFramework.assertDeoptimizedByC2(info.getTest()); + + for (int i = 0; i < 20_000; i++) { // tier 4 again + testNoTrapAfterDeopt(false); // should have no trap + } + } + + @Test + @IR(counts = {IRNode.UNREACHED_TRAP, "0"}) + public static void testNoTrapAfterDeoptInlined(boolean shouldThrow) { + // check that we handle exception thrown in inlinee, caught in caller. + // C2 handles exception dispatch differently for those cases + try { + inlinee(shouldThrow); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + private static void inlinee(boolean shouldThrow) { + outOfLine(shouldThrow); + } + + @Run(test = "testNoTrapAfterDeoptInlined", mode = RunMode.STANDALONE) + public static void runNoTrapAfterDeoptInlined(RunInfo info) { + for (int i = 0; i < 20_000; i++) { // tier 4 + testNoTrapAfterDeoptInlined(false); + } + + TestFramework.assertCompiledByC2(info.getTest()); + + testNoTrapAfterDeoptInlined(true); // deopt + mark ex handler as entered + + TestFramework.assertDeoptimizedByC2(info.getTest()); + + for (int i = 0; i < 20_000; i++) { // tier 4 again + testNoTrapAfterDeoptInlined(false); // should have no trap + } + } + + @Test + @IR(counts = {IRNode.UNREACHED_TRAP, "1"}) + public static void testThrowBeforeProfiling(boolean shouldThrow) { + try { + outOfLine(shouldThrow); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + @Run(test = "testThrowBeforeProfiling", mode = RunMode.STANDALONE) + public static void runThrowBeforeProfiling(RunInfo info) { + testThrowBeforeProfiling(true); + // this exception should not be profiled, as MDO has not been created yet + + for (int i = 0; i < 20_000; i++) { // tier 4 + testThrowBeforeProfiling(false); + } + // should have trap + } + + @Test + @IR(counts = {IRNode.UNREACHED_TRAP, "0"}) + public static void testInterpreterProfiling(boolean takeBranch, boolean shouldThrow) { + if (takeBranch) { + System.out.println("testInterpreterProfiling: branch taken"); + } + + try { + outOfLine(shouldThrow); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + @Run(test = "testInterpreterProfiling", mode = RunMode.STANDALONE) + public static void runInterpreterProfiling(RunInfo info) { + for (int i = 0; i < 20_000; i++) { // tier 4 + testInterpreterProfiling(false, false); + } + TestFramework.assertCompiledByC2(info.getTest()); + // should have no trap at this point + + testInterpreterProfiling(true, false); // take branch -> deopt due to unstable if + TestFramework.assertDeoptimizedByC2(info.getTest()); + + // continue in the interpreter: + testInterpreterProfiling(false, false); + // throw exception in the interpreter, test interpreter profiling: + testInterpreterProfiling(false, true); + + for (int i = 0; i < 20_000; i++) { // tier 4 again + testInterpreterProfiling(false, false); + } + // should have no trap + } + +} diff --git a/test/hotspot/jtreg/compiler/exceptions/TestDeoptExceptionState.java b/test/hotspot/jtreg/compiler/exceptions/TestDeoptExceptionState.java new file mode 100644 index 0000000000000..3e8d450e29975 --- /dev/null +++ b/test/hotspot/jtreg/compiler/exceptions/TestDeoptExceptionState.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** +* @test +* @bug 8316422 +* @summary Test exception state used for deoptimization. +* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyStack -XX:+DeoptimizeALot +* -Xcomp -XX:TieredStopAtLevel=1 -XX:CompileOnly=compiler.exceptions.TestDeoptExceptionState::test +* compiler.exceptions.TestDeoptExceptionState +*/ + +package compiler.exceptions; + +public class TestDeoptExceptionState { + private static int res = 0; + + public static void main(String args[]) { + int x = 42; + int y = 1 + test(); + System.out.println("Foo " + x + " " + y); + } + + public static int test() { + int x = 42; + int y = 1 + test1(); + return x + y; + } + + public static int test1() { + for (int i = 0; i < 100; i++) { + try { + divZero(); + } catch (ArithmeticException ea) { + // Expected + } + } + return 1; + } + + public static void divZero() { + res += div(0, 0); + } + + public static long div(long dividend, long divisor) { + return dividend / divisor; + } +} diff --git a/test/hotspot/jtreg/compiler/intrinsics/chacha/TestChaCha20.java b/test/hotspot/jtreg/compiler/intrinsics/chacha/TestChaCha20.java index b94e05fc1ffe9..ff5d931ec5131 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/chacha/TestChaCha20.java +++ b/test/hotspot/jtreg/compiler/intrinsics/chacha/TestChaCha20.java @@ -42,6 +42,10 @@ * @bug 8247645 * @summary ChaCha20 Intrinsics * @library /test/lib + * @requires (vm.cpu.features ~= ".*avx512.*" | vm.cpu.features ~= ".*avx2.*" | vm.cpu.features ~= ".*avx.*") | + * (os.arch=="aarch64" & vm.cpu.features ~= ".*simd.*") | + * (os.arch == "riscv64" & vm.cpu.features ~= ".*v,.*") | + * (os.arch == "loongarch64" & vm.cpu.features ~= ".*lsx.*") * @build compiler.intrinsics.chacha.ExerciseChaCha20 * jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -63,9 +67,13 @@ private static List mix(List o, String... mix) { return n; } - private static boolean containsFuzzy(List list, String sub) { + private static boolean containsFuzzy(List list, String sub, Boolean matchExactly) { for (String s : list) { - if (s.contains(sub)) return true; + if (matchExactly) { + if (s.equals(sub)) return true; + } else { + if (s.contains(sub)) return true; + } } return false; } @@ -85,32 +93,38 @@ public static void main(String... args) throws Exception { } // Otherwise, select the tests that make sense on current platform. - if (containsFuzzy(cpuFeatures, "avx512")) { + if (containsFuzzy(cpuFeatures, "avx512", false)) { System.out.println("Setting up AVX512 worker"); configs.add(List.of("-XX:UseAVX=3")); } - if (containsFuzzy(cpuFeatures, "avx2")) { + if (containsFuzzy(cpuFeatures, "avx2", false)) { System.out.println("Setting up AVX2 worker"); configs.add(List.of("-XX:UseAVX=2")); } - if (containsFuzzy(cpuFeatures, "avx")) { + if (containsFuzzy(cpuFeatures, "avx", false)) { System.out.println("Setting up AVX worker"); configs.add(List.of("-XX:UseAVX=1")); } } else if (Platform.isAArch64()) { // AArch64 intrinsics require the advanced simd instructions - if (containsFuzzy(cpuFeatures, "simd")) { + if (containsFuzzy(cpuFeatures, "simd", false)) { System.out.println("Setting up ASIMD worker"); configs.add(new ArrayList()); } + } else if (Platform.isRISCV64()) { + // Riscv64 intrinsics require the vector instructions + if (containsFuzzy(cpuFeatures, "v", true)) { + System.out.println("Setting up vector worker"); + configs.add(List.of("-XX:+UseRVV")); + } } else if (Platform.isLoongArch64()) { // LoongArch64 intrinsics require the lsx instructions - if (containsFuzzy(cpuFeatures, "lsx")) { + if (containsFuzzy(cpuFeatures, "lsx", false)) { System.out.println("Setting up LSX worker"); configs.add(new ArrayList()); } } else { - // We only have ChaCha20 intrinsics on x64 and aarch64 + // We only have ChaCha20 intrinsics on x64, aarch64, riscv64 and loongarch64 // currently. If the platform is neither of these then // the ChaCha20 known answer tests in // com/sun/crypto/provider/Cipher are sufficient. diff --git a/test/hotspot/jtreg/compiler/intrinsics/float16/Binary16Conversion.java b/test/hotspot/jtreg/compiler/intrinsics/float16/Binary16Conversion.java index 5a2f293e2599c..d9fe8c91dc463 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/float16/Binary16Conversion.java +++ b/test/hotspot/jtreg/compiler/intrinsics/float16/Binary16Conversion.java @@ -315,7 +315,8 @@ private static int testRoundFloatToBinary16FullBinade() { f_prime_diff > f_prime_up_diff) { errors++; System.out.println("Round-to-nearest violation on converting " + - Float.toHexString(f) + "/" + Integer.toHexString(i) + " to binary16 and back: " + Integer.toHexString(0xffff & f_as_bin16) + " f_prime: " + Float.toHexString(f_prime)); + Float.toHexString(f) + "/" + Integer.toHexString(i) + " to binary16 and back: " + + Integer.toHexString(0xffff & f_as_bin16) + " f_prime: " + Float.toHexString(f_prime)); } } return errors; @@ -332,11 +333,14 @@ private static int testAlternativeImplementation() { ell++) { float f = Float.intBitsToFloat((int)ell); short s1 = Float.floatToFloat16(f); - short s2 = testAltFloatToFloat16(f); + short s2 = testAltFloatToFloat16(f); if (s1 != s2) { errors++; - System.out.println("Different conversion of float value " + Float.toHexString(f)); + System.out.println("Different conversion of float value (" + f + "/" + + Integer.toHexString(Float.floatToRawIntBits(f)) + "): " + + Integer.toHexString(s1 & 0xffff) + "(" + s1 + ")" + " != " + + Integer.toHexString(s2 & 0xffff) + "(" + s2 + ")"); } } diff --git a/test/hotspot/jtreg/compiler/intrinsics/float16/TestAllFloat16ToFloat.java b/test/hotspot/jtreg/compiler/intrinsics/float16/TestAllFloat16ToFloat.java index 230aef2fb5870..e95554fa944b8 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/float16/TestAllFloat16ToFloat.java +++ b/test/hotspot/jtreg/compiler/intrinsics/float16/TestAllFloat16ToFloat.java @@ -59,45 +59,59 @@ public static short testRoundTrip(short s) { return Float.floatToFloat16(Float.float16ToFloat(s)); } - public static void verify(short sVal, float fVal, short sRes) { + public static int verify(short sVal, float fVal, short sRes, String prefix) { + int errors = 0; if (sRes != sVal) { if (!Float.isNaN(fVal) || ((sRes & ~0x0200) != (sVal & ~0x0200)) ) { + errors++; String fVal_hex = Integer.toHexString(Float.floatToRawIntBits(fVal)); String sRes_hex = Integer.toHexString(sRes & 0xffff); String sVal_hex = Integer.toHexString(sVal & 0xffff); - throw new RuntimeException("Inconsistent result for Float.floatToFloat16(" + fVal + "/" + fVal_hex + "): " + sRes_hex + " != " + sVal_hex); + System.out.println(prefix + "Inconsistent result for Float.floatToFloat16(" + fVal + "/" + fVal_hex + "): " + + sRes_hex + "(" + sRes + ")" + " != " + sVal_hex + "(" + sVal + ")"); } } + return errors; } - public static void run() { + public static int run() { + int errors = 0; // Testing all float16 values. for (short sVal = Short.MIN_VALUE; sVal < Short.MAX_VALUE; ++sVal) { float fVal = Float.float16ToFloat(sVal); short sRes = testFloatToFloat16(fVal); - verify(sVal, fVal, sRes); + errors += verify(sVal, fVal, sRes, "testFloatToFloat16: "); float fRes = testFloat16ToFloat(sVal); if (!Float.isNaN(fRes) && fRes != fVal) { + errors++; String sVal_hex = Integer.toHexString(sVal & 0xffff); String fRes_hex = Integer.toHexString(Float.floatToRawIntBits(fRes)); String fVal_hex = Integer.toHexString(Float.floatToRawIntBits(fVal)); - throw new RuntimeException("Inconsistent result for Float.float16ToFloat(" + sVal_hex + "): " + fRes + "/" + fRes_hex + " != " + fVal + "/" + fVal_hex); + System.out.println("Non-NaN res: " + "Inconsistent result for Float.float16ToFloat(" + sVal_hex + "): " + + fRes + "/" + fRes_hex + " != " + fVal + "/" + fVal_hex); } sRes = testRoundTrip(sVal); - verify(sVal, fVal, sRes); + errors += verify(sVal, fVal, sRes, "testRoundTrip: "); if (Float.floatToFloat16(fRes) != Float.floatToFloat16(fVal)) { + errors++; String sVal_hex = Integer.toHexString(sVal & 0xffff); String sfRes_hex = Integer.toHexString(Float.floatToFloat16(fRes) & 0xffff); - String sfVal_hex = Integer.toHexString(Float.floatToFloat16(fVal)& 0xffff); - throw new RuntimeException("Inconsistent result for Float.float16ToFloat(" + sVal_hex + "): " + sfRes_hex + " != " + sfVal_hex); + String sfVal_hex = Integer.toHexString(Float.floatToFloat16(fVal) & 0xffff); + System.out.println("Float16 not equal: " + "Inconsistent result for Float.float16ToFloat(" + sVal_hex + "): " + + sfRes_hex + " != " + sfVal_hex); } } + return errors; } public static void main(String[] args) { + int errors = 0; // Run twice to trigger compilation for (int i = 0; i < 2; i++) { - run(); + errors += run(); + } + if (errors > 0) { + throw new RuntimeException(errors + " errors"); } } } diff --git a/test/hotspot/jtreg/compiler/intrinsics/float16/TestConstFloat16ToFloat.java b/test/hotspot/jtreg/compiler/intrinsics/float16/TestConstFloat16ToFloat.java index 42a9d239e0693..27e6b86abe2a4 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/float16/TestConstFloat16ToFloat.java +++ b/test/hotspot/jtreg/compiler/intrinsics/float16/TestConstFloat16ToFloat.java @@ -133,7 +133,8 @@ public static void testFloatConst(short[] sRes) { sRes[13] = Float.floatToFloat16(BinaryF16.POSITIVE_INFINITY); } - public static void run() { + public static int run() { + int errors = 0; short s = Float.floatToFloat16(0.0f); // Load Float class // Testing constant float16 values. float[] fRes = new float[sCon.length]; @@ -141,10 +142,12 @@ public static void run() { for (int i = 0; i < sCon.length; i++) { float fVal = Float.float16ToFloat(sCon[i]); if (Float.floatToRawIntBits(fRes[i]) != Float.floatToRawIntBits(fVal)) { + errors++; String cVal_hex = Integer.toHexString(sCon[i] & 0xffff); String fRes_hex = Integer.toHexString(Float.floatToRawIntBits(fRes[i])); String fVal_hex = Integer.toHexString(Float.floatToRawIntBits(fVal)); - throw new RuntimeException("Inconsistent result for Float.float16ToFloat(" + cVal_hex + "): " + fRes[i] + "/" + fRes_hex + " != " + fVal + "/" + fVal_hex); + System.out.println("Inconsistent result for Float.float16ToFloat(" + cVal_hex + "): " + + fRes[i] + "/" + fRes_hex + " != " + fVal + "/" + fVal_hex); } } @@ -154,19 +157,26 @@ public static void run() { for (int i = 0; i < fCon.length; i++) { short sVal = Float.floatToFloat16(fCon[i]); if (sRes[i] != sVal) { + errors++; String cVal_hex = Integer.toHexString(Float.floatToRawIntBits(fCon[i])); String sRes_hex = Integer.toHexString(sRes[i] & 0xffff); String sVal_hex = Integer.toHexString(sVal & 0xffff); - throw new RuntimeException("Inconsistent result for Float.floatToFloat16(" + fCon[i] + "/" + cVal_hex + "): " + sRes_hex + " != " + sVal_hex); + System.out.println("Inconsistent result for Float.floatToFloat16(" + fCon[i] + "/" + cVal_hex + "): " + + sRes_hex + "(" + sRes + ")" + " != " + sVal_hex + "(" + sVal + ")"); } } + return errors; } public static void main(String[] args) { + int errors = 0; // Run twice to trigger compilation for (int i = 0; i < 2; i++) { - run(); + errors += run(); + } + if (errors > 0) { + throw new RuntimeException(errors + " errors"); } } } diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java index 76bfda0f51fa7..a7d2cfe7fa7ff 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java @@ -27,7 +27,7 @@ * @summary Verifies that string intrinsics throw array out of bounds exceptions. * @library /compiler/patches /test/lib * @build java.base/java.lang.Helper - * @run main/othervm -Xbatch -XX:CompileThreshold=100 compiler.intrinsics.string.TestStringIntrinsicRangeChecks + * @run main/othervm/timeout=300 -Xbatch -XX:CompileThreshold=100 compiler.intrinsics.string.TestStringIntrinsicRangeChecks */ package compiler.intrinsics.string; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 66a82f52154c7..2b25abb24c580 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -555,12 +555,12 @@ public class IRNode { // Does not work for VM builds without JVMCI like x86_32 (a rule containing this regex will be skipped without having JVMCI built). public static final String INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP = PREFIX + "INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP" + POSTFIX; static { - trapNodes(INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,"intrinsic_or_type_checked_inlining"); + trapNodes(INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP, "intrinsic_or_type_checked_inlining"); } public static final String INTRINSIC_TRAP = PREFIX + "INTRINSIC_TRAP" + POSTFIX; static { - trapNodes(INTRINSIC_TRAP,"intrinsic"); + trapNodes(INTRINSIC_TRAP, "intrinsic"); } // Is only supported on riscv64. @@ -1046,12 +1046,12 @@ public class IRNode { public static final String NULL_ASSERT_TRAP = PREFIX + "NULL_ASSERT_TRAP" + POSTFIX; static { - trapNodes(NULL_ASSERT_TRAP,"null_assert"); + trapNodes(NULL_ASSERT_TRAP, "null_assert"); } public static final String NULL_CHECK_TRAP = PREFIX + "NULL_CHECK_TRAP" + POSTFIX; static { - trapNodes(NULL_CHECK_TRAP,"null_check"); + trapNodes(NULL_CHECK_TRAP, "null_check"); } public static final String OR_VB = VECTOR_PREFIX + "OR_VB" + POSTFIX; @@ -1135,12 +1135,12 @@ public class IRNode { public static final String PREDICATE_TRAP = PREFIX + "PREDICATE_TRAP" + POSTFIX; static { - trapNodes(PREDICATE_TRAP,"predicate"); + trapNodes(PREDICATE_TRAP, "predicate"); } public static final String RANGE_CHECK_TRAP = PREFIX + "RANGE_CHECK_TRAP" + POSTFIX; static { - trapNodes(RANGE_CHECK_TRAP,"range_check"); + trapNodes(RANGE_CHECK_TRAP, "range_check"); } public static final String REPLICATE_B = VECTOR_PREFIX + "REPLICATE_B" + POSTFIX; @@ -1492,7 +1492,7 @@ public class IRNode { public static final String TRAP = PREFIX + "TRAP" + POSTFIX; static { - trapNodes(TRAP,"reason"); + trapNodes(TRAP, "reason"); } public static final String UDIV_I = PREFIX + "UDIV_I" + POSTFIX; @@ -1527,12 +1527,17 @@ public class IRNode { public static final String UNHANDLED_TRAP = PREFIX + "UNHANDLED_TRAP" + POSTFIX; static { - trapNodes(UNHANDLED_TRAP,"unhandled"); + trapNodes(UNHANDLED_TRAP, "unhandled"); } public static final String UNSTABLE_IF_TRAP = PREFIX + "UNSTABLE_IF_TRAP" + POSTFIX; static { - trapNodes(UNSTABLE_IF_TRAP,"unstable_if"); + trapNodes(UNSTABLE_IF_TRAP, "unstable_if"); + } + + public static final String UNREACHED_TRAP = PREFIX + "UNREACHED_TRAP" + POSTFIX; + static { + trapNodes(UNREACHED_TRAP, "unreached"); } public static final String URSHIFT = PREFIX + "URSHIFT" + POSTFIX; diff --git a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java index 90682278760d2..3d68f10723436 100644 --- a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java +++ b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java @@ -24,6 +24,7 @@ /* * @test * @summary Checks that -XX:CompileCommand=PrintMemStat,... works + * @requires vm.compiler1.enabled | vm.compiler2.enabled * @library /test/lib * @run driver compiler.print.CompileCommandPrintMemStat */ diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX1.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX1.java index 050777505e21f..944a48750ba15 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX1.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX1.java @@ -36,7 +36,7 @@ * @summary Test that vector cast intrinsics work as intended on avx1. * @requires vm.cpu.features ~= ".*avx.*" * @library /test/lib / - * @run main compiler.vectorapi.reshape.TestVectorCastAVX1 + * @run main/timeout=300 compiler.vectorapi.reshape.TestVectorCastAVX1 */ public class TestVectorCastAVX1 { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX2.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX2.java index 2c5cfe7de99c2..d748b82cb84a8 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX2.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX2.java @@ -36,7 +36,7 @@ * @summary Test that vector cast intrinsics work as intended on avx2. * @requires vm.cpu.features ~= ".*avx2.*" * @library /test/lib / - * @run main compiler.vectorapi.reshape.TestVectorCastAVX2 + * @run main/timeout=300 compiler.vectorapi.reshape.TestVectorCastAVX2 */ public class TestVectorCastAVX2 { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512.java index 903958943e3a0..fa11d1a5df756 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512.java @@ -36,7 +36,7 @@ * @summary Test that vector cast intrinsics work as intended on avx512. * @requires vm.cpu.features ~= ".*avx512.*" * @library /test/lib / - * @run main compiler.vectorapi.reshape.TestVectorCastAVX512 + * @run main/timeout=300 compiler.vectorapi.reshape.TestVectorCastAVX512 */ public class TestVectorCastAVX512 { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512BW.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512BW.java index d98379ae8009a..16e577b48a688 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512BW.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512BW.java @@ -36,7 +36,7 @@ * @summary Test that vector cast intrinsics work as intended on avx512bw. * @requires vm.cpu.features ~= ".*avx512bw.*" * @library /test/lib / - * @run main compiler.vectorapi.reshape.TestVectorCastAVX512BW + * @run main/timeout=300 compiler.vectorapi.reshape.TestVectorCastAVX512BW */ public class TestVectorCastAVX512BW { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512DQ.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512DQ.java index 952794236514c..3aa1b62650f18 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512DQ.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastAVX512DQ.java @@ -36,7 +36,7 @@ * @summary Test that vector cast intrinsics work as intended on avx512dq. * @requires vm.cpu.features ~= ".*avx512dq.*" * @library /test/lib / - * @run main compiler.vectorapi.reshape.TestVectorCastAVX512DQ + * @run main/timeout=300 compiler.vectorapi.reshape.TestVectorCastAVX512DQ */ public class TestVectorCastAVX512DQ { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastNeon.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastNeon.java index 75a8fada91ee0..b70517104dc38 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastNeon.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastNeon.java @@ -36,7 +36,7 @@ * @summary Test that vector cast intrinsics work as intended on neon. * @requires vm.cpu.features ~= ".*asimd.*" * @library /test/lib / - * @run main compiler.vectorapi.reshape.TestVectorCastNeon + * @run main/timeout=300 compiler.vectorapi.reshape.TestVectorCastNeon */ public class TestVectorCastNeon { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastSVE.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastSVE.java index e1cd1f0fdac4b..72624692352a8 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastSVE.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorCastSVE.java @@ -36,7 +36,7 @@ * @summary Test that vector cast intrinsics work as intended on sve. * @requires vm.cpu.features ~= ".*sve.*" * @library /test/lib / - * @run main compiler.vectorapi.reshape.TestVectorCastSVE + * @run main/timeout=300 compiler.vectorapi.reshape.TestVectorCastSVE */ public class TestVectorCastSVE { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/gc/g1/TestEvacuationFailure.java b/test/hotspot/jtreg/gc/g1/TestEvacuationFailure.java index bbd7fee046704..fd4a509475764 100644 --- a/test/hotspot/jtreg/gc/g1/TestEvacuationFailure.java +++ b/test/hotspot/jtreg/gc/g1/TestEvacuationFailure.java @@ -25,7 +25,7 @@ /* * @test TestEvacuationFailure - * @summary Ensure the output for a minor GC with G1 that has evacuation failure contains the correct strings. + * @summary Ensure the output for a minor GC with G1 that has allocation failure contains the correct strings. * @requires vm.gc.G1 * @requires vm.debug * @library /test/lib @@ -55,7 +55,7 @@ public static void main(String[] args) throws Exception { OutputAnalyzer output = new OutputAnalyzer(pb.start()); System.out.println(output.getStdout()); - output.shouldContain("(Evacuation Failure)"); + output.shouldContain("(Evacuation Failure:"); output.shouldHaveExitValue(0); } diff --git a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java index 6b249d69ab90d..6a024bb3ba2ff 100644 --- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java +++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java @@ -264,10 +264,11 @@ private void testConcurrentRefinementLogs() throws Exception { LogMessageWithLevel exhFailureMessages[] = new LogMessageWithLevel[] { new LogMessageWithLevel("Recalculate Used Memory", Level.DEBUG), new LogMessageWithLevel("Restore Preserved Marks", Level.DEBUG), - new LogMessageWithLevel("Restore Retained Regions", Level.DEBUG), + new LogMessageWithLevel("Restore Evacuation Failed Regions", Level.DEBUG), new LogMessageWithLevel("Process Evacuation Failed Regions", Level.DEBUG), new LogMessageWithLevel("Evacuation Failed Regions", Level.DEBUG), - new LogMessageWithLevel("New Retained Regions", Level.DEBUG), + new LogMessageWithLevel("Pinned Regions", Level.DEBUG), + new LogMessageWithLevel("Allocation Failed Regions", Level.DEBUG), }; private void testWithEvacuationFailureLogs() throws Exception { diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedHumongousFragmentation.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedHumongousFragmentation.java new file mode 100644 index 0000000000000..fad01ab002206 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedHumongousFragmentation.java @@ -0,0 +1,128 @@ +/* + + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Red Hat, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + /* + * @test id=g1 + * @summary Make sure G1 can handle humongous allocation fragmentation with region pinning in the mix, + * i.e. moving humongous objects around other pinned humongous objects even in a last resort + * full gc. + * Adapted from gc/TestAllocHumongousFragment.java + * @key randomness + * @requires vm.gc.G1 + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xlog:gc+region=trace -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g + * -XX:VerifyGCType=full -XX:+VerifyDuringGC -XX:+VerifyAfterGC -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * gc.g1.pinnedobjs.TestPinnedHumongousFragmentation + */ + +package gc.g1.pinnedobjs; + +import java.util.*; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +import jdk.test.whitebox.WhiteBox; + +public class TestPinnedHumongousFragmentation { + + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + + static final long TARGET_MB = 30_000; // 30 Gb allocations + static final long LIVE_MB = 700; // 700 Mb alive + static final int PINNED_PERCENT = 5; // 5% of objects pinned + + static volatile Object sink; + + class PinInformation { + int[] object; + long address; + + PinInformation(int[] object) { + this.object = object; + wb.pinObject(object); + this.address = wb.getObjectAddress(object); + } + + void release() { + long newAddress = wb.getObjectAddress(object); + if (address != newAddress) { + Asserts.fail("Object at " + address + " moved to " + newAddress); + } + wb.unpinObject(object); + object = null; + } + } + + static List objects; + static List pinnedObjects; + + public static void main(String[] args) throws Exception { + (new TestPinnedHumongousFragmentation()).run(); + } + + void run() throws Exception { + final int min = 128 * 1024; + final int max = 16 * 1024 * 1024; + final long count = TARGET_MB * 1024 * 1024 / (16 + 4 * (min + (max - min) / 2)); + + objects = new ArrayList(); + pinnedObjects = new ArrayList(); + long current = 0; + + Random rng = Utils.getRandomInstance(); + for (long c = 0; c < count; c++) { + while (current > LIVE_MB * 1024 * 1024) { + int idx = rng.nextInt(objects.size()); + int[] remove = objects.remove(idx); + current -= remove.length * 4 + 16; + } + + // Pin random objects before the allocation that is (likely) going to + // cause full gcs. Remember them for unpinning. + for (int i = 0; i < objects.size() * PINNED_PERCENT / 100; i++) { + int[] target = objects.get(rng.nextInt(objects.size())); + pinnedObjects.add(new PinInformation(target)); + } + + int[] newObj = new int[min + rng.nextInt(max - min)]; + current += newObj.length * 4 + 16; + objects.add(newObj); + sink = new Object(); + + // Unpin and clear remembered objects afterwards. + for (int i = 0; i < pinnedObjects.size(); i++) { + pinnedObjects.get(i).release(); + } + pinnedObjects.clear(); + + System.out.println("Allocated: " + (current / 1024 / 1024) + " Mb"); + } + } + +} diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectContents.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectContents.java new file mode 100644 index 0000000000000..e0fdc88e2a1ca --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectContents.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @summary Test that pinned objects we lost all Java references to keep + * the region and contents alive. + * This test simulates this behavior using Whitebox/Unsafe methods + * and not real native code for simplicity. + * @requires vm.gc.G1 + * @requires vm.debug + * @library /test/lib + * @modules java.base/jdk.internal.misc:+open + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -XX:+ZapUnusedHeapArea -Xlog:gc,gc+ergo+cset=trace gc.g1.pinnedobjs.TestPinnedObjectContents + */ + +package gc.g1.pinnedobjs; + +import jdk.internal.misc.Unsafe; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestPinnedObjectContents { + + private static final jdk.internal.misc.Unsafe unsafe = Unsafe.getUnsafe(); + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + + public static long pinAndGetAddress(Object o) { + wb.pinObject(o); + return wb.getObjectAddress(o); + } + + public static void main(String[] args) throws Exception { + // Remove garbage from VM initialization. + wb.fullGC(); + + // Allocate to-be pinned object and fill with "random" data. + final int ArraySize = 100; + int[] o = new int[ArraySize]; + for (int i = 0; i < o.length; i++) { + o[i] = i; + } + + Asserts.assertTrue(!wb.isObjectInOldGen(o), "should not be in old gen already"); + + // Remember memory offsets. + long baseOffset = unsafe.arrayBaseOffset(o.getClass()); + long indexScale = unsafe.arrayIndexScale(o.getClass()); + long address = pinAndGetAddress(o); + + o = null; // And forget the (Java) reference to the int array. + + // Do garbage collections to zap the data surrounding the "dead" object. + wb.youngGC(); + wb.youngGC(); + + for (int i = 0; i < ArraySize; i++) { + int actual = unsafe.getInt(null, address + baseOffset + i * indexScale); + if (actual != i) { + Asserts.fail("Pinned array at offset " + i + " should contain the value " + i + " but is " + actual); + } + } + } +} + diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectTypes.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectTypes.java new file mode 100644 index 0000000000000..ff730a30106f5 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectTypes.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @summary Test whether different object type can be pinned or not. + * @requires vm.gc.G1 + * @requires vm.debug + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver gc.g1.pinnedobjs.TestPinnedObjectTypes + */ + +package gc.g1.pinnedobjs; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestPinnedObjectTypes { + + public static void main(String[] args) throws Exception { + testPinning("Object", false); + testPinning("TypeArray", true); + testPinning("ObjArray", false); + } + + private static void testPinning(String type, boolean shouldSucceed) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UseG1GC", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:.", + "-Xmx32M", + "-Xmn16M", + "-Xlog:gc", + TestObjectPin.class.getName(), + type); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + if (shouldSucceed) { + output.shouldHaveExitValue(0); + } else { + output.shouldNotHaveExitValue(0); + } + } + +} + +class TestObjectPin { + + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + + public static void main(String[] args) { + Object o = switch (args[0]) { + case "Object" -> new Object(); + case "TypeArray" -> new int[100]; + case "ObjArray" -> new Object[100]; + default -> null; + }; + wb.pinObject(o); + } +} + diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectsEvacuation.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectsEvacuation.java new file mode 100644 index 0000000000000..7ffd9432eb582 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedObjectsEvacuation.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @summary Test pinned objects lifecycle from young gen to eventual reclamation. + * @requires vm.gc.G1 + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver gc.g1.pinnedobjs.TestPinnedObjectsEvacuation + */ + +package gc.g1.pinnedobjs; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestPinnedObjectsEvacuation { + + public static void main(String[] args) throws Exception { + testPinnedEvacuation(0, 0, 0, 1); + testPinnedEvacuation(1, 1, 0, 1); + testPinnedEvacuation(2, 1, 1, 0); + testPinnedEvacuation(3, 1, 1, 0); + } + + private static int numMatches(String stringToMatch, String pattern) { + Pattern r = Pattern.compile(pattern); + Matcher m = r.matcher(stringToMatch); + return (int)m.results().count(); + } + + private static void assertMatches(int expected, int actual, String what) { + if (expected != actual) { + Asserts.fail("Expected " + expected + " " + what + " events but got " + actual); + } + } + + private static void testPinnedEvacuation(int younGCsBeforeUnpin, int expectedSkipEvents, int expectedDropEvents, int expectedReclaimEvents) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UseG1GC", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:.", + "-Xmx32M", + "-Xmn16M", + "-XX:G1NumCollectionsKeepPinned=2", + "-XX:+VerifyAfterGC", + "-Xlog:gc,gc+ergo+cset=trace", + TestObjectPin.class.getName(), + String.valueOf(younGCsBeforeUnpin)); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + output.shouldHaveExitValue(0); + + assertMatches(expectedSkipEvents, numMatches(output.getStdout(), ".*Retained candidate \\d+ can not be reclaimed currently. Skipping.*"), "skip"); + assertMatches(expectedDropEvents, numMatches(output.getStdout(), ".*Retained candidate \\d+ can not be reclaimed currently. Dropping.*"), "drop"); + assertMatches(expectedReclaimEvents, numMatches(output.getStdout(), ".*Finish adding retained candidates to collection set. Initial: 1,.*"), "reclaim"); + } + +} + +class TestObjectPin { + + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + + public static long pinAndGetAddress(Object o) { + wb.pinObject(o); + return wb.getObjectAddress(o); + } + + public static void unpinAndCompareAddress(Object o, long expectedAddress) { + Asserts.assertEQ(expectedAddress, wb.getObjectAddress(o), "Object has moved during pinning."); + wb.unpinObject(o); + } + + public static void main(String[] args) { + + int youngGCBeforeUnpin = Integer.parseInt(args[0]); + + // Remove garbage from VM initialization. + wb.fullGC(); + + Object o = new int[100]; + Asserts.assertTrue(!wb.isObjectInOldGen(o), "should not be pinned in old gen"); + + long address = pinAndGetAddress(o); + + // First young GC: should move the object into old gen. + wb.youngGC(); + Asserts.assertTrue(wb.isObjectInOldGen(o), "Pinned object not in old gen after young GC"); + + // The object is (still) pinned. Do some configurable young gcs that fail to add it to the + // collection set candidates. + for (int i = 0; i < youngGCBeforeUnpin; i++) { + wb.youngGC(); + } + unpinAndCompareAddress(o, address); + + // Unpinned the object. This next gc should take the region if not dropped. + wb.youngGC(); + } +} + diff --git a/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedOldObjectsEvacuation.java b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedOldObjectsEvacuation.java new file mode 100644 index 0000000000000..86f7a62ef7042 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/pinnedobjs/TestPinnedOldObjectsEvacuation.java @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @summary Test pinned objects lifecycle from old gen to eventual reclamation. + * @requires vm.gc.G1 + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver gc.g1.pinnedobjs.TestPinnedOldObjectsEvacuation + */ + +package gc.g1.pinnedobjs; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +class TestResultTracker { + private int trackedRegion = -1; + private int curGC = -1; + private String stdout; + private int expectedMarkingSkipEvents; // How many times has the region from the "marking" collection set candidate set been "skipped". + private int expectedRetainedSkipEvents; // How many times has the region from the "retained" collection set candidate set been "skipped". + private int expectedDropEvents; // How many times has the region from the "retained" collection set candidate set been "dropped". + private int expectedMarkingReclaimEvents; // How many times has the region from the "marking" collection set candidate set been put into the collection set. + private int expectedRetainedReclaimEvents; // How many times has the region from the "marking" collection set candidate set been put into the collection set. + + TestResultTracker(String stdout, + int expectedMarkingSkipEvents, + int expectedRetainedSkipEvents, + int expectedDropEvents, + int expectedMarkingReclaimEvents, + int expectedRetainedReclaimEvents) { + this.stdout = stdout; + this.expectedMarkingSkipEvents = expectedMarkingSkipEvents; + this.expectedRetainedSkipEvents = expectedRetainedSkipEvents; + this.expectedDropEvents = expectedDropEvents; + this.expectedMarkingReclaimEvents = expectedMarkingReclaimEvents; + this.expectedRetainedReclaimEvents = expectedRetainedReclaimEvents; + } + + private void updateOrCompareCurRegion(String phase, int curRegion) { + if (trackedRegion == -1) { + trackedRegion = curRegion; + } else { + if (trackedRegion != curRegion) { + Asserts.fail("Expected region " + trackedRegion + " to be used but is " + curRegion); + } + } + } + + private void expectMoreMatches(Matcher matcher, String event) { + if (!matcher.find()) { + Asserts.fail("Expected one more " + event); + } + } + + private int expectIncreasingGC(Matcher matcher) { + int nextGC = Integer.parseInt(matcher.group(1)); + if (nextGC <= curGC) { + Asserts.fail("Non-increasing GC number from " + curGC + " to " + nextGC); + } + return nextGC; + } + + // Verify log messages based on expected events. + // + // There are two log messages printed with -Xlog:ergo+cset=trace that report about success or failure to + // evacuate particular regions (in this case) due to pinning: + // + // 1) GC() Marking/Retained candidate can not be reclaimed currently. Skipping/Dropping. + // + // and + // + // 2) GC() Finish adding retained/marking candidates to collection set. Initial: ... pinned: + // + // 1) reports about whether the given region has been added to the collection set or not. The last word indicates whether the + // region has been removed from the collection set candidates completely ("Dropping"), or just skipped for this collection + // ("Skipping") + // + // This message is printed for every such region, however since the test only pins a single object/region and can only be + // in one of the collection set candidate sets, there will be only one message per GC. + // + // 2) reports statistics about how many regions were added to the initial collection set, optional collection set (not shown + // here) and the amount of pinned regions for every kind of collection set candidate sets ("marking" or "retained"). + // + // There are two such messages per GC. + // + // The code below tracks that single pinned region through the various stages as defined by the policy. + // + public void verify() throws Exception { + final String skipDropEvents = "GC\\((\\d+)\\).*(Marking|Retained) candidate (\\d+) can not be reclaimed currently\\. (Skipping|Dropping)"; + final String reclaimEvents = "GC\\((\\d+)\\) Finish adding (retained|marking) candidates to collection set\\. Initial: (\\d+).*pinned: (\\d+)"; + + Matcher skipDropMatcher = Pattern.compile(skipDropEvents, Pattern.MULTILINE).matcher(stdout); + Matcher reclaimMatcher = Pattern.compile(reclaimEvents, Pattern.MULTILINE).matcher(stdout); + + for (int i = 0; i < expectedMarkingSkipEvents; i++) { + expectMoreMatches(skipDropMatcher, "expectedMarkingSkipEvents"); + curGC = expectIncreasingGC(skipDropMatcher); + + Asserts.assertEQ("Marking", skipDropMatcher.group(2), "Expected \"Marking\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(2) + "\""); + updateOrCompareCurRegion("MarkingSkip", Integer.parseInt(skipDropMatcher.group(3))); + Asserts.assertEQ("Skipping", skipDropMatcher.group(4), "Expected \"Skipping\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(4) + "\""); + + while (true) { + if (!reclaimMatcher.find()) { + Asserts.fail("Could not find \"Finish adding * candidates\" line for GC " + curGC); + } + if (reclaimMatcher.group(2).equals("retained")) { + continue; + } + if (Integer.parseInt(reclaimMatcher.group(1)) == curGC) { + int actual = Integer.parseInt(reclaimMatcher.group(4)); + Asserts.assertEQ(actual, 1, "Expected number of pinned to be 1 after marking skip but is " + actual); + break; + } + } + } + + for (int i = 0; i < expectedRetainedSkipEvents; i++) { + expectMoreMatches(skipDropMatcher, "expectedRetainedSkipEvents"); + curGC = expectIncreasingGC(skipDropMatcher); + + Asserts.assertEQ("Retained", skipDropMatcher.group(2), "Expected \"Retained\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(2) + "\""); + updateOrCompareCurRegion("RetainedSkip", Integer.parseInt(skipDropMatcher.group(3))); + Asserts.assertEQ("Skipping", skipDropMatcher.group(4), "Expected \"Skipping\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(4) + "\""); + + while (true) { + if (!reclaimMatcher.find()) { + Asserts.fail("Could not find \"Finish adding * candidates\" line for GC " + curGC); + } + if (reclaimMatcher.group(2).equals("marking")) { + continue; + } + if (Integer.parseInt(reclaimMatcher.group(1)) == curGC) { + int actual = Integer.parseInt(reclaimMatcher.group(4)); + Asserts.assertEQ(actual, 1, "Expected number of pinned to be 1 after retained skip but is " + actual); + break; + } + } + } + + for (int i = 0; i < expectedDropEvents; i++) { + expectMoreMatches(skipDropMatcher, "expectedDropEvents"); + curGC = expectIncreasingGC(skipDropMatcher); + + Asserts.assertEQ("Retained", skipDropMatcher.group(2), "Expected \"Retained\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(2) + "\""); + updateOrCompareCurRegion("RetainedDrop", Integer.parseInt(skipDropMatcher.group(3))); + Asserts.assertEQ("Dropping", skipDropMatcher.group(4), "Expected \"Dropping\" tag for GC " + curGC + " but got \"" + skipDropMatcher.group(4) + "\""); + + while (true) { + if (!reclaimMatcher.find()) { + Asserts.fail("Could not find \"Finish adding * candidates\" line for GC " + curGC); + } + if (reclaimMatcher.group(2).equals("marking")) { + continue; + } + if (Integer.parseInt(reclaimMatcher.group(1)) == curGC) { + int actual = Integer.parseInt(reclaimMatcher.group(4)); + if (actual != 1) { + Asserts.fail("Expected number of pinned to be 1 after dropping but is " + actual); + } + break; + } + } + } + + for (int i = 0; i < expectedMarkingReclaimEvents; i++) { + expectMoreMatches(reclaimMatcher, "\"Finish adding * candidates\" line for GC " + curGC); + + int nextGC = Integer.parseInt(reclaimMatcher.group(1)); + curGC = nextGC; + if (reclaimMatcher.group(2).equals("retained")) { + continue; + } + + if (Integer.parseInt(reclaimMatcher.group(1)) == nextGC) { + int actual = Integer.parseInt(reclaimMatcher.group(4)); + if (actual != 0) { + Asserts.fail("Expected number of pinned to be 0 after marking reclaim but is " + actual); + } + } + } + + for (int i = 0; i < expectedRetainedReclaimEvents; i++) { + expectMoreMatches(reclaimMatcher, "\"Finish adding * candidates\" line for GC " + curGC); + + int nextGC = Integer.parseInt(reclaimMatcher.group(1)); + curGC = nextGC; + if (reclaimMatcher.group(2).equals("marking")) { + continue; + } + + if (Integer.parseInt(reclaimMatcher.group(1)) == nextGC) { + int actual = Integer.parseInt(reclaimMatcher.group(4)); + if (actual != 0) { + Asserts.fail("Expected number of pinned to be 0 after retained reclaim but is " + actual); + } + } + } + } +} + +public class TestPinnedOldObjectsEvacuation { + + public static void main(String[] args) throws Exception { + // younGCsBeforeUnpin, expectedMarkingSkipEvents, expectedRetainedSkipEvents, expectedDropEvents, expectedMarkingReclaimEvents, expectedRetainedReclaimEvents + testPinnedEvacuation(1, 1, 0, 0, 0, 1); + testPinnedEvacuation(2, 1, 1, 0, 0, 1); + testPinnedEvacuation(3, 1, 2, 0, 0, 1); + testPinnedEvacuation(4, 1, 2, 1, 0, 0); + } + + private static int numMatches(String stringToMatch, String pattern) { + Pattern r = Pattern.compile(pattern); + Matcher m = r.matcher(stringToMatch); + return (int)m.results().count(); + } + + private static void assertMatches(int expected, int actual, String what) { + if (expected != actual) { + Asserts.fail("Expected " + expected + " " + what + " events but got " + actual); + } + } + + private static void testPinnedEvacuation(int youngGCsBeforeUnpin, + int expectedMarkingSkipEvents, + int expectedRetainedSkipEvents, + int expectedDropEvents, + int expectedMarkingReclaimEvents, + int expectedRetainedReclaimEvents) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UseG1GC", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:.", + "-Xmx32M", + "-Xmn16M", + "-XX:MarkSweepDeadRatio=0", + "-XX:G1NumCollectionsKeepPinned=3", + "-XX:+UnlockExperimentalVMOptions", + // Take all old regions to make sure that the pinned one is included in the collection set. + "-XX:G1MixedGCLiveThresholdPercent=100", + "-XX:G1HeapWastePercent=0", + "-XX:+VerifyAfterGC", + "-Xlog:gc,gc+ergo+cset=trace", + TestObjectPin.class.getName(), + String.valueOf(youngGCsBeforeUnpin)); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + output.shouldHaveExitValue(0); + + TestResultTracker t = new TestResultTracker(output.getStdout(), + expectedMarkingSkipEvents, + expectedRetainedSkipEvents, + expectedDropEvents, + expectedMarkingReclaimEvents, + expectedRetainedReclaimEvents); + t.verify(); + } + +} + +class TestObjectPin { + + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + + public static long pinAndGetAddress(Object o) { + wb.pinObject(o); + return wb.getObjectAddress(o); + } + + public static void unpinAndCompareAddress(Object o, long expectedAddress) { + Asserts.assertEQ(expectedAddress, wb.getObjectAddress(o), "Object has moved during pinning."); + wb.unpinObject(o); + } + + public static void main(String[] args) { + + int youngGCBeforeUnpin = Integer.parseInt(args[0]); + // Remove garbage from VM initialization + wb.fullGC(); + + Object o = new int[100]; + Asserts.assertTrue(!wb.isObjectInOldGen(o), "should not be pinned in old gen"); + + long address = pinAndGetAddress(o); + + // Move pinned object into old gen. That region containing it should be almost completely empty, + // so it will be picked up as collection set candidate. + wb.fullGC(); + Asserts.assertTrue(wb.isObjectInOldGen(o), "Pinned object not in old gen after young GC"); + + // Do a concurrent cycle to move the region into the marking candidates. + wb.g1RunConcurrentGC(); + // Perform the "Prepare Mixed" GC. + wb.youngGC(); + // The object is (still) pinned. Do some configurable young gcs that fail to add it to the + // collection set candidates. + for (int i = 0; i < youngGCBeforeUnpin; i++) { + wb.youngGC(); + } + unpinAndCompareAddress(o, address); + + // Unpinned the object. This next gc should take the region if not dropped. + wb.youngGC(); + } +} + diff --git a/test/hotspot/jtreg/gc/g1/plab/TestPLABEvacuationFailure.java b/test/hotspot/jtreg/gc/g1/plab/TestPLABEvacuationFailure.java index e4ca3f9ea85c1..ecceca21f2b81 100644 --- a/test/hotspot/jtreg/gc/g1/plab/TestPLABEvacuationFailure.java +++ b/test/hotspot/jtreg/gc/g1/plab/TestPLABEvacuationFailure.java @@ -24,7 +24,7 @@ /* * @test TestPLABEvacuationFailure * @bug 8148376 - * @summary Checks PLAB statistics on evacuation failure + * @summary Checks PLAB statistics on evacuation/allocation failure * @requires vm.gc.G1 * @library /test/lib / * @modules java.base/jdk.internal.misc @@ -196,7 +196,7 @@ private static Map getEvacFailureOldStats() { private static List getGcIdPlabEvacFailures(OutputAnalyzer out) { return out.asLines().stream() - .filter(line -> line.contains("(Evacuation Failure)")) + .filter(line -> line.contains("(Evacuation Failure")) .map(line -> LogParser.getGcIdFromLine(line, GC_ID_PATTERN)) .collect(Collectors.toList()); } diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java index 080842c37f90f..beb57b0b40122 100644 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java @@ -140,14 +140,10 @@ public static void main(String[] args) throws Exception { // Even when concurrent unloading is disabled, Full GC has to recover passWith("-XX:ShenandoahGCMode=" + mode, "-XX:ShenandoahGCHeuristics=" + h, "-XX:+ClassUnloading", "-XX:-ClassUnloadingWithConcurrentMark"); - passWith("-XX:ShenandoahGCMode=" + mode, "-XX:ShenandoahGCHeuristics=" + h, "-XX:+ClassUnloading", "-XX:-ClassUnloadingWithConcurrentMark", "-XX:ShenandoahUnloadClassesFrequency=0"); - passWith("-XX:ShenandoahGCMode=" + mode, "-XX:ShenandoahGCHeuristics=" + h, "-XX:+ClassUnloading", "-XX:+ClassUnloadingWithConcurrentMark", "-XX:ShenandoahUnloadClassesFrequency=0"); // Should OOME when unloading forcefully disabled, even if local flags try to enable it back failWith("-XX:ShenandoahGCMode=" + mode, "-XX:ShenandoahGCHeuristics=" + h, "-XX:-ClassUnloading"); failWith("-XX:ShenandoahGCMode=" + mode, "-XX:ShenandoahGCHeuristics=" + h, "-XX:-ClassUnloading", "-XX:+ClassUnloadingWithConcurrentMark"); - failWith("-XX:ShenandoahGCMode=" + mode, "-XX:ShenandoahGCHeuristics=" + h, "-XX:-ClassUnloading", "-XX:+ClassUnloadingWithConcurrentMark", "-XX:ShenandoahUnloadClassesFrequency=1"); - failWith("-XX:ShenandoahGCMode=" + mode, "-XX:ShenandoahGCHeuristics=" + h, "-XX:-ClassUnloading", "-XX:-ClassUnloadingWithConcurrentMark", "-XX:ShenandoahUnloadClassesFrequency=1"); } } } diff --git a/test/hotspot/jtreg/gc/stress/TestJNIBlockFullGC/TestJNIBlockFullGC.java b/test/hotspot/jtreg/gc/stress/TestJNIBlockFullGC/TestJNIBlockFullGC.java deleted file mode 100644 index 6a336e7b9f158..0000000000000 --- a/test/hotspot/jtreg/gc/stress/TestJNIBlockFullGC/TestJNIBlockFullGC.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017 SAP SE and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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.stress.TestJNIBlockFullGC; - -/* - * @test TestJNIBlockFullGC - * @summary Check that in G1 a Full GC to reclaim space can not be blocked out by the GC locker. - * @key randomness - * @requires vm.gc.G1 - * @library /test/lib - * @run main/othervm/native -Xmx64m -XX:+UseG1GC -Xlog:gc=info,gc+alloc=trace -XX:MaxGCPauseMillis=10 gc.stress.TestJNIBlockFullGC.TestJNIBlockFullGC 10 10000 10000 10000 30000 10000 0.7 - */ - -import java.lang.ref.SoftReference; -import java.util.Random; -import jdk.test.lib.Utils; - -public class TestJNIBlockFullGC { - private static final Random rng = Utils.getRandomInstance(); - - static { - System.loadLibrary("TestJNIBlockFullGC"); - } - - public static volatile Object tmp; - - public static volatile boolean hadError = false; - - private static native int TestCriticalArray0(int[] x); - - public static class Node { - public SoftReference next; - long payload1; - long payload2; - long payload3; - long payload4; - - public Node(int load) { - payload1 = payload2 = payload3 = payload4 = load; - } - } - - public static void warmUp(long warmupEndTimeNanos, int size, long seed) { - Random r = new Random(seed); - // First let the GC assume most of our objects will die. - Node[] roots = new Node[size]; - - while (System.nanoTime() - warmupEndTimeNanos < 0) { - int index = (int) (r.nextDouble() * roots.length); - roots[index] = new Node(1); - } - - // Make sure the young generation is empty. - for (int i = 0; i < roots.length; ++i) { - roots[i] = null; - } - } - - public static void runTest(long endTimeNanos, int size, double alive, long seed) { - Random r = new Random(seed); - final int length = 10000; - int[] array1 = new int[length]; - for (int x = 1; x < length; x++) { - array1[x] = x; - } - - Node[] roots = new Node[size]; - try { - int index = 0; - roots[0] = new Node(0); - - while (!hadError && (System.nanoTime() - endTimeNanos < 0)) { - int test_val1 = TestCriticalArray0(array1); - - if (r.nextDouble() > alive) { - tmp = new Node(test_val1); - } else { - index = (int) (r.nextDouble() * roots.length); - - if (roots[index] != null) { - Node node = new Node(test_val1); - node.next = new SoftReference(roots[index]); - roots[index] = node; - } else { - roots[index] = new Node(test_val1); - } - } - } - } catch (OutOfMemoryError e) { - hadError = true; - e.printStackTrace(); - } - } - - private static void joinThreads(Thread[] threads) throws Exception { - for (int i = 0; i < threads.length; i++) { - try { - if (threads[i] != null) { - threads[i].join(); - } - } catch (InterruptedException e) { - e.printStackTrace(); - throw e; - } - } - } - - public static void main(String[] args) throws Exception { - if (args.length < 7){ - System.out.println("Usage: java TestJNIBlockFullGC "); - System.exit(0); - } - - int warmupThreads = Integer.parseInt(args[0]); - System.out.println("# Warmup Threads = " + warmupThreads); - - long warmupDurationNanos = 1_000_000L * Integer.parseInt(args[1]); - System.out.println("WarmUp Duration Millis = " + args[1]); - int warmupIterations = Integer.parseInt(args[2]); - System.out.println("# Warmup Iterations = "+ warmupIterations); - - int mainThreads = Integer.parseInt(args[3]); - System.out.println("# Main Threads = " + mainThreads); - long mainDurationNanos = 1_000_000L * Integer.parseInt(args[4]); - System.out.println("Main Duration Millis = " + args[4]); - int mainIterations = Integer.parseInt(args[5]); - System.out.println("# Main Iterations = " + mainIterations); - - double liveFrac = Double.parseDouble(args[6]); - System.out.println("Live Fraction = " + liveFrac); - - Thread threads[] = new Thread[Math.max(warmupThreads, mainThreads)]; - - System.out.println("Start warm-up threads!"); - long warmupStartTimeNanos = System.nanoTime(); - for (int i = 0; i < warmupThreads; i++) { - long seed = rng.nextLong(); - threads[i] = new Thread() { - public void run() { - warmUp(warmupStartTimeNanos + warmupDurationNanos, warmupIterations, seed); - }; - }; - threads[i].start(); - } - - joinThreads(threads); - - System.gc(); - System.out.println("Keep alive a lot"); - - long startTimeNanos = System.nanoTime(); - for (int i = 0; i < mainThreads; i++) { - long seed = rng.nextLong(); - threads[i] = new Thread() { - public void run() { - runTest(startTimeNanos + mainDurationNanos, mainIterations, liveFrac, seed); - }; - }; - threads[i].start(); - } - System.out.println("All threads started"); - - joinThreads(threads); - - if (hadError) { - throw new RuntimeException("Experienced an OoME during execution."); - } - } -} diff --git a/test/hotspot/jtreg/gc/stress/gclocker/TestExcessGCLockerCollections.java b/test/hotspot/jtreg/gc/stress/gclocker/TestExcessGCLockerCollections.java index deaf08748a636..d18548abdfe18 100644 --- a/test/hotspot/jtreg/gc/stress/gclocker/TestExcessGCLockerCollections.java +++ b/test/hotspot/jtreg/gc/stress/gclocker/TestExcessGCLockerCollections.java @@ -31,6 +31,8 @@ * @requires vm.gc != "Z" * @requires vm.gc != "Epsilon" * @requires vm.gc != "Shenandoah" + * @requires vm.gc != "G1" + * @requires vm.gc != null * @library /test/lib * @modules java.base/jdk.internal.misc * @run driver/timeout=1000 gc.stress.gclocker.TestExcessGCLockerCollections 300 4 2 @@ -151,7 +153,7 @@ public class TestExcessGCLockerCollections { private static final String BAD_LOCKER = locker + " [1-9][0-9]?M"; private static final String[] COMMON_OPTIONS = new String[] { - "-Xmx1G", "-Xms1G", "-Xmn256M", "-Xlog:gc" }; + "-Xmx1G", "-Xms1G", "-Xmn256M", "-Xlog:gc,gc+ergo*=debug,gc+ergo+cset=trace:x.log", "-XX:+UnlockDiagnosticVMOptions", "-XX:+VerifyAfterGC"}; public static void main(String args[]) throws Exception { if (args.length < 3) { diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedCPUSpecificClassSpaceReservation.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedCPUSpecificClassSpaceReservation.java new file mode 100644 index 0000000000000..8a25b1eff88a5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedCPUSpecificClassSpaceReservation.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 the various CPU-specific reservation schemes + * @requires vm.bits == 64 & !vm.graal.enabled & vm.debug == true + * @requires vm.flagless + * @requires (os.family != "windows") & (os.family != "aix") + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver CompressedCPUSpecificClassSpaceReservation + */ + +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jtreg.SkippedException; + +import java.io.IOException; + +public class CompressedCPUSpecificClassSpaceReservation { + // Note: windows: On windows, we currently have the issue that os::reserve_memory_aligned relies on + // os::attempt_reserve_memory_at because VirtualAlloc cannot be unmapped in parts; this precludes use of + // +SimulateFullAddressSpace (VM won't be able to reserve heap). Therefore we exclude the test for windows + // for now. + + private static void do_test(boolean CDS) throws IOException { + // We start the VM with -XX:+SimulateFullAdressSpace, which means the JVM will go through all motions + // of reserving the cds+class space, but never succeed. That means we see every single allocation attempt. + // We start with -Xlog options enabled. The expected output goes like this: + // [0.017s][debug][os,map] reserve_between (range [0x0000000000000000-0x0000000100000000), size 0x41000000, alignment 0x1000000, randomize: 1) + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xshare:" + (CDS ? "on" : "off"), + "-Xmx128m", + "-XX:CompressedClassSpaceSize=128m", + "-Xlog:metaspace*", "-Xlog:metaspace+map=trace", "-Xlog:os+map=trace", + "-XX:+SimulateFullAddressSpace", // So that no resevation attempt will succeed + "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + final String tryReserveForUnscaled = "reserve_between (range [0x0000000000000000-0x0000000100000000)"; + final String tryReserveForZeroBased = "reserve_between (range [0x0000000100000000-0x0000000800000000)"; + final String tryReserveFor16bitMoveIntoQ3 = "reserve_between (range [0x0000000100000000-0x0001000000000000)"; + if (Platform.isAArch64()) { + if (CDS) { + output.shouldNotContain(tryReserveForUnscaled); + } else { + output.shouldContain(tryReserveForUnscaled); + } + output.shouldContain("Trying to reserve at an EOR-compatible address"); + output.shouldNotContain(tryReserveForZeroBased); + output.shouldContain(tryReserveFor16bitMoveIntoQ3); + } else if (Platform.isPPC()) { + if (CDS) { + output.shouldNotContain(tryReserveForUnscaled); + output.shouldNotContain(tryReserveForZeroBased); + } else { + output.shouldContain(tryReserveForUnscaled); + output.shouldContain(tryReserveForZeroBased); + } + output.shouldContain(tryReserveFor16bitMoveIntoQ3); + } else if (Platform.isRISCV64()) { + output.shouldContain(tryReserveForUnscaled); // unconditionally + if (CDS) { + output.shouldNotContain(tryReserveForZeroBased); + // bits 32..44 + output.shouldContain("reserve_between (range [0x0000000100000000-0x0000100000000000)"); + } else { + output.shouldContain(tryReserveForZeroBased); + // bits 32..44, but not lower than zero-based limit + output.shouldContain("reserve_between (range [0x0000000800000000-0x0000100000000000)"); + } + // bits 44..64 + output.shouldContain("reserve_between (range [0x0000100000000000-0xffffffffffffffff)"); + } else if (Platform.isS390x()) { + output.shouldContain(tryReserveForUnscaled); // unconditionally + if (CDS) { + output.shouldNotContain(tryReserveForZeroBased); + } else { + output.shouldContain(tryReserveForZeroBased); + } + output.shouldContain(tryReserveFor16bitMoveIntoQ3); + } else if (Platform.isX64()) { + if (CDS) { + output.shouldNotContain(tryReserveForUnscaled); + output.shouldNotContain(tryReserveForZeroBased); + } else { + output.shouldContain(tryReserveForUnscaled); + output.shouldContain(tryReserveForZeroBased); + } + } else { + throw new RuntimeException("Unexpected platform"); + } + + // In all cases we should have managed to map successfully eventually + if (CDS) { + output.shouldContain("CDS archive(s) mapped at:"); + } else { + output.shouldContain("CDS archive(s) not mapped"); + } + output.shouldContain("Compressed class space mapped at:"); + } + + public static void main(String[] args) throws Exception { + System.out.println("Test with CDS"); + do_test(true); + System.out.println("Test without CDS"); + do_test(false); + } +} diff --git a/test/hotspot/jtreg/runtime/Metaspace/FragmentMetaspace.java b/test/hotspot/jtreg/runtime/Metaspace/FragmentMetaspace.java index 61cebb528b41e..8dcc83a1122f8 100644 --- a/test/hotspot/jtreg/runtime/Metaspace/FragmentMetaspace.java +++ b/test/hotspot/jtreg/runtime/Metaspace/FragmentMetaspace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,16 @@ * @run main/othervm/timeout=200 -Xmx1g FragmentMetaspace */ +/** + * @test id=8320331 + * @bug 8320331 + * @requires vm.debug + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.compiler + * @run main/othervm/timeout=200 -XX:+UnlockDiagnosticVMOptions -XX:+VerifyDuringGC -Xmx1g FragmentMetaspace + */ + import java.io.IOException; import jdk.test.lib.classloader.GeneratingCompilingClassLoader; diff --git a/test/hotspot/jtreg/runtime/Monitor/MonitorUnlinkBatchTest.java b/test/hotspot/jtreg/runtime/Monitor/MonitorUnlinkBatchTest.java new file mode 100644 index 0000000000000..063940ce64314 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Monitor/MonitorUnlinkBatchTest.java @@ -0,0 +1,178 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/* + * @test id=defaults + * @bug 8319048 + * @summary Test the MonitorUnlinkBatch options + * @library /test/lib + * @run driver MonitorUnlinkBatchTest defaults + */ + +/* + * @test id=legal + * @library /test/lib + * @run driver MonitorUnlinkBatchTest legal + */ + +/* + * @test id=illegal + * @library /test/lib + * @run driver MonitorUnlinkBatchTest illegal + */ + +/* + * @test id=aggressive + * @library /test/lib + * @run driver MonitorUnlinkBatchTest aggressive + */ + +/* + * @test id=lazy + * @library /test/lib + * @run driver MonitorUnlinkBatchTest lazy + */ + + +public class MonitorUnlinkBatchTest { + + public static class Test { + // Inflate a lot of monitors, so that threshold heuristics definitely fires + private static final int MONITORS = 10_000; + + // Use a handful of threads to inflate the monitors, to eat the cost of + // wait(1) calls. This can be larger than available parallelism, since threads + // would be time-waiting. + private static final int THREADS = 16; + + private static Thread[] threads; + private static Object[] monitors; + + public static void main(String... args) throws Exception { + monitors = new Object[MONITORS]; + threads = new Thread[THREADS]; + + for (int t = 0; t < THREADS; t++) { + int monStart = t * MONITORS / THREADS; + int monEnd = (t + 1) * MONITORS / THREADS; + threads[t] = new Thread(() -> { + for (int m = monStart; m < monEnd; m++) { + Object o = new Object(); + synchronized (o) { + try { + o.wait(1); + } catch (InterruptedException e) { + } + } + monitors[m] = o; + } + }); + threads[t].start(); + } + + for (Thread t : threads) { + t.join(); + } + + try { + Thread.sleep(10_000); + } catch (InterruptedException ie) { + } + } + } + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + throw new IllegalArgumentException("Expect the test label"); + } + + String test = args[0]; + switch (test) { + case "defaults": + test(""); + break; + + case "legal": + // Legal, even if not useful settings + test("", + "-XX:MonitorDeflationMax=100000", + "-XX:MonitorUnlinkBatch=100001" + ); + break; + + case "illegal": + // Quick tests that should fail on JVM flags verification. + test("outside the allowed range", + "-XX:MonitorUnlinkBatch=-1" + ); + test("outside the allowed range", + "-XX:MonitorUnlinkBatch=0" + ); + break; + + case "aggressive": + // The smallest batch possible. + test("", + "-XX:MonitorUnlinkBatch=1" + ); + break; + + case "lazy": + // The largest batch possible. + test("", + "-XX:MonitorDeflationMax=1000000", + "-XX:MonitorUnlinkBatch=1000000" + ); + break; + + default: + throw new IllegalArgumentException("Unknown test: " + test); + } + } + + public static void test(String msg, String... args) throws Exception { + List opts = new ArrayList<>(); + opts.add("-Xmx128M"); + opts.add("-XX:+UnlockDiagnosticVMOptions"); + opts.add("-XX:GuaranteedAsyncDeflationInterval=100"); + opts.addAll(Arrays.asList(args)); + opts.add("MonitorUnlinkBatchTest$Test"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(opts); + OutputAnalyzer oa = new OutputAnalyzer(pb.start()); + if (msg.isEmpty()) { + oa.shouldHaveExitValue(0); + } else { + oa.shouldNotHaveExitValue(0); + oa.shouldContain(msg); + } + } + +} diff --git a/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java b/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java index e5a38114f6cb5..adccbd011700f 100644 --- a/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java +++ b/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Red Hat, Inc. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -31,59 +31,81 @@ * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail HugeArenaTracking + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary HugeArenaTracking */ import java.util.Random; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.whitebox.WhiteBox; public class HugeArenaTracking { - private static final long GB = 1024 * 1024 * 1024; + private static final long MB = 1024 * 1024; + private static final long GB = MB * 1024; public static void main(String args[]) throws Exception { - OutputAnalyzer output; final WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - long arena1 = wb.NMTNewArena(1024); long arena2 = wb.NMTNewArena(1024); - // Run 'jcmd VM.native_memory summary' - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=2KB, committed=2KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=K" }, + new String[] { "Test (reserved=2KB, committed=2KB)", + "(arena=2KB #2) (at peak)" }); Random rand = Utils.getRandomInstance(); // Allocate 2GB+ from arena long total = 0; while (total < 2 * GB) { - // Cap to 10M - long inc = rand.nextInt(10 * 1024 * 1024); - wb.NMTArenaMalloc(arena1, inc); - total += inc; + wb.NMTArenaMalloc(arena1, MB); + total += MB; } - ProcessBuilder pb2 = new ProcessBuilder(); - // Run 'jcmd VM.native_memory summary' - pb2.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary", "scale=GB"}); - output = new OutputAnalyzer(pb2.start()); - output.shouldContain("Test (reserved=2GB, committed=2GB)"); + // run a report at GB level. We should see our allocations; since they are rounded + // to GB, we expect an exact output match + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=G" }, + new String[] { "Test (reserved=2GB, committed=2GB)", + "(arena=2GB #2) (at peak)" }); + + // Repeat at MB level; we expect the same behavior + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=M" }, + new String[] { "Test (reserved=2048MB, committed=2048MB)", + "(arena=2048MB #2) (at peak)" }); wb.NMTFreeArena(arena1); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1KB, committed=1KB)"); + // Repeat report at GB level. Reserved should be 0 now. Current usage is 1KB, since arena2 is left, but that + // is below GB scale threshold, so should show up as 0. + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=G" }, + new String[] { "Test (reserved=0GB, committed=0GB)", + "(arena=0GB #1) (peak=2GB #2)" }); + + // Same, for MB scale + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=M" }, + new String[] { "Test (reserved=0MB, committed=0MB)", + "(arena=0MB #1) (peak=2048MB #2)" }); + + // At KB level we should see the remaining 1KB. Note that we refrain from testing peak here + // since the number gets fuzzy: it depends on the size of the initially allocated chunk. At MB + // and GB scale, these differences don't matter. + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=K" }, + new String[] { "Test (reserved=1KB, committed=1KB)", + "(arena=1KB #1) (peak=" }); + wb.NMTFreeArena(arena2); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved"); + // Everything free'd, current usage 0, peak should be preserved. + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=G" }, + new String[] { "Test (reserved=0GB, committed=0GB)", + "(arena=0GB #0) (peak=2GB #2)" }); } } diff --git a/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java b/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java index 653f8354e657a..30a03f973bf21 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -33,23 +34,14 @@ * */ -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; - import jdk.test.whitebox.WhiteBox; public class MallocRoundingReportTest { private static long K = 1024; public static void main(String args[]) throws Exception { - OutputAnalyzer output; WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - long[] additionalBytes = {0, 1, 512, 650}; long[] kByteSize = {1024, 2048}; long mallocd_total = 0; @@ -63,17 +55,18 @@ public static void main(String args[]) throws Exception { mallocd_total = wb.NMTMalloc(curKB); // Run 'jcmd VM.native_memory summary', check for expected output // NMT does not track memory allocations less than 1KB, and rounds to the nearest KB - String expectedOut = ("Test (reserved=" + numKB + "KB, committed=" + numKB + "KB)"); - - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldContain(expectedOut); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=" + numKB + "KB, committed=" + numKB + "KB)", + "(malloc=" + numKB + "KB #1) (at peak)" + ); wb.NMTFree(mallocd_total); + // Run 'jcmd VM.native_memory summary', check for expected output - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=" + numKB + "KB #1)" + ); } } } diff --git a/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java b/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java index 55531a68b9670..6af14268c7eba 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -130,7 +131,7 @@ public static void main(String args[]) throws Exception { // All test memory allocated should be released output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output.shouldContain("Test (reserved=0KB, committed=0KB)"); // Verify that tracking level has not been downgraded pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "statistics"}); diff --git a/test/hotspot/jtreg/runtime/NMT/MallocTestType.java b/test/hotspot/jtreg/runtime/NMT/MallocTestType.java index e091ab0f7a7a7..67df864e7b41c 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocTestType.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -32,37 +33,34 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocTestType */ -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class MallocTestType { public static void main(String args[]) throws Exception { - OutputAnalyzer output; WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - // Use WB API to alloc and free with the mtTest type - long memAlloc3 = wb.NMTMalloc(128 * 1024); - long memAlloc2 = wb.NMTMalloc(256 * 1024); - wb.NMTFree(memAlloc3); - long memAlloc1 = wb.NMTMalloc(512 * 1024); - wb.NMTFree(memAlloc2); + long memAlloc3 = wb.NMTMalloc(128 * 1024); // current +128K #1 peak +128K #1 + long memAlloc2 = wb.NMTMalloc(256 * 1024); // current +384K #2 peak +384K #2 + + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[]{"Test (reserved=384KB, committed=384KB)", + "(malloc=384KB #2) (at peak)"}); + + wb.NMTFree(memAlloc3); // current +256K #1 peak +384K #2 + long memAlloc1 = wb.NMTMalloc(512 * 1024); // current +768K #2 peak +768K #2 + wb.NMTFree(memAlloc2); // current +512K #1 peak +768K #2 - // Run 'jcmd VM.native_memory summary' - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=512KB, committed=512KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[]{"Test (reserved=512KB, committed=512KB)", + "(malloc=512KB #1) (peak=768KB #2)"}); // Free the memory allocated by NMTAllocTest - wb.NMTFree(memAlloc1); + wb.NMTFree(memAlloc1); // current 0K #0 peak +768K #2 - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[]{"Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=768KB #2)"}); } } diff --git a/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java b/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java index f89f15a764e19..0c08e07fd2588 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -31,7 +32,7 @@ * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocTrackingVerify + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary MallocTrackingVerify * */ @@ -53,11 +54,6 @@ public class MallocTrackingVerify { public static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String args[]) throws Exception { - OutputAnalyzer output; - - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); Random random = Utils.getRandomInstance(); // Allocate small amounts of memory with random pseudo call stack @@ -74,9 +70,10 @@ public static void main(String args[]) throws Exception { } } - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4KB, committed=4KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=4KB, committed=4KB)", + "(malloc=4KB #" + mallocd_memory.size() + ") (at peak)" + ); // Free for (MallocMemory mem : mallocd_memory) { @@ -84,10 +81,11 @@ public static void main(String args[]) throws Exception { } // Run 'jcmd VM.native_memory summary', check for expected output - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, - "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=4KB #" + + mallocd_memory.size() + ")" + ); + } static class MallocMemory { diff --git a/test/hotspot/jtreg/runtime/NMT/NMTTestUtils.java b/test/hotspot/jtreg/runtime/NMT/NMTTestUtils.java new file mode 100644 index 0000000000000..6cb0d727328d2 --- /dev/null +++ b/test/hotspot/jtreg/runtime/NMT/NMTTestUtils.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 Red Hat, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class NMTTestUtils { + + public static OutputAnalyzer startJcmdVMNativeMemory(String... additional_args) throws Exception { + if (additional_args == null) { + additional_args = new String[] {}; + } + String fullargs[] = new String[3 + additional_args.length]; + fullargs[0] = JDKToolFinder.getJDKTool("jcmd"); + fullargs[1] = Long.toString(ProcessTools.getProcessId()); + fullargs[2] = "VM.native_memory"; + System.arraycopy(additional_args, 0, fullargs, 3, additional_args.length); + ProcessBuilder pb = new ProcessBuilder(); + pb.command(fullargs); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + return output; + } + + public static OutputAnalyzer startJcmdVMNativeMemoryDetail(String... additional_args) throws Exception { + return startJcmdVMNativeMemory("detail"); + } + + public static void runJcmdSummaryReportAndCheckOutput(String[] additional_args, String[] pattern, boolean verbose) throws Exception { + OutputAnalyzer output = startJcmdVMNativeMemory(additional_args); + output.stdoutShouldContainMultiLinePattern(pattern, true); + } + + public static void runJcmdSummaryReportAndCheckOutput(String[] additional_args, String[] pattern) throws Exception { + runJcmdSummaryReportAndCheckOutput(additional_args, pattern, true); + } + + public static void runJcmdSummaryReportAndCheckOutput(String... pattern) throws Exception { + runJcmdSummaryReportAndCheckOutput(null, pattern, true); + } + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB, long peakKB) { + String peakString = (committedKB == peakKB) ? "at peak" : "peak=" + peakKB + "KB"; + output.stdoutShouldContainMultiLinePattern( + "Test (reserved=" + reservedKB + "KB, committed=" + committedKB + "KB)", + "(mmap: reserved=" + reservedKB + "KB, committed=" + committedKB + "KB, " + peakString + ")" + ); + } +} diff --git a/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java b/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java index 2b1faf9fb4314..290984c41854f 100644 --- a/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,12 +29,9 @@ * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ThreadedMallocTestType + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary ThreadedMallocTestType */ -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class ThreadedMallocTestType { @@ -42,13 +40,8 @@ public class ThreadedMallocTestType { public static long memAlloc3; public static void main(String args[]) throws Exception { - OutputAnalyzer output; final WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - Thread allocThread = new Thread() { public void run() { // Alloc memory using the WB api @@ -66,9 +59,10 @@ public void run() { System.out.println("memAlloc3:"+memAlloc3); // Run 'jcmd VM.native_memory summary' - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=896KB, committed=896KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=896KB, committed=896KB)", + "(malloc=896KB #3) (at peak)" + ); Thread freeThread = new Thread() { public void run() { @@ -82,7 +76,9 @@ public void run() { freeThread.start(); freeThread.join(); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=896KB #3)" + ); } } diff --git a/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java b/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java index 4bc6ffc90ccef..60a493ea728d6 100644 --- a/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -31,9 +32,7 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ThreadedVirtualAllocTestType */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class ThreadedVirtualAllocTestType { @@ -45,8 +44,6 @@ public class ThreadedVirtualAllocTestType { public static void main(String args[]) throws Exception { OutputAnalyzer output; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); Thread reserveThread = new Thread() { public void run() { @@ -56,9 +53,8 @@ public void run() { reserveThread.start(); reserveThread.join(); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=512KB, committed=0KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,512, 0); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 512KB for Test"); Thread commitThread = new Thread() { @@ -69,8 +65,8 @@ public void run() { commitThread.start(); commitThread.join(); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=512KB, committed=128KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,512, 128); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed 128KB"); Thread uncommitThread = new Thread() { @@ -81,7 +77,8 @@ public void run() { uncommitThread.start(); uncommitThread.join(); - output = new OutputAnalyzer(pb.start()); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,512, 0); output.shouldContain("Test (reserved=512KB, committed=0KB)"); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed"); @@ -93,9 +90,18 @@ public void run() { releaseThread.start(); releaseThread.join(); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,0, 0); output.shouldNotContain("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved"); } + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); + } + } diff --git a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java index 9cc8c39d897b1..4d691eb920ab9 100644 --- a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java +++ b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java @@ -36,9 +36,7 @@ * */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Platform; import jdk.test.whitebox.WhiteBox; @@ -53,16 +51,10 @@ public static void main(String args[]) throws Exception { long reserveSize = 4 * 1024 * 1024; // 4096KB long addr; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - // reserve addr = wb.NMTReserveMemory(reserveSize); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, - "VM.native_memory", "detail" }); - - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemory("detail"); + checkReservedCommittedSummary(output, 4096, 0); checkReserved(output, addr, reserveSize, "4096KB"); long addrA = addr + (0 * commitSize); @@ -75,8 +67,8 @@ public static void main(String args[]) throws Exception { // commit overlapping ABC, A, B, C wb.NMTCommitMemory(addrA, 3 * commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -84,8 +76,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -93,16 +85,16 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -110,8 +102,8 @@ public static void main(String args[]) throws Exception { // uncommit wb.NMTUncommitMemory(addrA, 3 * commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } // Test discontigous areas @@ -121,8 +113,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrE, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, commitSize, "128KB"); @@ -134,8 +126,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrC, commitSize); wb.NMTUncommitMemory(addrE, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } // Test contiguous areas @@ -144,8 +136,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrA, commitSize); wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "256KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 256); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 2 * commitSize, "256KB"); @@ -154,8 +146,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrA, commitSize); wb.NMTUncommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -163,8 +155,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "256KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 256); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 2 * commitSize, "256KB"); @@ -173,8 +165,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -183,8 +175,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -194,8 +186,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -204,8 +196,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -215,8 +207,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrC, commitSize); wb.NMTUncommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -225,8 +217,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrA, commitSize); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -236,8 +228,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrA, commitSize); wb.NMTUncommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -246,8 +238,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -257,8 +249,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrC, commitSize); wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -267,8 +259,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrA, commitSize); wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -278,8 +270,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrA, commitSize); wb.NMTUncommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -288,8 +280,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -299,20 +291,26 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } // release wb.NMTReleaseMemory(addr, reserveSize); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 0, 0); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); } - public static void checkReservedCommittedSummary(OutputAnalyzer output, String reservedString, String committedString) { - output.shouldContain("Test (reserved=" + reservedString + ", committed=" + committedString + ")"); + // running peak counter + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); } public static void checkReserved(OutputAnalyzer output, long addr, long size, String sizeString) { diff --git a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java index bfa4de6e0c215..ef6ee6f883863 100644 --- a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java +++ b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,10 +33,7 @@ * */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; - import jdk.test.whitebox.WhiteBox; public class VirtualAllocCommitUncommitRecommit { @@ -49,16 +46,10 @@ public static void main(String args[]) throws Exception { long reserveSize = 4 * 1024 * 1024; // 4096KB long addr; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - // reserve addr = wb.NMTReserveMemory(reserveSize); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, - "VM.native_memory", "detail" }); - - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=0KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -76,8 +67,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrD, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=512KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 512); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) @@ -86,9 +77,8 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=256KB)"); - + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 256); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -97,8 +87,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrE, commitSize); wb.NMTCommitMemory(addrF, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=512KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 512); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -106,8 +96,8 @@ public static void main(String args[]) throws Exception { // uncommit A wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=384KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -117,8 +107,8 @@ public static void main(String args[]) throws Exception { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=768KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 768); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -131,17 +121,27 @@ public static void main(String args[]) throws Exception { wb.NMTUncommitMemory(addrE, commitSize); wb.NMTUncommitMemory(addrF, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=0KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); // release wb.NMTReleaseMemory(addr, reserveSize); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 0, 0); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); } + + // running peak counter + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); + } } diff --git a/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java b/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java index 081ba6aafb664..f4321e80b220f 100644 --- a/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,9 +32,7 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail VirtualAllocTestType */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class VirtualAllocTestType { @@ -44,36 +42,120 @@ public static void main(String args[]) throws Exception { OutputAnalyzer output; long commitSize = 128 * 1024; long reserveSize = 256 * 1024; - long addr; + long addr1, addr2; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); + String info = "start"; - addr = wb.NMTReserveMemory(reserveSize); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail"}); + try { + // ------ + // Reserve first mapping + addr1 = wb.NMTReserveMemory(reserveSize); + info = "reserve 1: addr1=" + addr1; - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=256KB, committed=0KB)"); - output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 256KB for Test"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 256, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); - wb.NMTCommitMemory(addr, commitSize); + // ------ + // Reserve second mapping + addr2 = wb.NMTReserveMemory(reserveSize); + info = "reserve 2: addr2=" + addr2; + // If the second mapping happens to be adjacent to the first mapping, reserve another mapping and release the second mapping; for + // this test, we want to see two disjunct mappings. + if (addr2 == addr1 + reserveSize) { + long tmp = wb.NMTReserveMemory(reserveSize); + wb.NMTReleaseMemory(addr2, reserveSize); + addr2 = tmp; + } - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=256KB, committed=128KB)"); - output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed 128KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); - wb.NMTUncommitMemory(addr, commitSize); + // ------ + // Now commit the first mapping + wb.NMTCommitMemory(addr1, commitSize); + info = "commit 1"; + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 128); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=256KB, committed=0KB)"); - output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed"); + // ------ + // Now commit the second mapping + wb.NMTCommitMemory(addr2, commitSize); + info = "commit 2"; - wb.NMTReleaseMemory(addr, reserveSize); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 256); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); - output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved"); + // ------ + // Now uncommit the second mapping + wb.NMTUncommitMemory(addr2, commitSize); + info = "uncommit 2"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 128); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + // ------ + // Now uncommit the first mapping + wb.NMTUncommitMemory(addr1, commitSize); + info = "uncommit 1"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + // ---------- + // Release second mapping + wb.NMTReleaseMemory(addr2, reserveSize); + info = "release 2"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 256, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + // ---------- + // Release first mapping + wb.NMTReleaseMemory(addr1, reserveSize); + info = "release 1"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 0, 0); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + } catch (Exception e) { + throw new RuntimeException(e.getMessage() + " (" + info + ")"); + } + } + + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); } } diff --git a/test/hotspot/jtreg/runtime/posixSig/TestPosixSig.java b/test/hotspot/jtreg/runtime/posixSig/TestPosixSig.java index 1a9a283f740d8..337071269354f 100644 --- a/test/hotspot/jtreg/runtime/posixSig/TestPosixSig.java +++ b/test/hotspot/jtreg/runtime/posixSig/TestPosixSig.java @@ -44,8 +44,17 @@ public static void main(String[] args) throws Throwable { if (args.length == 0) { // Create a new java process for the TestPsig Java/JNI test. + // We run the VM in interpreted mode, because the JIT might mark + // a Java method as not-entrant, which means turning the first instruction + // into an illegal one. Calling such a method after establishing + // the new SIGILL signal handler with TestPosixSig.changeSigActionFor(4) + // below, but before the JNI checker noted and reacted on this signal handler + // modification, the JVM may crash or hang in an endless loop, where the + // illegal instruction will be continously executed, raising SIGILL, and + // the signal handler will return to the illegal instruction again... ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:+CheckJNICalls", + "-Xint", "-Djava.library.path=" + libpath + ":.", "TestPosixSig", "dummy"); diff --git a/test/hotspot/jtreg/serviceability/jvmti/thread/GetStackTrace/GetStackTraceAndRetransformTest/GetStackTraceAndRetransformTest.java b/test/hotspot/jtreg/serviceability/jvmti/thread/GetStackTrace/GetStackTraceAndRetransformTest/GetStackTraceAndRetransformTest.java new file mode 100644 index 0000000000000..eb7d30124cb75 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/thread/GetStackTrace/GetStackTraceAndRetransformTest/GetStackTraceAndRetransformTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, Datadog, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8313816 + * @summary Test that a sequence of method retransformation and stacktrace capture while the old method + * version is still on stack does not lead to a crash when that method's jmethodID is used as + * an argument for JVMTI functions. + * @requires vm.jvmti + * @requires vm.flagless + * @library /test/lib + * @build jdk.test.whitebox.WhiteBox + * @modules java.instrument + * java.compiler + * @compile GetStackTraceAndRetransformTest.java + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main RedefineClassHelper + * @run main/othervm/native -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -javaagent:redefineagent.jar -agentlib:GetStackTraceAndRetransformTest GetStackTraceAndRetransformTest + */ + +import jdk.test.whitebox.WhiteBox; + +class Transformable { + static final String newClass = """ + class Transformable { + static final String newClass = ""; + static void redefineAndStacktrace() throws Exception {} + static void stacktrace() throws Exception { + capture(Thread.currentThread()); + } + public static native void capture(Thread thread); + } + """; + static void redefineAndStacktrace() throws Exception { + // This call will cause the class to be retransformed. + // However, this method is still on stack so the subsequent attempt to capture the stacktrace + // will result into this frame being identified by the jmethodID of the previous method version. + RedefineClassHelper.redefineClass(Transformable.class, newClass); + capture(Thread.currentThread()); + } + + static void stacktrace() throws Exception { + } + + public static native void capture(Thread thread); +} + +public class GetStackTraceAndRetransformTest { + public static void main(String args[]) throws Throwable { + initialize(Transformable.class); + + Transformable.redefineAndStacktrace(); + Transformable.stacktrace(); + + WhiteBox.getWhiteBox().cleanMetaspaces(); + check(2); + } + + public static native void initialize(Class target); + public static native void check(int expected); +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/thread/GetStackTrace/GetStackTraceAndRetransformTest/libGetStackTraceAndRetransformTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/thread/GetStackTrace/GetStackTraceAndRetransformTest/libGetStackTraceAndRetransformTest.cpp new file mode 100644 index 0000000000000..3be6dcf824f86 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/thread/GetStackTrace/GetStackTraceAndRetransformTest/libGetStackTraceAndRetransformTest.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, Datadog, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include "jvmti.h" +#include "jvmti_common.h" +#include "../get_stack_trace.h" + + +extern "C" { + +static jvmtiEnv *jvmti = NULL; +static jmethodID* ids = NULL; +static int ids_size = 0; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + jint res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1); + if (res != JNI_OK || jvmti == NULL) { + printf("Wrong result of a valid call to GetEnv!\n"); + return JNI_ERR; + } + ids = (jmethodID*)malloc(sizeof(jmethodID) * 10); + return JNI_OK; +} + +JNIEXPORT void JNICALL +Java_GetStackTraceAndRetransformTest_initialize(JNIEnv *env, jclass cls, jclass tgt) { + // we need to force jmethodids to be created for the methods we are going to retransform + env->GetStaticMethodID(tgt, "redefineAndStacktrace", "()V"); + env->GetStaticMethodID(tgt, "stacktrace", "()V"); +} + +JNIEXPORT void JNICALL +Java_Transformable_capture(JNIEnv *env, jclass cls, jthread thread) { + jint count; + const int MAX_NUMBER_OF_FRAMES = 32; + jvmtiFrameInfo frames[MAX_NUMBER_OF_FRAMES]; + + jvmtiError err = jvmti->GetStackTrace(thread, 0, MAX_NUMBER_OF_FRAMES, frames, &count); + check_jvmti_status(env, err, "GetStackTrace failed."); + + ids[ids_size++] = frames[1].method; +} + +JNIEXPORT void JNICALL +Java_GetStackTraceAndRetransformTest_check(JNIEnv *jni, jclass cls, jint expected) { + if (ids_size != expected) { + fprintf(stderr, "Unexpected number methods captured: %d (expected %d)\n", ids_size, expected); + exit(2); + } + for (int i = 0; i < ids_size; i++) { + jclass rslt = NULL; + char* class_name = NULL; + jvmti->GetMethodDeclaringClass(ids[i], &rslt); + if (rslt != NULL) { + jvmti->GetClassSignature(rslt, &class_name, NULL); + } + } +} +} diff --git a/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread007.java b/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread007.java index 07031a783bfd2..26388c28d0a6b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread007.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread007.java @@ -32,7 +32,7 @@ * Try to start the given number of threads starting simultaneously * when notifyall() is signaled at the stopLine object. * - * @run main/othervm nsk.stress.thread.thread007 500 2m 5s + * @run main/othervm/timeout=300 nsk.stress.thread.thread007 500 2m 5s */ package nsk.stress.thread; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread008.java b/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread008.java index 40a76e00d11f8..2cbc198a69348 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread008.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/stress/thread/thread008.java @@ -33,7 +33,7 @@ * starting simultaneously when notifyall() is signaled at the * stopLine object. * - * @run main/othervm nsk.stress.thread.thread008 500 2m 5s + * @run main/othervm/timeout=300 nsk.stress.thread.thread008 500 2m 5s */ package nsk.stress.thread; diff --git a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPPolicyManager.java b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPPolicyManager.java index 4d0598696f356..2940045352c85 100644 --- a/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPPolicyManager.java +++ b/test/jaxp/javax/xml/jaxp/libs/jaxp/library/JAXPPolicyManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,6 +45,7 @@ * This is a base class that every test class must extend if it needs to be run * with security mode. */ +@SuppressWarnings("removal") public class JAXPPolicyManager { /* * Backing up policy. @@ -161,6 +162,7 @@ void removeTmpPermission(int index) { * Simple Policy class that supports the required Permissions to validate the * JAXP concrete classes. */ +@SuppressWarnings("removal") class TestPolicy extends Policy { private final static Set TEST_JARS = Set.of("jtreg.*jar", "javatest.*jar", "testng.*jar", "jcommander.*jar"); diff --git a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java index 5ddacba953b8b..e22c1edf6c295 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/catalog/CatalogSupportBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -371,7 +371,9 @@ public void testValidation(boolean setUseCatalog, boolean useCatalog, String cat if (setUseCatalog) { factory.setFeature(XMLConstants.USE_CATALOG, useCatalog); } - factory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + if (catalog != null) { + factory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + } Schema schema = factory.newSchema(new StreamSource(new StringReader(xsd))); success("XMLSchema.dtd and datatypes.dtd are resolved."); @@ -472,7 +474,9 @@ SAXParser getSAXParser(boolean setUseCatalog, boolean useCatalog, String catalog } SAXParser parser = spf.newSAXParser(); - parser.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + if (catalog != null) { + parser.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + } return parser; } @@ -495,7 +499,9 @@ XMLReader getXMLReader(boolean setUseCatalog, boolean useCatalog, String catalog if (setUseCatalog) { reader.setFeature(XMLConstants.USE_CATALOG, useCatalog); } - reader.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + if (catalog != null) { + reader.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + } return reader; } @@ -566,7 +572,9 @@ StAXSource getStaxSource(String xmlFile, String xmlFileId, boolean setUseCatalog if (setUseCatalog) { xif.setProperty(XMLConstants.USE_CATALOG, useCatalog); } - xif.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + if (catalog != null) { + xif.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); + } ss = new StAXSource(xif.createXMLEventReader( xmlFileId, new FileInputStream(xmlFile))); } catch (Exception e) {} @@ -1013,6 +1021,7 @@ public Source resolve(String href, String base) throws TransformerException { * Simple policy implementation that grants a set of permissions to all code * sources and protection domains. */ + @SuppressWarnings("removal") static class SimplePolicy extends Policy { private final Permissions perms; diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/CatalogTestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/CatalogTestBase.java new file mode 100644 index 0000000000000..f3f1209ce8244 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/CatalogTestBase.java @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.catalog; + +import common.util.TestBase; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.SAXParser; +import javax.xml.stream.XMLInputFactory; +import javax.xml.transform.TransformerFactory; +import javax.xml.validation.SchemaFactory; +//import org.testng.annotations.DataProvider; + +/** + * Tests the JDK Catalog + */ +public class CatalogTestBase extends TestBase { + /* + * DataProvider for testing configuring properties for parsers. + * + * Fields: + * file, FSP, state of setting, config file, system property, api property, + * Custom Catalog, error expected, error code or expected result + */ + //@DataProvider(name = "configWCatalogForParsers") + public Object[][] getConfigs(Processor processor) { + // file with an external DTD that's not in JdkCatalog + String fileDTDNotInC = "properties1.xml"; + // file with an external DTD that's in the Custom Catalog + String fileDTDInCC = "test.xml"; + // file with an external DTD that's in JdkCatalog + String javaDTD = "properties.xml"; + // file with an external DTD thats in the Custom Catalog + String w3cDTD = "xhtml11.xml"; + + // error code when CATALOG=strict; The cause for DOM + String errCode = "JAXP09040001"; + + // error (not from catalog) is expect when CATALOG=continue + boolean isErrExpected = true; + String expected1 = UNKNOWN_HOST; + + // expected when reference is resolved by Catalog + String expected3 = "", expected4 = ""; + switch (processor) { + case SAX: + errCode = "JAXP00090001"; + break; + case STAX: + errCode = "JAXP00090001"; + //errCode = "JAXP00090001"; + // StAX is non-validating parser + isErrExpected = false; + expected1 = ".*[\\w\\s]*(value1)[\\w\\s]*.*"; + expected3 = "Minimal XHTML 1.1 DocumentThis is a minimal XHTML 1.1 document."; + expected4 = ".*(123)[\\w\\s]*.*"; + break; + default: + break; + } + + return new Object[][]{ + // Case 1: external reference not in the JDKCatalog + /** + * Case 1-1: default setting; no Config file; Catalog: continue (by default) + * Expect: error as the parser continues and tries to access an invalid site + * java.net.UnknownHostException: invalid.site.com + */ + {fileDTDNotInC, null, null, null, null, null, null, isErrExpected, expected1}, + + /** + * Case 1-2: set JDK Catalog to strict in a Config file + * Expect: Exception since the external reference is not in the Catalog + * Error Msg: + * [Fatal Error] properties1.xml:2:75: JAXP00090001: The CatalogResolver is enabled with the catalog "JdkCatalog.xml", but a CatalogException is returned. + * org.xml.sax.SAXException: javax.xml.catalog.CatalogException: JAXP09040001: No match found for publicId 'null' and systemId 'http://invalid.site.com/dtd/properties1.dtd'. + * javax.xml.catalog.CatalogException: JAXP09040001: No match found for publicId 'null' and systemId 'http://invalid.site.com/dtd/properties1.dtd'. + */ + {fileDTDNotInC, null, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, null, true, errCode}, + + /** + * Case 1-3: set CATALOG back to continue through the System Property + * Expect: error as the parser continues and tries to access an invalid site + * java.net.UnknownHostException: invalid.site.com + */ + {fileDTDNotInC, null, PropertyState.CONFIG_FILE_SYSTEM, Properties.CONFIG_FILE_CATALOG_STRICT, new Properties[]{Properties.CATALOG0}, null, null, isErrExpected, expected1}, + + /** + * Case 1-4: override the settings in Case 3 with the API property, and set Catalog to strict + * Expect: Exception since the external reference is not in the Catalog + */ + {fileDTDNotInC, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, new Properties[]{Properties.CATALOG0}, new Properties[]{Properties.CATALOG2}, null, true, errCode}, + + // Case 2: external reference in the JDKCatalog + /** + * Case 2-1: set CATALOG to strict in a Config file + * Compare to: case 1-2 + * Expect: pass without error + */ + {javaDTD, null, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, null, false, expected1}, + + /** + * Case 2-2: override the settings in Case 3 with the API property, and set Catalog to strict + * Compare to: case 1-4 + * Expect: pass without error + */ + {javaDTD, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, new Properties[]{Properties.CATALOG0}, new Properties[]{Properties.CATALOG2}, null, false, expected1}, + + // Case 3: external reference in the Custom Catalog + /** + * Case 3-1: set CATALOG to strict in a Config file + * Compare to: case 1-2, would have resulted in an error without the + * custom catalog + * Expect: pass without error because the external reference is in + * the custom catalog + */ + {fileDTDInCC, null, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, CustomCatalog.STRICT, false, expected4}, + + /** + * Case 3-2: override the settings in Case 3 with the API property, and set Catalog to strict + * Compare to: case 1-4, would have resulted in an error without the + * custom catalog + * Expect: pass without error + */ + {fileDTDInCC, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, new Properties[]{Properties.CATALOG0}, new Properties[]{Properties.CATALOG2}, CustomCatalog.STRICT, false, expected4}, + + // Case 4: Parameter Entity reference + /** + * Case 4-1: set CATALOG to strict in a Config file + * Compare to: case 1-2, would have resulted in an error since the external + * reference can not be found + * Expect: pass without error because the external reference is in + * the custom catalog + */ + {"testExternalParameter.xml", null, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, CustomCatalog.STRICT, false, expected1}, + + // Case 5: resolve xInclude with the Custom Catalog + /** + * Case 5-1: set CATALOG to strict in a Config file + * Compare to: case 1-2, would have resulted in an error without the + * custom catalog + * Expect: pass without error because the external reference is in + * the custom catalog + */ + {"XI_roottest.xml", null, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, CustomCatalog.STRICT, false, ""}, + + }; + } + + /* + * DataProvider for testing configuring properties for validation or transform. + * + * Fields: + * xml file, xsd or xsl file, FSP, state of setting, config file, system property, + * api property, Custom Catalog, error expected, error code or expected result + */ + //@DataProvider(name = "validationOrTransform") + public Object[][] getConfig(String m) { + // Schema Import + String xmlFile = "XSDImport_company.xsd"; + String xsdOrXsl = null; + String expected = ""; + String errCode = "JAXP00090001"; + + switch (m) { + case "SchemaTest2": + // Schema Include + xmlFile = "XSDInclude_company.xsd"; + break; + case "Validation": + // Schema Location + xmlFile = "val_test.xml"; + break; + case "Stylesheet": + errCode = "JAXP09040001"; + xmlFile = "XSLDTD.xsl"; + break; + case "Transform": + xmlFile = "XSLPI.xml"; + errCode = "JAXP09040001"; + xsdOrXsl = "" + + "" + + "" + + "" + +"]>" + + "" + + "" + + " "; + break; + default: + break; + } + + return new Object[][]{ + // Case 1: external reference not in the JDKCatalog + /** + * Case 1-1: default setting; no Config file; Catalog: continue + * Expect: pass without error + */ + {xmlFile, xsdOrXsl, null, null, null, null, null, null, false, expected}, + + /** + * Case 1-2: set CATALOG to strict in a Config file + * Expect: Exception since the external reference is not in the Catalog + * Sample Error Msg: + * org.xml.sax.SAXParseException; systemId: file:path/XSDImport_company.xsd; + * lineNumber: 10; columnNumber: 11; + * JAXP00090001: The CatalogResolver is enabled with the catalog "JdkCatalog.xml", + * but a CatalogException is returned. + */ + {xmlFile, xsdOrXsl, null, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, null, true, errCode}, + + /** + * Case 1-3: set CATALOG back to continue through the System Property + * Expect: pass without error + */ + {xmlFile, xsdOrXsl, null, PropertyState.CONFIG_FILE_SYSTEM, Properties.CONFIG_FILE_CATALOG_STRICT, new Properties[]{Properties.CATALOG0}, null, null, false, expected}, + + /** + * Case 1-4: override the settings in Case 3 with the API property, and set Catalog to strict + * Expect: Exception since the external reference is not in the Catalog + */ + {xmlFile, xsdOrXsl, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, new Properties[]{Properties.CATALOG0}, new Properties[]{Properties.CATALOG2}, null, true, errCode}, + + /** + * Case 1-5: use Custom Catalog to resolve external references + * Expect: pass without error + */ + {xmlFile, xsdOrXsl, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, new Properties[]{Properties.CATALOG0}, new Properties[]{Properties.CATALOG2}, CustomCatalog.STRICT, false, expected}, + + }; + } + +// @Test(dataProvider = "configWCatalogForParsers", priority=0) + public void testDOM(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + DocumentBuilderFactory dbf = getDBF(fsp, state, config, sysProp, apiProp, cc); + process(filename, dbf, expectError, error); + } + +// @Test(dataProvider = "configWCatalogForParsers") + public void testSAX(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SAXParser parser = getSAXParser(fsp, state, config, sysProp, apiProp, cc); + process(filename, parser, expectError, error); + } + +// @Test(dataProvider = "configWCatalogForParsers") + public void testStAX(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + XMLInputFactory xif = getXMLInputFactory(state, config, sysProp, apiProp, cc); + process(filename, xif, expectError, error); + } + +// @Test(dataProvider = "validationOrTransform") + public void testSchema1(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SchemaFactory sf = getSchemaFactory(fsp, state, config, sysProp, apiProp, cc); + process(filename, sf, expectError, error); + } + +// @Test(dataProvider = "validationOrTransform") + public void testSchema2(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + testSchema1(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + } + +// @Test(dataProvider = "validationOrTransform") + public void testValidation(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SchemaFactory sf = getSchemaFactory(fsp, state, config, sysProp, apiProp, cc); + validate(filename, sf, expectError, error); + } + +// @Test(dataProvider = "validationOrTransform") + public void testStylesheet(String filename, String xsl, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + TransformerFactory tf = getTransformerFactory(fsp, state, config, sysProp, apiProp, cc); + process(filename, tf, expectError, error); + } + +// @Test(dataProvider = "validationOrTransform") + public void testTransform(String filename, String xsl, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + TransformerFactory tf = getTransformerFactory(fsp, state, config, sysProp, apiProp, cc); + transform(filename, xsl, tf, expectError, error); + } + + // parameters in the same order as the test method + String filename; String xsd; String xsl; Properties fsp; PropertyState state; + Properties config; Properties[] sysProp; Properties[] apiProp; CustomCatalog cc; + boolean expectError; String error; + + // Maps the DataProvider array to individual parameters + public void paramMap(Processor processor, String method, String index) { + int i = 0; + Object[][] params; + if (processor == Processor.VALIDATOR || + processor == Processor.TRANSFORMER) { + params = getConfig(method); + i = 1; + } else { + params = getConfigs(processor); + } + Object[] param = params[Integer.parseInt(index)]; + filename = (String)param[0]; + if (processor == Processor.VALIDATOR) { + xsd = (String)param[i]; + } else if (processor == Processor.TRANSFORMER) { + xsl = (String)param[i]; + } + fsp = (Properties)param[i + 1]; + state = (PropertyState)param[i + 2]; + config = (Properties)param[i + 3]; + sysProp = (Properties[])param[i + 4]; + apiProp = (Properties[])param[i + 5]; + cc = (CustomCatalog)param[i + 6]; + expectError = (boolean)param[i + 7]; + error = (String)param[i + 8]; + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/DOMTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/DOMTest.java new file mode 100644 index 0000000000000..b5eeed4829014 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/DOMTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.catalog; + +/** + * @test @bug 8306055 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.catalog.DOMTest 0 // verifies default setting catalog.resolve=allow + * @run driver common.catalog.DOMTest 1 // verifies overriding with catalog.resolve=strict in a config file + * @run driver common.catalog.DOMTest 2 // verifies overriding with system property + * @run driver common.catalog.DOMTest 3 // verifies overriding with factory setting (catalog.resolve=strict) + * @run driver common.catalog.DOMTest 4 // verifies external DTD resolution with the JDK Catalog while resolve=strict in config file + * @run driver common.catalog.DOMTest 5 // verifies external DTD resolution with the JDK Catalog while resolve=strict in API setting + * @run driver common.catalog.DOMTest 6 // verifies external DTD resolution with a custom Catalog while resolve=strict in config file + * @run driver common.catalog.DOMTest 7 // verifies external DTD resolution with a custom Catalog while resolve=strict in API setting + * @run driver common.catalog.DOMTest 8 // verifies external parameter are resolved with a custom Catalog though resolve=strict in API setting + * @run driver common.catalog.DOMTest 9 // verifies XInclude are resolved with a custom Catalog though resolve=strict in API setting + * @summary verifies DOM's support of the JDK Catalog. + */ +public class DOMTest extends CatalogTestBase { + public static void main(String args[]) throws Exception { + new DOMTest().run(args[0]); + } + + public void run(String index) throws Exception { + paramMap(Processor.DOM, null, index); + super.testDOM(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SAXTest.java new file mode 100644 index 0000000000000..109568de28766 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SAXTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.catalog; + +/** + * @test @bug 8306055 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.catalog.SAXTest 0 // verifies default setting catalog.resolve=allow + * @run driver common.catalog.SAXTest 1 // verifies overriding with catalog.resolve=strict in a config file + * @run driver common.catalog.SAXTest 2 // verifies overriding with system property + * @run driver common.catalog.SAXTest 3 // verifies overriding with factory setting (catalog.resolve=strict) + * @run driver common.catalog.SAXTest 4 // verifies external DTD resolution with the JDK Catalog while resolve=strict in config file + * @run driver common.catalog.SAXTest 5 // verifies external DTD resolution with the JDK Catalog while resolve=strict in API setting + * @run driver common.catalog.SAXTest 6 // verifies external DTD resolution with a custom Catalog while resolve=strict in config file + * @run driver common.catalog.SAXTest 7 // verifies external DTD resolution with a custom Catalog while resolve=strict in API setting + * @run driver common.catalog.SAXTest 8 // verifies external parameter are resolved with a custom Catalog though resolve=strict in API setting + * @run driver common.catalog.SAXTest 9 // verifies XInclude are resolved with a custom Catalog though resolve=strict in API setting + * @summary verifies DOM's support of the JDK Catalog. + + */ +public class SAXTest extends CatalogTestBase { + public static void main(String args[]) throws Exception { + new SAXTest().run(args[0]); + } + + public void run(String index) throws Exception { + paramMap(Processor.SAX, null, index); + super.testSAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SchemaTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SchemaTest.java new file mode 100644 index 0000000000000..698a5882d615a --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SchemaTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.catalog; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.catalog.SchemaTest SchemaTest1 0 // verifies default setting dtd.support=allow + * @run driver common.catalog.SchemaTest SchemaTest1 1 // verifies overriding with config file + * @run driver common.catalog.SchemaTest SchemaTest1 2 // verifies overriding with system property + * @run driver common.catalog.SchemaTest SchemaTest1 3 // verifies overriding with factory setting (DTD=deny) + * @run driver common.catalog.SchemaTest SchemaTest1 4 // verifies DTD=ignore + * @run driver common.catalog.SchemaTest SchemaTest2 0 // verifies default setting dtd.support=allow + * @run driver common.catalog.SchemaTest SchemaTest2 1 // verifies overriding with config file + * @run driver common.catalog.SchemaTest SchemaTest2 2 // verifies overriding with system property + * @run driver common.catalog.SchemaTest SchemaTest2 3 // verifies overriding with factory setting (DTD=deny) + * @run driver common.catalog.SchemaTest SchemaTest2 4 // verifies DTD=ignore + * @run driver common.catalog.SchemaTest Validation 0 // verifies default setting dtd.support=allow + * @run driver common.catalog.SchemaTest Validation 1 // verifies overriding with config file + * @run driver common.catalog.SchemaTest Validation 2 // verifies overriding with system property + * @run driver common.catalog.SchemaTest Validation 3 // verifies overriding with factory setting (DTD=deny) + * @run driver common.catalog.SchemaTest Validation 4 // verifies DTD=ignore + * @summary verifies Schema and Validation's support of the property jdk.xml.dtd.support. + */ +public class SchemaTest extends CatalogTestBase { + + public static void main(String args[]) throws Exception { + new SchemaTest().run(args[0], args[1]); + } + + public void run(String method, String index) throws Exception { + paramMap(Processor.VALIDATOR, method, index); + switch (method) { + case "SchemaTest1": + super.testSchema1(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "SchemaTest2": + super.testSchema2(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "Validation": + super.testValidation(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/StAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/StAXTest.java new file mode 100644 index 0000000000000..d5a530bac2505 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/StAXTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.catalog; + +/** + * @test @bug 8306055 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.catalog.StAXTest 0 // verifies default setting catalog.resolve=allow + * @run driver common.catalog.StAXTest 1 // verifies overriding with catalog.resolve=strict in a config file + * @run driver common.catalog.StAXTest 2 // verifies overriding with system property + * @run driver common.catalog.StAXTest 3 // verifies overriding with factory setting (catalog.resolve=strict) + * @run driver common.catalog.StAXTest 4 // verifies external DTD resolution with the JDK Catalog while resolve=strict in config file + * @run driver common.catalog.StAXTest 5 // verifies external DTD resolution with the JDK Catalog while resolve=strict in API setting + * @run driver common.catalog.StAXTest 6 // verifies external DTD resolution with a custom Catalog while resolve=strict in config file + * @run driver common.catalog.StAXTest 7 // verifies external DTD resolution with a custom Catalog while resolve=strict in API setting + * @run driver common.catalog.StAXTest 8 // verifies external parameter are resolved with a custom Catalog though resolve=strict in API setting + * @run driver common.catalog.StAXTest 9 // verifies XInclude are resolved with a custom Catalog though resolve=strict in API setting + * @summary verifies DOM's support of the JDK Catalog. + */ +public class StAXTest extends CatalogTestBase { + public static void main(String args[]) throws Exception { + new StAXTest().run(args[0]); + } + + public void run(String index) throws Exception { + paramMap(Processor.STAX, null, index); + super.testStAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/TransformTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/TransformTest.java new file mode 100644 index 0000000000000..ff88a0054482b --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/TransformTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.catalog; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.catalog.TransformTest Stylesheet 0 // verifies default setting dtd.support=allow + * @run driver common.catalog.TransformTest Stylesheet 1 // verifies overriding with config file + * @run driver common.catalog.TransformTest Stylesheet 2 // verifies overriding with system property + * @run driver common.catalog.TransformTest Stylesheet 3 // verifies overriding with factory setting (DTD=deny) + * @run driver common.catalog.TransformTest Stylesheet 4 // verifies DTD=ignore + * @run driver common.catalog.TransformTest Transform 0 // verifies default setting dtd.support=allow + * @run driver common.catalog.TransformTest Transform 1 // verifies overriding with config file + * @run driver common.catalog.TransformTest Transform 2 // verifies overriding with system property + * @run driver common.catalog.TransformTest Transform 3 // verifies overriding with factory setting (DTD=deny) + * @run driver common.catalog.TransformTest Transform 4 // verifies DTD=ignore + * @summary verifies Transform's support of the property jdk.xml.dtd.support. + */ +public class TransformTest extends CatalogTestBase { + + public static void main(String args[]) throws Exception { + new TransformTest().run(args[0], args[1]); + } + + public void run(String method, String index) throws Exception { + paramMap(Processor.TRANSFORMER, method, index); + switch (method) { + case "Stylesheet": + super.testStylesheet(filename, xsl, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "Transform": + super.testTransform(filename, xsl, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml new file mode 100644 index 0000000000000..216b3b81543cc --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/XSLDTD.dtd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/XSLDTD.dtd new file mode 100644 index 0000000000000..4317283e3d10b --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/XSLDTD.dtd @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/paramEntity.dtd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/paramEntity.dtd new file mode 100644 index 0000000000000..61768c96412e5 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/paramEntity.dtd @@ -0,0 +1,2 @@ + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/test.dtd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/test.dtd new file mode 100644 index 0000000000000..eb32c89a739e7 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/dtds/test.dtd @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_red.dtd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_red.dtd new file mode 100644 index 0000000000000..147fe93bde0ee --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_red.dtd @@ -0,0 +1,4 @@ + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_simple.xml b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_simple.xml new file mode 100644 index 0000000000000..da7b962b62c6f --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_simple.xml @@ -0,0 +1,18 @@ + + + + + + + + text + + + + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_test2.xml b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_test2.xml new file mode 100644 index 0000000000000..96c173a833ff2 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_test2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_utf8.xml b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_utf8.xml new file mode 100644 index 0000000000000..8b591c92888ad --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xinclude/XI_utf8.xml @@ -0,0 +1,4 @@ + + value1 trjsagdkasgdhasdgashgdhsadgashdg + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDImport_person.xsd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDImport_person.xsd new file mode 100644 index 0000000000000..ac5d718f5d31d --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDImport_person.xsd @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDImport_product.xsd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDImport_product.xsd new file mode 100644 index 0000000000000..73385552b4208 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDImport_product.xsd @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDInclude_person.xsd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDInclude_person.xsd new file mode 100644 index 0000000000000..f4a5586bf7766 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDInclude_person.xsd @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDInclude_product.xsd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDInclude_product.xsd new file mode 100644 index 0000000000000..0c4ee7a2b9282 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/XSDInclude_product.xsd @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/val_test.xsd b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/val_test.xsd new file mode 100644 index 0000000000000..562eb6b2291f1 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/xsds/val_test.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/config/files/catalog2.properties b/test/jaxp/javax/xml/jaxp/unittest/common/config/files/catalog2.properties new file mode 100644 index 0000000000000..06c25c3ba816c --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/config/files/catalog2.properties @@ -0,0 +1,36 @@ +################################################################################ +# XML Library (java.xml) Configuration File +# +# This file is in java.util.Properties format and typically located in the conf +# directory of the Java installation. It may contain key/value pairs for specifying +# the implementation class of a factory and/or properties that have corresponding +# system properties. +# +# This file can be replaced by specifying a filename with the java.xml.config.file +# system property. For example java -Djava.xml.config.file=myfile +################################################################################ + +# ---- Config File: for testing the CATALOG property ---- +# +# strict: report error if not resolved by the JDK Catalog +jdk.xml.jdkcatalog.resolve=strict +# Enable Extension Functions +jdk.xml.enableExtensionFunctions=true +# Disallow overriding the default parser +jdk.xml.overrideDefaultParser=false +# +# Implementation specific limits: +# +jdk.xml.entityExpansionLimit=64000 +jdk.xml.elementAttributeLimit=10000 +jdk.xml.maxOccurLimit=5000 +jdk.xml.totalEntitySizeLimit=100000 +jdk.xml.maxGeneralEntitySizeLimit=0 +jdk.xml.maxParameterEntitySizeLimit=1000000 +jdk.xml.entityReplacementLimit=300000 +jdk.xml.maxElementDepth=0 +jdk.xml.maxXMLNameLimit=1000 +jdk.xml.xpathExprGrpLimit=10 +jdk.xml.xpathExprOpLimit=100 +jdk.xml.xpathTotalOpLimit=10000 + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/config/files/jaxp.properties b/test/jaxp/javax/xml/jaxp/unittest/common/config/files/jaxp.properties index b43b5a3e8d178..53074816cb977 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/config/files/jaxp.properties +++ b/test/jaxp/javax/xml/jaxp/unittest/common/config/files/jaxp.properties @@ -47,11 +47,11 @@ # the default property values. The format is: # system-property-name=value # -# For example, the FILES property in CatalogFeatures has an associated system -# property called javax.xml.catalog.files. An entry for the FILES property in the -# configuration file would therefore use javax.xml.catalog.files as the key, that +# For example, the RESOLVE property in CatalogFeatures has an associated system +# property called javax.xml.catalog.resolve. An entry for the RESOLVE property in the +# configuration file would therefore use javax.xml.catalog.resolve as the key, that # is: -# javax.xml.catalog.files=strict +# javax.xml.catalog.resolve=strict # # # Extension Functions: @@ -128,6 +128,24 @@ jdk.xml.overrideDefaultParser=false # # javax.xml.useCatalog=true # +# Implementation Specific Properties - jdkcatalog.resolve +# +# This property instructs the JDK default CatalogResolver to act in accordance with +# the setting when unable to resolve an external reference with the built-in Catalog. +# The options are: +# continue -- indicates that the processing should continue +# ignore -- indicates that the reference is skipped +# strict -- indicates that the resolver should throw a CatalogException +# +# The following setting would cause the resolve to throw a CatalogException when +# unable to resolve an external reference: +# jdk.xml.jdkcatalog.resolve=strict +# +# Implementation Specific Properties - DTD +# +# This property instructs the parsers to: deny, ignore or allow DTD processing. +# The following setting would cause the parser to reject DTD by throwing an exception. +# jdk.xml.dtd.support=deny # # Implementation Specific Properties - Limits # diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java index f5985bd3615a9..b37b7da4e9a26 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java @@ -17,15 +17,6 @@ * The DTD property controls how DTDs are processed. */ public class DTDTestBase extends TestBase { - static final String SRC_DIR; - static { - String srcDir = System.getProperty("test.src", "."); - if (IS_WINDOWS) { - srcDir = srcDir.replace('\\', '/'); - } - SRC_DIR = srcDir; - TEST_SOURCE_DIR = srcDir + "/../xmlfiles/"; - } public void testDOM(String filename, Properties fsp, PropertyState state, Properties config, Properties[] sysProp, Properties[] apiProp, diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java index de0316aa79fd2..6be967d8dd5a2 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java @@ -67,13 +67,31 @@ public class TestBase { ORACLE_JAXP_PROPERTY_PREFIX + "getEntityCountInfo"; public static final String CATALOG_FILE = CatalogFeatures.Feature.FILES.getPropertyName(); public static final boolean IS_WINDOWS = System.getProperty("os.name").contains("Windows"); - public static String SRC_DIR = System.getProperty("test.src", "."); - public static String TEST_SOURCE_DIR; + public static String SRC_DIR; + public static String TEST_SOURCE_DIR, CONFIG_FILE_PATH, CATALOG_PATH; + static { + String srcDir = System.getProperty("test.src", "."); + if (IS_WINDOWS) { + srcDir = srcDir.replace('\\', '/'); + } + SRC_DIR = srcDir; + if (IS_WINDOWS) { + CATALOG_PATH = "file:///" + SRC_DIR + "/../catalog/testcatalog/TestCatalog.xml"; + } else { + CATALOG_PATH = "file://" + SRC_DIR + "/../catalog/testcatalog/TestCatalog.xml"; + } + TEST_SOURCE_DIR = srcDir + "/../xmlfiles/"; + CONFIG_FILE_PATH = SRC_DIR + "/../config/files/"; + } // configuration file system property private static final String CONFIG_FILE = "java.xml.config.file"; + // CATALOG Abbreviation: C + static final String C_FILE = CatalogFeatures.Feature.FILES.getPropertyName(); + static final String C_RESOLVE = CatalogFeatures.Feature.RESOLVE.getPropertyName(); + // Xerces Property public static final String DISALLOW_DTD = "http://apache.org/xml/features/disallow-doctype-decl"; public static final String LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; @@ -84,6 +102,7 @@ public class TestBase { // Impl Specific Properties public static final String SP_DTD = "jdk.xml.dtd.support"; + public static final String SP_CATALOG = "jdk.xml.jdkcatalog.resolve"; public static final String OVERRIDE_PARSER = "jdk.xml.overrideDefaultParser"; // DTD/CATALOG constants @@ -97,8 +116,12 @@ public class TestBase { // JAXP Configuration File(JCF) location // DTD = deny - public static final String JCF_DTD2 = "../config/files/dtd2.properties"; + public static final String JCF_DTD2 = "dtd2.properties"; + + // CATALOG=strict + public static final String CONFIG_CATALOG_STRICT = "catalog2.properties"; + public static final String UNKNOWN_HOST = "invalid.site.com"; String xmlExternalEntity, xmlExternalEntityId; String xmlGE_Expansion, xmlGE_ExpansionId; @@ -107,7 +130,9 @@ public static enum Processor { DOM, SAX, STAX, VALIDATOR, TRANSFORMER }; static enum SourceType { STREAM, SAX, STAX, DOM }; public static enum Properties { - CONFIG_FILE_DTD2(null, CONFIG_FILE, Type.FEATURE, getPath(JCF_DTD2)), + // config file: CATALOG = strict + CONFIG_FILE_CATALOG_STRICT(null, CONFIG_FILE, Type.FEATURE, getPath(CONFIG_FILE_PATH, CONFIG_CATALOG_STRICT)), + CONFIG_FILE_DTD2(null, CONFIG_FILE, Type.FEATURE, getPath(CONFIG_FILE_PATH, JCF_DTD2)), FSP(XMLConstants.FEATURE_SECURE_PROCESSING, null, Type.FEATURE, "true"), FSP_FALSE(XMLConstants.FEATURE_SECURE_PROCESSING, null, Type.FEATURE, "false"), @@ -115,6 +140,9 @@ public static enum Properties { DTD0(SP_DTD, "ditto", Type.PROPERTY, DTD_ALLOW), DTD1(SP_DTD, "ditto", Type.PROPERTY, DTD_IGNORE), DTD2(SP_DTD, "ditto", Type.PROPERTY, DTD_DENY), + CATALOG0(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_CONTINUE), + CATALOG1(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_IGNORE), + CATALOG2(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_STRICT), // StAX properties SUPPORT_DTD(XMLInputFactory.SUPPORT_DTD, null, Type.FEATURE, "true"), @@ -181,11 +209,34 @@ public static enum PropertyState { CONFIG_FILE_SYSTEM_API, } + public static enum CustomCatalog { + // continue processing if no match found + CONTINUE(CATALOG_PATH, "continue"), + // skip if no match found + IGNORE(CATALOG_PATH, "ignore"), + // throws CatalogException if no match found + STRICT(CATALOG_PATH, "strict"); + + String file, resolve; + CustomCatalog(String file, String resolve) { + this.file = file; + this.resolve = resolve; + } + + public String file() { + return file; + } + + public String resolve() { + return resolve; + } + } + protected void process(String filename, DocumentBuilderFactory dbf, boolean expectError, String error) throws Exception { //dbf.setAttribute(CatalogFeatures.Feature.RESOLVE.getPropertyName(), "continue"); DocumentBuilder builder = dbf.newDocumentBuilder(); - File file = new File(getPath(filename)); + File file = new File(getPath(TEST_SOURCE_DIR, filename)); try { Document document = builder.parse(file); Assert.assertTrue(!expectError); @@ -198,7 +249,7 @@ protected void process(String filename, DocumentBuilderFactory dbf, boolean expe protected void process(String filename, SAXParser parser, boolean expectError, String error) throws Exception { - File file = new File(getPath(filename)); + File file = new File(getPath(TEST_SOURCE_DIR, filename)); try { parser.parse(file, new DefaultHandler()); Assert.assertTrue(!expectError); @@ -211,7 +262,7 @@ protected void process(String filename, SAXParser parser, boolean expectError, protected void process(String filename, XMLInputFactory xif, boolean expectError, String expected) throws Exception { - String xml = getPath(filename); + String xml = getPath(TEST_SOURCE_DIR, filename); try { InputStream entityxml = new FileInputStream(xml); XMLStreamReader streamReader = xif.createXMLStreamReader(xml, entityxml); @@ -228,7 +279,7 @@ protected void process(String filename, XMLInputFactory xif, boolean expectError protected void process(String filename, SchemaFactory sf, boolean expectError, String expected) throws Exception { - String xsd = getPath(filename); + String xsd = getPath(TEST_SOURCE_DIR, filename); try { Schema schema = sf.newSchema(new StreamSource(new File(xsd))); Assert.assertTrue(!expectError); @@ -240,7 +291,7 @@ protected void process(String filename, SchemaFactory sf, boolean expectError, protected void process(String filename, TransformerFactory tf, boolean expectError, String expected) throws Exception { - String xsl = getPath(filename); + String xsl = getPath(TEST_SOURCE_DIR, filename); try { SAXSource xslSource = new SAXSource(new InputSource(xsl)); xslSource.setSystemId(xsl); @@ -254,7 +305,7 @@ protected void process(String filename, TransformerFactory tf, boolean expectErr protected void transform(String xmlFile, String xsl, TransformerFactory tf, boolean expectError, String expected) throws Exception { - String xmlSysId = getPath(xmlFile); + String xmlSysId = getPath(TEST_SOURCE_DIR, xmlFile); try { SAXSource xslSource = new SAXSource(new InputSource(new StringReader(xsl))); //SAXSource xslSource = new SAXSource(new InputSource(xslSysId)); @@ -264,14 +315,13 @@ protected void transform(String xmlFile, String xsl, TransformerFactory tf, transformer.transform(getSource(SourceType.STREAM, xmlSysId), new StreamResult(sw)); Assert.assertTrue(!expectError); } catch (Exception e) { - e.printStackTrace(); processError(expectError, expected, e); } } protected void validate(String filename, SchemaFactory sf, boolean expectError, String expected) throws Exception { - String xml = getPath(filename); + String xml = getPath(TEST_SOURCE_DIR, filename); try { Schema schema = sf.newSchema(); Validator validator = schema.newValidator(); @@ -285,15 +335,17 @@ protected void validate(String filename, SchemaFactory sf, boolean expectError, protected void processError(boolean expectError, String error, Exception e) throws Exception { - //e.printStackTrace(); String str = e.getMessage(); -// System.out.println("Exp Msg: " + str); - //e.printStackTrace(); if (!expectError) { - Assert.assertTrue(false, "Expected pass, but Exception is thrown " + - str); + Assert.assertTrue(false, "Expected pass, but Exception is thrown " + str); } else { - Assert.assertTrue((str != null) && str.contains(error)); + // This check is necessary since errors other than UnknownHostException + // can contain the host name in the System ID + if (UNKNOWN_HOST.equals(error)) { + Assert.assertTrue((str != null) && str.equals(error)); + } else { + Assert.assertTrue((str != null) && str.contains(error)); + } } } @@ -309,6 +361,11 @@ protected void processError(boolean expectError, String error, Exception e) */ protected DocumentBuilderFactory getDBF(Properties fsp, PropertyState state, Properties config, Properties[] sysProp, Properties[] apiProp) { + return getDBF(fsp, state, config, sysProp, apiProp, null); + } + + protected DocumentBuilderFactory getDBF(Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc) { setSystemProperty(config, state, sysProp); DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultNSInstance(); @@ -335,6 +392,10 @@ protected DocumentBuilderFactory getDBF(Properties fsp, PropertyState state, } } } + if (cc != null) { + dbf.setAttribute(C_FILE, cc.file()); + dbf.setAttribute(C_RESOLVE, cc.resolve()); + } clearSystemProperty(state, sysProp); @@ -355,6 +416,11 @@ protected DocumentBuilderFactory getDBF(Properties fsp, PropertyState state, */ public SAXParser getSAXParser(Properties fsp, PropertyState state, Properties config, Properties[] sysProp, Properties[] apiProp) throws Exception { + return getSAXParser(fsp, state, config, sysProp, apiProp, null); + } + + public SAXParser getSAXParser(Properties fsp, PropertyState state, Properties config, + Properties[] sysProp, Properties[] apiProp, CustomCatalog cc) throws Exception { setSystemProperty(config, state, sysProp); SAXParserFactory spf = SAXParserFactory.newDefaultNSInstance(); @@ -387,12 +453,22 @@ public SAXParser getSAXParser(Properties fsp, PropertyState state, Properties co } } + if (cc != null) { + parser.setProperty(C_FILE, cc.file()); + parser.setProperty(C_RESOLVE, cc.resolve()); + } + clearSystemProperty(state, sysProp); return parser; } protected XMLInputFactory getXMLInputFactory(PropertyState state, Properties config, Properties[] sysProp, Properties[] apiProp) { + return getXMLInputFactory(state, config, sysProp, apiProp, null); + } + + protected XMLInputFactory getXMLInputFactory(PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc) { setSystemProperty(config, state, sysProp); XMLInputFactory factory = XMLInputFactory.newInstance(); @@ -402,6 +478,11 @@ protected XMLInputFactory getXMLInputFactory(PropertyState state, } } + if (cc != null) { + factory.setProperty(C_FILE, cc.file()); + factory.setProperty(C_RESOLVE, cc.resolve()); + } + clearSystemProperty(state, sysProp); return factory; @@ -410,6 +491,12 @@ protected XMLInputFactory getXMLInputFactory(PropertyState state, protected SchemaFactory getSchemaFactory(Properties fsp, PropertyState state, Properties config, Properties[] sysProp, Properties[] apiProp) throws Exception { + return getSchemaFactory(fsp, state, config, sysProp, apiProp, null); + } + + protected SchemaFactory getSchemaFactory(Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc) + throws Exception { setSystemProperty(config, state, sysProp); SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); @@ -427,6 +514,11 @@ protected SchemaFactory getSchemaFactory(Properties fsp, PropertyState state, } } + if (cc != null) { + factory.setProperty(C_FILE, cc.file()); + factory.setProperty(C_RESOLVE, cc.resolve()); + } + clearSystemProperty(state, sysProp); return factory; @@ -435,6 +527,12 @@ protected SchemaFactory getSchemaFactory(Properties fsp, PropertyState state, protected TransformerFactory getTransformerFactory(Properties fsp, PropertyState state, Properties config, Properties[] sysProp, Properties[] apiProp) throws Exception { + return getTransformerFactory(fsp, state, config, sysProp, apiProp, null); + } + + protected TransformerFactory getTransformerFactory(Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc) + throws Exception { setSystemProperty(config, state, sysProp); TransformerFactory tf = TransformerFactory.newInstance(); //tf.setAttribute(JDK_ENTITY_COUNT_INFO, "yes"); @@ -450,6 +548,10 @@ protected TransformerFactory getTransformerFactory(Properties fsp, PropertyState } } } + if (cc != null) { + tf.setAttribute(C_FILE, cc.file()); + tf.setAttribute(C_RESOLVE, cc.resolve()); + } clearSystemProperty(state, sysProp); @@ -605,8 +707,8 @@ protected void clearSystemProperty1(PropertyState m, Properties property) { } } - static String getPath(String file) { - String temp = TEST_SOURCE_DIR + file; + static String getPath(String base, String file) { + String temp = base + file; if (IS_WINDOWS) { temp = "/" + temp; } diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_roottest.xml b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_roottest.xml new file mode 100644 index 0000000000000..bc6c2968f5960 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_roottest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/test.xml b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/test.xml new file mode 100644 index 0000000000000..991d823a2fe54 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/test.xml @@ -0,0 +1,6 @@ + + + ]> +123&x1; diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/testExternalParameter.xml b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/testExternalParameter.xml new file mode 100644 index 0000000000000..192d9ed633ebd --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/testExternalParameter.xml @@ -0,0 +1,12 @@ + + +%paraEntity; +]> + + value value1 + 10016 + + + + diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index a510a1e15e952..a76ed37aced08 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -487,7 +487,7 @@ java/beans/XMLEncoder/Test6570354.java 8015593 macosx-all java/lang/ProcessHandle/InfoTest.java 8211847 aix-ppc64 java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java 8151492 generic-all java/lang/invoke/LFCaching/LFGarbageCollectedTest.java 8078602 generic-all -java/lang/invoke/lambda/LambdaFileEncodingSerialization.java 8249079 linux-x64 +java/lang/invoke/lambda/LambdaFileEncodingSerialization.java 8249079 linux-all java/lang/invoke/RicochetTest.java 8251969 generic-all ############################################################################ @@ -570,6 +570,7 @@ java/nio/channels/DatagramChannel/ManySourcesAndTargets.java 8264385 macosx-a java/nio/channels/DatagramChannel/Unref.java 8233437 generic-all java/nio/file/Path/ToRealPath.java 8315273 windows-all +java/nio/file/Files/probeContentType/Basic.java 8320943 windows-all jdk/nio/zipfs/TestLocOffsetFromZip64EF.java 8301183 linux-all @@ -622,12 +623,6 @@ com/sun/security/sasl/gsskerb/NoSecurityLayer.java 8039280 generic- sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.java 8039280 generic-all sun/security/provider/PolicyParser/PrincipalExpansionError.java 8039280 generic-all -sun/security/tools/keytool/NssTest.java 8295343 generic-all -sun/security/pkcs11/Signature/TestRSAKeyLength.java 8295343 generic-all -sun/security/pkcs11/rsa/TestSignatures.java 8295343 generic-all -sun/security/pkcs11/rsa/TestKeyPairGenerator.java 8295343 generic-all -sun/security/pkcs11/rsa/TestKeyFactory.java 8295343 generic-all -sun/security/pkcs11/KeyStore/Basic.java 8295343 generic-all sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java 8316183 linux-ppc64le sun/security/pkcs11/Provider/MultipleLogins.sh 8319128 linux-aarch64 @@ -666,6 +661,7 @@ javax/swing/JTree/DnD/LastNodeLowerHalfDrop.java 8159131 linux-all javax/swing/JTree/4633594/JTreeFocusTest.java 7105441 macosx-all javax/swing/AbstractButton/6711682/bug6711682.java 8060765 windows-all,macosx-all javax/swing/JFileChooser/6396844/TwentyThousandTest.java 8198003 generic-all +javax/swing/JFileChooser/8194044/FileSystemRootTest.java 8320944 windows-all javax/swing/JPopupMenu/6800513/bug6800513.java 7184956 macosx-all javax/swing/JTabbedPane/8007563/Test8007563.java 8051591 generic-all javax/swing/JTabbedPane/4624207/bug4624207.java 8064922 macosx-all diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 2f39f3fdceffb..4b332b94ca357 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -75,11 +75,12 @@ tier2_part3 = \ tier3 = \ :build \ :jdk_vector \ + -:jdk_vector_sanity \ :jdk_rmi \ :jdk_svc \ - -:jdk_svc_sanity \ - -:jdk_vector_sanity \ - -:svc_tools + -:jdk_svc_sanity \ + -:svc_tools \ + :jdk_jpackage # Everything not in other tiers tier4 = \ @@ -284,10 +285,11 @@ jdk_loom = \ jdk/jfr/threading # -# Tool (and tool API) tests are split into core and svc groups +# Tool (and tool API) tests are mostly split into core and svc groups # core_tools = \ tools \ + -tools/jpackage \ jdk/internal/jrtfs \ sun/tools/jrunscript @@ -299,11 +301,15 @@ svc_tools = \ jdk_tools = \ :core_tools \ - :svc_tools + :svc_tools \ + :jdk_jpackage jdk_jfr = \ jdk/jfr +jdk_jpackage = \ + tools/jpackage + # # Catch-all for other areas with a small number of tests # @@ -579,7 +585,9 @@ jdk_nio_networkchannel = \ jdk_core_manual = \ :jdk_core_manual_no_input \ - :jdk_core_manual_no_input_security + :jdk_security_manual_no_input \ + :jdk_core_manual_interactive \ + :jdk_security_manual_interactive jdk_core_manual_no_input = \ java/net/HugeDataTransferTest.java \ @@ -598,7 +606,7 @@ jdk_core_manual_no_input = \ com/sun/net/httpserver/simpleserver/jwebserver/CommandLinePortNotSpecifiedTest.java \ javax/xml/jaxp/datatype/8033980/GregorianCalAndDurSerDataUtil.java -jdk_core_manual_no_input_security = \ +jdk_security_manual_no_input = \ com/sun/crypto/provider/Cipher/DES/PerformanceTest.java \ com/sun/crypto/provider/Cipher/AEAD/GCMIncrementByte4.java \ com/sun/crypto/provider/Cipher/AEAD/GCMIncrementDirect4.java \ @@ -625,14 +633,15 @@ jdk_core_manual_no_input_security = \ jdk_core_manual_interactive = \ com/sun/jndi/dns/Test6991580.java \ java/util/TimeZone/DefaultTimeZoneTest.java \ - sun/security/tools/keytool/i18n.java \ java/nio/MappedByteBuffer/PmemTest.java \ java/rmi/registry/nonLocalRegistry/NonLocalRegistryTest.java \ - java/rmi/registry/nonLocalRegistry/NonLocalSkeletonTest.java \ + java/rmi/registry/nonLocalRegistry/NonLocalSkeletonTest.java + +jdk_security_manual_interactive = \ + sun/security/tools/keytool/i18n.java \ java/security/Policy/Root/Root.java \ sun/security/krb5/config/native/TestDynamicStore.java - # Test sets for running inside container environment jdk_containers_extended = \ :jdk_io \ diff --git a/test/jdk/java/foreign/TestHandshake.java b/test/jdk/java/foreign/TestHandshake.java index 4dc96ee9b3b25..79825ce4750b3 100644 --- a/test/jdk/java/foreign/TestHandshake.java +++ b/test/jdk/java/foreign/TestHandshake.java @@ -33,6 +33,7 @@ import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; @@ -48,6 +49,7 @@ import org.testng.annotations.Test; import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_INT; import static org.testng.Assert.*; public class TestHandshake { @@ -98,16 +100,19 @@ static abstract class AbstractSegmentAccessor implements Runnable { @Override public final void run() { start("\"Accessor #\" + id"); - outer: while (segment.scope().isAlive()) { + while (segment.scope().isAlive()) { try { doAccess(); } catch (IllegalStateException ex) { - long delay = System.currentTimeMillis() - start.get(); - System.out.println("Accessor #" + id + " suspending - elapsed (ms): " + delay); - backoff(); - delay = System.currentTimeMillis() - start.get(); - System.out.println("Accessor #" + id + " resuming - elapsed (ms): " + delay); - continue outer; + if (!failed.get()) { + // ignore - this means segment was alive, but was closed while we were accessing it + // next isAlive test should fail + assertFalse(segment.scope().isAlive()); + failed.set(true); + } else { + // rethrow! + throw ex; + } } } long delay = System.currentTimeMillis() - start.get(); @@ -174,6 +179,30 @@ public void doAccess() { } } + static class SegmentSwappyCopyAccessor extends AbstractSegmentAccessor { + + MemorySegment first, second; + ValueLayout sourceLayout, destLayout; + long count; + + + SegmentSwappyCopyAccessor(int id, MemorySegment segment, Arena _unused) { + super(id, segment); + long split = segment.byteSize() / 2; + first = segment.asSlice(0, split); + sourceLayout = JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN); + second = segment.asSlice(split); + destLayout = JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN); + count = Math.min(first.byteSize() / sourceLayout.byteSize(), + second.byteSize() / destLayout.byteSize()); + } + + @Override + public void doAccess() { + MemorySegment.copy(first, sourceLayout, 0L, second, destLayout, 0L, count); + } + } + static class SegmentFillAccessor extends AbstractSegmentAccessor { SegmentFillAccessor(int id, MemorySegment segment, Arena _unused) { @@ -246,14 +275,7 @@ static class Handshaker implements Runnable { @Override public void run() { start("Handshaker"); - while (true) { - try { - arena.close(); - break; - } catch (IllegalStateException ex) { - Thread.onSpinWait(); - } - } + arena.close(); // This should NOT throw long delay = System.currentTimeMillis() - start.get(); System.out.println("Segment closed - elapsed (ms): " + delay); } @@ -268,6 +290,7 @@ static Object[][] accessors() { return new Object[][] { { "SegmentAccessor", (AccessorFactory)SegmentAccessor::new }, { "SegmentCopyAccessor", (AccessorFactory)SegmentCopyAccessor::new }, + { "SegmentSwappyCopyAccessor", (AccessorFactory)SegmentSwappyCopyAccessor::new }, { "SegmentMismatchAccessor", (AccessorFactory)SegmentMismatchAccessor::new }, { "SegmentFillAccessor", (AccessorFactory)SegmentFillAccessor::new }, { "BufferAccessor", (AccessorFactory)BufferAccessor::new }, diff --git a/test/jdk/java/foreign/nested/libNested.c b/test/jdk/java/foreign/nested/libNested.c index a1414d6ed6179..c31fb0dc4cc92 100644 --- a/test/jdk/java/foreign/nested/libNested.c +++ b/test/jdk/java/foreign/nested/libNested.c @@ -26,6 +26,9 @@ #else #define EXPORT #endif +#ifdef _AIX +#pragma align (natural) +#endif struct S1{ double f0; long long f1; double f2; int f3; }; union U1{ short f0; long long f1; short f2; char f3[4][3]; }; @@ -92,3 +95,7 @@ EXPORT struct S13 test_S13(struct S13 arg, struct S13(*cb)(struct S13)) { return EXPORT struct S14 test_S14(struct S14 arg, struct S14(*cb)(struct S14)) { return cb(arg); } EXPORT union U16 test_U16(union U16 arg, union U16(*cb)(union U16)) { return cb(arg); } EXPORT struct S15 test_S15(struct S15 arg, struct S15(*cb)(struct S15)) { return cb(arg); } + +#ifdef _AIX +#pragma align (reset) +#endif diff --git a/test/jdk/java/foreign/shared.h b/test/jdk/java/foreign/shared.h index 1cadc1075d613..0f6183891a720 100644 --- a/test/jdk/java/foreign/shared.h +++ b/test/jdk/java/foreign/shared.h @@ -34,6 +34,9 @@ #else #define EXPORT #endif +#ifdef _AIX +#pragma align (natural) +#endif struct S_I { int p0; }; struct S_F { float p0; }; @@ -120,3 +123,7 @@ struct S_PPF { void* p0; void* p1; float p2; }; struct S_PPD { void* p0; void* p1; double p2; }; struct S_PPP { void* p0; void* p1; void* p2; }; struct S_FFFF { float p0; float p1; float p2; float p3; }; + +#ifdef _AIX +#pragma align (reset) +#endif diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java index 1489a6d55b78d..8e31f35bef55e 100644 --- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java +++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java @@ -24,12 +24,16 @@ import java.io.Serializable; import java.lang.Enum.EnumDesc; import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.reflect.AccessFlag; import java.lang.runtime.SwitchBootstraps; import java.util.concurrent.atomic.AtomicBoolean; +import jdk.internal.classfile.Classfile; import org.testng.annotations.Test; @@ -42,6 +46,7 @@ * @test * @bug 8318144 * @enablePreview + * @modules java.base/jdk.internal.classfile * @compile SwitchBootstrapsTest.java * @run testng/othervm SwitchBootstrapsTest */ @@ -113,9 +118,12 @@ public void testTypes() throws Throwable { } catch (IllegalArgumentException ex) { //OK } - testType("", 0, 0, String.class, String.class, String.class); - testType("", 1, 1, String.class, String.class, String.class); - testType("", 2, 2, String.class, String.class, String.class); + testType("", 0, 0, String.class, String.class, String.class, String.class, String.class); + testType("", 1, 1, String.class, String.class, String.class, String.class, String.class); + testType("", 2, 2, String.class, String.class, String.class, String.class, String.class); + testType("", 3, 3, String.class, String.class, String.class, String.class, String.class); + testType("", 3, 3, String.class, String.class, String.class, String.class, String.class); + testType("", 4, 4, String.class, String.class, String.class, String.class, String.class); testType("", 0, 0); } @@ -346,4 +354,32 @@ public void testIncorrectTypeStartIndex() throws Throwable { } } + public void testHiddenClassAsCaseLabel() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + byte[] classBytes = createClass(); + Class classA = lookup.defineHiddenClass(classBytes, false).lookupClass(); + Class classB = lookup.defineHiddenClass(classBytes, false).lookupClass(); + Object[] labels = new Object[] { + classA, + classB, + }; + testType(classA.getConstructor().newInstance(), 0, 0, labels); + testType(classB.getConstructor().newInstance(), 0, 1, labels); + } + + private static byte[] createClass() { + return Classfile.of().build(ClassDesc.of("C"), clb -> { + clb.withFlags(AccessFlag.SYNTHETIC) + .withMethodBody("", + MethodTypeDesc.of(ConstantDescs.CD_void), + Classfile.ACC_PUBLIC, + cb -> { + cb.aload(0); + cb.invokespecial(ConstantDescs.CD_Object, + "", + MethodTypeDesc.of(ConstantDescs.CD_void)); + cb.return_(); + }); + }); + } } diff --git a/test/jdk/java/net/httpclient/RedirectTimeoutTest.java b/test/jdk/java/net/httpclient/RedirectTimeoutTest.java new file mode 100644 index 0000000000000..e907bcd8228a0 --- /dev/null +++ b/test/jdk/java/net/httpclient/RedirectTimeoutTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8304701 + * @summary Verifies that for a redirected request, the given HttpClient + * will clear and start a new response timer instead of throwing + * an HttpTimeoutException during the redirected request. + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.test.lib.net.SimpleSSLContext + * @run testng/othervm -Djdk.httpclient.HttpClient.log=errors,trace -Djdk.internal.httpclient.debug=false RedirectTimeoutTest + */ + +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestExchange; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestResponseHeaders; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; +import org.testng.TestException; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpTimeoutException; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.Instant; + +import static java.net.http.HttpClient.Redirect.ALWAYS; +import static java.net.http.HttpClient.Version.HTTP_1_1; +import static java.net.http.HttpClient.Version.HTTP_2; +import static jdk.test.lib.Utils.adjustTimeout; + +public class RedirectTimeoutTest { + + static HttpTestServer h1TestServer, h2TestServer; + static URI h1Uri, h1RedirectUri, h2Uri, h2RedirectUri, h2WarmupUri, testRedirectURI; + private static final long TIMEOUT_MILLIS = 3000L; // 3s + private static final long SLEEP_TIME = 1500L; // 1.5s + public static final int ITERATIONS = 4; + private static final PrintStream out = System.out; + + @BeforeTest + public void setup() throws IOException { + h1TestServer = HttpTestServer.create(HTTP_1_1); + h2TestServer = HttpTestServer.create(HTTP_2); + h1Uri = URI.create("http://" + h1TestServer.serverAuthority() + "/h1_test"); + h1RedirectUri = URI.create("http://" + h1TestServer.serverAuthority() + "/h1_redirect"); + h2Uri = URI.create("http://" + h2TestServer.serverAuthority() + "/h2_test"); + h2RedirectUri = URI.create("http://" + h2TestServer.serverAuthority() + "/h2_redirect"); + h2WarmupUri = URI.create("http://" + h2TestServer.serverAuthority() + "/h2_warmup"); + h1TestServer.addHandler(new GetHandler(), "/h1_test"); + h1TestServer.addHandler(new RedirectHandler(), "/h1_redirect"); + h2TestServer.addHandler(new GetHandler(), "/h2_test"); + h2TestServer.addHandler(new RedirectHandler(), "/h2_redirect"); + h2TestServer.addHandler(new Http2Warmup(), "/h2_warmup"); + h1TestServer.start(); + h2TestServer.start(); + } + + @AfterTest + public void teardown() { + h1TestServer.stop(); + h2TestServer.stop(); + } + + @DataProvider(name = "testData") + public Object[][] testData() { + return new Object[][] { + { HTTP_1_1, h1Uri, h1RedirectUri }, + { HTTP_2, h2Uri, h2RedirectUri } + }; + } + + @Test(dataProvider = "testData") + public void test(Version version, URI uri, URI redirectURI) throws InterruptedException { + out.println("Testing for " + version); + testRedirectURI = redirectURI; + HttpClient.Builder clientBuilder = HttpClient.newBuilder().followRedirects(ALWAYS); + HttpRequest request = HttpRequest.newBuilder().uri(uri) + .GET() + .version(version) + .timeout(Duration.ofMillis(adjustTimeout(TIMEOUT_MILLIS))) + .build(); + + try (HttpClient client = clientBuilder.build()) { + if (version.equals(HTTP_2)) + client.send(HttpRequest.newBuilder(h2WarmupUri).HEAD().build(), HttpResponse.BodyHandlers.discarding()); + /* + With TIMEOUT_MILLIS set to 1500ms and the server's RedirectHandler sleeping for 750ms before responding + to each request, 4 iterations will take a guaranteed minimum time of 3000ms which will ensure that any + uncancelled/uncleared timers will fire within the test window. + */ + for (int i = 0; i < ITERATIONS; i++) { + out.println(Instant.now() + ": Client: Sending request #" + (i + 1)); + client.send(request, HttpResponse.BodyHandlers.ofString()); + out.println("Request complete"); + } + } catch (IOException e) { + if (e.getClass() == HttpTimeoutException.class) { + e.printStackTrace(System.out); + throw new TestException("Timeout from original HttpRequest expired on redirect when it should have been cancelled."); + } else { + throw new RuntimeException(e); + } + } + } + + public static class Http2Warmup implements HttpTestHandler { + + @Override + public void handle(HttpTestExchange t) throws IOException { + t.sendResponseHeaders(200, 0); + } + } + + public static class GetHandler implements HttpTestHandler { + + @Override + public void handle(HttpTestExchange exchange) throws IOException { + out.println(Instant.now() + ": Server: Get Handler Called"); + HttpTestResponseHeaders responseHeaders = exchange.getResponseHeaders(); + responseHeaders.addHeader("Location", testRedirectURI.toString()); + exchange.sendResponseHeaders(302, 0); + } + } + + public static class RedirectHandler implements HttpTestHandler { + + @Override + public void handle(HttpTestExchange exchange) throws IOException { + out.println(Instant.now() + ": Server: Redirect Handler Called"); + byte[] data = "Test".getBytes(StandardCharsets.UTF_8); + try { + Thread.sleep(adjustTimeout(SLEEP_TIME)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + exchange.sendResponseHeaders(200, data.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(data); + } + } + } +} \ No newline at end of file diff --git a/test/jdk/java/util/Calendar/SupplementalJapaneseEraTestRun.java b/test/jdk/java/util/Calendar/SupplementalJapaneseEraTestRun.java index 34363bf711d1c..5383d0d124055 100644 --- a/test/jdk/java/util/Calendar/SupplementalJapaneseEraTestRun.java +++ b/test/jdk/java/util/Calendar/SupplementalJapaneseEraTestRun.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,10 +34,15 @@ import java.util.List; import java.util.Locale; import java.util.TimeZone; -import static java.util.Calendar.*; +import java.util.stream.Stream; + +import static java.util.Calendar.DAY_OF_YEAR; +import static java.util.Calendar.ERA; +import static java.util.Calendar.FEBRUARY; +import static java.util.Calendar.LONG; +import static java.util.Calendar.YEAR; import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.Utils; import org.testng.annotations.DataProvider; @@ -91,19 +96,18 @@ public void InvalidPropertyValuesTest(String prop) } private static void testRun(String property, List javaParam) - throws Throwable{ - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(Utils.TEST_CLASS_PATH) - .addToolArg("-Djdk.calendar.japanese.supplemental.era=" + property) - .addToolArg("SupplementalJapaneseEraTest"); - for (String para: javaParam) { - launcher.addToolArg(para); - } - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + throws Throwable { + List params = List.of( + "-ea", "-esa", + "-cp", Utils.TEST_CLASS_PATH, + "-Djdk.calendar.japanese.supplemental.era=" + property, + "SupplementalJapaneseEraTest"); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + Stream.concat(params.stream(), javaParam.stream()).toList()); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); + System.out.println(property + ":pass"); if (exitCode != 0) { System.out.println(property + ":fail"); diff --git a/test/jdk/java/util/Currency/PropertiesTest.sh b/test/jdk/java/util/Currency/PropertiesTest.sh deleted file mode 100644 index 6e8bbc2397029..0000000000000 --- a/test/jdk/java/util/Currency/PropertiesTest.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/sh - -# Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please 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 6332666 6863624 7180362 8003846 8074350 8074351 8130246 8149735 7102969 -# 8157138 8190904 -# @summary tests the capability of replacing the currency data with user -# specified currency properties file -# @build PropertiesTest -# @run shell/timeout=600 PropertiesTest.sh - -if [ "${TESTSRC}" = "" ] -then - echo "TESTSRC not set. Test cannot execute. Failed." - exit 1 -fi -echo "TESTSRC=${TESTSRC}" -if [ "${TESTJAVA}" = "" ] -then - echo "TESTJAVA not set. Test cannot execute. Failed." - exit 1 -fi -echo "TESTJAVA=${TESTJAVA}" -if [ "${TESTCLASSES}" = "" ] -then - echo "TESTCLASSES not set. Test cannot execute. Failed." - exit 1 -fi -echo "TESTCLASSES=${TESTCLASSES}" -echo "CLASSPATH=${CLASSPATH}" - -# set platform-dependent variables -OS=`uname -s` -case "$OS" in - Linux | Darwin | AIX ) - PS=":" - FS="/" - ;; - Windows* ) - PS=";" - FS="/" - ;; - CYGWIN*|MSYS*|MINGW* ) - PS=";" - FS="/" - TESTJAVA=`cygpath -u ${TESTJAVA}` - ;; - * ) - echo "Unrecognized system!" - exit 1; - ;; -esac - -failures=0 - -run() { - echo '' - ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -cp ${TESTCLASSES} $* 2>&1 - if [ $? != 0 ]; then failures=`expr $failures + 1`; fi -} - -PROPS=${TESTSRC}${FS}currency.properties - - -# Dump built-in currency data - -run PropertiesTest -d dump1 -if [ ! -f dump1 ]; then echo "file dump1 not created. Test cannot execute. Failed."; exit 1; fi - -# Dump built-in currency data + overrides in properties file specified -# by system property. - -run -Djava.util.currency.data=${PROPS} PropertiesTest -d dump2 -if [ ! -f dump2 ]; then echo "file dump2 not created. Test cannot execute. Failed."; exit 1; fi -run PropertiesTest -c dump1 dump2 ${PROPS} - - -# Dump built-in currency data + overrides in properties file copied into -# JRE image. - -# Make a private copy of the jdk so we can write to the properties file location -# without disturbing other users, including concurrently executing tests. -WRITABLEJDK=.${FS}testjava -cp -H -R $TESTJAVA $WRITABLEJDK || exit 1 -PROPLOCATION=${WRITABLEJDK}${FS}lib -chmod -R u+w $WRITABLEJDK || exit 1 -cp ${PROPS} $PROPLOCATION || exit 1 -echo "Properties location: ${PROPLOCATION}" - -# run -echo '' -${WRITABLEJDK}${FS}bin${FS}java ${TESTVMOPTS} -cp ${TESTCLASSES} PropertiesTest -d dump3 -if [ $? != 0 ]; then failures=`expr $failures + 1`; fi -if [ ! -f dump3 ]; then echo "file dump3 not created. Test cannot execute. Failed."; exit 1; fi - -# run bug7102969 test -echo '' -${WRITABLEJDK}${FS}bin${FS}java ${TESTVMOPTS} -cp ${TESTCLASSES} PropertiesTest bug7102969 -if [ $? != 0 ]; then failures=`expr $failures + 1`; fi - -# run bug8157138 test -echo '' -${WRITABLEJDK}${FS}bin${FS}java ${TESTVMOPTS} -cp ${TESTCLASSES} PropertiesTest bug8157138 -if [ $? != 0 ]; then failures=`expr $failures + 1`; fi - -# run bug8190904 test -echo '' -${WRITABLEJDK}${FS}bin${FS}java ${TESTVMOPTS} -cp ${TESTCLASSES} PropertiesTest bug8190904 -if [ $? != 0 ]; then failures=`expr $failures + 1`; fi - -# Cleanup -rm -rf $WRITABLEJDK - -# compare the two dump files -run PropertiesTest -c dump1 dump3 ${PROPS} - - -# Results -echo '' -if [ $failures -gt 0 ]; - then echo "$failures tests failed"; - else echo "All tests passed"; fi -exit $failures diff --git a/test/jdk/java/util/Currency/PropertiesTestRun.java b/test/jdk/java/util/Currency/PropertiesTestRun.java new file mode 100644 index 0000000000000..6f2ea28a90d3a --- /dev/null +++ b/test/jdk/java/util/Currency/PropertiesTestRun.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 6332666 6863624 7180362 8003846 8074350 8074351 8130246 8149735 7102969 + * 8157138 8190904 8210410 + * @summary Tests the capability of replacing the currency data with a user + * specified currency properties file in lib directory (old way) or + * via the system property in the cmdline (new way). + * @library /test/lib + * @build PropertiesTest + * @run junit PropertiesTestRun + */ + +import jdk.test.lib.Utils; +import jdk.test.lib.process.ProcessTools; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; + +public class PropertiesTestRun { + + // String paths used for the cmdline processes + private static final String TEST_JDK = Utils.TEST_JDK; + private static final String TEST_PROPS = + Utils.TEST_SRC+Utils.FILE_SEPARATOR+"currency.properties"; + private static final String WRITABLE_JDK = + "."+Utils.FILE_SEPARATOR+"WRITABLE_JDK"; + private static final String WRITABLE_JDK_LIB = + WRITABLE_JDK+Utils.FILE_SEPARATOR+"lib"; + private static final String WRITABLE_JDK_BIN = + WRITABLE_JDK+Utils.FILE_SEPARATOR+"bin"; + private static final String WRITABLE_JDK_JAVA_PATH = + WRITABLE_JDK_BIN + Utils.FILE_SEPARATOR + "java"; + + // Create a writable JDK and set up dumps 1-3 + @BeforeAll + static void setUp() throws Throwable { + // Create separate JDK to supersede currencies via lib directory + createWritableJDK(); + // Create dump without user defined prop file + executeTestJDKMethod("PropertiesTest", "-d", "dump1"); + // Create dump with user defined prop file (via system property) + executeTestJDKMethod("-Djava.util.currency.data="+TEST_PROPS, + "PropertiesTest", "-d", "dump2"); + // Create dump with user defined prop file (via lib) + executeWritableJDKMethod("PropertiesTest", "-d", "dump3"); + } + + // Need to create a separate JDK to insert the user defined properties file + // into the lib folder. Create separate JDK to not disturb current TEST JDK. + private static void createWritableJDK() throws Throwable { + // Copy Test JDK into a separate JDK folder + executeProcess(new String[]{"cp", "-H", "-R", TEST_JDK, WRITABLE_JDK}); + // Make the separate JDK writable + executeProcess(new String[]{"chmod", "-R", "u+w", WRITABLE_JDK_LIB}); + // Copy the properties file into the writable JDK lib folder + executeProcess(new String[]{"cp", TEST_PROPS, WRITABLE_JDK_LIB}); + } + + // Compares the dumped output is expected between the default currencies + // and the user-defined custom currencies + @Test + void compareDumps() throws Throwable { + // Compare dump (from sys prop) + executeTestJDKMethod("PropertiesTest", "-c", "dump1", "dump2", + TEST_PROPS); + // Compare dump (from lib) + executeTestJDKMethod("PropertiesTest", "-c", "dump1", "dump3", + TEST_PROPS); + } + + // Launch a test from PropertiesTest. See PropertiesTest.java for more + // detail regarding a specific test that was launched. + @ParameterizedTest + @MethodSource("PropertiesTestMethods") + void launchPropertiesTests(String methodName) throws Throwable { + // Test via both the lib and system property + executeWritableJDKMethod("PropertiesTest", methodName); + executeTestJDKMethod("-Djava.util.currency.data="+TEST_PROPS, + "PropertiesTest", methodName); + } + + private static Stream PropertiesTestMethods() { + return Stream.of("bug7102969", "bug8157138", "bug8190904"); + } + + // Launch a PropertiesTest method using the TEST JDK + private static void executeTestJDKMethod(String... params) throws Throwable { + int exitStatus = ProcessTools.executeTestJvm(params).getExitValue(); + if (exitStatus != 0) { + fail("Process started with: " + Arrays.toString(params) + " failed"); + } + } + + // Launch a PropertiesTest method using the WRITABLE JDK + private static void executeWritableJDKMethod(String... params) throws Throwable { + // Need to include WritableJDK javapath, TEST JDK classpath + String[] allParams = new String[3+params.length+Utils.getTestJavaOpts().length]; + // We don't use executeTestJvm() because we want to point to separate JDK java path + allParams[0] = WRITABLE_JDK_JAVA_PATH; + allParams[1] = "-cp"; + allParams[2] = System.getProperty("java.class.path"); + // Add test.vm.opts and test.java.opts + System.arraycopy(Utils.getTestJavaOpts(), 0, allParams, 3, + Utils.getTestJavaOpts().length); + // Add the rest of the actual arguments + System.arraycopy(params, 0, allParams, Utils.getTestJavaOpts().length+3, + params.length); + // Launch the actual test method with all parameters set + executeProcess(allParams); + } + + // Execute a process and fail if the command is not successful + private static void executeProcess(String[] params) throws Throwable { + System.out.println("Command line: " + Arrays.toString(params)); + int exitStatus = ProcessTools.executeProcess(params).getExitValue(); + if (exitStatus != 0) { + fail("Process started with: " + Arrays.toString(params) + " failed"); + } + } + + @AfterAll + static void tearDown() throws Throwable { + // Remove the copied writable JDK image from scratch folder + executeProcess(new String[]{"rm", "-rf", WRITABLE_JDK}); + } +} diff --git a/test/jdk/java/util/Locale/LocaleProvidersRun.java b/test/jdk/java/util/Locale/LocaleProvidersRun.java index beeb1444e935b..947633a8e21ab 100644 --- a/test/jdk/java/util/Locale/LocaleProvidersRun.java +++ b/test/jdk/java/util/Locale/LocaleProvidersRun.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ * 8150432 8215913 8220227 8228465 8232871 8232860 8236495 8245241 * 8246721 8248695 8257964 8261919 * @summary tests for "java.locale.providers" system property + * @requires vm.flagless * @library /test/lib * @build LocaleProviders * providersrc.spi.src.tznp @@ -39,7 +40,6 @@ import java.util.Locale; -import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.Utils; @@ -178,22 +178,18 @@ public static void main(String[] args) throws Throwable { } private static void testRun(String prefList, String methodName, - String param1, String param2, String param3) throws Throwable{ - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(Utils.TEST_CLASS_PATH) - .addToolArg("-Djava.util.logging.config.class=LocaleProviders$LogConfig") - .addToolArg("-Djava.locale.providers=" + prefList) - .addToolArg("--add-exports=java.base/sun.util.locale.provider=ALL-UNNAMED") - .addToolArg("LocaleProviders") - .addToolArg(methodName) - .addToolArg(param1) - .addToolArg(param2) - .addToolArg(param3); - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + String param1, String param2, String param3) throws Throwable { + + // Build process (without VM flags) + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-ea", "-esa", + "-cp", Utils.TEST_CLASS_PATH, + "-Djava.util.logging.config.class=LocaleProviders$LogConfig", + "-Djava.locale.providers=" + prefList, + "--add-exports=java.base/sun.util.locale.provider=ALL-UNNAMED", + "LocaleProviders", methodName, param1, param2, param3); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); if (exitCode != 0) { throw new RuntimeException("Unexpected exit code: " + exitCode); } diff --git a/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java b/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java index 0ed86582846bb..30cd81757df6d 100644 --- a/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java +++ b/test/jdk/java/util/ResourceBundle/Control/MissingResourceCauseTestRun.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,15 +84,13 @@ private static void runCmd() throws Throwable { // UnreadableRB.properties is in current directory String cp = Utils.TEST_CLASSES + File.pathSeparator + Utils.TEST_SRC + File.pathSeparator + "."; - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(cp) - .addToolArg("MissingResourceCauseTest"); - - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-ea", "-esa", + "-cp", cp, + "MissingResourceCauseTest"); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); if (exitCode != 0) { throw new RuntimeException("Execution of the test failed. " + "Unexpected exit code: " + exitCode); diff --git a/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java b/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java index 1d8bdbb89db15..24c6e181ccc04 100644 --- a/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java +++ b/test/jdk/java/util/ResourceBundle/modules/ModuleTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.util.stream.Stream; import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.Utils; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.ProcessTools; @@ -133,17 +134,15 @@ public static void copyResFiles(Path src, Path dest, String mn, */ public static void runModule(String mp, String mn, List localeList) throws Throwable { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-p") - .addToolArg(mp) - .addToolArg("-m") - .addToolArg(mn); - localeList.forEach(launcher::addToolArg); - - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + List args = List.of( + "-ea", "-esa", + "-p", mp, + "-m", mn); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + Stream.concat(args.stream(), localeList.stream()).toList()); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); if (exitCode != 0) { throw new RuntimeException("Execution of the test failed. " + "Unexpected exit code: " + exitCode); @@ -161,19 +160,17 @@ public static void runModule(String mp, String mn, List localeList) */ public static void runModuleWithCp(String cp, String mp, String mn, List localeList, boolean expected) throws Throwable { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(cp) - .addToolArg("-p") - .addToolArg(mp) - .addToolArg("-m") - .addToolArg(mn); - localeList.forEach(launcher::addToolArg); + List args = List.of( + "-ea", "-esa", + "-cp", cp, + "-p", mp, + "-m", mn); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + Stream.concat(args.stream(), localeList.stream()).toList()); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); if (expected) { if (exitCode != 0) { throw new RuntimeException("Execution of the test loads bundles " @@ -198,18 +195,17 @@ public static void runModuleWithCp(String cp, String mp, String mn, */ public static void runModuleWithLegacyCode(String mp, String mn, List localeList) throws Throwable { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-Djava.locale.useOldISOCodes=true") - .addToolArg("-p") - .addToolArg(mp) - .addToolArg("-m") - .addToolArg(mn); - localeList.forEach(launcher::addToolArg); + List args = List.of( + "-ea", "-esa", + "-Djava.locale.useOldISOCodes=true", + "-p", mp, + "-m", mn); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + Stream.concat(args.stream(), localeList.stream()).toList()); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); if (exitCode != 0) { throw new RuntimeException("Execution of the test failed. " + "Unexpected exit code: " + exitCode); diff --git a/test/jdk/java/util/ResourceBundle/modules/layer/LayerTest.java b/test/jdk/java/util/ResourceBundle/modules/layer/LayerTest.java index b1c79c0367f6f..85e8e531a31ab 100644 --- a/test/jdk/java/util/ResourceBundle/modules/layer/LayerTest.java +++ b/test/jdk/java/util/ResourceBundle/modules/layer/LayerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,16 +71,13 @@ private static void compileCmd() throws Throwable { } private static void runCmd() throws Throwable { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(Utils.TEST_CLASSES) - .addToolArg("Main") - .addToolArg(Utils.TEST_CLASSES); - - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-ea", "-esa", + "-cp", Utils.TEST_CLASSES, + "Main", Utils.TEST_CLASSES); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); if (exitCode != 0) { throw new RuntimeException("Execution of the test failed. " + "Unexpected exit code: " + exitCode); diff --git a/test/jdk/java/util/ResourceBundle/modules/unnamed/UnNamedTest.java b/test/jdk/java/util/ResourceBundle/modules/unnamed/UnNamedTest.java index 4540d5a378b1a..e9567fcdaadc9 100644 --- a/test/jdk/java/util/ResourceBundle/modules/unnamed/UnNamedTest.java +++ b/test/jdk/java/util/ResourceBundle/modules/unnamed/UnNamedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.stream.Stream; import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.Utils; @@ -73,42 +74,34 @@ private static void compileCmd() throws Throwable { private static void runCmd() throws Throwable { // access resource bundles that are exported private unconditionally. - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(Utils.TEST_CLASSES) - .addToolArg("--module-path") - .addToolArg(MODS_DIR.toString()) - .addToolArg("--add-modules") - .addToolArg("bundles") - .addToolArg("Main"); - LOCALE_LIST.forEach(launcher::addToolArg); - - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + List args = List.of( + "-ea", "-esa", + "-cp", Utils.TEST_CLASSES, + "--module-path", MODS_DIR.toString(), + "--add-modules", "bundles", + "Main"); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + Stream.concat(args.stream(), LOCALE_LIST.stream()).toList()); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); if (exitCode != 0) { throw new RuntimeException("Execution of the test1 failed. " + "Unexpected exit code: " + exitCode); } // --add-exports can't open resources - launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(Utils.TEST_CLASSES) - .addToolArg("--module-path") - .addToolArg(MODS_DIR.toString()) - .addToolArg("--add-modules") - .addToolArg("bundles") - .addToolArg("--add-opens") - .addToolArg("bundles/jdk.test.internal.resources=ALL-UNNAMED") - .addToolArg("Main"); - LOCALE_LIST.forEach(launcher::addToolArg); + List argsWithOpens = List.of( + "-ea", "-esa", + "-cp", Utils.TEST_CLASSES, + "--module-path", MODS_DIR.toString(), + "--add-modules", "bundles", + "--add-opens", "bundles/jdk.test.internal.resources=ALL-UNNAMED", + "Main"); + pb = ProcessTools.createTestJavaProcessBuilder( + Stream.concat(argsWithOpens.stream(), LOCALE_LIST.stream()).toList()); - exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + // Evaluate process status + exitCode = ProcessTools.executeCommand(pb).getExitValue(); if (exitCode != 0) { throw new RuntimeException("Execution of the test2 failed. " + "Unexpected exit code: " + exitCode); diff --git a/test/jdk/java/util/ResourceBundle/modules/visibility/VisibilityTest.java b/test/jdk/java/util/ResourceBundle/modules/visibility/VisibilityTest.java index 7f9405863376d..e0fdb9a93abb9 100644 --- a/test/jdk/java/util/ResourceBundle/modules/visibility/VisibilityTest.java +++ b/test/jdk/java/util/ResourceBundle/modules/visibility/VisibilityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ * @bug 8137317 8139238 8210408 * @summary Visibility tests for ResourceBundle.getBundle with and without * an unnamed module argument. + * @requires vm.flagless * @library /test/lib * .. * @build jdk.test.lib.JDKToolLauncher @@ -39,10 +40,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.stream.Stream; import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; + import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -328,11 +331,10 @@ public void RunWithPkgRes(List argsList) throws Throwable { } private int runCmd(List argsList) throws Throwable { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-ea") - .addToolArg("-esa"); - argsList.forEach(launcher::addToolArg); - - return ProcessTools.executeCommand(launcher.getCommand()).getExitValue(); + // Build process (without VM flags) + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + Stream.concat(Stream.of("-ea", "-esa"), argsList.stream()).toList()); + // Evaluate process status + return ProcessTools.executeCommand(pb).getExitValue(); } -} \ No newline at end of file +} diff --git a/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java b/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java index ece90a9d7cd5e..5fd48efadcc3a 100644 --- a/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java +++ b/test/jdk/java/util/TimeZone/CustomTzIDCheckDST.java @@ -29,16 +29,18 @@ * @requires os.family != "windows" * @run main/othervm CustomTzIDCheckDST */ + import java.util.Calendar; import java.util.Date; -import java.util.List; import java.util.SimpleTimeZone; import java.util.TimeZone; import java.time.DayOfWeek; import java.time.ZonedDateTime; import java.time.temporal.TemporalAdjusters; + import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; + public class CustomTzIDCheckDST { // Northern Hemisphere private static String CUSTOM_TZ = "MEZ-1MESZ,M3.5.0,M10.5.0/3"; @@ -46,7 +48,7 @@ public class CustomTzIDCheckDST { private static String CUSTOM_TZ2 = "MEZ-1MESZ,M10.5.0,M3.5.0/3"; public static void main(String args[]) throws Throwable { if (args.length == 0) { - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(List.of("CustomTzIDCheckDST", "runTZTest")); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder("CustomTzIDCheckDST", "runTZTest"); pb.environment().put("TZ", CUSTOM_TZ); OutputAnalyzer output = ProcessTools.executeProcess(pb); output.shouldHaveExitValue(0); diff --git a/test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheckRun.java b/test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheckRun.java index c510c39cd165b..f5d80cd6069a3 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheckRun.java +++ b/test/jdk/java/util/TimeZone/TimeZoneDatePermissionCheckRun.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,17 +66,15 @@ public static void main(String[] args) throws Throwable { //run it with the security manager on, plus accesscontroller debugging //will go into infinite recursion trying to get enough permissions for //printing Date of failing certificate unless fix is applied. - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("java"); - launcher.addToolArg("-Djava.security.manager") - .addToolArg("-Djava.security.debug=access,failure,policy") - .addToolArg("-ea") - .addToolArg("-esa") - .addToolArg("-cp") - .addToolArg(jarPath) - .addToolArg("TimeZoneDatePermissionCheck"); - - int exitCode = ProcessTools.executeCommand(launcher.getCommand()) - .getExitValue(); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-Djava.security.manager", + "-Djava.security.debug=access,failure,policy", + "-ea", "-esa", + "-cp", jarPath, + "TimeZoneDatePermissionCheck"); + // Evaluate process status + int exitCode = ProcessTools.executeCommand(pb).getExitValue(); if (exitCode != 0) { throw new RuntimeException("Unexpected exit code: " + exitCode); } diff --git a/test/jdk/java/util/concurrent/atomic/VMSupportsCS8.java b/test/jdk/java/util/concurrent/atomic/VMSupportsCS8.java deleted file mode 100644 index a485a490bb659..0000000000000 --- a/test/jdk/java/util/concurrent/atomic/VMSupportsCS8.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 4992443 4994819 - * @modules java.base/java.util.concurrent.atomic:open - * @run main VMSupportsCS8 - * @summary Checks that the value of VMSupportsCS8 matches system properties. - */ - -import java.lang.reflect.Field; - -public class VMSupportsCS8 { - public static void main(String[] args) throws Exception { - String isalist = System.getProperty("sun.cpu.isalist"); - if (isalist != null && isalist.matches - (".*\\b(pentium_pro|ia64|amd64).*") - || - System.getProperty("os.arch").matches - (".*\\b(ia64|amd64).*")) { - - System.out.println("This system is known to have hardware CS8"); - - Class klass = Class.forName("java.util.concurrent.atomic.AtomicLong"); - Field field = klass.getDeclaredField("VM_SUPPORTS_LONG_CAS"); - field.setAccessible(true); - boolean VMSupportsCS8 = field.getBoolean(null); - if (! VMSupportsCS8) - throw new Exception("Unexpected value for VMSupportsCS8"); - } - } -} diff --git a/test/jdk/java/util/logging/LoggingDeadlock2.java b/test/jdk/java/util/logging/LoggingDeadlock2.java index 4aa3121abc1cd..e8a0bb49d87d6 100644 --- a/test/jdk/java/util/logging/LoggingDeadlock2.java +++ b/test/jdk/java/util/logging/LoggingDeadlock2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,12 @@ * @test * @bug 6467152 6716076 6829503 8132550 * @summary deadlock occurs in LogManager initialization and JVM termination + * @key randomness + * @library /test/lib * @author Serguei Spitsyn / Hitachi / Martin Buchholz * * @build LoggingDeadlock2 * @run main LoggingDeadlock2 - * @key randomness */ /* @@ -50,7 +51,6 @@ * This is a regression test for this bug. */ -import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.CyclicBarrier; @@ -63,6 +63,8 @@ import java.io.Reader; import java.util.concurrent.TimeUnit; +import jdk.test.lib.process.ProcessTools; + public class LoggingDeadlock2 { // ask child process to dumpstack after 60secs @@ -74,7 +76,9 @@ public class LoggingDeadlock2 { public static void realMain(String arg[]) throws Throwable { try { System.out.println(javaChildArgs); - ProcessBuilder pb = new ProcessBuilder(javaChildArgs); + // Build process (with VM flags) + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + javaChildArgs); ProcessResults r = run(pb.start()); equal(r.exitValue(), 99); @@ -151,9 +155,6 @@ public void run() { //---------------------------------------------------------------- // The rest of this test is copied from ProcessBuilder/Basic.java //---------------------------------------------------------------- - private static final String javaExe = - System.getProperty("java.home") + - File.separator + "bin" + File.separator + "java"; private static final String jstackExe = System.getProperty("java.home") + File.separator + "bin" + File.separator + "jstack"; @@ -161,10 +162,8 @@ public void run() { private static final String classpath = System.getProperty("java.class.path"); - private static final List javaChildArgs = - Arrays.asList(new String[] - { javaExe, "-classpath", classpath, - "LoggingDeadlock2$JavaChild"}); + private static final List javaChildArgs = List.of( + "-classpath", classpath, "LoggingDeadlock2$JavaChild"); private static class ProcessResults { private final String out; diff --git a/test/jdk/java/util/zip/EntryCount64k.java b/test/jdk/java/util/zip/EntryCount64k.java index 68b86e50ece23..08d896a124a2a 100644 --- a/test/jdk/java/util/zip/EntryCount64k.java +++ b/test/jdk/java/util/zip/EntryCount64k.java @@ -37,7 +37,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.RandomAccessFile; -import java.nio.file.Paths; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -161,11 +160,7 @@ static void checkCanRead(File zipFile, int entryCount) throws Throwable { } // Check java -jar - String javaHome = System.getProperty("java.home"); - String java = Paths.get(javaHome, "bin", "java").toString(); - String[] cmd = { java, "-jar", zipFile.getName() }; - ProcessBuilder pb = new ProcessBuilder(cmd); - OutputAnalyzer a = ProcessTools.executeProcess(pb); + OutputAnalyzer a = ProcessTools.executeTestJvm("-jar", zipFile.getName()); a.shouldHaveExitValue(0); a.stdoutShouldMatch("\\AMain\\Z"); a.stderrShouldMatch("\\A\\Z"); diff --git a/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java b/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java index c03c75adee5c0..e5e0c0493cb1a 100644 --- a/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java +++ b/test/jdk/javax/management/remote/mandatory/notif/ListenerScaleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,6 +77,7 @@ public class ListenerScaleTest { private static final int WARMUP_WITH_ONE_MBEAN = 1000; private static final int NOTIFS_TO_TIME = 100; private static final int EXTRA_MBEANS = 20000; + private static final double RATIO_FAIL_VALUE = 2500.0; private static final ObjectName testObjectName; static { @@ -187,8 +188,9 @@ private static void test(MBeanServer mbs, JMXConnectorServer cs, long manyMBeansTime = timeNotif(mbs); System.out.println("Time with many MBeans: " + manyMBeansTime + "ns"); double ratio = (double) manyMBeansTime / singleMBeanTime; - if (ratio > 500.0) + if (ratio > RATIO_FAIL_VALUE) { throw new Exception("Failed: ratio=" + ratio); + } System.out.println("Test passed: ratio=" + ratio); } } diff --git a/test/jdk/javax/script/JDK_8196959/BadFactoryTest.sh b/test/jdk/javax/script/JDK_8196959/BadFactoryTest.sh index 21becec9f7134..e5d1a063840ba 100644 --- a/test/jdk/javax/script/JDK_8196959/BadFactoryTest.sh +++ b/test/jdk/javax/script/JDK_8196959/BadFactoryTest.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -56,5 +56,5 @@ fi echo "Running test without security manager ..." $JAVA ${TESTVMOPTS} -classpath \ - "${TESTCLASSES}${PS}${TESTCLASSES}/badfactoty.jar" \ + "${TESTCLASSES}${PS}${TESTCLASSES}/badfactory.jar" \ BadFactoryTest diff --git a/test/jdk/javax/swing/JProgressBar/TestProgressBarBorder.java b/test/jdk/javax/swing/JProgressBar/TestProgressBarBorder.java new file mode 100644 index 0000000000000..06ccfc3b9bfbc --- /dev/null +++ b/test/jdk/javax/swing/JProgressBar/TestProgressBarBorder.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import javax.imageio.ImageIO; +import javax.swing.JComponent; +import javax.swing.JProgressBar; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +import static java.awt.image.BufferedImage.TYPE_INT_RGB; + +/* + * @test + * @bug 8224261 + * @key headful + * @library ../regtesthelpers + * @build Util + * @summary Verifies JProgressBar border is not painted when border + * painting is set to false + * @run main TestProgressBarBorder + */ + +public class TestProgressBarBorder { + public static void main(String[] args) throws Exception { + for (UIManager.LookAndFeelInfo laf : + UIManager.getInstalledLookAndFeels()) { + if (!laf.getName().contains("Nimbus") && !laf.getName().contains("GTK")) { + continue; + } + System.out.println("Testing LAF: " + laf.getName()); + SwingUtilities.invokeAndWait(() -> test(laf)); + } + } + + private static void test(UIManager.LookAndFeelInfo laf) { + setLookAndFeel(laf); + JProgressBar progressBar = createProgressBar(); + progressBar.setBorderPainted(true); + BufferedImage withBorder = paintToImage(progressBar); + progressBar.setBorderPainted(false); + BufferedImage withoutBorder = paintToImage(progressBar); + + boolean equal = Util.compareBufferedImages(withBorder, withoutBorder); + if (equal) { + try { + ImageIO.write(withBorder, "png", new File("withBorder.png")); + ImageIO.write(withoutBorder, "png", new File("withoutBorder.png")); + } catch (IOException ignored) {} + + throw new RuntimeException("JProgressBar border is painted when border " + + "painting is set to false"); + } + } + + private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (UnsupportedLookAndFeelException ignored) { + System.out.println("Unsupported LAF: " + laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static JProgressBar createProgressBar() { + JProgressBar progressBar = new JProgressBar(); + progressBar.setSize(100, 50); + progressBar.setValue(0); + progressBar.setStringPainted(true); + return progressBar; + } + + private static BufferedImage paintToImage(JComponent content) { + BufferedImage im = new BufferedImage(content.getWidth(), content.getHeight(), + TYPE_INT_RGB); + Graphics g = im.getGraphics(); + content.paint(g); + g.dispose(); + return im; + } +} diff --git a/test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java b/test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java index 304f1e83542a7..aa7a294c57b06 100644 --- a/test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java +++ b/test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java @@ -31,7 +31,7 @@ /* * @test - * @bug 7083187 + * @bug 7083187 8318113 * @summary Verifies if CSS.CSSValue attribute is same * @run main CSSAttributeEqualityBug */ @@ -72,6 +72,9 @@ public class CSSAttributeEqualityBug { "background-position: 1em 2em", "border-width: medium", + + "background-image: none", + "background-image: url(image.png)", }; /** @@ -86,6 +89,8 @@ public class CSSAttributeEqualityBug { {"margin-top: 42px", "margin-top: 22px"}, {"margin-top: 42px", "margin-top: 42pt"}, {"margin-top: 100%", "margin-top: 50%"}, + + {"background-image: none", "background-image: url(image.png)"}, }; private static final String[][] EQUALS_WITH_SPACE = { diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index 50b85db5323c5..3950540500144 100644 --- a/test/jdk/jdk/classfile/StackMapsTest.java +++ b/test/jdk/jdk/classfile/StackMapsTest.java @@ -24,7 +24,7 @@ /* * @test * @summary Testing Classfile stack maps generator. - * @bug 8305990 8320222 + * @bug 8305990 8320222 8320618 * @build testdata.* * @run junit StackMapsTest */ @@ -43,7 +43,6 @@ import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; -import java.util.List; import java.lang.reflect.AccessFlag; /** @@ -237,6 +236,18 @@ void testClassVersions() throws Exception { .verify(null)); } + @Test + void testInvalidAALOADStack() { + Classfile.of().build(ClassDesc.of("Test"), clb + -> clb.withMethodBody("test", ConstantDescs.MTD_void, 0, cob + -> cob.bipush(10) + .anewarray(ConstantDescs.CD_Object) + .lconst_1() //long on stack caused NPE, see 8320618 + .aaload() + .astore(2) + .return_())); + } + private static final FileSystem JRT = FileSystems.getFileSystem(URI.create("jrt:/")); private static void testTransformedStackMaps(String classPath, Classfile.Option... options) throws Exception { diff --git a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java index 9d656f0e33854..65a0acf6dac5c 100644 --- a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Byte128VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Byte128VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java index 31ecc509c3917..87c5f3a39c2b3 100644 --- a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Byte256VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Byte256VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java index b677d6fde5959..97e70d2f85d5f 100644 --- a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Byte512VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Byte512VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java index 3c33d91854c05..92019e6957a6b 100644 --- a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Byte64VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Byte64VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java index 3d19d2ab6d0b6..921b2be725ceb 100644 --- a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation ByteMaxVectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation ByteMaxVectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Double128VectorTests.java b/test/jdk/jdk/incubator/vector/Double128VectorTests.java index 5e81cb946bcaf..f7c955a08ce13 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Double128VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Double128VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Double256VectorTests.java b/test/jdk/jdk/incubator/vector/Double256VectorTests.java index ef4f3f0f97511..27f9c0df19425 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Double256VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Double256VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Double512VectorTests.java b/test/jdk/jdk/incubator/vector/Double512VectorTests.java index 21209c23ea10e..440a553dc335d 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Double512VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Double512VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Double64VectorTests.java b/test/jdk/jdk/incubator/vector/Double64VectorTests.java index 360445a6b7f2e..73ac9b00c05f9 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Double64VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Double64VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java index 01d72b64d2d92..296e23a70231e 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation DoubleMaxVectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation DoubleMaxVectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Float128VectorTests.java b/test/jdk/jdk/incubator/vector/Float128VectorTests.java index 9f1b28a80c6a5..bf61881e3d8d5 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Float128VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float128VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Float256VectorTests.java b/test/jdk/jdk/incubator/vector/Float256VectorTests.java index 649b06a007d6c..7292f55aa6d5a 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Float256VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float256VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Float512VectorTests.java b/test/jdk/jdk/incubator/vector/Float512VectorTests.java index 5bd3932cef158..01d1e27209b50 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Float512VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float512VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Float64VectorTests.java b/test/jdk/jdk/incubator/vector/Float64VectorTests.java index 1922be94f403f..b7fd8ab276787 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Float64VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Float64VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java index 3e72f9077f624..5592f5985b043 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation FloatMaxVectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation FloatMaxVectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Int128VectorTests.java b/test/jdk/jdk/incubator/vector/Int128VectorTests.java index f69423c12e2b5..fcfe286034d2c 100644 --- a/test/jdk/jdk/incubator/vector/Int128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int128VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Int128VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Int128VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Int256VectorTests.java b/test/jdk/jdk/incubator/vector/Int256VectorTests.java index f580c39c0910d..64cad182a24bd 100644 --- a/test/jdk/jdk/incubator/vector/Int256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int256VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Int256VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Int256VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Int512VectorTests.java b/test/jdk/jdk/incubator/vector/Int512VectorTests.java index 89cd557ec9b89..468a2c3a8f13f 100644 --- a/test/jdk/jdk/incubator/vector/Int512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int512VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Int512VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Int512VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Int64VectorTests.java b/test/jdk/jdk/incubator/vector/Int64VectorTests.java index 0affccd742b53..14b853c1e7222 100644 --- a/test/jdk/jdk/incubator/vector/Int64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int64VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Int64VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Int64VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java index c0fdcbf58a317..b608b548ceeb2 100644 --- a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation IntMaxVectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation IntMaxVectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Long128VectorTests.java b/test/jdk/jdk/incubator/vector/Long128VectorTests.java index a706b884f8bc5..e80497c358441 100644 --- a/test/jdk/jdk/incubator/vector/Long128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long128VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Long128VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Long128VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Long256VectorTests.java b/test/jdk/jdk/incubator/vector/Long256VectorTests.java index 3056c8cbcaa65..b3762b4c15a17 100644 --- a/test/jdk/jdk/incubator/vector/Long256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long256VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Long256VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Long256VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Long512VectorTests.java b/test/jdk/jdk/incubator/vector/Long512VectorTests.java index 5a0a5d106bf33..57ba7738d13aa 100644 --- a/test/jdk/jdk/incubator/vector/Long512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long512VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Long512VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Long512VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Long64VectorTests.java b/test/jdk/jdk/incubator/vector/Long64VectorTests.java index 1a3ecebd57c7d..8917281196f42 100644 --- a/test/jdk/jdk/incubator/vector/Long64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long64VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Long64VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Long64VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java index d467107e9f2a1..913f7d6f52779 100644 --- a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation LongMaxVectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation LongMaxVectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Short128VectorTests.java b/test/jdk/jdk/incubator/vector/Short128VectorTests.java index 81568661570f0..e40a40686c980 100644 --- a/test/jdk/jdk/incubator/vector/Short128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short128VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Short128VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Short128VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Short256VectorTests.java b/test/jdk/jdk/incubator/vector/Short256VectorTests.java index 14a54fcc1df77..02138e3e8aa2f 100644 --- a/test/jdk/jdk/incubator/vector/Short256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short256VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Short256VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Short256VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Short512VectorTests.java b/test/jdk/jdk/incubator/vector/Short512VectorTests.java index 8d869e5a66f24..9577f22f58cdf 100644 --- a/test/jdk/jdk/incubator/vector/Short512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short512VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Short512VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Short512VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/Short64VectorTests.java b/test/jdk/jdk/incubator/vector/Short64VectorTests.java index 47f37beecb616..71b3c6046b45c 100644 --- a/test/jdk/jdk/incubator/vector/Short64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short64VectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation Short64VectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation Short64VectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java index 7e657a3f39832..4a6adbf2c8ea0 100644 --- a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation ShortMaxVectorTests + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation ShortMaxVectorTests */ // -- This file was mechanically generated: Do not edit! -- // diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index 87364836f7cbf..4d3795ea3d170 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -24,7 +24,7 @@ /* * @test * @modules jdk.incubator.vector - * @run testng/othervm -ea -esa -Xbatch -XX:-TieredCompilation $vectorteststype$ + * @run testng/othervm/timeout=300 -ea -esa -Xbatch -XX:-TieredCompilation $vectorteststype$ */ #warn This file is preprocessed before being compiled diff --git a/test/jdk/jdk/jfr/api/metadata/annotations/TestStackFilter.java b/test/jdk/jdk/jfr/api/metadata/annotations/TestStackFilter.java new file mode 100644 index 0000000000000..2acd369b6cd8f --- /dev/null +++ b/test/jdk/jdk/jfr/api/metadata/annotations/TestStackFilter.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.api.metadata.annotations; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import jdk.jfr.api.metadata.annotations.UnloadableClass; +import jdk.jfr.Event; +import jdk.jfr.AnnotationElement; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordingStream; +import jdk.jfr.events.StackFilter; +import jdk.jfr.Recording; +import jdk.jfr.Name; +import jdk.jfr.EventType; +import jdk.jfr.EventFactory; +import jdk.jfr.FlightRecorder; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.TestClassLoader; + +/** + * @test + * @key jfr + * @requires vm.hasJFR + * @modules jdk.jfr/jdk.jfr.events + * @library /test/lib /test/jdk + * @run main/othervm -Xlog:jfr=warning jdk.jfr.api.metadata.annotations.TestStackFilter + */ +public class TestStackFilter { + private static class Quux { + private static void one() throws Exception { + two(); + } + private static void two() throws Exception { + three(); + } + + private static void three() throws Exception { + TestStackFilter.qux(); + } + } + private final static String PACKAGE = "jdk.jfr.api.metadata.annotations.TestStackFilter"; + private final static String M1 = PACKAGE + "::foo"; + private final static String M2 = PACKAGE + "::baz"; + private final static String C1 = PACKAGE + "$Quux"; + + @StackFilter({ M1, M2 }) + @Name("MethodFilter") + public static class MethodFilterEvent extends Event { + } + + @StackFilter(C1) + @Name("ClassFilter") + public static class ClassFilterEvent extends Event { + } + + @StackFilter({}) + @Name("Empty") + public static class EmptyEvent extends Event { + } + + @StackFilter(PACKAGE + "::testUnload") + @Name("Unload") + public static class UnloadEvent extends Event { + } + + @StackFilter(PACKAGE + "::emitCommitter") + @Name("Reuse") + public static class ReuseEvent extends Event { + } + + @StackFilter(PACKAGE + "::emitCommitter") + @Name("Max") + public static class ExceedMaxEvent extends Event { + } + + public static void main(String[] args) throws Exception { + testMethodFilter(); + testClassFilter(); + testUnload(); + testReuse(); + testExceedMax(); + } + + // Use more stack filters than there is capacity for + private static void testExceedMax() throws Exception { + try (Recording r = new Recording()) { + r.start(); + // Maximum number of simultaneous event classes that can + // use a filter is 4096. Additional filters will be ignored. + var classes = new ArrayList<>(); + for (int i = 0; i < 4200; i++) { + Class eventClass = UnloadableClass.load(ExceedMaxEvent.class); + emitCommitter(eventClass); + classes.add(eventClass); + } + List events = Events.fromRecording(r); + if (events.size() != 4200) { + throw new Exception("Expected 4200 'Max' events"); + } + int emitCommitterCount = 0; + int textExceedMaxCount = 0; + for (RecordedEvent event : events) { + RecordedStackTrace s = event.getStackTrace(); + if (s == null) { + System.out.println(event); + throw new Exception("Expected stack trace for 'Max' event"); + } + + RecordedFrame f = s.getFrames().get(0); + if (!f.isJavaFrame()) { + throw new Exception("Expected Java frame for 'Max' event"); + } + String methodName = f.getMethod().getName(); + switch (methodName) { + case "emitCommitter": + emitCommitterCount++; + break; + case "testExceedMax": + textExceedMaxCount++; + break; + default: + System.out.println(event); + throw new Exception("Unexpected top frame " + methodName + " for 'Max' event"); + } + } + // Can't match exact because filters from previous tests may be in use + // or because filters added by JDK events filters + if (emitCommitterCount == 0) { + throw new Exception("Expected at least some events with emitCommitter() as top frame, found " + emitCommitterCount); + } + if (textExceedMaxCount < 500) { + throw new Exception("Expected at least 500 events with testExceedMax() as top frame, found " + textExceedMaxCount); + } + } + } + + // Tests that event classes with @StackFilter that are unloaded + // reuses the memory slot used to bookkeep things in native + private static void testReuse() throws Exception { + try (Recording r = new Recording()) { + r.enable("Reuse"); + r.start(); + for (int i = 0; i < 48; i++) { + Class eventClass = UnloadableClass.load(ReuseEvent.class); + emitCommitter(eventClass); + if (i % 16 == 0) { + System.gc(); + rotate(); + } + } + r.stop(); + List events = Events.fromRecording(r); + if (events.size() != 48) { + throw new Exception("Expected 48 'Reuse' events"); + } + for (RecordedEvent event : events) { + assertTopFrame(event, "testReuse"); + } + } + + } + + // This test registers a stack filter, emits an event with the filter + // and unregisters it. While this is happening, another + // filter is being used. + private static void testUnload() throws Exception { + try (Recording r = new Recording()) { + r.start(); + Class eventClass = UnloadableClass.load(UnloadEvent.class); + emitCommitter(eventClass); + EventType type = getType("Unload"); + if (type == null) { + throw new Exception("Expected event type named 'Unload'"); + } + eventClass = null; + while (true) { + System.gc(); + rotate(); + type = getType("Unload"); + if (type == null) { + return; + } + System.out.println("Unload class not unloaded. Retrying ..."); + } + } + } + + private static void testMethodFilter() throws Exception { + try (Recording r = new Recording()) { + r.enable(MethodFilterEvent.class); + r.start(); + foo(); + bar(); + empty(); + r.stop(); + List events = Events.fromRecording(r); + if (events.isEmpty()) { + throw new Exception("Excected events"); + } + + RecordedEvent e1 = events.get(0); + assertTopFrame(e1, "testMethodFilter"); + + RecordedEvent e2 = events.get(1); + assertTopFrame(e2, "bar"); + + RecordedEvent e3 = events.get(2); + assertTopFrame(e3, "empty"); + } + } + + private static void testClassFilter() throws Exception { + try (Recording r = new Recording()) { + r.enable(MethodFilterEvent.class); + r.start(); + Quux.one(); + r.stop(); + List events = Events.fromRecording(r); + if (events.isEmpty()) { + throw new Exception("Excected events"); + } + + RecordedEvent e = events.get(0); + assertTopFrame(e, "qux"); + for (RecordedFrame f : e.getStackTrace().getFrames()) { + if (f.getMethod().getType().getName().contains("Quux")) { + System.out.println(e); + throw new Exception("Didn't expect Quux class in stack trace"); + } + } + } + } + + private static void empty() { + EmptyEvent event = new EmptyEvent(); + event.commit(); + } + + static void foo() { + baz(); + } + + static void bar() { + baz(); + } + + static void baz() { + MethodFilterEvent event = new MethodFilterEvent(); + event.commit(); + } + + static void qux() { + ClassFilterEvent event = new ClassFilterEvent(); + event.commit(); + } + + private static void rotate() { + try (Recording r = new Recording()) { + r.start(); + } + } + + private static EventType getType(String name) { + for (EventType et : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (et.getName().equals(name)) { + return et; + } + + } + return null; + } + + private static void emitCommitter(Class eventClass) throws Exception { + Event event = eventClass.getConstructor().newInstance(); + event.commit(); + } + + private static void assertTopFrame(RecordedEvent event, String methodName) throws Exception { + RecordedStackTrace stackTrace = event.getStackTrace(); + if (stackTrace == null) { + System.out.println(event); + throw new Exception("No stack trace found when looking for top frame '" + methodName + "'"); + } + RecordedFrame frame = stackTrace.getFrames().get(0); + RecordedMethod method = frame.getMethod(); + if (!methodName.equals(method.getName())) { + System.out.println(event); + throw new Exception("Expected top frame '" + methodName + "'"); + } + } +} diff --git a/test/jdk/jdk/jfr/api/metadata/annotations/UnloadableClass.java b/test/jdk/jdk/jfr/api/metadata/annotations/UnloadableClass.java new file mode 100644 index 0000000000000..4a4a57a0e55d9 --- /dev/null +++ b/test/jdk/jdk/jfr/api/metadata/annotations/UnloadableClass.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.api.metadata.annotations; + +import java.io.DataInputStream; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; + +/* Purpose of this class is to load a specified class in its + * own class loader, but delegate every other class. + */ +public final class UnloadableClass extends ClassLoader { + private final String className; + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static Class load(Class clazz) throws ClassNotFoundException { + UnloadableClass cl = new UnloadableClass(clazz.getName()); + return cl.loadClass(cl.className); + } + + private UnloadableClass(String className) { + super("Class loader for class " + className, ClassLoader.getSystemClassLoader()); + this.className = className; + } + + public Class loadClass(String name) throws ClassNotFoundException { + if (!className.equals(name)) { + return super.loadClass(name); + } + String resourceName = name.replace('.', '/') + ".class"; + try (var is = getResourceAsStream(resourceName); var dis = new DataInputStream(is)) { + int size = is.available(); + byte buffer[] = new byte[size]; + dis.readFully(buffer); + CodeSource cs = new CodeSource(getResource(resourceName), (Certificate[]) null); + ProtectionDomain pd = new ProtectionDomain(cs, null); + return defineClass(name, buffer, 0, buffer.length, pd); + } catch (Exception e) { + throw new InternalError(e); + } + } +} diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java index 3d378712c422e..1e64a0bb4e44e 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java @@ -125,7 +125,7 @@ public static void main(String[] args) throws IOException { // since we can not reliably guarantee that they occur (or not). Set optPhases = of( // The following phases only occur on evacuation failure. - "RestoreRetainedRegions", + "RestoreEvacuationFailedRegions", "RemoveSelfForwards", "RestorePreservedMarks", "ProcessEvacuationFailedRegions", diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java index 5ec810b11c861..bd3a7f63a8a99 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java @@ -40,7 +40,7 @@ public static void main(String[] args) throws Exception { String testID = "G1ConcurrentMark"; String[] vmFlags = {"-XX:+UseG1GC", "-XX:+ExplicitGCInvokesConcurrent"}; String[] gcNames = {GCHelper.gcG1New, GCHelper.gcG1Old, GCHelper.gcG1Full}; - String[] gcCauses = {"Metadata GC Threshold", "GCLocker Initiated GC", "G1 Evacuation Pause", "G1 Preventive Collection", + String[] gcCauses = {"Metadata GC Threshold", "G1 Evacuation Pause", "G1 Preventive Collection", "G1 Compaction Pause", "System.gc()"}; GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); } diff --git a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java index a83e0ba6d6f70..072c3905baf59 100644 --- a/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java +++ b/test/jdk/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java @@ -40,7 +40,7 @@ public static void main(String[] args) throws Exception { String testID = "G1FullCollection"; String[] vmFlags = {"-XX:+UseG1GC"}; String[] gcNames = {GCHelper.gcG1New, GCHelper.gcG1Old, GCHelper.gcG1Full}; - String[] gcCauses = {"Metadata GC Threshold", "GCLocker Initiated GC", "G1 Evacuation Pause", "G1 Preventive Collection", + String[] gcCauses = {"Metadata GC Threshold", "G1 Evacuation Pause", "G1 Preventive Collection", "G1 Compaction Pause", "System.gc()"}; GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); } diff --git a/test/jdk/jdk/jfr/event/gc/detailed/TestGCLockerEvent.java b/test/jdk/jdk/jfr/event/gc/detailed/TestGCLockerEvent.java index f0d9fa3f35a69..596af47de39bc 100644 --- a/test/jdk/jdk/jfr/event/gc/detailed/TestGCLockerEvent.java +++ b/test/jdk/jdk/jfr/event/gc/detailed/TestGCLockerEvent.java @@ -26,11 +26,12 @@ * @test TestGCLockerEvent * @key jfr * @requires vm.hasJFR - * @requires vm.gc.G1 + * @requires vm.gc.Serial | vm.gc.Parallel + * @requires vm.gc != null * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx32m -Xms32m -Xmn12m -XX:+UseG1GC jdk.jfr.event.gc.detailed.TestGCLockerEvent + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx32m -Xms32m -Xmn12m jdk.jfr.event.gc.detailed.TestGCLockerEvent */ package jdk.jfr.event.gc.detailed; diff --git a/test/jdk/jdk/jfr/event/os/TestProcessStart.java b/test/jdk/jdk/jfr/event/os/TestProcessStart.java index 32d3be0e980f2..bcf216857bb9b 100644 --- a/test/jdk/jdk/jfr/event/os/TestProcessStart.java +++ b/test/jdk/jdk/jfr/event/os/TestProcessStart.java @@ -47,7 +47,7 @@ public class TestProcessStart { public static void main(String[] args) throws Throwable { try (Recording recording = new Recording()) { - recording.enable(EVENT_NAME); + recording.enable(EVENT_NAME).withStackTrace(); recording.start(); List commandList = new ArrayList<>(); if (Platform.isWindows()) { @@ -74,6 +74,7 @@ public static void main(String[] args) throws Throwable { Events.assertField(event, "pid").equal(p.pid()); Events.assertField(event, "directory").equal(pb.directory().toString()); Events.assertField(event, "command").equal(command.toString()); + Events.assertTopFrame(event, TestProcessStart.class, "main"); } } } diff --git a/test/jdk/jdk/jfr/event/runtime/TestThreadSleepEvent.java b/test/jdk/jdk/jfr/event/runtime/TestThreadSleepEvent.java index afd96b66540b8..c084808ce79ae 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestThreadSleepEvent.java +++ b/test/jdk/jdk/jfr/event/runtime/TestThreadSleepEvent.java @@ -56,13 +56,7 @@ public static void main(String[] args) throws Throwable { recording.enable(EVENT_NAME).withoutThreshold().withStackTrace(); recording.start(); Thread.sleep(SLEEP_TIME_MS); - Thread virtualThread = Thread.ofVirtual().start(() -> { - try { - Thread.sleep(SLEEP_TIME_MS); - } catch (InterruptedException ie) { - throw new RuntimeException(ie); - } - }); + Thread virtualThread = Thread.ofVirtual().start(TestThreadSleepEvent::virtualSleep); virtualThread.join(); recording.stop(); @@ -74,14 +68,24 @@ public static void main(String[] args) throws Throwable { System.out.println(event.getStackTrace()); if (event.getThread().getJavaThreadId() == Thread.currentThread().getId()) { threadCount--; + Events.assertTopFrame(event, TestThreadSleepEvent.class, "main"); Events.assertDuration(event, "time", Duration.ofMillis(SLEEP_TIME_MS)); } if (event.getThread().getJavaThreadId() == virtualThread.getId()) { threadCount--; + Events.assertTopFrame(event, TestThreadSleepEvent.class, "virtualSleep"); Events.assertDuration(event, "time", Duration.ofMillis(SLEEP_TIME_MS)); } } Asserts.assertEquals(threadCount, 0, "Could not find all expected events"); } } + + private static void virtualSleep() { + try { + Thread.sleep(SLEEP_TIME_MS); + } catch (InterruptedException ie) { + throw new RuntimeException(ie); + } + } } diff --git a/test/jdk/jdk/jfr/event/security/TestSecurityPropertyModificationEvent.java b/test/jdk/jdk/jfr/event/security/TestSecurityPropertyModificationEvent.java index 0c1e5b32c5cb4..ff5919b25909a 100644 --- a/test/jdk/jdk/jfr/event/security/TestSecurityPropertyModificationEvent.java +++ b/test/jdk/jdk/jfr/event/security/TestSecurityPropertyModificationEvent.java @@ -58,7 +58,7 @@ public static void main(String[] args) throws Exception { } try (Recording recording = new Recording()) { - recording.enable(EventNames.SecurityProperty); + recording.enable(EventNames.SecurityProperty).withStackTrace(); recording.start(); for (String key: keys) { Security.setProperty(key, keyValue); @@ -78,6 +78,7 @@ private static void assertEvent(List events) throws Exception { if (keys.contains(e.getString("key"))) { Events.assertField(e, "value").equal(keyValue); i++; + Events.assertTopFrame(e, TestSecurityPropertyModificationEvent.class, "main"); } else { System.out.println(events); throw new Exception("Unexpected event at index:" + i); diff --git a/test/jdk/jdk/jfr/event/security/TestSecurityProviderServiceEvent.java b/test/jdk/jdk/jfr/event/security/TestSecurityProviderServiceEvent.java index a886a7e3b2047..df801c5190742 100644 --- a/test/jdk/jdk/jfr/event/security/TestSecurityProviderServiceEvent.java +++ b/test/jdk/jdk/jfr/event/security/TestSecurityProviderServiceEvent.java @@ -52,32 +52,38 @@ public class TestSecurityProviderServiceEvent { public static void main(String[] args) throws Exception { testAlg(cipherFunc, "AES", "SunJCE", - "SunEC", "Cipher", 1, Collections.emptyList()); + "SunEC", "Cipher", 1, Collections.emptyList(), + javax.crypto.Cipher.class.getName(), "getInstance"); testAlg(signatureFunc, "SHA256withRSA", "SunRsaSign", - "SunEC", "Signature", 2, List.of("MessageDigest")); + "SunEC", "Signature", 2, List.of("MessageDigest"), + "sun.security.jca.GetInstance", "getService"); testAlg(messageDigestFunc, "SHA-512", "SUN", - "SunEC", "MessageDigest", 1, Collections.emptyList()); + "SunEC", "MessageDigest", 1, Collections.emptyList(), + "sun.security.jca.GetInstance", "getService"); testAlg(keystoreFunc, "PKCS12", "SUN", - "SunEC", "KeyStore", 1, Collections.emptyList()); + "SunEC", "KeyStore", 1, Collections.emptyList(), + "sun.security.jca.GetInstance", "getService"); testAlg(certPathBuilderFunc, "PKIX", "SUN", - "SunEC", "CertPathBuilder", 2, List.of("CertificateFactory")); + "SunEC", "CertPathBuilder", 2, List.of("CertificateFactory"), + "sun.security.jca.GetInstance", "getService"); } private static void testAlg(BiFunction bif, String alg, String workingProv, String brokenProv, String algType, - int expected, List other) throws Exception { + int expected, List other, + String clazz, String method) throws Exception { // bootstrap security Provider services Provider p = bif.apply(alg, workingProv); try (Recording recording = new Recording()) { - recording.enable(EventNames.SecurityProviderService); + recording.enable(EventNames.SecurityProviderService).withStackTrace(); recording.start(); p = bif.apply(alg, workingProv); bif.apply(alg, brokenProv); recording.stop(); List events = Events.fromRecording(recording); Asserts.assertEquals(events.size(), expected, "Incorrect number of events"); - assertEvent(events, algType, alg, p.getName(), other); + assertEvent(events, algType, alg, p.getName(), other, clazz, method); } } @@ -137,7 +143,8 @@ private static void testAlg(BiFunction bif, String alg }; private static void assertEvent(List events, String type, - String alg, String workingProv, List other) { + String alg, String workingProv, List other, String clazz, + String method) { boolean secondaryEventOK = other.isEmpty() ? true : false; for (RecordedEvent e : events) { if (other.contains(e.getValue("type"))) { @@ -148,10 +155,10 @@ private static void assertEvent(List events, String type, Events.assertField(e, "provider").equal(workingProv); Events.assertField(e, "type").equal(type); Events.assertField(e, "algorithm").equal(alg); + Events.assertTopFrame(e, clazz, method); } if (!secondaryEventOK) { throw new RuntimeException("Secondary events missing"); } - } } diff --git a/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.java b/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.java index 48e1fdfdd590d..7c5240a2a72e5 100644 --- a/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.java +++ b/test/jdk/jdk/jfr/event/security/TestTLSHandshakeEvent.java @@ -43,7 +43,7 @@ public class TestTLSHandshakeEvent { public static void main(String[] args) throws Exception { try (Recording recording = new Recording()) { - recording.enable(EventNames.TLSHandshake); + recording.enable(EventNames.TLSHandshake).withStackTrace(); recording.start(); TestTLSHandshake handshake = new TestTLSHandshake(); handshake.run(); @@ -63,6 +63,10 @@ private static void assertEvent(List events, TestTLSHandshake han Events.assertField(e, "protocolVersion").equal(handshake.protocolVersion); Events.assertField(e, "certificateId").equal(TestTLSHandshake.CERT_ID); Events.assertField(e, "cipherSuite").equal(TestTLSHandshake.CIPHER_SUITE); + var method = e.getStackTrace().getFrames().get(0).getMethod(); + if (method.getName().equals("recordEvent")) { + throw new Exception("Didn't expected recordEvent as top frame"); + } return; } } diff --git a/test/jdk/jdk/jfr/jvm/TestGetStackTraceId.java b/test/jdk/jdk/jfr/jvm/TestGetStackTraceId.java index cdb8a9a5c43ef..8a23e55140886 100644 --- a/test/jdk/jdk/jfr/jvm/TestGetStackTraceId.java +++ b/test/jdk/jdk/jfr/jvm/TestGetStackTraceId.java @@ -52,7 +52,7 @@ public static void main(String... args) { } private static void assertMaxSkip() { - Asserts.assertEquals(JVM.getStackTraceId(Integer.MAX_VALUE), 0L, "Insane skip level " + Asserts.assertEquals(JVM.getStackTraceId(Integer.MAX_VALUE, -1), 0L, "Insane skip level " + Integer.MAX_VALUE + " should not return a valid stack trace id"); } @@ -64,6 +64,6 @@ public static long getStackIdOfDepth(int depth) { if (depth > 0) { return getStackIdOfDepth(depth - 1); } - return JVM.getStackTraceId(0); + return JVM.getStackTraceId(0, -1); } } diff --git a/test/jdk/sun/security/pkcs11/PKCS11Test.java b/test/jdk/sun/security/pkcs11/PKCS11Test.java index 9e7745a8e5a10..ccdbd2d8e49cf 100644 --- a/test/jdk/sun/security/pkcs11/PKCS11Test.java +++ b/test/jdk/sun/security/pkcs11/PKCS11Test.java @@ -21,12 +21,6 @@ * questions. */ -/* - * This file has been modified by Loongson Technology in 2022, These - * modifications are Copyright (c) 2021, 2022, Loongson Technology, and are made - * available on the same license terms set forth above. - */ - // common infrastructure for SunPKCS11 tests import java.io.ByteArrayOutputStream; @@ -52,7 +46,6 @@ import java.security.spec.ECParameterSpec; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -264,30 +257,19 @@ private static Path getNSSLibPath() throws Exception { static Path getNSSLibPath(String library) throws Exception { String osid = getOsId(); - String[] nssLibDirs = getNssLibPaths(osid); - if (nssLibDirs == null) { - System.out.println("Warning: unsupported OS: " + osid + String nssLibDir = fetchNssLib(osid); + if (nssLibDir == null) { + throw new SkippedException("Warning: unsupported OS: " + osid + ", please initialize NSS library location, skipping test"); - return null; - } - if (nssLibDirs.length == 0) { - System.out.println("Warning: NSS not supported on this platform, skipping test"); - return null; } - Path nssLibPath = null; - for (String dir : nssLibDirs) { - Path libPath = Paths.get(dir).resolve(System.mapLibraryName(library)); - if (Files.exists(libPath)) { - nssLibPath = libPath; - break; - } - } - if (nssLibPath == null) { - System.out.println("Warning: can't find NSS library on this machine, skipping test"); - return null; + String libraryName = System.mapLibraryName(library); + Path libPath = Paths.get(nssLibDir).resolve(libraryName); + if (!Files.exists(libPath)) { + throw new SkippedException("NSS library \"" + libraryName + "\" was not found in " + nssLibDir); } - return nssLibPath; + + return libPath; } private static String getOsId() { @@ -611,75 +593,6 @@ private static ECParameterSpec getECParameterSpec(Provider p, String name) return parameters.getParameterSpec(ECParameterSpec.class); } - // Location of the NSS libraries on each supported platform - private static Map getOsMap() { - if (osMap != null) { - return osMap; - } - - osMap = new HashMap<>(); - osMap.put("Linux-i386-32", new String[]{ - "/usr/lib/i386-linux-gnu/", - "/usr/lib32/", - "/usr/lib/"}); - osMap.put("Linux-amd64-64", new String[]{ - "/usr/lib/x86_64-linux-gnu/", - "/usr/lib/x86_64-linux-gnu/nss/", - "/usr/lib64/"}); - osMap.put("Linux-ppc64-64", new String[]{"/usr/lib64/"}); - osMap.put("Linux-ppc64le-64", new String[]{ - "/usr/lib/powerpc64le-linux-gnu/", - "/usr/lib/powerpc64le-linux-gnu/nss/", - "/usr/lib64/"}); - osMap.put("Linux-loongarch64-64", new String[]{"/usr/lib/loongarch64-linux-gnu/", - "/usr/lib64/"}); - osMap.put("Linux-s390x-64", new String[]{"/usr/lib64/"}); - osMap.put("Windows-x86-32", new String[]{}); - osMap.put("Windows-amd64-64", new String[]{}); - osMap.put("MacOSX-x86_64-64", new String[]{}); - osMap.put("Linux-arm-32", new String[]{ - "/usr/lib/arm-linux-gnueabi/nss/", - "/usr/lib/arm-linux-gnueabihf/nss/"}); - osMap.put("Linux-aarch64-64", new String[] { - "/usr/lib/aarch64-linux-gnu/", - "/usr/lib/aarch64-linux-gnu/nss/", - "/usr/lib64/" }); - return osMap; - } - - private static String[] getNssLibPaths(String osId) { - String[] preferablePaths = getPreferableNssLibPaths(osId); - if (preferablePaths.length != 0) { - return preferablePaths; - } else { - return getOsMap().get(osId); - } - } - - private static String[] getPreferableNssLibPaths(String osId) { - List nssLibPaths = new ArrayList<>(); - - String customNssLibPaths = System.getProperty("test.nss.lib.paths"); - if (customNssLibPaths == null) { - // If custom local NSS lib path is not provided, - // try to download NSS libs from artifactory - String path = fetchNssLib(osId); - if (path != null) { - nssLibPaths.add(path); - } - } else { - String[] paths = customNssLibPaths.split(","); - for (String path : paths) { - if (!path.endsWith(File.separator)) { - nssLibPaths.add(path + File.separator); - } else { - nssLibPaths.add(path); - } - } - } - - return nssLibPaths.toArray(new String[0]); - } public static String toString(byte[] b) { if (b == null) { diff --git a/test/jdk/sun/security/pkcs11/README b/test/jdk/sun/security/pkcs11/README index 31efd034501f3..9b4c39dd41c20 100644 --- a/test/jdk/sun/security/pkcs11/README +++ b/test/jdk/sun/security/pkcs11/README @@ -4,14 +4,15 @@ perform as a result of bugs or features in NSS or other pkcs11 libraries. - How to get NSS libraries? The libraries come from the following sources. -1. Specified by system property test.nss.lib.paths -System property test.nss.lib.paths can specify a set of absolute paths to -the local NSS library directories. The paths are separated by comma. +1. Specified by system property jdk.test.lib.artifacts. +The system property, jdk.test.lib.artifacts., can specify an absolute path +to the local NSS library directory. The component should be replaced with +the name element of the appropriate @Artifact class. +(See `test/jdk/sun/security/pkcs11/PKCS11Test.java`) 2. Pre-built NSS libraries from artifactory server -If the value of system property test.nss.lib.paths is not set, the tests will try -to download pre-built NSS libraries from artifactory server. Currently, the -tests only looks for libraries for Windows and MacOSX platforms on artifactory. +If the value of system property jdk.test.lib.artifacts. is not set, the +tests will try to download pre-built NSS libraries from artifactory server. Please note that JIB jar MUST be present in classpath when downloading the libraries. diff --git a/test/jdk/sun/security/tools/keytool/NssTest.java b/test/jdk/sun/security/tools/keytool/NssTest.java index 968e19df28dcc..db5d124015308 100644 --- a/test/jdk/sun/security/tools/keytool/NssTest.java +++ b/test/jdk/sun/security/tools/keytool/NssTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,8 +58,9 @@ private static void copyFiles() throws IOException { Path dbPath = srcPath.getParent().getParent() .resolve("pkcs11").resolve("nss").resolve("db"); - Files.copy(dbPath.resolve("cert8.db"), Paths.get("cert8.db")); - Files.copy(dbPath.resolve("key3.db"), Paths.get("key3.db")); - Files.copy(dbPath.resolve("secmod.db"), Paths.get("secmod.db")); + Path destDir = Path.of( "tmpdb"); + Files.createDirectory(destDir); + Files.copy(dbPath.resolve("cert9.db"), destDir.resolve("cert9.db")); + Files.copy(dbPath.resolve("key4.db"), destDir.resolve("key4.db")); } } diff --git a/test/jdk/sun/security/tools/keytool/p11-nss.txt b/test/jdk/sun/security/tools/keytool/p11-nss.txt index dd200a326c002..9c8ac0a43ab98 100644 --- a/test/jdk/sun/security/tools/keytool/p11-nss.txt +++ b/test/jdk/sun/security/tools/keytool/p11-nss.txt @@ -6,7 +6,7 @@ slot = 2 library = ${nss.lib} -nssArgs = "configdir='.' certPrefix='' keyPrefix='' secmod='secmod.db'" +nssArgs = "configdir='sql:./tmpdb' certPrefix='' keyPrefix='' secmod='secmod.db'" #forceLogin = true 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 d2d7ac9f66ee9..6c388ac77ffb9 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,6 +76,11 @@ final public AdditionalLauncher addJavaOptions(String... v) { return this; } + final public AdditionalLauncher setVerifyUninstalled(boolean value) { + verifyUninstalled = value; + return this; + } + final public AdditionalLauncher setLauncherAsService() { return addRawProperties(LAUNCHER_AS_SERVICE); } @@ -141,6 +146,13 @@ final public void applyTo(JPackageCommand cmd) { final public void applyTo(PackageTest test) { test.addInitializer(this::initialize); test.addInstallVerifier(this::verify); + if (verifyUninstalled) { + test.addUninstallVerifier(this::verifyUninstalled); + } + } + + final public void verifyRemovedInUpgrade(PackageTest test) { + test.addInstallVerifier(this::verifyUninstalled); } static void forEachAdditionalLauncher(JPackageCommand cmd, @@ -318,10 +330,40 @@ private void verifyDescription(JPackageCommand cmd) throws IOException { } } + 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); @@ -394,6 +436,7 @@ private static String resolveVariables(JPackageCommand cmd, String str) { return str; } + private boolean verifyUninstalled; private List javaOptions; private List defaultArguments; private Path icon; 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 e328fe996c0cb..30a4c6f8cd8e5 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -888,10 +888,17 @@ private void assertFileInAppImage(Path filename, Path expectedPath) { final Path rootDir = isImagePackageType() ? outputBundle() : pathToUnpackedPackageFile( appInstallationDirectory()); - try ( Stream walk = ThrowingSupplier.toSupplier(() -> Files.walk( - rootDir)).get()) { - List files = walk.filter(path -> path.getFileName().equals( - filename)).map(Path::toString).toList(); + try ( Stream walk = ThrowingSupplier.toSupplier(() -> { + if (TKit.isLinux() && rootDir.equals(Path.of("/"))) { + // Installed package with split app image on Linux. Iterate + // through package file list instead of the entire file system. + return LinuxHelper.getPackageFiles(this); + } else { + return Files.walk(rootDir); + } + }).get()) { + List files = walk.filter(path -> filename.equals( + path.getFileName())).map(Path::toString).toList(); if (expectedPath == null) { TKit.assertStringListEquals(List.of(), files, String.format( 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 a695a3b693d5f..b7ebc4939ba38 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ package jdk.jpackage.test; import java.io.IOException; +import java.nio.file.FileSystemException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -30,6 +31,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -58,10 +60,16 @@ public Builder setAppOutputFileName(String v) { return this; } + public Builder setAdditionalLauncherCallback(Consumer v) { + additionalLauncherCallback = v; + return this; + } + public LauncherAsServiceVerifier create() { Objects.requireNonNull(expectedValue); return new LauncherAsServiceVerifier(launcherName, appOutputFileName, - expectedValue); + expectedValue, + launcherName != null ? additionalLauncherCallback : null); } public Builder applyTo(PackageTest pkg) { @@ -72,6 +80,7 @@ public Builder applyTo(PackageTest pkg) { private String launcherName; private String expectedValue; private String appOutputFileName = "launcher-as-service.txt"; + private Consumer additionalLauncherCallback; } public static Builder build() { @@ -80,10 +89,12 @@ public static Builder build() { private LauncherAsServiceVerifier(String launcherName, String appOutputFileName, - String expectedArgValue) { + String expectedArgValue, + Consumer additionalLauncherCallback) { this.expectedValue = expectedArgValue; this.launcherName = launcherName; this.appOutputFileName = Path.of(appOutputFileName); + this.additionalLauncherCallback = additionalLauncherCallback; } public void applyTo(PackageTest pkg) { @@ -233,7 +244,7 @@ private void applyToMainLauncher(PackageTest pkg) { outputFilePath.toString()) .addDefaultArguments(expectedValue) .verifyOutput(); - TKit.deleteIfExists(outputFilePath); + deleteOutputFile(outputFilePath); } }); pkg.addInstallVerifier(cmd -> { @@ -242,13 +253,13 @@ private void applyToMainLauncher(PackageTest pkg) { } private void applyToAdditionalLauncher(PackageTest pkg) { - new AdditionalLauncher(launcherName) { + AdditionalLauncher al = new AdditionalLauncher(launcherName) { @Override protected void verify(JPackageCommand cmd) throws IOException { if (canVerifyInstall(cmd)) { delayInstallVerify(); super.verify(cmd); - TKit.deleteIfExists(appOutputFilePathVerify(cmd)); + deleteOutputFile(appOutputFilePathVerify(cmd)); } LauncherAsServiceVerifier.verify(cmd, launcherName); } @@ -256,8 +267,26 @@ protected void verify(JPackageCommand cmd) throws IOException { .addJavaOptions("-Djpackage.test.appOutput=" + appOutputFilePathInitialize().toString()) .addJavaOptions("-Djpackage.test.noexit=true") - .addDefaultArguments(expectedValue) - .applyTo(pkg); + .addDefaultArguments(expectedValue); + + Optional.ofNullable(additionalLauncherCallback).ifPresent(v -> v.accept(al)); + + al.applyTo(pkg); + } + + private static void deleteOutputFile(Path file) throws IOException { + try { + TKit.deleteIfExists(file); + } catch (FileSystemException ex) { + if (TKit.isLinux() || TKit.isOSX()) { + // Probably "Operation no permitted" error. Try with "sudo" as the + // file is created by a launcher started under root account. + Executor.of("sudo", "rm", "-f").addArgument(file.toString()). + execute(); + } else { + throw ex; + } + } } private static void verify(JPackageCommand cmd, String launcherName) throws @@ -337,6 +366,7 @@ private Path appOutputFilePathVerify(JPackageCommand cmd) { private final String expectedValue; private final String launcherName; private final Path appOutputFileName; + private final Consumer additionalLauncherCallback; final static Set SUPPORTED_PACKAGES = Stream.of(LINUX, WINDOWS, Set.of(MAC_PKG)).flatMap(x -> x.stream()).collect(Collectors.toSet()); 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 44212587f7dd9..bf5ced09bc771 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -464,7 +464,7 @@ static void initFileAssociationsTestFile(Path testFile) { } } - private static Path getSystemDesktopFilesFolder() { + static Path getSystemDesktopFilesFolder() { return Stream.of("/usr/share/applications", "/usr/local/share/applications").map(Path::of).filter(dir -> { return Files.exists(dir.resolve("defaults.list")); @@ -558,12 +558,17 @@ private static void verifyIconInScriptlet(Scriptlet scriptletType, final String xdgCmdName = "xdg-icon-resource"; Stream scriptletBodyStream = scriptletBody.stream() - .filter(str -> str.startsWith(xdgCmdName)) .filter(str -> Pattern.compile( "\\b" + dashMime + "\\b").matcher(str).find()); if (scriptletType == Scriptlet.PostInstall) { + scriptletBodyStream = scriptletBodyStream.filter(str -> str. + startsWith(xdgCmdName)); scriptletBodyStream = scriptletBodyStream.filter(str -> List.of( str.split("\\s+")).contains(iconPathInPackage.toString())); + } else { + scriptletBodyStream = scriptletBodyStream.filter(str -> str. + contains(xdgCmdName)).filter(str -> str.startsWith( + "do_if_file_belongs_to_single_package")); } scriptletBodyStream.peek(xdgCmd -> { diff --git a/test/jdk/tools/jpackage/linux/ServiceAndDesktopTest.java b/test/jdk/tools/jpackage/linux/ServiceAndDesktopTest.java new file mode 100644 index 0000000000000..b047ce3e8f427 --- /dev/null +++ b/test/jdk/tools/jpackage/linux/ServiceAndDesktopTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.LauncherAsServiceVerifier; +import jdk.jpackage.test.TKit; + +/** + * Test how services and desktop integration align together in the same package. + * On Linux these features share common code in custom actions (common_utils.sh). + * Test correctness of integration of this code. + * + * The test is not intended to be executed by SQE. It is for internal use only + */ + +/* + * @test + * @summary jpackage with desktop integration and services on Linux + * @library ../helpers + * @key jpackagePlatformPackage + * @requires jpackage.test.SQETest == null + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.jpackage/jdk.jpackage.internal + * @compile ServiceAndDesktopTest.java + * @run main/othervm/timeout=720 jdk.jpackage.test.Main + * --jpt-run=ServiceAndDesktopTest + */ + +public class ServiceAndDesktopTest { + + @Test + public static void test() { + var pkg = new PackageTest() + .configureHelloApp() + .addBundleDesktopIntegrationVerifier(true) + .addInitializer(cmd -> { + // Want a .desktop file for the main launcher + cmd.addArguments("--icon", GOLDEN_ICON.toString()); + }); + LauncherAsServiceVerifier.build().setLauncherName("foo"). + setExpectedValue("Fun").setAdditionalLauncherCallback(al -> { + // Don't want .desktop file for service launcher + al.setNoIcon(); + }).applyTo(pkg); + pkg.run(); + } + + private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( + "resources", "icon" + TKit.ICON_SUFFIX)); +} diff --git a/test/jdk/tools/jpackage/linux/UpgradeTest.java b/test/jdk/tools/jpackage/linux/UpgradeTest.java new file mode 100644 index 0000000000000..1a4234db86f62 --- /dev/null +++ b/test/jdk/tools/jpackage/linux/UpgradeTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Map; +import java.nio.file.Path; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + +/* + * @test + * @summary Linux upgrade testing + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.jpackage/jdk.jpackage.internal + * @compile UpgradeTest.java + * @run main/othervm/timeout=360 jdk.jpackage.test.Main + * --jpt-run=UpgradeTest + */ +public class UpgradeTest { + + @Test + public void testDesktopFiles() { + // Create two packages with the same name but different versions. + // The first will have `launcherA`, and `launcherB` additional launchers. + // The second will have `launcherB`, and `launcherC` additional launchers. + // Launchers are configured in a way to have correpsonding .desktop files. + // These files will be installed in system directories. + // After the upgrade `launcherA`-related files must be deleted and + // `launcherB`-related files from the first package must be replaced with + // the files from the second package. + // Checks that correct files are installed in system directories + // encapsulated in AdditionalLauncher class. + + var pkg = createPackageTest().disablePackageUninstaller(); + + var alA = createAdditionalLauncher("launcherA"); + + alA.applyTo(pkg); + createAdditionalLauncher("launcherB").addRawProperties(Map.entry( + "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("launcherC").applyTo(pkg2); + + new PackageTest.Group(pkg, pkg2).run(); + } + + private static PackageTest createPackageTest() { + return new PackageTest().configureHelloApp().addInitializer( + JPackageCommand::setInputToEmptyDirectory).addInitializer( + JPackageCommand::setFakeRuntime). + addBundleDesktopIntegrationVerifier(true); + } + + private static AdditionalLauncher createAdditionalLauncher(String name) { + // Configure additionl launcher in a way to trigger jpackage create + // corresponding .desktop file. + return new AdditionalLauncher(name).setIcon(GOLDEN_ICON); + } + + private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( + "resources", "icon" + TKit.ICON_SUFFIX)); +} diff --git a/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java b/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java index b3cefa7d38fe2..8b1cec04ab9c7 100644 --- a/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java +++ b/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ * @build MacAppStoreJLinkOptionsTest * @modules jdk.jpackage/jdk.jpackage.internal * @requires (os.family == "mac") - * @run main/othervm -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main * --jpt-run=MacAppStoreJLinkOptionsTest */ public class MacAppStoreJLinkOptionsTest { diff --git a/test/langtools/jdk/javadoc/doclet/testGenericTypeLink/TestGenericTypeLink.java b/test/langtools/jdk/javadoc/doclet/testGenericTypeLink/TestGenericTypeLink.java index c0b45b3491453..d8bf1fc2bdfcf 100644 --- a/test/langtools/jdk/javadoc/doclet/testGenericTypeLink/TestGenericTypeLink.java +++ b/test/langtools/jdk/javadoc/doclet/testGenericTypeLink/TestGenericTypeLink.java @@ -112,8 +112,8 @@ interface in java.util" class="external-link">link to generic type with labelString,A.SomeException>

  • Link to generic type with \ - label
  • + s or interface in java.util" class="external-link">Link to generic type with label<\ + /a>
""" diff --git a/test/langtools/jdk/javadoc/doclet/testLegalNotices/TestLegalNotices.java b/test/langtools/jdk/javadoc/doclet/testLegalNotices/TestLegalNotices.java index 148accdbfd4af..29da4d989720a 100644 --- a/test/langtools/jdk/javadoc/doclet/testLegalNotices/TestLegalNotices.java +++ b/test/langtools/jdk/javadoc/doclet/testLegalNotices/TestLegalNotices.java @@ -111,6 +111,8 @@ void test(Path base, Path src, Path legal, OptionKind optionKind, IndexKind inde super.out.println(" Found: " + foundFiles); if (foundFiles.equals(expectFiles)) { passed("Found all expected files"); + } else { + failed("Did not find all expected files"); } // See JDK-8306980 @@ -168,4 +170,4 @@ Set listFiles(Path dir, Predicate filter) throws IOException { return files; } } -} \ No newline at end of file +} diff --git a/test/langtools/jdk/javadoc/doclet/testSeeLinkAnchor/TestSeeLinkAnchor.java b/test/langtools/jdk/javadoc/doclet/testSeeLinkAnchor/TestSeeLinkAnchor.java index 4fc515f9427dc..626e487040107 100644 --- a/test/langtools/jdk/javadoc/doclet/testSeeLinkAnchor/TestSeeLinkAnchor.java +++ b/test/langtools/jdk/javadoc/doclet/testSeeLinkAnchor/TestSeeLinkAnchor.java @@ -79,8 +79,8 @@ public void testPackage(Path base) throws Exception { """ Plain link to sub heading above""", """ -
  • See main heading in p2.Class2
  • -
  • See heading in p2
  • +
  • See main heading in p2.Class2
  • +
  • See heading in p2
  • """); checkOrder("p2/Class2.html", """ @@ -89,13 +89,13 @@ public void testPackage(Path base) throws Exception { Plain link to Class1."""); checkOrder("p2/package-summary.html", """ - See sub heading in p2.Class2"""); + See sub heading in p2.Class2"""); checkOrder("p2/doc-files/file.html", """ Plain link to heading in p1.ClassA.""", """ - See main heading in p2.ClassB"""); + See main heading in p2.ClassB"""); } @Test @@ -111,13 +111,13 @@ public void testModule(Path base) throws Exception { checkExit(Exit.OK); checkOrder("m1/module-summary.html", """ - See main heading in Class2"""); + See main heading in Class2"""); checkOrder("m1/com/m1/Class1.html", """ sub heading in Class2.""", """ -
  • See main heading in Class2
  • -
  • See heading in module m1
  • +
  • See main heading in Class2
  • +
  • See heading in module m1
  • """); checkOrder("m2/com/m2/Class2.html", """ @@ -128,7 +128,7 @@ public void testModule(Path base) throws Exception { """ Link to heading in Class2.""", """ -
  • Heading in module m1
  • """); +
  • Heading in module m1
  • """); } @Test diff --git a/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java index c0c730bf6c417..f3294f6647c4a 100644 --- a/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java +++ b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTag.java @@ -62,7 +62,7 @@ public void test() {
  • Test.InnerOne.foo()
  • Test.InnerOne.bar(Object)
  • Javadoc
  • -
  • something
  • +
  • something
  • \ Test.InnerOne.format(java.lang.String, java.lang.Object...)
  • @@ -80,7 +80,7 @@ public void test() {
    """); @@ -213,17 +213,17 @@ public void twoArgs(int a1, int a2) { } "
    ", """ """, "
    ", """ """, "
    ", diff --git a/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTagFont.java b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTagFont.java new file mode 100644 index 0000000000000..3892ec6d999e4 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testSeeTag/TestSeeTagFont.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8320207 + * @summary doclet incorrectly chooses code font for a See Also link + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @run main TestSeeTagFont + */ + +import java.nio.file.Path; + +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +public class TestSeeTagFont extends JavadocTester { + public static void main(String... args) throws Exception { + var tester = new TestSeeTagFont(); + tester.runTests(); + } + + private final ToolBox tb = new ToolBox(); + + @Test + public void testPlain(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + """ + package p; + import p2.Other; + /** + * Description. + * @see Other multi-word phrase + * @see Other Other + * @see Other Other() with trailing text + * @see Other simpleNameMismatch + * + * @see Other#Other() multi-word phrase + * @see Other#Other() Other#Other() with trailing text + * @see Other#Other() simpleNameMismatch + * + * @see Other#m() Other.m with formatting and trailing text + */ + public class C { } + """, + """ + package p2; + /** Lorem ipsum. */ + public class Other { + /** Lorem ipsum. */ + public void m() { } + } + """); + + javadoc("-d", base.resolve("api").toString(), + "-Xdoclint:none", + "-sourcepath", src.toString(), + "p", "p2"); + checkExit(Exit.OK); + + // none of the following should contain ... + checkOutput("p/C.html", true, + """ + + """); + } + + @Test + public void testCode(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, + """ + package p; + import p2.Other; + /** + * Description. + * @see Other + * @see p2.Other Other + * + * @see Other#Other() Other + * @see Other#m() m + * @see Other#m() Other.m + */ + public class C { } + """, + """ + package p2; + /** Lorem ipsum. */ + public class Other { + /** Lorem ipsum. */ + public void m() { } + } + """); + + javadoc("-d", base.resolve("api").toString(), + "-Xdoclint:none", + "-sourcepath", src.toString(), + "p", "p2"); + checkExit(Exit.OK); + + // all of the following should contain ... + checkOutput("p/C.html", true, + """ + + """); + } +} diff --git a/test/langtools/jdk/jshell/AnalyzeSnippetTest.java b/test/langtools/jdk/jshell/AnalyzeSnippetTest.java index b566a023caff0..3e2e1a839e2bd 100644 --- a/test/langtools/jdk/jshell/AnalyzeSnippetTest.java +++ b/test/langtools/jdk/jshell/AnalyzeSnippetTest.java @@ -64,6 +64,7 @@ public void setUp() { state = JShell.builder() .out(new PrintStream(new ByteArrayOutputStream())) .err(new PrintStream(new ByteArrayOutputStream())) + .executionEngine(Presets.TEST_DEFAULT_EXECUTION) .build(); sca = state.sourceCodeAnalysis(); } diff --git a/test/langtools/jdk/jshell/CustomInputToolBuilder.java b/test/langtools/jdk/jshell/CustomInputToolBuilder.java index 3b3d5616a9468..523981b3d915c 100644 --- a/test/langtools/jdk/jshell/CustomInputToolBuilder.java +++ b/test/langtools/jdk/jshell/CustomInputToolBuilder.java @@ -90,7 +90,8 @@ private void doTest(boolean interactiveTerminal, String code, String... expected .interactiveTerminal(interactiveTerminal) .promptCapture(true) .persistence(new HashMap<>()) - .start("--no-startup"); + .start("--no-startup", + "--execution", Presets.TEST_DEFAULT_EXECUTION); String actual = new String(out.toByteArray()); List actualLines = Arrays.asList(actual.split("\\R")); diff --git a/test/langtools/jdk/jshell/ExecutionControlTestBase.java b/test/langtools/jdk/jshell/ExecutionControlTestBase.java index 9035d84d4a56f..20336c902cffc 100644 --- a/test/langtools/jdk/jshell/ExecutionControlTestBase.java +++ b/test/langtools/jdk/jshell/ExecutionControlTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,14 @@ import org.testng.annotations.Test; import jdk.jshell.VarSnippet; -import java.net.InetAddress; import static jdk.jshell.Snippet.Status.VALID; import static jdk.jshell.Snippet.SubKind.*; public class ExecutionControlTestBase extends KullaTesting { - String standardListenSpec() { - String loopback = InetAddress.getLoopbackAddress().getHostAddress(); - return "jdi:hostname(" + loopback + ")"; - } - - String standardLaunchSpec() { - return "jdi:launch(true)"; - } - - String standardJdiSpec() { - return "jdi"; - } - - String standardSpecs() { - return "5(" + standardListenSpec() + "), 6(" + standardLaunchSpec() + "), 7(" + standardJdiSpec() + ")"; + String alwaysPassingSpec() { + return "5(local)"; } @Test diff --git a/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java b/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java index da838798f8ee3..a094ed4a86f3d 100644 --- a/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java +++ b/test/langtools/jdk/jshell/FailOverDirectExecutionControlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -129,9 +129,7 @@ public void setUp() { Map pm = provider.defaultParameters(); pm.put("0", "alwaysFailing"); pm.put("1", "alwaysFailing"); - pm.put("2", standardListenSpec()); - pm.put("3", standardLaunchSpec()); - pm.put("4", standardJdiSpec()); + pm.put("2", "local"); setUp(builder -> builder.executionEngine(provider, pm)); } @@ -159,9 +157,7 @@ public void variables() { assertTrue(log.contains("This operation intentionally broken"), log); log = logged.get(Level.FINEST).get(0); assertTrue( - log.contains("Success failover -- 2 = " + standardListenSpec()) - || log.contains("Success failover -- 3 = " + standardLaunchSpec()) - || log.contains("Success failover -- 4 = " + standardJdiSpec()), + log.contains("Success failover -- 2 = local"), log); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java index f3218fab7c76d..31011960880dd 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlDyingLaunchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,6 @@ public class FailOverExecutionControlDyingLaunchTest extends ExecutionControlTes public void setUp() { setUp(builder -> builder.executionEngine( "failover:0(jdi:remoteAgent(DyingRemoteAgent),launch(true)), " - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java index 778d004915c20..9958b7a3284e6 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlHangingLaunchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,6 @@ public class FailOverExecutionControlHangingLaunchTest extends ExecutionControlT public void setUp() { setUp(builder -> builder.executionEngine( "failover:0(jdi:remoteAgent(HangingRemoteAgent),launch(true)), " - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java index f22dd821f4049..4f29bfe9c7a82 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlHangingListenTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,6 +45,6 @@ public void setUp() { String loopback = InetAddress.getLoopbackAddress().getHostAddress(); setUp(builder -> builder.executionEngine( "failover:0(jdi:remoteAgent(HangingRemoteAgent),hostname(" + loopback + "))," - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/FailOverExecutionControlTest.java b/test/langtools/jdk/jshell/FailOverExecutionControlTest.java index 0843351815f87..80dc56d72c484 100644 --- a/test/langtools/jdk/jshell/FailOverExecutionControlTest.java +++ b/test/langtools/jdk/jshell/FailOverExecutionControlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ public class FailOverExecutionControlTest extends ExecutionControlTestBase { @Override public void setUp() { setUp(builder -> builder.executionEngine("failover:0(expectedFailureNonExistent1), 1(expectedFailureNonExistent2), " - + standardSpecs())); + + alwaysPassingSpec())); } } diff --git a/test/langtools/jdk/jshell/IdGeneratorTest.java b/test/langtools/jdk/jshell/IdGeneratorTest.java index 23727aef643a8..e8a38dfe7f05f 100644 --- a/test/langtools/jdk/jshell/IdGeneratorTest.java +++ b/test/langtools/jdk/jshell/IdGeneratorTest.java @@ -53,7 +53,8 @@ public JShell.Builder getBuilder() { return JShell.builder() .in(inStream) .out(new PrintStream(outStream)) - .err(new PrintStream(errStream)); + .err(new PrintStream(errStream)) + .executionEngine(Presets.TEST_DEFAULT_EXECUTION); } public void testTempNameGenerator() { diff --git a/test/langtools/jdk/jshell/JdiStarterTest.java b/test/langtools/jdk/jshell/JdiStarterTest.java new file mode 100644 index 0000000000000..0beee9ceda164 --- /dev/null +++ b/test/langtools/jdk/jshell/JdiStarterTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8319311 + * @summary Tests JdiStarter + * @modules jdk.jshell/jdk.jshell jdk.jshell/jdk.jshell.spi jdk.jshell/jdk.jshell.execution + * @run testng JdiStarterTest + */ + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.testng.annotations.Test; +import jdk.jshell.JShell; +import jdk.jshell.SnippetEvent; +import jdk.jshell.execution.JdiDefaultExecutionControl.JdiStarter; +import jdk.jshell.execution.JdiDefaultExecutionControl.JdiStarter.TargetDescription; +import jdk.jshell.execution.JdiExecutionControlProvider; +import jdk.jshell.execution.JdiInitiator; +import static org.testng.Assert.assertEquals; + +@Test +public class JdiStarterTest { + + public void jdiStarter() { + // turn on logging of launch failures + Logger.getLogger("jdk.jshell.execution").setLevel(Level.ALL); + JdiStarter starter = (env, parameters, port) -> { + assertEquals(parameters.get(JdiExecutionControlProvider.PARAM_HOST_NAME), ""); + assertEquals(parameters.get(JdiExecutionControlProvider.PARAM_LAUNCH), "false"); + assertEquals(parameters.get(JdiExecutionControlProvider.PARAM_REMOTE_AGENT), "jdk.jshell.execution.RemoteExecutionControl"); + assertEquals(parameters.get(JdiExecutionControlProvider.PARAM_TIMEOUT), "5000"); + JdiInitiator jdii = + new JdiInitiator(port, + env.extraRemoteVMOptions(), + "jdk.jshell.execution.RemoteExecutionControl", + false, + null, + 5000, + Collections.emptyMap()); + return new TargetDescription(jdii.vm(), jdii.process()); + }; + JShell jshell = + JShell.builder() + .executionEngine(new JdiExecutionControlProvider(starter), Map.of()) + .build(); + List evts = jshell.eval("1 + 2"); + assertEquals(1, evts.size()); + assertEquals("3", evts.get(0).value()); + } +} diff --git a/test/langtools/jdk/jshell/KullaTesting.java b/test/langtools/jdk/jshell/KullaTesting.java index 369b2ed1f44a2..d74f3484f4bd8 100644 --- a/test/langtools/jdk/jshell/KullaTesting.java +++ b/test/langtools/jdk/jshell/KullaTesting.java @@ -100,7 +100,9 @@ public class KullaTesting { private Set allSnippets = new LinkedHashSet<>(); static { - JShell js = JShell.create(); + JShell js = JShell.builder() + .executionEngine(Presets.TEST_DEFAULT_EXECUTION) + .build(); MAIN_SNIPPET = js.eval("MAIN_SNIPPET").get(0).snippet(); js.close(); assertTrue(MAIN_SNIPPET != null, "Bad MAIN_SNIPPET set-up -- must not be null"); @@ -192,7 +194,8 @@ public int read(byte[] b, int off, int len) throws IOException { JShell.Builder builder = JShell.builder() .in(in) .out(new PrintStream(outStream)) - .err(new PrintStream(errStream)); + .err(new PrintStream(errStream)) + .executionEngine(Presets.TEST_DEFAULT_EXECUTION); bc.accept(builder); state = builder.build(); allSnippets = new LinkedHashSet<>(); diff --git a/test/langtools/jdk/jshell/Presets.java b/test/langtools/jdk/jshell/Presets.java new file mode 100644 index 0000000000000..b9a93c967dc71 --- /dev/null +++ b/test/langtools/jdk/jshell/Presets.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.InetAddress; +import java.util.*; + +public class Presets { + public static final String TEST_DEFAULT_EXECUTION; + public static final String TEST_STANDARD_EXECUTION; + + static { + String loopback = InetAddress.getLoopbackAddress().getHostAddress(); + + TEST_DEFAULT_EXECUTION = "failover:0(jdi:hostname(" + loopback + "))," + + "1(jdi:launch(true)), 2(jdi), 3(local)"; + TEST_STANDARD_EXECUTION = "failover:0(jdi:hostname(" + loopback + "))," + + "1(jdi:launch(true)), 2(jdi)"; + } + + public static String[] addExecutionIfMissing(String[] args) { + if (Arrays.stream(args).noneMatch(Presets::remoteRelatedOption)) { + List augmentedArgs = new ArrayList<>(); + + augmentedArgs.add("--execution"); + augmentedArgs.add(Presets.TEST_DEFAULT_EXECUTION); + augmentedArgs.addAll(List.of(args)); + + return augmentedArgs.toArray(s -> new String[s]); + } + + return args; + } + + private static boolean remoteRelatedOption(String option) { + return "--execution".equals(option) || + "--add-modules".equals(option) || + option.startsWith("-R"); + } +} diff --git a/test/langtools/jdk/jshell/ReplToolTesting.java b/test/langtools/jdk/jshell/ReplToolTesting.java index 09ee4117c1817..52eb2d5798c6f 100644 --- a/test/langtools/jdk/jshell/ReplToolTesting.java +++ b/test/langtools/jdk/jshell/ReplToolTesting.java @@ -304,7 +304,7 @@ protected JavaShellToolBuilder builder(Locale locale) { private void testRaw(Locale locale, String[] args, String expectedErrorOutput, ReplTest... tests) { testRawInit(tests); - testRawRun(locale, args); + testRawRun(locale, Presets.addExecutionIfMissing(args)); testRawCheck(locale, expectedErrorOutput); } diff --git a/test/langtools/jdk/jshell/StartOptionTest.java b/test/langtools/jdk/jshell/StartOptionTest.java index df445d49750a8..aa8d9be03a9ef 100644 --- a/test/langtools/jdk/jshell/StartOptionTest.java +++ b/test/langtools/jdk/jshell/StartOptionTest.java @@ -81,7 +81,7 @@ private JavaShellToolBuilder builder() { protected int runShell(String... args) { try { return builder() - .start(args); + .start(Presets.addExecutionIfMissing(args)); } catch (Exception ex) { fail("Repl tool died with exception", ex); } diff --git a/test/langtools/jdk/jshell/ToolReloadTest.java b/test/langtools/jdk/jshell/ToolReloadTest.java index 13d583e51f5c1..4709584cd1268 100644 --- a/test/langtools/jdk/jshell/ToolReloadTest.java +++ b/test/langtools/jdk/jshell/ToolReloadTest.java @@ -201,7 +201,7 @@ public void testReloadCrashRestore() { } public void testEnvBadModule() { - test( + test(new String[] {"--execution", Presets.TEST_STANDARD_EXECUTION}, (a) -> assertVariable(a, "int", "x", "5", "5"), (a) -> assertMethod(a, "int m(int z) { return z * z; }", "(int)int", "m"), diff --git a/test/langtools/jdk/jshell/UITesting.java b/test/langtools/jdk/jshell/UITesting.java index 484bfde266212..473ba36c5aedb 100644 --- a/test/langtools/jdk/jshell/UITesting.java +++ b/test/langtools/jdk/jshell/UITesting.java @@ -93,7 +93,8 @@ protected void doRunTest(Test test) throws Exception { .promptCapture(true) .persistence(new HashMap<>()) .locale(Locale.US) - .run("--no-startup"); + .run("--no-startup", + "--execution", Presets.TEST_DEFAULT_EXECUTION); } catch (Exception ex) { throw new IllegalStateException(ex); } diff --git a/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out b/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out index c04b45bce1e7d..140521689a095 100644 --- a/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out +++ b/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out @@ -1,2 +1,2 @@ -AnonymousInSuperCallNegTest.java:23:49: compiler.err.cant.ref.before.ctor.called: x +AnonymousInSuperCallNegTest.java:23:49: compiler.err.no.encl.instance.of.type.in.scope: AnonymousInSuperCallNegTest.JavacBug 1 error diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.java b/test/langtools/tools/javac/SuperInit/SuperInitFails.java new file mode 100644 index 0000000000000..cd0fc3fd903b0 --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.java @@ -0,0 +1,171 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8194743 + * @summary Permit additional statements before this/super in constructors + * @compile/fail/ref=SuperInitFails.out -XDrawDiagnostics SuperInitFails.java + * @enablePreview + */ +import java.util.concurrent.atomic.AtomicReference; +public class SuperInitFails extends AtomicReference implements Iterable { + + private int x; + +/// GOOD EXAMPLES + + public SuperInitFails() { // this should be OK + } + + public SuperInitFails(Object x) { + this.x = x.hashCode(); // this should be OK + } + + public SuperInitFails(byte x) { + super(); // this should be OK + } + + public SuperInitFails(char x) { + this((int)x); // this should be OK + } + +/// FAIL EXAMPLES + + { + this(1); // this should FAIL + } + + { + super(); // this should FAIL + } + + void normalMethod1() { + super(); // this should FAIL + } + + void normalMethod2() { + this(); // this should FAIL + } + + void normalMethod3() { + Runnable r = () -> super(); // this should FAIL + } + + void normalMethod4() { + Runnable r = () -> this(); // this should FAIL + } + + public SuperInitFails(short x) { + hashCode(); // this should FAIL + super(); + } + + public SuperInitFails(float x) { + this.hashCode(); // this should FAIL + super(); + } + + public SuperInitFails(int x) { + super.hashCode(); // this should FAIL + super(); + } + + public SuperInitFails(long x) { + SuperInitFails.this.hashCode(); // this should FAIL + super(); + } + + public SuperInitFails(double x) { + SuperInitFails.super.hashCode(); // this should FAIL + super(); + } + + public SuperInitFails(byte[] x) { + { + super(); // this should FAIL + } + } + + public SuperInitFails(char[] x) { + if (x.length == 0) + return; // this should FAIL + super(); + } + + public SuperInitFails(short[] x) { + this.x = x.length; // this should FAIL + super(); + } + + public SuperInitFails(float[] x) { + System.identityHashCode(this); // this should FAIL + super(); + } + + public SuperInitFails(int[] x) { + this(this); // this should FAIL + } + + public SuperInitFails(long[] x) { + this(Object.this); // this should FAIL + } + + public SuperInitFails(double[] x) { + Iterable.super.spliterator(); // this should FAIL + super(); + } + + public SuperInitFails(byte[][] x) { + super(new Object() { + { + super(); // this should FAIL + } + }); + } + + public SuperInitFails(char[][] x) { + new Inner1(); // this should FAIL + super(); + } + + class Inner1 { + } + + record Record1(int value) { + Record1(float x) { // this should FAIL + } + } + + record Record2(int value) { + Record2(float x) { // this should FAIL + super(); + } + } + + @Override + public java.util.Iterator iterator() { + return null; + } + + public SuperInitFails(short[][] x) { + class Foo { + Foo() { + SuperInitFails.this.hashCode(); + } + }; + new Foo(); // this should FAIL + super(); + } + + public SuperInitFails(float[][] x) { + Runnable r = () -> { + super(); // this should FAIL + }; + } + + public SuperInitFails(int[][] z) { + super((Runnable)() -> x); // this should FAIL + } + + public SuperInitFails(long[][] z) { + super(new Inner1()); // this should FAIL + } +} diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.out b/test/langtools/tools/javac/SuperInit/SuperInitFails.out new file mode 100644 index 0000000000000..60bbd83dcc034 --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.out @@ -0,0 +1,29 @@ +SuperInitFails.java:57:9: compiler.err.cant.ref.before.ctor.called: hashCode() +SuperInitFails.java:62:9: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:67:9: compiler.err.cant.ref.before.ctor.called: super +SuperInitFails.java:72:23: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:77:23: compiler.err.cant.ref.before.ctor.called: super +SuperInitFails.java:94:9: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:99:33: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:104:14: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:108:20: compiler.err.not.encl.class: java.lang.Object +SuperInitFails.java:112:17: compiler.err.cant.ref.before.ctor.called: super +SuperInitFails.java:119:22: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:125:9: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:133:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record1 +SuperInitFails.java:138:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record2 +SuperInitFails.java:154:9: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:165:31: compiler.err.cant.ref.before.ctor.called: x +SuperInitFails.java:169:15: compiler.err.cant.ref.before.ctor.called: this +SuperInitFails.java:33:13: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:37:14: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:41:14: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:45:13: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:49:33: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:53:32: compiler.err.call.must.only.appear.in.ctor +SuperInitFails.java:83:18: compiler.err.ctor.calls.not.allowed.here +SuperInitFails.java:89:13: compiler.err.return.before.superclass.initialized +SuperInitFails.java:160:18: compiler.err.ctor.calls.not.allowed.here +- compiler.note.preview.filename: SuperInitFails.java, DEFAULT +- compiler.note.preview.recompile +26 errors diff --git a/test/langtools/tools/javac/SuperInit/SuperInitGood.java b/test/langtools/tools/javac/SuperInit/SuperInitGood.java new file mode 100644 index 0000000000000..cbcbee8fef0bb --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/SuperInitGood.java @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 8194743 + * @summary Test valid placements of super()/this() in constructors + * @enablePreview + */ + +import java.util.concurrent.atomic.AtomicReference; + +public class SuperInitGood { + + SuperInitGood(Object obj) { + } + + SuperInitGood(int x) { + } + + // Default constructor provided by compiler + static class Test0 { + } + + // No explicit calls to this()/super() + static class Test1 { + Test1() { + } + Test1(int a) { + this.hashCode(); + } + } + + // Explicit calls to this()/super() + static class Test2 { + static int i; + Test2() { + this(0); + } + Test2(int i) { + Test2.i = i; + super(); + } + Test2(T obj) { + this(java.util.Objects.hashCode(obj)); + } + public T get() { + return null; + } + } + + // Explicit this()/super() with stuff in front + static class Test3 { + int x; + final int y; + final int z; + + Test3() { + new Object().hashCode(); + new Object().hashCode(); + super(); + this.x = new Object().hashCode(); + this.y = new Object().hashCode() % 17; + this.z = this.x + this.y; + } + } + + // Reference within constructor to outer class that's also my superclass + class Test5 extends SuperInitGood { + Test5(Object obj) { + if (obj == null) + throw new IllegalArgumentException(); + super(SuperInitGood.this); // NOT a 'this' reference + } + } + + // Initialization blocks + class Test6 { + final long startTime; + final int x; + { + this.x = 12; + } + Test6() { + long now = System.nanoTime(); + long then = now + 1000000L; + while (System.nanoTime() < then) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + super(); + this.startTime = now; + } + } + + // Mix up inner classes, proxies, and super() calls + // Copied mostly from UnverifiableInitForNestedLocalClassTest.java + public static void test7(final String arg) { + final String inlined = " inlined "; + class LocalClass { + String m() { + return "LocalClass " + arg + inlined; + } + + class SubClass extends LocalClass { + @Override + String m() { + return "SubClass " + arg + inlined; + } + } + + class SubSubClass extends SubClass { + @Override + String m() { + return "SubSubClass " + arg + inlined; + } + } + + class AnotherLocal { + class AnotherSub extends LocalClass { + AnotherSub() { + } + AnotherSub(int x) { + this((char)x); + } + AnotherSub(char y) { + super(); + } + @Override + String m() { + return "AnotherSub " + arg + inlined; + } + } + } + } + } + + // Anonymous inner class + public static void test8() { + new Test2(null) { + @Override + public Byte get() { + return (byte)-1; + } + }; + } + + // Qualified super() invocation + public static class Test9 extends Test5 { + + public Test9(SuperInitGood implicit, Object obj) { + obj.hashCode(); + implicit.super(obj); + } + } + + // Copied from WhichImplicitThis6 + public static class Test10 { + private int i; + public Test10(int i) {} + public class Sub extends Test10 { + public Sub() { + super(i); // i is not inherited, so it is the enclosing i + } + } + } + + // Two constructors where only one invokes super() + public static class Test11 { + public Test11() { + } + public Test11(int x) { + super(); + } + } + + // Nested version of the previous test + public static class Test12 { + Test12() { + class Sub { + public Sub() { + } + public Sub(int j) { + super(); + } + } + } + } + + // Nested super()'s requiring initialization code appended + public static class Test13 extends SuperInitGood { + final int x = new Object().hashCode(); + Test13() { + super(new Object() { + public void foo() { + class Bar { + final int y = new Object().hashCode(); + Bar() { + super(); + } + Bar(int ignored) { + } + } + } + }); + } + } + + // Initializer in initializer block + public static class Test14 { + final int x; // initialized in constructor + final int y; // initialized in initialization block + final int z = 13; // initialized with intializer value + public Test14() { + this(0); + } + public Test14(boolean z) { + this.x = z ? 1 : 0; + } + public Test14(int x) { + super(); + this.x = x; + } + { + this.y = -1; + } + } + + // Qualified super() invocation with superclass instance + public static class Test15 { + + final String name; + + public Test15(String name) { + this.name = name; + } + + public class Test15b extends Test15 { + + public Test15b(String name) { + super(name); + } + + public String getName() { + return Test15.this.name; + } + } + } + + public static class Test15c extends Test15.Test15b { + public Test15c(Test15 a, String name) { + a.super(name); + } + } + + // Mixing up outer instances, proxies, and initializers + public static class Test16 { + + final String x = String.valueOf(new Object().hashCode()); + + public void run() { + + final String y = String.valueOf(new Object().hashCode()); + + class Sub { + + final String z; + + Sub(String z, int ignored) { + this(z, (float)ignored); + } + + Sub(String z, float ignored) { + this.z = z; + } + + Sub(String z, byte ignored) { + super(); + this.z = z; + } + + Sub(String z, char ignored) { + this(z, (int)ignored); + } + + String x() { + return x; + } + + String y() { + return y; + } + + String z() { + return z; + } + } + + final String z = String.valueOf(new Object().hashCode()); + + final Sub[] subs = new Sub[] { + new Sub(z, 1), + new Sub(z, -1), + new Sub(z, (float)0), + new Sub(z, (byte)0), + new Sub(z, (char)0) + }; + + for (int i = 0; i < subs.length; i++) { + //System.err.println("i = " + i); + final Sub sub = subs[i]; + final String subx = sub.x(); + final String suby = sub.y(); + final String subz = sub.z(); + if (!x.equals(subx)) + throw new RuntimeException("x=" + x + " but sub[" + i + "].x()=" + subx); + if (!y.equals(suby)) + throw new RuntimeException("y=" + y + " but sub[" + i + "].y()=" + suby); + if (!z.equals(subz)) + throw new RuntimeException("z=" + z + " but sub[" + i + "].z()=" + subz); + } + } + } + + // Records + public class Test17 { + + record Rectangle(float length, float width) { } + + record StringHolder(String string) { + StringHolder { + java.util.Objects.requireNonNull(string); + } + } + + record ValueHolder(int value) { + ValueHolder(float x) { + if (Float.isNaN(x)) + throw new IllegalArgumentException(); + this((int)x); + } + } + } + + // Exceptions thrown by initializer block + public static class Test18 extends AtomicReference { + + { + if ((this.get().hashCode() % 3) == 0) + throw new MyException(); + } + + public Test18(Object obj) throws MyException { + super(obj); + } + + public Test18(boolean fail) throws MyException { + Object obj; + for (obj = new Object(); true; obj = new Object()) { + if (((obj.hashCode() % 3) == 0) != fail) + continue; + break; + } + this(obj); + } + + public static class MyException extends Exception { + } + } + + // super()/this() within outer try block but inside inner class + public static class Test19 { + public Test19(int x) { + try { + new Test1(x) { + @Override + public int hashCode() { + return x ^ super.hashCode(); + } + }; + } catch (StackOverflowError e) { + // ignore + } + } + } + + // local class declared before super(), but not used until after super() + public static class Test20 { + public Test20() { + class Foo { + Foo() { + Test20.this.hashCode(); + } + } + super(); + new Foo(); + } + } + + // local class inside super() parameter list + public static class Test21 extends AtomicReference { + private int x; + public Test21() { + super(switch ("foo".hashCode()) { + default -> { + class Nested {{ System.out.println(x); }} // class is NOT instantiated - OK + yield "bar"; + } + }); + } + } + + public static void main(String[] args) { + new Test0(); + new Test1(); + new Test1(7); + new Test2(); + new Test2<>(args); + new Test3(); + new SuperInitGood(3).new Test5(3); + new SuperInitGood(3).new Test6(); + SuperInitGood.test7("foo"); + SuperInitGood.test8(); + new Test9(new SuperInitGood(5), "abc"); + new Test10(7); + new Test11(9); + new Test12(); + new Test13(); + Test14 t14 = new Test14(); + assert t14.x == 0 && t14.y == -1 && t14.z == 13; + t14 = new Test14(7); + assert t14.x == 7 && t14.y == -1 && t14.z == 13; + new Test15c(new Test15("foo"), "bar"); + new Test16().run(); + new Test17.StringHolder("foo"); + try { + new Test17.StringHolder(null); + throw new Error(); + } catch (NullPointerException e) { + // expected + } + try { + new Test18(true); + assert false : "expected exception"; + } catch (Test18.MyException e) { + // expected + } + try { + new Test18(false); + } catch (Test18.MyException e) { + assert false : "unexpected exception: " + e; + } + new Test19(123); + new Test20(); + new Test21(); + } +} diff --git a/test/langtools/tools/javac/T8222035/MinContextOpTest_A.out b/test/langtools/tools/javac/T8222035/MinContextOpTest_A.out index 2321b85909ddc..f5b2d806c472d 100644 --- a/test/langtools/tools/javac/T8222035/MinContextOpTest_A.out +++ b/test/langtools/tools/javac/T8222035/MinContextOpTest_A.out @@ -1,4 +1,4 @@ -- compiler.warn.source.no.system.modules.path: 15 +- compiler.warn.source.no.system.modules.path: 15, (compiler.misc.source.no.system.modules.path: 15) MinContextOpTest.java:16:25: compiler.err.mod.not.allowed.here: static MinContextOpTest.java:22:25: compiler.err.mod.not.allowed.here: static MinContextOpTest.java:28:34: compiler.err.prob.found.req: (compiler.misc.infer.no.conforming.assignment.exists: T,K,V,E, (compiler.misc.inconvertible.types: java.util.function.Function, java.util.function.Function>)) diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnosOnConstructorsTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnosOnConstructorsTest.java new file mode 100644 index 0000000000000..996d12b35a4f8 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnosOnConstructorsTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8320001 + * @summary javac crashes while adding type annotations to the return type of a constructor + * @library /tools/lib /tools/javac/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main TypeAnnosOnConstructorsTest + */ + +import java.io.IOException; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.Task.Mode; +import toolbox.Task.OutputKind; +import toolbox.TestRunner; +import toolbox.ToolBox; + +public class TypeAnnosOnConstructorsTest extends TestRunner { + protected ToolBox tb; + + TypeAnnosOnConstructorsTest() { + super(System.err); + tb = new ToolBox(); + } + + public static void main(String... args) throws Exception { + new TypeAnnosOnConstructorsTest().runTests(); + } + + protected void runTests() throws Exception { + runTests(m -> new Object[]{Paths.get(m.getName())}); + } + + Path[] findJavaFiles(Path... paths) throws IOException { + return tb.findJavaFiles(paths); + } + + @Test + public void testAnnoOnConstructors(Path base) throws Exception { + Path src = base.resolve("src"); + Path y = src.resolve("Y.java"); + Path classes = base.resolve("classes"); + + Files.createDirectories(classes); + + tb.writeJavaFiles(src, + """ + import java.lang.annotation.Target; + import java.lang.annotation.ElementType; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + + class Y { + @TA public Y() {} + } + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.RUNTIME) + @interface TA {} + """); + + // we need to compile Y first + new JavacTask(tb) + .files(y) + .outdir(classes) + .run(); + + Path classDir = getClassDir(); + new JavacTask(tb) + .classpath(classes, classDir) + .options("-processor", SimpleProcessor.class.getName()) + .classes("Y") + .outdir(classes) + .run(Task.Expect.SUCCESS); + } + + public Path getClassDir() { + String classes = ToolBox.testClasses; + if (classes == null) { + return Paths.get("build"); + } else { + return Paths.get(classes); + } + } + + @SupportedAnnotationTypes("*") + public static final class SimpleProcessor extends AbstractProcessor { + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + } +} diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 06b7cea1afe16..a9ba23fa3743d 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -141,7 +141,6 @@ compiler.warn.invalid.path # this warning is genera compiler.err.invalid.path # this error is generated only in Windows systems compiler.note.multiple.elements # needs user code compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile -compiler.warn.preview.feature.use # preview feature support: not generated currently compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498) compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed? @@ -207,8 +206,8 @@ compiler.err.two.class.loaders.2 compiler.err.unmatched.quote compiler.err.unsupported.release.version compiler.warn.profile.target.conflict -compiler.warn.source.target.conflict -compiler.warn.target.default.source.conflict +compiler.err.source.target.conflict +compiler.err.target.default.source.conflict compiler.err.preview.not.latest compiler.err.preview.without.source.or.release compiler.misc.illegal.signature # the compiler can now detect more non-denotable types before class writing diff --git a/test/langtools/tools/javac/diags/examples/CallMustBeFirst.java b/test/langtools/tools/javac/diags/examples/CallOnlyInConstructor.java similarity index 84% rename from test/langtools/tools/javac/diags/examples/CallMustBeFirst.java rename to test/langtools/tools/javac/diags/examples/CallOnlyInConstructor.java index 3f0987062450c..f1018be46cebe 100644 --- a/test/langtools/tools/javac/diags/examples/CallMustBeFirst.java +++ b/test/langtools/tools/javac/diags/examples/CallOnlyInConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,10 @@ * questions. */ -// key: compiler.err.call.must.be.first.stmt.in.ctor +// key: compiler.err.call.must.only.appear.in.ctor -class CallMustBeFirst { - CallMustBeFirst() { - int i = 0; +class CallOnlyInConstructor { + void foo() { super(); } } diff --git a/src/java.base/share/native/libjava/AtomicLong.c b/test/langtools/tools/javac/diags/examples/CallsNotAllowedHere.java similarity index 65% rename from src/java.base/share/native/libjava/AtomicLong.c rename to test/langtools/tools/javac/diags/examples/CallsNotAllowedHere.java index bc4fd5db9708a..2f9239f09c024 100644 --- a/src/java.base/share/native/libjava/AtomicLong.c +++ b/test/langtools/tools/javac/diags/examples/CallsNotAllowedHere.java @@ -1,12 +1,10 @@ /* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * 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. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -23,12 +21,12 @@ * questions. */ -#include -#include -#include "java_util_concurrent_atomic_AtomicLong.h" +// key: compiler.err.ctor.calls.not.allowed.here -JNIEXPORT jboolean JNICALL -Java_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8(JNIEnv *env, jclass cls) -{ - return JVM_SupportsCX8(); +class CallsNotAllowedHere { + public CallsNotAllowedHere() { + { + super(); + } + } } diff --git a/test/langtools/tools/javac/diags/examples/EnumsMustBeStatic.java b/test/langtools/tools/javac/diags/examples/EnumsMustBeStatic.java index 222f2eb290364..6b9283827d41f 100644 --- a/test/langtools/tools/javac/diags/examples/EnumsMustBeStatic.java +++ b/test/langtools/tools/javac/diags/examples/EnumsMustBeStatic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,7 @@ */ // key: compiler.err.static.declaration.not.allowed.in.inner.classes -// key: compiler.warn.source.no.system.modules.path -// options: -source 15 +// options: --release 15 class EnumsMustBeStatic { class Nested { diff --git a/test/langtools/tools/javac/diags/examples/Expected3.java b/test/langtools/tools/javac/diags/examples/Expected3.java index 2b1ec3256187d..1a527034ff3e9 100644 --- a/test/langtools/tools/javac/diags/examples/Expected3.java +++ b/test/langtools/tools/javac/diags/examples/Expected3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ // key: compiler.err.expected3 -// key: compiler.warn.source.no.system.modules.path -// options: -source 15 +// options: --release 15 int Expected3; diff --git a/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java b/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java new file mode 100644 index 0000000000000..f0e44836850b4 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + // key: compiler.misc.feature.super.init + // key: compiler.warn.preview.feature.use + // options: --enable-preview -source ${jdk.version} -Xlint:preview + +class FeatureStatementsBeforeSuper { + FeatureStatementsBeforeSuper() { + System.out.println(); + super(); + } +} diff --git a/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java b/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java index e2478244cc90c..da7b69ba16cd1 100644 --- a/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java +++ b/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java @@ -21,7 +21,7 @@ * questions. */ -// key: compiler.err.first.statement.must.be.call.to.another.constructor +// key: compiler.err.non.canonical.constructor.invoke.another.constructor record R(int x) { public R(int x, int y) { this.x = x; } diff --git a/test/langtools/tools/javac/diags/examples/InnerClassCantHaveStatic.java b/test/langtools/tools/javac/diags/examples/InnerClassCantHaveStatic.java index b1eaad961db6d..abe95b8a23ddb 100644 --- a/test/langtools/tools/javac/diags/examples/InnerClassCantHaveStatic.java +++ b/test/langtools/tools/javac/diags/examples/InnerClassCantHaveStatic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,7 @@ */ // key: compiler.err.icls.cant.have.static.decl -// key: compiler.warn.source.no.system.modules.path -// options: -source 15 +// options: --release 15 class InnerClassCantHaveStatic { class Inner { diff --git a/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java b/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java index a72e7ef12bd84..3c5d5602aa452 100644 --- a/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java +++ b/test/langtools/tools/javac/diags/examples/InterfaceNotAllowed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,7 @@ */ // key: compiler.err.intf.not.allowed.here -// key: compiler.warn.source.no.system.modules.path -// options: -source 15 +// options: --release 15 class InterfaceNotAllowed { void m() { diff --git a/test/langtools/tools/javac/diags/examples/LocalEnum.java b/test/langtools/tools/javac/diags/examples/LocalEnum.java index fdf875c969776..a7cc2b6dc0baf 100644 --- a/test/langtools/tools/javac/diags/examples/LocalEnum.java +++ b/test/langtools/tools/javac/diags/examples/LocalEnum.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,7 @@ */ // key: compiler.err.local.enum -// key: compiler.warn.source.no.system.modules.path -// options: -source 15 +// options: --release 15 class LocalEnum { void m() { diff --git a/test/langtools/tools/javac/diags/examples/ObsoleteSourceAndTarget.java b/test/langtools/tools/javac/diags/examples/ObsoleteSourceAndTarget.java index 5cf52f2d76aeb..d4a94f032b0b4 100644 --- a/test/langtools/tools/javac/diags/examples/ObsoleteSourceAndTarget.java +++ b/test/langtools/tools/javac/diags/examples/ObsoleteSourceAndTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ // key: compiler.warn.option.obsolete.target // key: compiler.warn.option.obsolete.suppression // key: compiler.warn.source.no.bootclasspath +// key: compiler.misc.source.no.bootclasspath.with.target // options: -source 1.8 -target 1.8 class ObsoleteSourceAndTarget { diff --git a/test/langtools/tools/javac/diags/examples/OptionRemovedSource.java b/test/langtools/tools/javac/diags/examples/OptionRemovedSource.java index fadc5142f0c2b..f40ea3225e9e5 100644 --- a/test/langtools/tools/javac/diags/examples/OptionRemovedSource.java +++ b/test/langtools/tools/javac/diags/examples/OptionRemovedSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ // key: compiler.err.option.removed.source // key: compiler.warn.source.no.bootclasspath +// key: compiler.misc.source.no.bootclasspath // options: -source 1.5 class RemovedSourceAndTarget { diff --git a/test/langtools/tools/javac/diags/examples/OptionRemovedTarget.java b/test/langtools/tools/javac/diags/examples/OptionRemovedTarget.java index 81f6d8e0ee066..e38994ec532fd 100644 --- a/test/langtools/tools/javac/diags/examples/OptionRemovedTarget.java +++ b/test/langtools/tools/javac/diags/examples/OptionRemovedTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ // key: compiler.err.option.removed.source // key: compiler.err.option.removed.target // key: compiler.warn.source.no.bootclasspath +// key: compiler.misc.source.no.bootclasspath.with.target // options: -source 1.5 -target 1.5 class RemovedSourceAndTarget { diff --git a/test/langtools/tools/javac/diags/examples/Records.java b/test/langtools/tools/javac/diags/examples/Records.java index 9acc44d0f4393..f356ceed5998f 100644 --- a/test/langtools/tools/javac/diags/examples/Records.java +++ b/test/langtools/tools/javac/diags/examples/Records.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ // key: compiler.misc.feature.records // key: compiler.err.feature.not.supported.in.source.plural -// key: compiler.warn.source.no.system.modules.path -// options: -source 15 +// options: --release 15 record R() {} diff --git a/test/langtools/tools/javac/diags/examples/RedundantSuperclassInit.java b/test/langtools/tools/javac/diags/examples/RedundantSuperclassInit.java new file mode 100644 index 0000000000000..82c5faf1d0443 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/RedundantSuperclassInit.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.redundant.superclass.init + +class RedundantSuperclassInit { + RedundantSuperclassInit() { + super(); + super(); + } +} diff --git a/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java b/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java new file mode 100644 index 0000000000000..dc883b59b8f69 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.err.return.before.superclass.initialized +// key: compiler.note.preview.filename +// key: compiler.note.preview.recompile +// options: --enable-preview -source ${jdk.version} + +class ReturnBeforeSuperclassInit { + ReturnBeforeSuperclassInit(boolean maybe) { + if (maybe) + return; + super(); + } +} diff --git a/test/langtools/tools/javac/diags/examples/SealedTypes.java b/test/langtools/tools/javac/diags/examples/SealedTypes.java index 85aca18b2afa1..4bd55525554b7 100644 --- a/test/langtools/tools/javac/diags/examples/SealedTypes.java +++ b/test/langtools/tools/javac/diags/examples/SealedTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. 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. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,7 @@ // key: compiler.misc.feature.sealed.classes // key: compiler.err.feature.not.supported.in.source.plural -// key: compiler.warn.source.no.system.modules.path -// options: -source 16 +// options: --release 16 sealed class Sealed {} diff --git a/test/langtools/tools/javac/diags/examples/SourceNoBootclasspath.java b/test/langtools/tools/javac/diags/examples/SourceNoBootclasspath.java index 2c30b9e1a3302..5f02a437dbe80 100644 --- a/test/langtools/tools/javac/diags/examples/SourceNoBootclasspath.java +++ b/test/langtools/tools/javac/diags/examples/SourceNoBootclasspath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ */ // key: compiler.warn.source.no.bootclasspath +// key: compiler.misc.source.no.bootclasspath // key: compiler.warn.option.obsolete.source // key: compiler.warn.option.obsolete.suppression // options: -source 8 diff --git a/test/langtools/tools/javac/diags/examples/SourceNoSystemModulesPath.java b/test/langtools/tools/javac/diags/examples/SourceNoSystemModulesPath.java index 915d5600a6ffe..5967a9b65715d 100644 --- a/test/langtools/tools/javac/diags/examples/SourceNoSystemModulesPath.java +++ b/test/langtools/tools/javac/diags/examples/SourceNoSystemModulesPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ */ // key: compiler.warn.source.no.system.modules.path +// key: compiler.misc.source.no.system.modules.path // options: -source 9 class SourceNoSystemModulesPath { } diff --git a/test/langtools/tools/javac/diags/examples/SourceNoSystemModulesPathWithTarget.java b/test/langtools/tools/javac/diags/examples/SourceNoSystemModulesPathWithTarget.java new file mode 100644 index 0000000000000..6132c44db6872 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/SourceNoSystemModulesPathWithTarget.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.warn.source.no.system.modules.path +// key: compiler.misc.source.no.system.modules.path.with.target +// options: -source 9 -target 9 + +class SourceNoSystemModulesPath { } diff --git a/test/langtools/tools/javac/diags/examples/TextBlockSource.java b/test/langtools/tools/javac/diags/examples/TextBlockSource.java index f805491cd42ad..9fa6ecca1b8ef 100644 --- a/test/langtools/tools/javac/diags/examples/TextBlockSource.java +++ b/test/langtools/tools/javac/diags/examples/TextBlockSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,7 @@ // key: compiler.misc.feature.text.blocks // key: compiler.err.feature.not.supported.in.source.plural - // key: compiler.warn.source.no.system.modules.path - // options: -source 14 + // options: --release 14 class TextBlockSource { String m() { diff --git a/test/langtools/tools/javac/diags/examples/UnderscoreInLambdaExpression.java b/test/langtools/tools/javac/diags/examples/UnderscoreInLambdaExpression.java index dd68a2c212879..d88aa9bc1272e 100644 --- a/test/langtools/tools/javac/diags/examples/UnderscoreInLambdaExpression.java +++ b/test/langtools/tools/javac/diags/examples/UnderscoreInLambdaExpression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,7 @@ // key: compiler.err.feature.not.supported.in.source.plural // key: compiler.misc.feature.unnamed.variables -// key: compiler.warn.source.no.system.modules.path -// options: -source 21 +// options: --release 21 public class UnderscoreInLambdaExpression { java.util.function.Function f = _ -> "x"; diff --git a/test/langtools/tools/javac/options/BCPOrSystemNotSpecified.java b/test/langtools/tools/javac/options/BCPOrSystemNotSpecified.java index a4dea63ca364d..bfe1d9d11fd0f 100644 --- a/test/langtools/tools/javac/options/BCPOrSystemNotSpecified.java +++ b/test/langtools/tools/javac/options/BCPOrSystemNotSpecified.java @@ -78,7 +78,7 @@ public void testSource8(Path base) throws IOException { List log; List expected = Arrays.asList( - "- compiler.warn.source.no.bootclasspath: 8", + "- compiler.warn.source.no.bootclasspath: 8, (compiler.misc.source.no.bootclasspath: 8)", "- compiler.warn.option.obsolete.source: 8", "- compiler.warn.option.obsolete.suppression", "3 warnings" @@ -125,7 +125,7 @@ public void testSource9(Path base) throws IOException { List log; List expected = Arrays.asList( - "- compiler.warn.source.no.system.modules.path: 9", + "- compiler.warn.source.no.system.modules.path: 9, (compiler.misc.source.no.system.modules.path: 9)", "1 warning" ); diff --git a/test/langtools/tools/javac/options/T6900037.out b/test/langtools/tools/javac/options/T6900037.out index dd5352edc7c46..3e610c371c957 100644 --- a/test/langtools/tools/javac/options/T6900037.out +++ b/test/langtools/tools/javac/options/T6900037.out @@ -1,4 +1,4 @@ -- compiler.warn.source.no.bootclasspath: 8 +- compiler.warn.source.no.bootclasspath: 8, (compiler.misc.source.no.bootclasspath: 8) - compiler.warn.option.obsolete.source: 8 - compiler.warn.option.obsolete.suppression - compiler.err.warnings.and.werror diff --git a/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java b/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java index f128bf14f7fba..0fae2fab1f0ad 100644 --- a/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java +++ b/test/langtools/tools/javac/options/smokeTests/OptionSmokeTest.java @@ -139,13 +139,13 @@ public void sourceAndModuleSourceCantBeTogether(Path base) throws Exception { @Test public void sourceAndTargetMismatch(Path base) throws Exception { - doTest(base, String.format("warning: source release %s requires target release %s", Source.DEFAULT.name, Source.DEFAULT.name), + doTest(base, String.format("error: specified target release %s is too old for the specified source release %s", Source.MIN.name, Source.DEFAULT.name), String.format("-source %s -target %s", Source.DEFAULT.name, Source.MIN.name)); } @Test public void targetConflictsWithDefaultSource(Path base) throws Exception { - doTest(base, String.format("warning: target release %s conflicts with default source release %s", Source.MIN.name, Source.DEFAULT.name), + doTest(base, String.format("error: specified target release %s is too old for the default source release %s", Source.MIN.name, Source.DEFAULT.name), String.format("-target %s", Source.MIN.name)); } diff --git a/test/langtools/tools/javac/platform/CanHandleClassFilesTest.java b/test/langtools/tools/javac/platform/CanHandleClassFilesTest.java index ae4f525e4ee12..0a87f67bbe52f 100644 --- a/test/langtools/tools/javac/platform/CanHandleClassFilesTest.java +++ b/test/langtools/tools/javac/platform/CanHandleClassFilesTest.java @@ -109,10 +109,10 @@ protected Class findClass(String name) throws ClassNotFoundException { var createSymbolsClass = Class.forName("build.tools.symbolgenerator.CreateSymbols", false, cl); var main = createSymbolsClass.getMethod("main", String[].class); var symbols = targetDir.resolve("symbols"); - var systemModules = targetDir.resolve("system-modules"); + var modules = targetDir.resolve("modules"); try (Writer w = Files.newBufferedWriter(symbols)) {} - try (Writer w = Files.newBufferedWriter(systemModules)) {} + Files.createDirectories(modules); main.invoke(null, (Object) new String[] {"build-description-incremental", @@ -126,7 +126,8 @@ protected Class findClass(String name) throws ClassNotFoundException { targetDir.resolve("ct.sym").toAbsolutePath().toString(), Long.toString(System.currentTimeMillis() / 1000), "" + SourceVersion.latest().ordinal(), - systemModules.toAbsolutePath().toString()}); + "", + modules.toAbsolutePath().toString()}); } } diff --git a/test/langtools/tools/javac/platform/ModuleVersionTest.java b/test/langtools/tools/javac/platform/ModuleVersionTest.java new file mode 100644 index 0000000000000..693daef58f900 --- /dev/null +++ b/test/langtools/tools/javac/platform/ModuleVersionTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8318913 + * @summary Verify correct module versions are recorded when --release is used. + * @library /tools/lib + * @modules + * java.base/jdk.internal.classfile + * java.base/jdk.internal.classfile.attribute + * java.base/jdk.internal.classfile.constantpool + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.platform + * jdk.compiler/com.sun.tools.javac.util:+open + * @run junit ModuleVersionTest + */ + + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdk.internal.classfile.Attributes; +import jdk.internal.classfile.ClassModel; +import jdk.internal.classfile.Classfile; +import jdk.internal.classfile.attribute.ModuleAttribute; +import jdk.internal.classfile.attribute.ModuleRequireInfo; + +import org.junit.Test; + +import toolbox.JavacTask; +import toolbox.ToolBox; + +import static org.junit.Assert.*; +public class ModuleVersionTest { + + private static final Pattern VERSION_PATTERN = + Pattern.compile("^([0-9]+)(.[0-9]+)*(-.*)?"); + + @Test + public void testVersionInDependency() throws Exception { + doTestVersionInDependency("11", "11"); + + String expectedVersion = System.getProperty("java.version"); + Matcher m = VERSION_PATTERN.matcher(expectedVersion); + + if (m.find()) { + String preRelease = m.group(3); + + expectedVersion = m.group(1); + + if (preRelease != null) { + expectedVersion += preRelease; + } + } + + doTestVersionInDependency(System.getProperty("java.specification.version"), expectedVersion); + } + + private void doTestVersionInDependency(String specificationVersion, + String expectedVersion) throws Exception { + Path root = Paths.get("."); + Path classes = root.resolve("classes"); + Files.createDirectories(classes); + ToolBox tb = new ToolBox(); + + new JavacTask(tb) + .outdir(classes) + .options("--release", specificationVersion) + .sources(""" + module test {} + """, + """ + package test; + public class Test { + } + """) + .run() + .writeAll(); + + Path moduleInfo = classes.resolve("module-info.class"); + ClassModel clazz = Classfile.of().parse(moduleInfo); + + assertTrue(clazz.isModuleInfo()); + ModuleAttribute module = clazz.findAttribute(Attributes.MODULE).get(); + ModuleRequireInfo req = module.requires().get(0); + assertEquals("java.base", req.requires().name().stringValue()); + assertEquals(expectedVersion, req.requiresVersion().get().stringValue()); + } + +} diff --git a/test/langtools/tools/javac/platform/NonExportedPermittedTypes.java b/test/langtools/tools/javac/platform/NonExportedPermittedTypes.java new file mode 100644 index 0000000000000..bdafef7e39adb --- /dev/null +++ b/test/langtools/tools/javac/platform/NonExportedPermittedTypes.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8318913 + * @summary Verify no error is when compiling a class whose permitted types are not exported + * @modules jdk.compiler + * @compile/fail/ref=NonExportedPermittedTypes.out -XDrawDiagnostics NonExportedPermittedTypes.java + * @compile/fail/ref=NonExportedPermittedTypes.out --release 21 -XDrawDiagnostics NonExportedPermittedTypes.java + * @compile/fail/ref=NonExportedPermittedTypes.out --release ${jdk.version} -XDrawDiagnostics NonExportedPermittedTypes.java + */ + + +import java.lang.constant.ConstantDesc; + +public class NonExportedPermittedTypes { + + public void test1(ConstantDesc cd) { + switch (cd) { + case String s -> {} + } + } + + public void test2(ConstantDesc cd) { + switch (cd) { + case String s -> {} + default -> {} + } + } + +} diff --git a/test/langtools/tools/javac/platform/NonExportedPermittedTypes.out b/test/langtools/tools/javac/platform/NonExportedPermittedTypes.out new file mode 100644 index 0000000000000..e17d397328f4b --- /dev/null +++ b/test/langtools/tools/javac/platform/NonExportedPermittedTypes.out @@ -0,0 +1,2 @@ +NonExportedPermittedTypes.java:40:9: compiler.err.not.exhaustive.statement +1 error diff --git a/test/langtools/tools/javac/platform/NonExportedSuperTypes.java b/test/langtools/tools/javac/platform/NonExportedSuperTypes.java index b3f17fd6e3c45..8506c3c591362 100644 --- a/test/langtools/tools/javac/platform/NonExportedSuperTypes.java +++ b/test/langtools/tools/javac/platform/NonExportedSuperTypes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. 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. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ * @modules jdk.compiler * jdk.jfr * @compile --release 17 NonExportedSuperTypes.java + * @compile --release ${jdk.version} NonExportedSuperTypes.java */ import jdk.jfr.Event; diff --git a/test/langtools/tools/javac/platform/ReleaseAndEnablePreview.java b/test/langtools/tools/javac/platform/ReleaseAndEnablePreview.java new file mode 100644 index 0000000000000..0c70d75d7f8da --- /dev/null +++ b/test/langtools/tools/javac/platform/ReleaseAndEnablePreview.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8318913 + * @summary Ensure release and enable-preview work well together. + * @modules jdk.compiler + * @compile --release ${jdk.version} --enable-preview ReleaseAndEnablePreview.java + */ + +public class ReleaseAndEnablePreview { + + public String evt(String str) { + return str.length() + "" + str.charAt(0); + } + +} diff --git a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java index 307c53135a774..17e9e1703abe7 100644 --- a/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java +++ b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java @@ -751,14 +751,14 @@ void doTestComplex(String printClass, }; new CreateSymbols().createBaseLine(versions, acceptAll, ctSym, new String[0]); Path symbolsDesc = ctSym.resolve("symbols"); - Path systemModules = ctSym.resolve("systemModules"); + Path modules = ctSym.resolve("modules"); - Files.newBufferedWriter(systemModules).close(); + Files.createDirectories(modules); Path classesZip = output.resolve("classes.zip"); Path classesDir = output.resolve("classes"); - new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classesZip.toAbsolutePath().toString(), 0, "9", systemModules.toString()); + new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classesZip.toAbsolutePath().toString(), 0, "9", "", modules.toString()); try (JarFile jf = new JarFile(classesZip.toFile())) { Enumeration en = jf.entries(); @@ -1037,12 +1037,12 @@ protected boolean includeEffectiveAccess(ClassList classes, ClassDescription cla } }.createBaseLine(versions, acceptAll, descDest, new String[0]); Path symbolsDesc = descDest.resolve("symbols"); - Path systemModules = descDest.resolve("systemModules"); + Path modules = descDest.resolve("modules"); - Files.newBufferedWriter(systemModules).close(); + Files.createDirectories(modules); try { - new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classDest, 0, "8", systemModules.toString()); + new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classDest, 0, "8", "", modules.toString()); } catch (Throwable t) { t.printStackTrace(); throw t; diff --git a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java index 5f20459832428..5a51496ff1973 100644 --- a/test/langtools/tools/javac/records/RecordCompilationTests.java +++ b/test/langtools/tools/javac/records/RecordCompilationTests.java @@ -403,11 +403,11 @@ record R(String v) { assertFail("compiler.err.invalid.canonical.constructor.in.record", "record R(int x, int y) { public R(int y, int x) { this.x = this.y = 0; }}"); - // first invocation should be one to the canonical - assertFail("compiler.err.first.statement.must.be.call.to.another.constructor", + // constructor is not canonical, so it must only invoke another constructor + assertFail("compiler.err.non.canonical.constructor.invoke.another.constructor", "record R(int x, int y) { public R(int y, int x, int z) { this.x = this.y = 0; } }"); - assertFail("compiler.err.first.statement.must.be.call.to.another.constructor", + assertFail("compiler.err.non.canonical.constructor.invoke.another.constructor", "record R(int x, int y) { public R(int y, int x, int z) { super(); this.x = this.y = 0; } }"); assertOK("record R(int x, int y) { " + diff --git a/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01_source10.out b/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01_source10.out index f424f013a957c..a7c5bae106bbc 100644 --- a/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01_source10.out +++ b/test/langtools/tools/javac/var_implicit_lambda/VarInImplicitLambdaNegTest01_source10.out @@ -1,4 +1,4 @@ -- compiler.warn.source.no.system.modules.path: 10 +- compiler.warn.source.no.system.modules.path: 10, (compiler.misc.source.no.system.modules.path: 10) VarInImplicitLambdaNegTest01.java:12:36: compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.var.syntax.in.implicit.lambda), 10, 11 VarInImplicitLambdaNegTest01.java:15:28: compiler.err.invalid.lambda.parameter.declaration: (compiler.misc.implicit.and.explicit.not.allowed) VarInImplicitLambdaNegTest01.java:17:52: compiler.err.restricted.type.not.allowed.here: var diff --git a/test/langtools/tools/javac/versions/Versions.java b/test/langtools/tools/javac/versions/Versions.java index ee9e6f2be06f2..554d384948b67 100644 --- a/test/langtools/tools/javac/versions/Versions.java +++ b/test/langtools/tools/javac/versions/Versions.java @@ -25,7 +25,7 @@ * @test * @bug 4981566 5028634 5094412 6304984 7025786 7025789 8001112 8028545 * 8000961 8030610 8028546 8188870 8173382 8173382 8193290 8205619 8028563 - * 8245147 8245586 8257453 8286035 8306586 + * 8245147 8245586 8257453 8286035 8306586 8320806 * @summary Check interpretation of -target and -source options * @modules java.compiler * jdk.compiler @@ -272,8 +272,8 @@ protected void check(String major, List args) { * the uncommon program that is accepted in one version of the * language and rejected in a later version.) * - * When version of the language get a new, non-preview feature, a - * new source example enum constant should be added. + * When a version of the language gets a new, non-preview feature, + * a new source example enum constant should be added. */ enum SourceExample { BASE(7, "Base.java", "public class Base { }\n"), @@ -375,6 +375,20 @@ public static void main(String... args) { } """), + SOURCE_22(22, "New22.java", + // New feature in 22: Unnamed Variables & Patterns + """ + public class New22 { + public static void main(String... args) { + Object o = new Object(){}; + + System.out.println(switch (o) { + case Integer _ -> "Hello world."; + default -> o.toString(); + }); + } + } + """), ; // Reduce code churn when appending new constants private int sourceLevel; diff --git a/test/lib/jdk/test/lib/jfr/Events.java b/test/lib/jdk/test/lib/jfr/Events.java index 620e5b0fcc3e7..e3e5979817962 100644 --- a/test/lib/jdk/test/lib/jfr/Events.java +++ b/test/lib/jdk/test/lib/jfr/Events.java @@ -365,20 +365,42 @@ private static boolean containsEvent(List events, String name) { return false; } + public static void assertTopFrame(RecordedEvent event, Class expectedClass, String expectedMethodName) { + assertTopFrame(event, expectedClass.getName(), expectedMethodName); + } + + public static void assertTopFrame(RecordedEvent event, String expectedClass, String expectedMethodName) { + RecordedStackTrace stackTrace = event.getStackTrace(); + Asserts.assertNotNull(stackTrace, "Missing stack trace"); + RecordedFrame topFrame = stackTrace.getFrames().get(0); + if (isFrame(topFrame, expectedClass, expectedMethodName)) { + return; + } + String expected = expectedClass + "::" + expectedMethodName; + Asserts.fail("Expected top frame " + expected + ". Found " + topFrame); + } + public static void assertFrame(RecordedEvent event, Class expectedClass, String expectedMethodName) { RecordedStackTrace stackTrace = event.getStackTrace(); Asserts.assertNotNull(stackTrace, "Missing stack trace"); for (RecordedFrame frame : stackTrace.getFrames()) { - if (frame.isJavaFrame()) { - RecordedMethod method = frame.getMethod(); - RecordedClass type = method.getType(); - if (expectedClass.getName().equals(type.getName())) { - if (expectedMethodName.equals(method.getName())) { - return; - } - } + if (isFrame(frame, expectedClass.getName(), expectedMethodName)) { + return; } } Asserts.fail("Expected " + expectedClass.getName() + "::"+ expectedMethodName + " in stack trace"); } + + private static boolean isFrame(RecordedFrame frame, String expectedClass, String expectedMethodName) { + if (frame.isJavaFrame()) { + RecordedMethod method = frame.getMethod(); + RecordedClass type = method.getType(); + if (expectedClass.equals(type.getName())) { + if (expectedMethodName.equals(method.getName())) { + return true; + } + } + } + return false; + } } diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index fab7b99196def..ae04d9d1bc4b4 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java @@ -621,6 +621,14 @@ public List asLines() { return asLines(getOutput()); } + public List stdoutAsLines() { + return asLines(getStdout()); + } + + public List stderrAsLines() { + return asLines(getStderr()); + } + private List asLines(String buffer) { return Arrays.asList(buffer.split("\\R")); } @@ -786,4 +794,72 @@ private int indexOf(List lines, String regexp, int fromIndex) { return -1; } + private void searchLinesForMultiLinePattern(String[] haystack, String[] needles, boolean verbose) { + + if (needles.length == 0) { + return; + } + + int firstNeedlePos = 0; + for (int i = 0; i < haystack.length; i++) { + if (verbose) { + System.out.println("" + i + ":" + haystack[i]); + } + if (haystack[i].contains(needles[0])) { + if (verbose) { + System.out.println("Matches pattern 0 (\"" + needles[0] + "\")"); + } + firstNeedlePos = i; + break; + } + } + + for (int i = 1; i < needles.length; i++) { + int haystackPos = firstNeedlePos + i; + if (haystackPos < haystack.length) { + if (verbose) { + System.out.println("" + haystackPos + ":" + haystack[haystackPos]); + } + if (haystack[haystackPos].contains(needles[i])) { + if (verbose) { + System.out.println("Matches pattern " + i + "(\"" + needles[i] + "\")"); + } + } else { + String err = "First unmatched pattern: " + i + " (\"" + needles[i] + "\")"; + if (!verbose) { // don't print twice + reportDiagnosticSummary(); + } + throw new RuntimeException(err); + } + } + } + } + + public void stdoutShouldContainMultiLinePattern(String[] needles, boolean verbose) { + String [] stdoutLines = stdoutAsLines().toArray(new String[0]); + searchLinesForMultiLinePattern(stdoutLines, needles, verbose); + } + + public void stdoutShouldContainMultiLinePattern(String... needles) { + stdoutShouldContainMultiLinePattern(needles, true); + } + + public void stderrShouldContainMultiLinePattern(String[] needles, boolean verbose) { + String [] stderrLines = stdoutAsLines().toArray(new String[0]); + searchLinesForMultiLinePattern(stderrLines, needles, verbose); + } + + public void stderrShouldContainMultiLinePattern(String... needles) { + stderrShouldContainMultiLinePattern(needles, true); + } + + public void shouldContainMultiLinePattern(String[] needles, boolean verbose) { + String [] lines = asLines().toArray(new String[0]); + searchLinesForMultiLinePattern(lines, needles, verbose); + } + + public void shouldContainMultiLinePattern(String... needles) { + shouldContainMultiLinePattern(needles, true); + } + } diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 3743aa506ff94..2d33182331d4f 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -522,6 +522,8 @@ public void clearInlineCaches(boolean preserve_static_stubs) { public native long metaspaceCapacityUntilGC(); public native long metaspaceSharedRegionAlignment(); + public native void cleanMetaspaces(); + // Metaspace Arena Tests public native long createMetaspaceTestContext(long commit_limit, long reserve_limit); public native void destroyMetaspaceTestContext(long context); @@ -785,6 +787,10 @@ public native int validateCgroup(String procCgroups, public native void unlockCritical(); + public native void pinObject(Object o); + + public native void unpinObject(Object o); + public native boolean setVirtualThreadsNotifyJvmtiMode(boolean enabled); public native void preTouchMemory(long addr, long size); diff --git a/test/lib/native/testlib_threads.h b/test/lib/native/testlib_threads.h index 3faf94f8e9fca..575a5a3c15fc5 100644 --- a/test/lib/native/testlib_threads.h +++ b/test/lib/native/testlib_threads.h @@ -81,10 +81,15 @@ void run_in_new_thread_and_join(PROCEDURE proc, void* context) { } #else pthread_t thread; - int result = pthread_create(&thread, NULL, procedure, &helper); + pthread_attr_t attr; + pthread_attr_init(&attr); + size_t stack_size = 0x100000; + pthread_attr_setstacksize(&attr, stack_size); + int result = pthread_create(&thread, &attr, procedure, &helper); if (result != 0) { fatal("failed to create thread", result); } + pthread_attr_destroy(&attr); result = pthread_join(thread, NULL); if (result != 0) { fatal("failed to join thread", result); diff --git a/test/micro/org/openjdk/bench/java/lang/ArrayCopyAlignedLarge.java b/test/micro/org/openjdk/bench/java/lang/ArrayCopyAlignedLarge.java new file mode 100644 index 0000000000000..f5f6bb2949059 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ArrayCopyAlignedLarge.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.concurrent.TimeUnit; + +/** + * Benchmark measuring aligned System.arraycopy. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class ArrayCopyAlignedLarge { + + @Param({"100000", "1000000", "2000000", "5000000", "10000000"}) + int length; + + int fromPos, toPos; + byte[] fromByteArr, toByteArr; + + @Setup + public void setup() { + // Both positions aligned + fromPos = 0; + toPos = 0; + + fromByteArr = new byte[length]; + toByteArr = new byte[length]; + } + + @Benchmark + public void testByte() { + System.arraycopy(fromByteArr, fromPos, toByteArr, toPos, length); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromSliceTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromSliceTest.java index ff73699bd300b..390add8801ce0 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromSliceTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromSliceTest.java @@ -63,18 +63,17 @@ public void setup() { @Benchmark public MemorySegment alloc_confined() { - Arena arena = Arena.ofConfined(); - MemorySegment segment = arena.allocate(size); - MemorySegment.copy(arr, start, segment, C_CHAR, 0, size); - arena.close(); - return segment; + try (Arena arena = Arena.ofConfined()) { + MemorySegment segment = arena.allocate(size); + MemorySegment.copy(arr, start, segment, C_CHAR, 0, size); + return segment; + } } @Benchmark public MemorySegment alloc_confined_slice() { - Arena arena = Arena.ofConfined(); - MemorySegment segment = arena.allocateFrom(C_CHAR, MemorySegment.ofArray(arr), C_CHAR, start, size); - arena.close(); - return segment; + try (Arena arena = Arena.ofConfined()) { + return arena.allocateFrom(C_CHAR, MemorySegment.ofArray(arr), C_CHAR, start, size); + } } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromTest.java index a0c5d5be28927..fcd79870ca0bf 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/AllocFromTest.java @@ -68,34 +68,30 @@ public void setup() { @Benchmark public MemorySegment alloc_confined() { - Arena arena = Arena.ofConfined(); - MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); - arena.close(); - return segment; + try (Arena arena = Arena.ofConfined()) { + return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); + } } @Benchmark public MemorySegment alloc_malloc_arena() { - MallocArena arena = new MallocArena(); - MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); - arena.close(); - return segment; + try (MallocArena arena = new MallocArena()) { + return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); + } } @Benchmark public MemorySegment alloc_unsafe_arena() { - UnsafeArena arena = new UnsafeArena(); - MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); - arena.close(); - return segment; + try (UnsafeArena arena = new UnsafeArena()) { + return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); + } } @Benchmark public MemorySegment alloc_pool_arena() { - Arena arena = pool.acquire(); - MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); - arena.close(); - return segment; + try (Arena arena = pool.acquire()) { + return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); + } } static class SlicingPool { diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/AllocTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/AllocTest.java index b9b5a3f30da05..b7e4bfd5f57d4 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/AllocTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/AllocTest.java @@ -65,26 +65,23 @@ public void tearDown() { @Benchmark public MemorySegment alloc_confined() { - Arena arena = Arena.ofConfined(); - MemorySegment segment = arena.allocate(size); - arena.close(); - return segment; + try (Arena arena = Arena.ofConfined()) { + return arena.allocate(size); + } } @Benchmark public long alloc_calloc_arena() { - CallocArena arena = new CallocArena(); - MemorySegment segment = arena.allocate(size); - arena.close(); - return segment.address(); + try (CallocArena arena = new CallocArena()) { + return arena.allocate(size).address(); + } } @Benchmark public long alloc_unsafe_arena() { - UnsafeArena arena = new UnsafeArena(); - MemorySegment segment = arena.allocate(size); - arena.close(); - return segment.address(); + try (UnsafeArena arena = new UnsafeArena()) { + return arena.allocate(size).address(); + } } public static class CallocArena implements Arena { diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ResourceScopeCloseMin.java b/test/micro/org/openjdk/bench/java/lang/foreign/ResourceScopeCloseMin.java new file mode 100644 index 0000000000000..51c6d72b3e04a --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ResourceScopeCloseMin.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.foreign; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3) +public class ResourceScopeCloseMin { + + Runnable dummy = () -> {}; + + static class ConfinedScope { + final Thread owner; + boolean closed; + final List resources = new ArrayList<>(); + + private void checkState() { + if (closed) { + throw new AssertionError("Closed"); + } else if (owner != Thread.currentThread()) { + throw new AssertionError("Wrong thread"); + } + } + + ConfinedScope() { + this.owner = Thread.currentThread(); + } + + void addCloseAction(Runnable runnable) { + checkState(); + resources.add(runnable); + } + + public void close() { + checkState(); + closed = true; + for (Runnable r : resources) { + r.run(); + } + } + } + + @Benchmark + public void confined_close() { + ConfinedScope scope = new ConfinedScope(); + try { // simulate TWR + scope.addCloseAction(dummy); + scope.close(); + } catch (RuntimeException ex) { + scope.close(); + throw ex; + } + } + + @Benchmark + public void confined_close_notry() { + ConfinedScope scope = new ConfinedScope(); + scope.addCloseAction(dummy); + scope.close(); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java index 83751d07eda26..d50b8d0900fa3 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/StrLenTest.java @@ -90,7 +90,7 @@ public int jni_strlen() throws Throwable { } @Benchmark - public int panama_strlen() throws Throwable { + public int panama_strlen_alloc() throws Throwable { try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocateFrom(str); return (int)STRLEN.invokeExact(segment); @@ -104,10 +104,9 @@ public int panama_strlen_ring() throws Throwable { @Benchmark public int panama_strlen_pool() throws Throwable { - Arena arena = pool.acquire(); - int l = (int) STRLEN.invokeExact(arena.allocateFrom(str)); - arena.close(); - return l; + try (Arena arena = pool.acquire()) { + return (int) STRLEN.invokeExact(arena.allocateFrom(str)); + } } @Benchmark diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ToCStringTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/ToCStringTest.java index 7194dec9b2320..8ed0f10e0be5c 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/ToCStringTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ToCStringTest.java @@ -82,10 +82,9 @@ public long jni_writeString() throws Throwable { @Benchmark public MemorySegment panama_writeString() throws Throwable { - Arena arena = Arena.ofConfined(); - MemorySegment segment = arena.allocateFrom(str); - arena.close(); - return segment; + try (Arena arena = Arena.ofConfined()) { + return arena.allocateFrom(str); + } } static native long writeString(String str);